first commit
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
version = "0.0.1.0"
|
||||
|
||||
# ===================================================
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import platform
|
||||
|
||||
path_app_root = os.path.dirname(
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
)
|
||||
path_data = os.path.join(path_app_root, "data")
|
||||
flag_system_loading = False
|
||||
|
||||
import traceback
|
||||
|
||||
# third - party
|
||||
from flask import (
|
||||
Flask,
|
||||
redirect,
|
||||
render_template,
|
||||
Response,
|
||||
request,
|
||||
jsonify,
|
||||
send_file,
|
||||
send_from_directory,
|
||||
abort,
|
||||
Markup,
|
||||
)
|
||||
from flask_socketio import SocketIO, emit
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_login import (
|
||||
LoginManager,
|
||||
login_user,
|
||||
logout_user,
|
||||
current_user,
|
||||
login_required,
|
||||
)
|
||||
|
||||
# gommi 공용
|
||||
from .init_args import args
|
||||
from .py_version_func import *
|
||||
from framework.logger import get_logger
|
||||
from framework.class_scheduler import Scheduler
|
||||
from .menu import init_menu, get_menu_map
|
||||
from .user import User
|
||||
from .init_etc import make_default_dir, config_initialize, check_api
|
||||
from .init_web import jinja_initialize
|
||||
|
||||
|
||||
# =================================================
|
||||
# App Start
|
||||
# =================================================
|
||||
|
||||
# 기본디렉토리 생성
|
||||
make_default_dir(path_data)
|
||||
|
||||
package_name = __name__.split(".")[0]
|
||||
print(package_name)
|
||||
|
||||
logger = get_logger(package_name)
|
||||
print(logger)
|
||||
|
||||
try:
|
||||
# pass
|
||||
# Global
|
||||
logger.debug("Path app root : %s", path_app_root)
|
||||
logger.debug("Path app data : %s", path_data)
|
||||
logger.debug("Platform : %s", platform.system())
|
||||
|
||||
app = Flask("gommi")
|
||||
|
||||
app.secret_key = os.urandom(24)
|
||||
# ------------------------------
|
||||
# sqlAlchemy
|
||||
# ------------------------------
|
||||
app.config[
|
||||
"SQLALCHEMY_DATABASE_URI"
|
||||
] = "sqlite:///data/db/gommi.db?check_same_thread=False"
|
||||
app.config["SQLALCHEMY_BINDS"] = {"gommi": "sqlite:///data/db/gommi.db"}
|
||||
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
|
||||
app.config["TEMPLATES_AUTO_RELOAD"] = True
|
||||
app.config["config"] = {}
|
||||
app.config["JSON_AS_ASCII"] = False
|
||||
|
||||
config_initialize("start")
|
||||
|
||||
db = SQLAlchemy(app, session_options={"autoflush": False})
|
||||
|
||||
scheduler = Scheduler(args)
|
||||
|
||||
if args is not None and args.use_gevent is False:
|
||||
socketio = SocketIO(
|
||||
app, cors_allowed_origins="*", async_mode="threading"
|
||||
)
|
||||
else:
|
||||
socketio = SocketIO(
|
||||
app, cors_allowed_origins="*"
|
||||
) # , async_mode='gevent')
|
||||
|
||||
from flask_cors import CORS
|
||||
|
||||
CORS(app)
|
||||
|
||||
# markdown extension
|
||||
from flaskext.markdown import Markdown
|
||||
|
||||
Markdown(app)
|
||||
|
||||
# login manager
|
||||
login_manager = LoginManager()
|
||||
login_manager.init_app(app)
|
||||
login_manager.login_view = "login"
|
||||
exit_code = -1
|
||||
|
||||
# 추후 삭제
|
||||
# USERS = {
|
||||
# "sjva" + version: User("sjva" + version, passwd_hash="sjva" + version),
|
||||
# }
|
||||
|
||||
# app route가 되어 있는데 import 해야지만 routing이 됨
|
||||
from .log_viewer import *
|
||||
|
||||
# from .manual import *
|
||||
|
||||
USERS = {
|
||||
"gommi": User("sjva", passwd_hash="sjva"),
|
||||
"sjva": User("sjva", passwd_hash="sjva"),
|
||||
}
|
||||
|
||||
from .init_celery import celery
|
||||
import framework.common.celery
|
||||
|
||||
# ========================================================================
|
||||
# system plugin
|
||||
# ========================================================================
|
||||
import system
|
||||
from system.model import ModelSetting as SystemModelSetting
|
||||
|
||||
# epg 없이 klive 만 있고 db 파일이 없을 때 아예 다른 모듈이 로딩안되는 문제 발생
|
||||
# klive에서 epg 칼럼을 참조해서 그러는것 같음. 방어코드이나 확인못함
|
||||
try:
|
||||
db.create_all()
|
||||
except Exception as exception:
|
||||
logger.error("CRITICAL db.create_all()!!!")
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
# 인증처리
|
||||
config_initialize("auth")
|
||||
|
||||
system.plugin_load()
|
||||
flag_system_loading = True # 로그레벨에서 사용. 필요한가??
|
||||
|
||||
# app blueprint 등록
|
||||
app.register_blueprint(system.blueprint)
|
||||
config_initialize("system_loading_after")
|
||||
|
||||
plugin_menu = []
|
||||
plugin_menu.append(system.menu)
|
||||
plugin_instance_list = {}
|
||||
|
||||
jinja_initialize(app)
|
||||
|
||||
# ------------------------------------------
|
||||
# plugins
|
||||
# ------------------------------------------
|
||||
system.LogicPlugin.custom_plugin_update()
|
||||
from .init_plugin import plugin_init
|
||||
|
||||
plugin_init()
|
||||
|
||||
logger.debug("#------------ plugin loading completed ------------")
|
||||
# ------------------------------------------
|
||||
# menu
|
||||
# ------------------------------------------
|
||||
init_menu(plugin_menu)
|
||||
system.SystemLogic.apply_menu_link()
|
||||
logger.debug("#-------- menu loading completed ------")
|
||||
|
||||
logger.debug("### init app.config['config]")
|
||||
app.config["config"]["port"] = 0
|
||||
if sys.argv[0] == "gommi.py" or sys.argv[0] == "gommi3.py":
|
||||
try:
|
||||
app.config["config"]["port"] = SystemModelSetting.get_int("port")
|
||||
if (
|
||||
app.config["config"]["port"] == 19999
|
||||
and app.config["config"]["running_type"] == "docker"
|
||||
and not os.path.exists("/usr/sbin/nginx")
|
||||
):
|
||||
SystemModelSetting.set("port", "7771")
|
||||
app.config["config"]["port"] = 7771
|
||||
except Exception:
|
||||
app.config["config"]["port"] = 7771
|
||||
|
||||
if args is not None:
|
||||
if args.port is not None:
|
||||
app.config["config"]["port"] = args.port
|
||||
app.config["config"]["repeat"] = args.repeat
|
||||
app.config["config"]["use_celery"] = args.use_celery
|
||||
if platform.system() == "Windows":
|
||||
app.config["config"]["use_celery"] = False
|
||||
app.config["config"]["use_gevent"] = args.use_gevent
|
||||
logger.debug("### config ###")
|
||||
logger.debug(json.dumps(app.config["config"], indent=4))
|
||||
|
||||
logger.debug("### LAST")
|
||||
logger.debug("### PORT:%s", app.config["config"]["port"])
|
||||
logger.debug("### Now you can access GOMMI by webbrowser!!")
|
||||
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
# 반드시 마지막에
|
||||
# import init_route
|
||||
from .init_route import *
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,188 @@
|
||||
import traceback
|
||||
from pytz import timezone
|
||||
import sys
|
||||
import threading
|
||||
from datetime import datetime, timedelta
|
||||
from random import randint
|
||||
import time
|
||||
|
||||
# third-party
|
||||
from apscheduler.jobstores.base import JobLookupError
|
||||
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
|
||||
|
||||
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
|
||||
jobstores = {"default": SQLAlchemyJobStore(url="sqlite:///data/db/gommi.db")}
|
||||
|
||||
excutors = {"default": ThreadPoolExecutor(20)}
|
||||
|
||||
job_defaults = {"coalesce": False, "max_instances": 1}
|
||||
|
||||
# gommi 공용
|
||||
from framework.logger import get_logger
|
||||
|
||||
# 패키지
|
||||
# ==========================================================================
|
||||
package_name = __name__.split(".")[0]
|
||||
logger = get_logger(package_name)
|
||||
|
||||
|
||||
class Scheduler(object):
|
||||
job_list = []
|
||||
first_run_check_thread = None
|
||||
|
||||
def __init__(self, args):
|
||||
# (jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc)
|
||||
# self.sched = GeventScheduler( executors=executors)
|
||||
try:
|
||||
if args is None or (args is not None and args.use_gevent):
|
||||
from apscheduler.schedulers.gevent import GeventScheduler
|
||||
|
||||
self.sched = GeventScheduler(timezone="Asia/Seoul")
|
||||
else:
|
||||
raise Exception("")
|
||||
except Exception:
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
|
||||
self.sched = BackgroundScheduler(timezone="Asia/Seoul")
|
||||
# jobstores=jobstores, 에러 pickle 직렬화 할수없다
|
||||
# self.sched.configure(executors=executors, job_defaults=job_defaults, timezone='Asia/Seoul')
|
||||
# self.sched.configure(executors=executors, timezone='Asia/Seoul')
|
||||
self.sched.start()
|
||||
logger.debug("SCHEDULER...")
|
||||
|
||||
def first_run_check_thread_function(self):
|
||||
logger.warning("XX first_run_check_thread_function")
|
||||
|
||||
try:
|
||||
# time.sleep(60)
|
||||
# for i in range(5):
|
||||
flag_exit = True
|
||||
for job_instance in self.job_list:
|
||||
if not job_instance.run:
|
||||
continue
|
||||
if (
|
||||
job_instance.count == 0
|
||||
and not job_instance.is_running
|
||||
and job_instance.is_interval
|
||||
):
|
||||
# if job_instance.count == 0 and not job_instance.is_running:
|
||||
job = self.sched.get_job(job_instance.job_id)
|
||||
if job is not None:
|
||||
logger.warning("job_instance : %s", job_instance.plugin)
|
||||
logger.warning("XX job re-sched:%s", job)
|
||||
flag_exit = False
|
||||
tmp = randint(1, 20)
|
||||
job.modify(
|
||||
next_run_time=datetime.now(timezone("Asia/Seoul"))
|
||||
+ timedelta(seconds=tmp)
|
||||
)
|
||||
# break
|
||||
else:
|
||||
pass
|
||||
if flag_exit:
|
||||
self.remove_job("scheduler_check")
|
||||
# time.sleep(30)
|
||||
logger.warning("first_run_check_thread_function end!!")
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
def get_job_list_info(self):
|
||||
|
||||
ret = []
|
||||
idx = 0
|
||||
job_list = self.sched.get_jobs()
|
||||
# logger.debug('len jobs %s %s', len(jobs), len(Scheduler.job_list))
|
||||
for j in job_list:
|
||||
idx += 1
|
||||
entity = {}
|
||||
entity["no"] = idx
|
||||
entity["id"] = j.id
|
||||
entity["next_run_time"] = j.next_run_time.strftime("%m-%d %H:%M:%S")
|
||||
remain = j.next_run_time - datetime.now(timezone("Asia/Seoul"))
|
||||
tmp = ""
|
||||
if remain.days > 0:
|
||||
tmp += "%s일 " % (remain.days)
|
||||
remain = remain.seconds
|
||||
if remain // 3600 > 0:
|
||||
tmp += "%s시간 " % (remain // 3600)
|
||||
remain = remain % 3600
|
||||
if remain // 60 > 0:
|
||||
tmp += "%s분 " % (remain // 60)
|
||||
tmp += "%s초" % (remain % 60)
|
||||
# entity['remain_time'] = (j.next_run_time - datetime.now(timezone('Asia/Seoul'))).seconds
|
||||
entity["remain_time"] = tmp
|
||||
job = self.get_job_instance(j.id)
|
||||
if job is not None:
|
||||
entity["count"] = job.count
|
||||
entity["plugin"] = job.plugin
|
||||
if job.is_cron:
|
||||
entity["interval"] = job.interval
|
||||
elif job.interval == 9999:
|
||||
entity["interval"] = "항상 실행"
|
||||
entity["remain_time"] = ""
|
||||
else:
|
||||
entity["interval"] = "%s분 %s초" % (
|
||||
job.interval,
|
||||
job.interval_seconds,
|
||||
)
|
||||
entity["is_running"] = job.is_running
|
||||
entity["description"] = job.description
|
||||
entity["running_timedelta"] = (
|
||||
job.running_timedelta.seconds
|
||||
if job.running_timedelta is not None
|
||||
else "-"
|
||||
)
|
||||
entity["make_time"] = job.make_time.strftime("%m-%d %H:%M:%S")
|
||||
entity["run"] = job.run
|
||||
else:
|
||||
entity["count"] = ""
|
||||
entity["plugin"] = ""
|
||||
entity["interval"] = ""
|
||||
entity["is_running"] = ""
|
||||
entity["description"] = ""
|
||||
entity["running_timedelta"] = ""
|
||||
entity["make_time"] = ""
|
||||
entity["run"] = True
|
||||
|
||||
ret.append(entity)
|
||||
return ret
|
||||
|
||||
def add_job_instance(self, job_instance, run=True):
|
||||
from framework import app
|
||||
|
||||
if app.config["config"]["run_by_real"] and app.config["config"]["auth_status"]:
|
||||
if not self.is_include(job_instance.job_id):
|
||||
job_instance.run = run
|
||||
Scheduler.job_list.append(job_instance)
|
||||
if job_instance.is_interval:
|
||||
self.sched.add_job(
|
||||
job_instance.job_function,
|
||||
"interval",
|
||||
minutes=job_instance.interval,
|
||||
seconds=job_instance.interval_seconds,
|
||||
id=job_instance.job_id,
|
||||
args=(None),
|
||||
)
|
||||
elif job_instance.is_cron:
|
||||
self.sched.add_job(
|
||||
job_instance.job_function,
|
||||
CronTrigger.from_crontab(job_instance.interval),
|
||||
id=job_instance.job_id,
|
||||
args=(None),
|
||||
)
|
||||
# self.sched.add_job(job_instance.job_function, 'interval', minutes=job_instance.interval,
|
||||
# id=job_instance.job_id, args=(None))
|
||||
job = self.sched.get_job(job_instance.job_id)
|
||||
if run and job_instance.is_interval:
|
||||
tmp = randint(5, 20)
|
||||
job.modify(
|
||||
next_run_time=datetime.now(timezone("Asia/Seoul"))
|
||||
+ timedelta(seconds=tmp)
|
||||
)
|
||||
|
||||
def is_include(self, job_id):
|
||||
job = self.sched.get_job(job_id)
|
||||
return job is not None
|
||||
@@ -0,0 +1 @@
|
||||
import os
|
||||
Binary file not shown.
Executable
+1
@@ -0,0 +1 @@
|
||||
from .shutil_task import move, copytree, copy, rmtree, move_exist_remove
|
||||
Binary file not shown.
Binary file not shown.
Executable
+156
@@ -0,0 +1,156 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import traceback
|
||||
import shutil
|
||||
|
||||
from framework import app, celery, logger
|
||||
|
||||
# run_in_celery=True 이미 celery안에서 실행된다. 바로 콜한다.
|
||||
def move(source_path, target_path, run_in_celery=False):
|
||||
try:
|
||||
if app.config['config']['use_celery'] and run_in_celery == False:
|
||||
result = _move_task.apply_async((source_path, target_path))
|
||||
return result.get()
|
||||
else:
|
||||
return _move_task(source_path, target_path)
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return _move_task(source_path, target_path)
|
||||
|
||||
|
||||
@celery.task
|
||||
def _move_task(source_path, target_path):
|
||||
try:
|
||||
logger.debug('_move_task:%s %s', source_path, target_path)
|
||||
shutil.move(source_path, target_path)
|
||||
logger.debug('_move_task end')
|
||||
return True
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
def move_exist_remove(source_path, target_path, run_in_celery=False):
|
||||
try:
|
||||
if app.config['config']['use_celery'] and run_in_celery == False:
|
||||
result = _move_exist_remove_task.apply_async((source_path, target_path))
|
||||
return result.get()
|
||||
else:
|
||||
return _move_exist_remove_task(source_path, target_path)
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return _move_exist_remove_task(source_path, target_path)
|
||||
|
||||
@celery.task
|
||||
def _move_exist_remove_task(source_path, target_path):
|
||||
try:
|
||||
target_file_path = os.path.join(target_path, os.path.basename(source_path))
|
||||
if os.path.exists(target_file_path):
|
||||
os.remove(source_path)
|
||||
return True
|
||||
logger.debug('_move_exist_remove:%s %s', source_path, target_path)
|
||||
shutil.move(source_path, target_path)
|
||||
logger.debug('_move_exist_remove end')
|
||||
return True
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
|
||||
def copytree(source_path, target_path):
|
||||
try:
|
||||
if app.config['config']['use_celery']:
|
||||
result = _copytree_task.apply_async((source_path, target_path))
|
||||
return result.get()
|
||||
else:
|
||||
return _copytree_task(source_path, target_path)
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return _copytree_task(source_path, target_path)
|
||||
|
||||
@celery.task
|
||||
def _copytree_task(source_path, target_path):
|
||||
try:
|
||||
shutil.copytree(source_path, target_path)
|
||||
return True
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
# copy
|
||||
def copy(source_path, target_path):
|
||||
try:
|
||||
if app.config['config']['use_celery']:
|
||||
result = _copy_task.apply_async((source_path, target_path))
|
||||
return result.get()
|
||||
else:
|
||||
return _copy_task(source_path, target_path)
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return _copy_task(source_path, target_path)
|
||||
|
||||
@celery.task
|
||||
def _copy_task(source_path, target_path):
|
||||
try:
|
||||
shutil.copy(source_path, target_path)
|
||||
return True
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
# rmtree
|
||||
def rmtree(source_path):
|
||||
try:
|
||||
if app.config['config']['use_celery']:
|
||||
result = _rmtree_task.apply_async((source_path,))
|
||||
return result.get()
|
||||
else:
|
||||
return _rmtree_task(source_path)
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return _rmtree_task(source_path)
|
||||
|
||||
@celery.task
|
||||
def _rmtree_task(source_path):
|
||||
try:
|
||||
shutil.rmtree(source_path)
|
||||
return True
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
|
||||
def remove(remove_path):
|
||||
try:
|
||||
logger.debug('CELERY os.remove start : %s', remove_path)
|
||||
if app.config['config']['use_celery']:
|
||||
result = _remove_task.apply_async((remove_path,))
|
||||
return result.get()
|
||||
else:
|
||||
return _remove_task(remove_path)
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return _remove_task(remove_path)
|
||||
finally:
|
||||
logger.debug('CELERY os.remove end : %s', remove_path)
|
||||
|
||||
|
||||
@celery.task
|
||||
def _remove_task(remove_path):
|
||||
try:
|
||||
os.remove(remove_path)
|
||||
return True
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
@@ -0,0 +1 @@
|
||||
from .env import *
|
||||
@@ -0,0 +1,72 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import io
|
||||
import traceback
|
||||
import platform
|
||||
|
||||
from framework import app, logger
|
||||
|
||||
|
||||
def is_arm():
|
||||
try:
|
||||
ret = False
|
||||
import platform
|
||||
if platform.system() == 'Linux':
|
||||
if platform.platform().find('86') == -1 and platform.platform().find('64') == -1:
|
||||
ret = True
|
||||
if platform.platform().find('arch') != -1:
|
||||
ret = True
|
||||
if platform.platform().find('arm') != -1:
|
||||
ret = True
|
||||
return ret
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
def is_native():
|
||||
try:
|
||||
return (app.config['config']['running_type'] == 'native')
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
def is_termux():
|
||||
try:
|
||||
return (app.config['config']['running_type'] == 'termux')
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
def is_windows():
|
||||
try:
|
||||
return (app.config['config']['running_type'] == 'native' and platform.system() == 'Windows')
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
def is_mac():
|
||||
try:
|
||||
return (app.config['config']['running_type'] == 'native' and platform.system() == 'Darwin')
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
def is_docker():
|
||||
try:
|
||||
return (app.config['config']['running_type'] == 'docker')
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
def is_linux():
|
||||
try:
|
||||
# return (app.config['config']['running_type'] == 'native' and platform.system() == 'Linux')
|
||||
return (platform.system() == 'Linux')
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
@@ -0,0 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#########################################################
|
||||
import sys
|
||||
|
||||
args = None
|
||||
|
||||
|
||||
def process_args():
|
||||
from gommi import process_args
|
||||
|
||||
args = process_args()
|
||||
return args
|
||||
|
||||
|
||||
if sys.argv[0] == "gommi.py":
|
||||
args = process_args()
|
||||
else:
|
||||
args = None
|
||||
@@ -0,0 +1,60 @@
|
||||
import os, sys, platform
|
||||
|
||||
from framework import app, logger, args
|
||||
|
||||
try:
|
||||
from celery import Celery
|
||||
|
||||
# if app.config['config']['use_celery'] == False: # 변수 할당 전
|
||||
if (
|
||||
args is not None and args.use_gevent == False
|
||||
) or platform.system() == "Windows":
|
||||
raise Exception("no celery")
|
||||
try:
|
||||
redis_port = os.environ["REDIS_PORT"]
|
||||
except:
|
||||
redis_port = "6379"
|
||||
|
||||
app.config["CELERY_BROKER_URL"] = "redis://localhost:%s/0" % redis_port
|
||||
app.config["CELERY_RESULT_BACKEND"] = "redis://localhost:%s/0" % redis_port
|
||||
celery = Celery(
|
||||
app.name,
|
||||
broker=app.config["CELERY_BROKER_URL"],
|
||||
backend=app.config["CELERY_RESULT_BACKEND"],
|
||||
)
|
||||
# celery.conf.update(app.config)
|
||||
celery.conf["CELERY_ENABLE_UTC"] = False
|
||||
# celery.conf['CELERY_TIMEZONE'] = 'Asia/Seoul'
|
||||
celery.conf.update(
|
||||
task_serializer="pickle",
|
||||
result_serializer="pickle",
|
||||
accept_content=["pickle"],
|
||||
timezone="Asia/Seoul",
|
||||
)
|
||||
|
||||
except:
|
||||
"""
|
||||
from functools import wraps
|
||||
class DummyCelery:
|
||||
def task(self, original_function):
|
||||
@wraps(original_function)
|
||||
def wrapper_function(*args, **kwargs): #1
|
||||
return original_function(*args, **kwargs) #2
|
||||
return wrapper_function
|
||||
|
||||
celery = DummyCelery()
|
||||
"""
|
||||
|
||||
def ffff():
|
||||
pass
|
||||
|
||||
class celery(object):
|
||||
class task(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
if len(args) > 0:
|
||||
self.f = args[0]
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
if len(args) > 0 and type(args[0]) == type(ffff):
|
||||
return args[0]
|
||||
self.f(*args, **kwargs)
|
||||
@@ -0,0 +1,129 @@
|
||||
import os, traceback, sys # noqa F401
|
||||
|
||||
|
||||
def check_api(original_function):
|
||||
pass
|
||||
|
||||
|
||||
def make_default_dir(path_data):
|
||||
try:
|
||||
if not os.path.exists(path_data):
|
||||
os.mkdir(path_data)
|
||||
tmp = os.path.join(path_data, "tmp")
|
||||
try:
|
||||
import shutil
|
||||
|
||||
if os.path.exists(tmp):
|
||||
shutil.rmtree(tmp)
|
||||
except Exception:
|
||||
pass
|
||||
sub = ["db", "log", "download", "command", "custom", "output", "tmp"]
|
||||
for item in sub:
|
||||
tmp = os.path.join(path_data, item)
|
||||
if not os.path.exists(tmp):
|
||||
os.mkdir(tmp)
|
||||
except Exception as exception:
|
||||
print("Exception:%s", exception)
|
||||
print(traceback.format_exc())
|
||||
|
||||
|
||||
def config_initialize(action):
|
||||
from . import logger, app
|
||||
|
||||
if action == "start":
|
||||
logger.debug("=========== action: strart ===========================")
|
||||
# sjva server 초기에 필요한 정보 불러오는 루틴
|
||||
init_define()
|
||||
app.config["config"]["run_by_real"] = (
|
||||
True
|
||||
if sys.argv[0] == "sjva.py" or sys.argv[0] == "sjva3.py"
|
||||
else False
|
||||
)
|
||||
# app.config['config']['run_by_migration'] = True if sys.argv[-2] == 'db' else False
|
||||
app.config["config"]["run_by_worker"] = (
|
||||
True if sys.argv[0].find("celery") != -1 else False
|
||||
)
|
||||
app.config["config"]["run_by_init_db"] = (
|
||||
True if sys.argv[-1] == "init_db" else False
|
||||
)
|
||||
if sys.version_info[0] == 2:
|
||||
app.config["config"]["pip"] = "pip"
|
||||
app.config["config"]["is_py2"] = True
|
||||
app.config["config"]["is_py3"] = False
|
||||
else:
|
||||
app.config["config"]["is_py2"] = False
|
||||
app.config["config"]["is_py3"] = True
|
||||
app.config["config"]["pip"] = "pip3"
|
||||
|
||||
app.config["config"]["is_debug"] = False
|
||||
app.config["config"]["repeat"] = -1
|
||||
|
||||
if app.config["config"]["run_by_real"]:
|
||||
try:
|
||||
if len(sys.argv) > 2:
|
||||
app.config["config"]["repeat"] = int(sys.argv[2])
|
||||
except:
|
||||
app.config["config"]["repeat"] = 0
|
||||
if len(sys.argv) > 3:
|
||||
try:
|
||||
app.config["config"]["is_debug"] = sys.argv[-1] == "debug"
|
||||
except:
|
||||
app.config["config"]["is_debug"] = False
|
||||
app.config["config"]["use_celery"] = True
|
||||
for tmp in sys.argv:
|
||||
if tmp == "no_celery":
|
||||
app.config["config"]["use_celery"] = False
|
||||
break
|
||||
# logger.debug('use_celery : %s', app.config['config']['use_celery'])
|
||||
logger.debug("======================================")
|
||||
elif action == "auth":
|
||||
from system.logic_auth import SystemLogicAuth
|
||||
|
||||
# 2021-08-11 로딩시 인증 실행 여부
|
||||
# SystemLogicAuth.do_auth()
|
||||
|
||||
tmp = SystemLogicAuth.get_auth_status()
|
||||
app.config["config"]["auth_status"] = tmp["ret"]
|
||||
app.config["config"]["auth_desc"] = tmp["desc"]
|
||||
app.config["config"]["level"] = tmp["level"]
|
||||
app.config["config"]["point"] = tmp["point"]
|
||||
# app.config['config']['auth_status'] = True
|
||||
elif action == "system_loading_after":
|
||||
from . import SystemModelSetting
|
||||
|
||||
try:
|
||||
app.config["config"]["is_server"] = (
|
||||
SystemModelSetting.get("ddns")
|
||||
== app.config["DEFINE"]["MAIN_SERVER_URL"]
|
||||
)
|
||||
except Exception:
|
||||
app.config["config"]["is_server"] = False
|
||||
|
||||
if (
|
||||
app.config["config"]["is_server"]
|
||||
or app.config["config"]["is_debug"]
|
||||
):
|
||||
app.config["config"]["server"] = True
|
||||
app.config["config"]["is_admin"] = True
|
||||
else:
|
||||
app.config["config"]["server"] = False
|
||||
app.config["config"]["is_admin"] = False
|
||||
|
||||
app.config["config"]["running_type"] = "native"
|
||||
# print(f"os.environ:: {os.environ}")
|
||||
|
||||
if "SJVA_RUNNING_TYPE" in os.environ:
|
||||
app.config["config"]["running_type"] = os.environ[
|
||||
"SJVA_RUNNING_TYPE"
|
||||
]
|
||||
else:
|
||||
import platform
|
||||
|
||||
if platform.system() == "Windows":
|
||||
app.config["config"]["running_type"] = "windows"
|
||||
|
||||
|
||||
def init_define():
|
||||
from . import logger, app
|
||||
|
||||
app.config["DEFINE"] = {}
|
||||
@@ -0,0 +1,306 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ==================================================================
|
||||
# python
|
||||
# ==================================================================
|
||||
import os, sys, traceback, threading, platform
|
||||
from framework import app, db, logger, plugin_instance_list, plugin_menu
|
||||
import system
|
||||
|
||||
# ==================================================================
|
||||
# plugin
|
||||
# ==================================================================
|
||||
|
||||
|
||||
def is_include_menu(plugin_name):
|
||||
try:
|
||||
if plugin_name not in [
|
||||
"daum_tv",
|
||||
"ffmpeg",
|
||||
"fileprocess_movie",
|
||||
"gdrive_scan",
|
||||
"ktv",
|
||||
"plex",
|
||||
"rclone",
|
||||
]:
|
||||
return True
|
||||
if (
|
||||
system.SystemLogic.get_setting_value("use_plugin_%s" % plugin_name)
|
||||
== "True"
|
||||
):
|
||||
return True
|
||||
elif (
|
||||
system.SystemLogic.get_setting_value("use_plugin_%s" % plugin_name)
|
||||
== "False"
|
||||
):
|
||||
return False
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return True
|
||||
|
||||
|
||||
def plugin_init():
|
||||
try:
|
||||
if not app.config["config"]["auth_status"]:
|
||||
return
|
||||
import inspect
|
||||
|
||||
plugin_path = os.path.join(
|
||||
os.path.dirname(
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
),
|
||||
"plugin",
|
||||
)
|
||||
sys.path.insert(0, plugin_path)
|
||||
from . import SystemModelSetting
|
||||
|
||||
"""
|
||||
try:
|
||||
from . import SystemModelSetting
|
||||
plugins = ['command', 'mod']
|
||||
for plugin in os.listdir(plugin_path):
|
||||
plugins.append(plugin)
|
||||
except:
|
||||
plugins = os.listdir(plugin_path)
|
||||
"""
|
||||
plugins = os.listdir(plugin_path)
|
||||
|
||||
pass_include = []
|
||||
except_plugin_list = []
|
||||
|
||||
# 2019-07-17
|
||||
try:
|
||||
plugin_path = os.path.join(
|
||||
os.path.dirname(
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
),
|
||||
"data",
|
||||
"custom",
|
||||
)
|
||||
sys.path.insert(1, plugin_path)
|
||||
tmps = os.listdir(plugin_path)
|
||||
add_plugin_list = []
|
||||
for t in tmps:
|
||||
if not t.startswith("_") and os.path.isdir(
|
||||
os.path.join(plugin_path, t)
|
||||
):
|
||||
add_plugin_list.append(t)
|
||||
plugins = plugins + add_plugin_list
|
||||
pass_include = pass_include + add_plugin_list
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
# 2018-09-04
|
||||
try:
|
||||
plugin_path = SystemModelSetting.get("plugin_dev_path")
|
||||
if plugin_path != "":
|
||||
if os.path.exists(plugin_path):
|
||||
sys.path.insert(0, plugin_path)
|
||||
tmps = os.listdir(plugin_path)
|
||||
add_plugin_list = []
|
||||
for t in tmps:
|
||||
if not t.startswith("_") and os.path.isdir(
|
||||
os.path.join(plugin_path, t)
|
||||
):
|
||||
add_plugin_list.append(t)
|
||||
if app.config["config"]["level"] < 4:
|
||||
break
|
||||
plugins = plugins + add_plugin_list
|
||||
pass_include = pass_include + add_plugin_list
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
plugins = sorted(plugins)
|
||||
|
||||
logger.debug(plugins)
|
||||
for plugin_name in plugins:
|
||||
# logger.debug(len(system.LogicPlugin.current_loading_plugin_list))
|
||||
if plugin_name.startswith("_"):
|
||||
continue
|
||||
if plugin_name == "terminal" and platform.system() == "Windows":
|
||||
continue
|
||||
if plugin_name in except_plugin_list:
|
||||
logger.debug("Except plugin : %s" % plugin_menu)
|
||||
continue
|
||||
logger.debug(f"[+] PLUGIN LOADING Start.. [{plugin_name}]")
|
||||
try:
|
||||
mod = __import__("%s" % (plugin_name), fromlist=[])
|
||||
mod_plugin_info = None
|
||||
# 2021-12-31
|
||||
if (
|
||||
plugin_name
|
||||
not in system.LogicPlugin.current_loading_plugin_list
|
||||
):
|
||||
system.LogicPlugin.current_loading_plugin_list[
|
||||
plugin_name
|
||||
] = {"status": "loading"}
|
||||
|
||||
try:
|
||||
mod_plugin_info = getattr(mod, "plugin_info")
|
||||
if (
|
||||
"category" not in mod_plugin_info
|
||||
and "category_name" in mod_plugin_info
|
||||
):
|
||||
mod_plugin_info["category"] = mod_plugin_info[
|
||||
"category_name"
|
||||
]
|
||||
if "policy_point" in mod_plugin_info:
|
||||
if (
|
||||
mod_plugin_info["policy_point"]
|
||||
> app.config["config"]["point"]
|
||||
):
|
||||
system.LogicPlugin.current_loading_plugin_list[
|
||||
plugin_name
|
||||
]["status"] = "violation_policy_point"
|
||||
continue
|
||||
if "policy_level" in mod_plugin_info:
|
||||
if (
|
||||
mod_plugin_info["policy_level"]
|
||||
> app.config["config"]["level"]
|
||||
):
|
||||
system.LogicPlugin.current_loading_plugin_list[
|
||||
plugin_name
|
||||
]["status"] = "violation_policy_level"
|
||||
continue
|
||||
if (
|
||||
"category" in mod_plugin_info
|
||||
and mod_plugin_info["category"] == "beta"
|
||||
):
|
||||
if SystemModelSetting.get_bool("use_beta") == False:
|
||||
system.LogicPlugin.current_loading_plugin_list[
|
||||
plugin_name
|
||||
]["status"] = "violation_beta"
|
||||
continue
|
||||
except Exception as exception:
|
||||
# logger.error('Exception:%s', exception)
|
||||
# logger.error(traceback.format_exc())
|
||||
logger.debug(f"[!] PLUGIN_INFO not exist : [{plugin_name}]")
|
||||
|
||||
try:
|
||||
mod_blue_print = getattr(mod, "blueprint")
|
||||
if mod_blue_print:
|
||||
if plugin_name in pass_include or is_include_menu(
|
||||
plugin_name
|
||||
):
|
||||
app.register_blueprint(mod_blue_print)
|
||||
except Exception as exception:
|
||||
# logger.error('Exception:%s', exception)
|
||||
# logger.error(traceback.format_exc())
|
||||
logger.debug(f"[!] BLUEPRINT not exist : [{plugin_name}]")
|
||||
plugin_instance_list[plugin_name] = mod
|
||||
system.LogicPlugin.current_loading_plugin_list[plugin_name][
|
||||
"status"
|
||||
] = "success"
|
||||
system.LogicPlugin.current_loading_plugin_list[plugin_name][
|
||||
"info"
|
||||
] = mod_plugin_info
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
logger.debug("no blueprint")
|
||||
|
||||
# from tool_base import d
|
||||
# logger.error(d(system.LogicPlugin.current_loading_plugin_list))
|
||||
# 2021-07-01 모듈에 있는 DB 테이블 생성이 안되는 문제
|
||||
# 기존 구조 : db.create_all() => 모듈 plugin_load => celery task 등록 후 리턴
|
||||
# 변경 구조 : 모듈 plugin_load => db.create_all() => celery인 경우 리턴
|
||||
|
||||
# plugin_load 를 해야 하위 로직에 있는 DB가 로딩된다.
|
||||
# plugin_load 에 db는 사용하는 코드가 있으면 안된다. (테이블도 없을 때 에러발생)
|
||||
try:
|
||||
# logger.warning('module plugin_load in celery ')
|
||||
plugin_instance_list["mod"].plugin_load()
|
||||
except Exception as exception:
|
||||
logger.debug("mod plugin_load error!!")
|
||||
# logger.error('Exception:%s', exception)
|
||||
# logger.error(traceback.format_exc())
|
||||
|
||||
# import가 끝나면 DB를 만든다.
|
||||
# 플러그인 로드시 DB 초기화를 할 수 있다.
|
||||
if not app.config["config"]["run_by_worker"]:
|
||||
try:
|
||||
db.create_all()
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
logger.debug("db.create_all error")
|
||||
|
||||
if not app.config["config"]["run_by_real"]:
|
||||
# 2021-06-03
|
||||
# 모듈의 로직에 있는 celery 함수는 등록해주어야한다.
|
||||
# try:
|
||||
# logger.warning('module plugin_load in celery ')
|
||||
# plugin_instance_list['mod'].plugin_load()
|
||||
# except Exception as exception:
|
||||
# logger.error('module plugin_load error')
|
||||
# logger.error('Exception:%s', exception)
|
||||
# logger.error(traceback.format_exc())
|
||||
# 2021-07-01
|
||||
# db때문에 위에서 로딩함.
|
||||
return
|
||||
|
||||
for key, mod in plugin_instance_list.items():
|
||||
try:
|
||||
mod_plugin_load = getattr(mod, "plugin_load")
|
||||
if mod_plugin_load and (
|
||||
key in pass_include or is_include_menu(key)
|
||||
):
|
||||
|
||||
def func(mod, key):
|
||||
try:
|
||||
logger.debug(
|
||||
f"[!] plugin_load threading start : [{key}]"
|
||||
)
|
||||
mod.plugin_load()
|
||||
logger.debug(
|
||||
f"[!] plugin_load threading end : [{key}]"
|
||||
)
|
||||
except Exception as exception:
|
||||
logger.error("### plugin_load exception : %s", key)
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
# mod는 위에서 로딩
|
||||
if key != "mod":
|
||||
t = threading.Thread(target=func, args=(mod, key))
|
||||
t.setDaemon(True)
|
||||
t.start()
|
||||
# if key == 'mod':
|
||||
# t.join()
|
||||
except Exception as exception:
|
||||
logger.debug(f"[!] PLUGIN_LOAD function not exist : [{key}]")
|
||||
# logger.error('Exception:%s', exception)
|
||||
# logger.error(traceback.format_exc())
|
||||
# logger.debug('no init_scheduler')
|
||||
try:
|
||||
mod_menu = getattr(mod, "menu")
|
||||
if mod_menu and (key in pass_include or is_include_menu(key)):
|
||||
plugin_menu.append(mod_menu)
|
||||
except Exception as exception:
|
||||
logger.debug("no menu")
|
||||
logger.debug(
|
||||
"### plugin_load threading all start.. : %s ",
|
||||
len(plugin_instance_list),
|
||||
)
|
||||
# 모든 모듈을 로드한 이후에 app 등록, table 생성, start
|
||||
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
def plugin_unload():
|
||||
for key, mod in plugin_instance_list.items():
|
||||
try:
|
||||
# if plugin_name == 'rss':
|
||||
# continue
|
||||
mod_plugin_unload = getattr(mod, "plugin_unload")
|
||||
if mod_plugin_unload:
|
||||
mod.plugin_unload()
|
||||
except Exception as exception:
|
||||
logger.error("module:%s", key)
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
system.plugin_unload()
|
||||
@@ -0,0 +1,97 @@
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
import json
|
||||
import traceback
|
||||
|
||||
# third-party
|
||||
from flask import (
|
||||
redirect,
|
||||
render_template,
|
||||
Response,
|
||||
request,
|
||||
jsonify,
|
||||
send_from_directory,
|
||||
)
|
||||
from flask_login import login_user, logout_user, current_user, login_required
|
||||
|
||||
# sjva 공용
|
||||
from framework import (
|
||||
app,
|
||||
db,
|
||||
version,
|
||||
USERS,
|
||||
login_manager,
|
||||
logger,
|
||||
path_data,
|
||||
check_api,
|
||||
)
|
||||
import system
|
||||
|
||||
|
||||
@app.route("/login", methods=["GET", "POST"])
|
||||
def login():
|
||||
if request.method == "POST":
|
||||
username = request.form["username"]
|
||||
password = request.form["password"]
|
||||
remember = request.form["remember"] == "True"
|
||||
if username not in USERS:
|
||||
return jsonify("no_id")
|
||||
elif not USERS[username].can_login(password):
|
||||
return jsonify("wrong_password")
|
||||
else:
|
||||
USERS[username].authenticated = True
|
||||
login_user(USERS[username], remember=remember)
|
||||
return jsonify("redirect")
|
||||
else:
|
||||
if (
|
||||
db.session.query(system.ModelSetting)
|
||||
.filter_by(key="use_login")
|
||||
.first()
|
||||
.value
|
||||
== "False"
|
||||
):
|
||||
username = (
|
||||
db.session.query(system.ModelSetting)
|
||||
.filter_by(key="id")
|
||||
.first()
|
||||
.value
|
||||
)
|
||||
logger.debug(f"username:: {username}")
|
||||
logger.debug(f"USERS:: {USERS}")
|
||||
USERS[username].authenticated = True
|
||||
login_user(USERS[username], remember=True)
|
||||
# current_user = USERS[username]
|
||||
return redirect(request.args.get("next"))
|
||||
|
||||
return render_template("login.html", next=request.args.get("next"))
|
||||
|
||||
|
||||
@app.errorhandler(401)
|
||||
def custom_401(error):
|
||||
|
||||
# return Response('<Why access is denied string goes here...>', 401, {'WWW-Authenticate':'Basic realm="Login Required"'})
|
||||
return "login_required"
|
||||
|
||||
|
||||
# important!!
|
||||
@login_manager.user_loader
|
||||
def user_loader(user_id):
|
||||
return USERS[user_id]
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# API
|
||||
# ============================================================================
|
||||
@app.route("/")
|
||||
@app.route("/None")
|
||||
@app.route("/home")
|
||||
def home():
|
||||
logger.warning(request.host_url)
|
||||
return redirect("/system/home")
|
||||
|
||||
|
||||
@app.route("/version")
|
||||
def get_version():
|
||||
# return jsonify(version)
|
||||
return version
|
||||
@@ -0,0 +1,118 @@
|
||||
import re
|
||||
from flask_login import current_user
|
||||
|
||||
|
||||
def get_menu(full_query):
|
||||
match = re.compile(r"\/(?P<menu>.*?)\/manual\/(?P<sub2>.*?)($|\?)").match(
|
||||
full_query
|
||||
)
|
||||
if match:
|
||||
return match.group("menu"), "manual", match.group("sub2")
|
||||
|
||||
match = re.compile(
|
||||
r"\/(?P<menu>.*?)\/(?P<sub>.*?)\/(?P<sub2>.*?)($|\/|\?)"
|
||||
).match(full_query)
|
||||
if match:
|
||||
return match.group("menu"), match.group("sub"), match.group("sub2")
|
||||
|
||||
match = re.compile(r"\/(?P<menu>.*?)\/(?P<sub>.*?)($|\/|\?)").match(
|
||||
full_query
|
||||
)
|
||||
if match:
|
||||
return match.group("menu"), match.group("sub"), None
|
||||
|
||||
match = re.compile(r"\/(?P<menu>.*?)($|\/|\?)").match(full_query)
|
||||
if match:
|
||||
return match.group("menu"), None, None
|
||||
return "home", None, None
|
||||
|
||||
|
||||
def get_theme():
|
||||
|
||||
theme_list = {
|
||||
"Default": 56,
|
||||
"Cerulean": 56,
|
||||
"Cosmo": 54,
|
||||
"Cyborg": 54,
|
||||
"Darkly": 70,
|
||||
"Flatly": 70,
|
||||
"Journal": 56,
|
||||
"Litera": 57,
|
||||
"Lumen": 56,
|
||||
"Lux": 88,
|
||||
"Materia": 80,
|
||||
"Minty": 56,
|
||||
"Morph": 56,
|
||||
"Pulse": 75,
|
||||
"Quartz": 92,
|
||||
"Sandstone": 53,
|
||||
"Simplex": 67,
|
||||
"Sketchy": 56,
|
||||
"Slate": 53,
|
||||
"Solar": 56,
|
||||
"Spacelab": 58,
|
||||
"Superhero": 48,
|
||||
"United": 56,
|
||||
"Vapor": 56,
|
||||
"Yeti": 54,
|
||||
"Zephyr": 68,
|
||||
}
|
||||
from system.model import ModelSetting as SystemModelSetting
|
||||
|
||||
theme = SystemModelSetting.get("theme")
|
||||
return [theme, theme_list[theme]]
|
||||
|
||||
|
||||
def get_login_status():
|
||||
if current_user is None:
|
||||
return False
|
||||
return current_user.is_authenticated
|
||||
|
||||
|
||||
def get_web_title():
|
||||
try:
|
||||
from system.model import ModelSetting as SystemModelSetting
|
||||
|
||||
return SystemModelSetting.get("web_title")
|
||||
except Exception:
|
||||
return "GOMMI Assitant"
|
||||
|
||||
|
||||
def show_menu():
|
||||
from flask import request
|
||||
from system.model import ModelSetting as SystemModelSetting
|
||||
|
||||
if SystemModelSetting.get_bool("hide_menu"):
|
||||
if request.full_path.find("/login") != -1:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def is_https():
|
||||
from system.model import ModelSetting as SystemModelSetting
|
||||
|
||||
return SystemModelSetting.get("ddns").find("https://") != -1
|
||||
|
||||
|
||||
def jinja_initialize(app):
|
||||
from .menu import get_menu_map, get_plugin_menu
|
||||
|
||||
app.jinja_env.globals.update(get_menu=get_menu)
|
||||
app.jinja_env.globals.update(get_theme=get_theme)
|
||||
app.jinja_env.globals.update(get_menu_map=get_menu_map)
|
||||
app.jinja_env.globals.update(get_login_status=get_login_status)
|
||||
app.jinja_env.globals.update(get_web_title=get_web_title)
|
||||
app.jinja_env.globals.update(get_plugin_menu=get_plugin_menu)
|
||||
app.jinja_env.globals.update(show_menu=show_menu)
|
||||
app.jinja_env.globals.update(is_https=is_https)
|
||||
app.jinja_env.filters["get_menu"] = get_menu
|
||||
app.jinja_env.filters["get_theme"] = get_theme
|
||||
app.jinja_env.filters["get_menu_map"] = get_menu_map
|
||||
app.jinja_env.filters["get_login_status"] = get_login_status
|
||||
app.jinja_env.filters["get_web_title"] = get_web_title
|
||||
app.jinja_env.filters["get_plugin_menu"] = get_plugin_menu
|
||||
app.jinja_env.filters["show_menu"] = show_menu
|
||||
app.jinja_env.filters["is_https"] = is_https
|
||||
app.jinja_env.auto_reload = True
|
||||
|
||||
app.jinja_env.add_extension("jinja2.ext.loopcontrols")
|
||||
@@ -0,0 +1,121 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#########################################################
|
||||
# python
|
||||
import traceback
|
||||
import threading
|
||||
|
||||
from datetime import datetime
|
||||
from pytz import timezone
|
||||
from random import randint
|
||||
|
||||
# third-party
|
||||
|
||||
# sjva 공용
|
||||
from framework import scheduler, app
|
||||
from framework.logger import get_logger
|
||||
|
||||
# 패키지
|
||||
|
||||
# 로그
|
||||
package_name = __name__.split(".")[0]
|
||||
logger = get_logger(package_name)
|
||||
#########################################################
|
||||
|
||||
|
||||
def multiprocessing_target(*a, **b):
|
||||
job_id = a[0]
|
||||
job = scheduler.get_job_instance(job_id)
|
||||
if job.args is None:
|
||||
job.target_function()
|
||||
else:
|
||||
job.target_function(job.args)
|
||||
|
||||
|
||||
class Job(object):
|
||||
def __init__(
|
||||
self,
|
||||
plugin,
|
||||
job_id,
|
||||
interval,
|
||||
target_function,
|
||||
description,
|
||||
can_remove_by_framework,
|
||||
args=None,
|
||||
):
|
||||
self.plugin = plugin
|
||||
self.job_id = job_id
|
||||
self.interval = "%s" % interval
|
||||
self.interval_seconds = randint(1, 59)
|
||||
self.target_function = target_function
|
||||
self.description = description
|
||||
self.can_remove_by_framework = can_remove_by_framework
|
||||
self.is_running = False
|
||||
self.thread = None
|
||||
self.start_time = None
|
||||
self.end_time = None
|
||||
self.running_timedelta = None
|
||||
self.status = None
|
||||
self.count = 0
|
||||
self.make_time = datetime.now(timezone("Asia/Seoul"))
|
||||
if len(self.interval.strip().split(" ")) == 5:
|
||||
self.is_cron = True
|
||||
self.is_interval = False
|
||||
else:
|
||||
self.is_cron = False
|
||||
self.is_interval = True
|
||||
if self.is_interval:
|
||||
if app.config["config"]["is_py2"]:
|
||||
# if isinstance(self.interval, unicode) or isinstance(self.interval, str):
|
||||
# self.interval = int(self.interval)
|
||||
if type(self.interval) == type("") or type(
|
||||
self.interval
|
||||
) == type(""):
|
||||
self.interval = int(self.interval)
|
||||
else:
|
||||
if isinstance(self.interval, str):
|
||||
self.interval = int(self.interval)
|
||||
|
||||
self.args = args
|
||||
self.run = True
|
||||
|
||||
def job_function(self):
|
||||
try:
|
||||
# if self.count > 1:
|
||||
# logger.debug(hm.check('JOB START %s' % self.job_id))
|
||||
# logger.debug(hm.getHeap())
|
||||
self.is_running = True
|
||||
self.start_time = datetime.now(timezone("Asia/Seoul"))
|
||||
# import gipc
|
||||
# from multiprocessing import Process
|
||||
if self.args is None:
|
||||
self.thread = threading.Thread(
|
||||
target=self.target_function, args=()
|
||||
)
|
||||
# self.thread = Process(target=multiprocessing_target, args=(self.job_id,))
|
||||
# self.thread = gipc.start_process(target=multiprocessing_target, args=(self.job_id,), daemon=True)
|
||||
# self.target_function()
|
||||
else:
|
||||
self.thread = threading.Thread(
|
||||
target=self.target_function, args=(self.args,)
|
||||
)
|
||||
# self.thread = Process(target=multiprocessing_target, args=(self.job_id,))
|
||||
# self.thread = gipc.start_process(target=multiprocessing_target, args=(self.job_id,), daemon=True)
|
||||
# self.target_function(self.args)
|
||||
self.thread.daemon = True
|
||||
self.thread.start()
|
||||
self.thread.join()
|
||||
self.end_time = datetime.now(timezone("Asia/Seoul"))
|
||||
self.running_timedelta = self.end_time - self.start_time
|
||||
self.status = "success"
|
||||
if not scheduler.is_include(self.job_id):
|
||||
scheduler.remove_job_instance(self.job_id)
|
||||
self.count += 1
|
||||
except Exception as exception:
|
||||
self.status = "exception"
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
finally:
|
||||
self.is_running = False
|
||||
# if self.count > 1:
|
||||
# logger.debug(hm.check('JOB END %s' % self.job_id))
|
||||
# logger.debug(hm.getHeap())
|
||||
@@ -0,0 +1,166 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#########################################################
|
||||
# python
|
||||
import os
|
||||
import traceback
|
||||
import time
|
||||
import threading
|
||||
|
||||
# third-party
|
||||
from flask import request
|
||||
from flask_socketio import SocketIO, emit
|
||||
|
||||
# sjva 공용
|
||||
from framework import app, socketio, path_data, logger
|
||||
from framework.logger import get_logger
|
||||
from framework.util import SingletonClass
|
||||
|
||||
|
||||
# 패키지
|
||||
|
||||
# 로그
|
||||
|
||||
#########################################################
|
||||
|
||||
namespace = "log"
|
||||
|
||||
|
||||
@socketio.on("connect", namespace="/%s" % namespace)
|
||||
def socket_connect():
|
||||
logger.debug("log connect")
|
||||
|
||||
|
||||
@socketio.on("start", namespace="/%s" % namespace)
|
||||
def socket_file(data):
|
||||
try:
|
||||
package = filename = None
|
||||
if "package" in data:
|
||||
package = data["package"]
|
||||
else:
|
||||
filename = data["filename"]
|
||||
LogViewer.instance().start(package, filename, request.sid)
|
||||
logger.debug(
|
||||
"start package:%s filename:%s sid:%s",
|
||||
package,
|
||||
filename,
|
||||
request.sid,
|
||||
)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@socketio.on("disconnect", namespace="/%s" % namespace)
|
||||
def disconnect():
|
||||
try:
|
||||
LogViewer.instance().disconnect(request.sid)
|
||||
logger.debug("disconnect sid:%s", request.sid)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
class WatchThread(threading.Thread):
|
||||
def __init__(self, package, filename):
|
||||
super(WatchThread, self).__init__()
|
||||
self.stop_flag = False
|
||||
self.package = package
|
||||
self.filename = filename
|
||||
self.daemon = True
|
||||
|
||||
def stop(self):
|
||||
self.stop_flag = True
|
||||
|
||||
def run(self):
|
||||
logger.debug("WatchThread.. Start %s", self.package)
|
||||
if self.package is not None:
|
||||
logfile = os.path.join(path_data, "log", "%s.log" % self.package)
|
||||
key = "package"
|
||||
value = self.package
|
||||
else:
|
||||
logfile = os.path.join(path_data, "log", self.filename)
|
||||
key = "filename"
|
||||
value = self.filename
|
||||
if os.path.exists(logfile):
|
||||
with open(logfile, "r") as f:
|
||||
f.seek(0, os.SEEK_END)
|
||||
while not self.stop_flag:
|
||||
line = f.readline()
|
||||
if not line:
|
||||
time.sleep(0.1) # Sleep briefly
|
||||
continue
|
||||
socketio.emit(
|
||||
"add",
|
||||
{key: value, "data": line},
|
||||
namespace="/log",
|
||||
broadcast=True,
|
||||
)
|
||||
logger.debug("WatchThread.. End %s", value)
|
||||
else:
|
||||
socketio.emit(
|
||||
"add",
|
||||
{key: value, "data": "not exist logfile"},
|
||||
namespace="/log",
|
||||
broadcast=True,
|
||||
)
|
||||
|
||||
|
||||
class LogViewer(SingletonClass):
|
||||
|
||||
watch_list = {}
|
||||
|
||||
@classmethod
|
||||
def start(cls, package, filename, sid):
|
||||
# 2019-04-02 간만에 봤더니 헷깔려서 적는다
|
||||
# 이 쓰레드는 오픈시 이전 데이타만을 보내는 쓰레드다. 실시간보는거 아님.
|
||||
|
||||
def thread_function():
|
||||
if package is not None:
|
||||
logfile = os.path.join(path_data, "log", "%s.log" % package)
|
||||
else:
|
||||
logfile = os.path.join(path_data, "log", filename)
|
||||
if os.path.exists(logfile):
|
||||
ins_file = open(logfile, "r", encoding="utf8") ## 3)
|
||||
line = ins_file.read()
|
||||
socketio.emit("on_start", {"data": line}, namespace="/log")
|
||||
logger.debug("on_start end")
|
||||
else:
|
||||
socketio.emit(
|
||||
"on_start", {"data": "not exist logfile"}, namespace="/log"
|
||||
)
|
||||
|
||||
if package is not None:
|
||||
key = package
|
||||
else:
|
||||
key = filename
|
||||
thread = threading.Thread(target=thread_function, args=())
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
if key not in cls.watch_list:
|
||||
cls.watch_list[key] = {}
|
||||
cls.watch_list[key]["sid"] = []
|
||||
cls.watch_list[key]["thread"] = WatchThread(package, filename)
|
||||
cls.watch_list[key]["thread"].start()
|
||||
cls.watch_list[key]["sid"].append(sid)
|
||||
|
||||
@classmethod
|
||||
def disconnect(cls, sid):
|
||||
find = False
|
||||
find_key = None
|
||||
for key, value in cls.watch_list.items():
|
||||
logger.debug("key:%s value:%s", key, value)
|
||||
for s in value["sid"]:
|
||||
if sid == s:
|
||||
find = True
|
||||
find_key = key
|
||||
value["sid"].remove(s)
|
||||
break
|
||||
if find:
|
||||
break
|
||||
if not find:
|
||||
return
|
||||
if not cls.watch_list[find_key]["sid"]:
|
||||
logger.debug("thread kill")
|
||||
cls.watch_list[find_key]["thread"].stop()
|
||||
del cls.watch_list[find_key]
|
||||
@@ -0,0 +1,76 @@
|
||||
import os
|
||||
import logging
|
||||
import logging.handlers
|
||||
from datetime import datetime
|
||||
from framework import path_data
|
||||
|
||||
from pytz import timezone, utc
|
||||
from support.logger import CustomFormatter
|
||||
|
||||
level_unset_logger_list = []
|
||||
logger_list = []
|
||||
|
||||
|
||||
def get_logger(name):
|
||||
logger = logging.getLogger(name)
|
||||
# print(logger.handlers)
|
||||
if not logger.handlers:
|
||||
global level_unset_logger_list
|
||||
global logger_list
|
||||
level = logging.DEBUG
|
||||
from framework import flag_system_loading
|
||||
|
||||
try:
|
||||
if flag_system_loading:
|
||||
try:
|
||||
pass
|
||||
except Exception:
|
||||
level = logging.DEBUG
|
||||
if level_unset_logger_list is not None:
|
||||
for item in level_unset_logger_list:
|
||||
item.setLevel(level)
|
||||
level_unset_logger_list = None
|
||||
else:
|
||||
level_unset_logger_list.append(logger)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
logger.setLevel(level)
|
||||
formatter = logging.Formatter(
|
||||
"[%(asctime)s|%(levelname)s|%(filename)s:%(lineno)s] %(message)s"
|
||||
)
|
||||
|
||||
def customTime(*args):
|
||||
utc_dt = utc.localize(datetime.utcnow())
|
||||
my_tz = timezone("Asia/Seoul")
|
||||
converted = utc_dt.astimezone(my_tz)
|
||||
return converted.timetuple()
|
||||
|
||||
formatter.converter = customTime
|
||||
file_max_bytes = 1 * 1024 * 1024
|
||||
fileHandler = logging.handlers.RotatingFileHandler(
|
||||
filename=os.path.join(path_data, "log", "%s.log" % name),
|
||||
maxBytes=file_max_bytes,
|
||||
backupCount=5,
|
||||
encoding="utf8",
|
||||
delay=True,
|
||||
)
|
||||
streamHandler = logging.StreamHandler()
|
||||
|
||||
# handler에 fommater 세팅
|
||||
fileHandler.setFormatter(formatter)
|
||||
streamHandler.setFormatter(CustomFormatter())
|
||||
|
||||
# Handler를 logging에 추가
|
||||
logger.addHandler(fileHandler)
|
||||
logger.addHandler(streamHandler)
|
||||
return logger
|
||||
|
||||
|
||||
def set_level(level):
|
||||
global logger_list
|
||||
try:
|
||||
for log in logger_list:
|
||||
log.setLevel(level)
|
||||
except Exception:
|
||||
pass
|
||||
@@ -0,0 +1,427 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import copy
|
||||
|
||||
MENU_MAP = [
|
||||
{
|
||||
"category": "토렌트",
|
||||
"name": "torrent",
|
||||
"type": "plugin",
|
||||
"position": "left",
|
||||
"list": [
|
||||
{"type": "plugin", "plugin": "rss2", "name": "RSS2"},
|
||||
{"type": "divider"},
|
||||
{"type": "plugin", "plugin": "downloader", "name": "다운로드"},
|
||||
{"type": "plugin", "plugin": "rss_downloader", "name": "RSS 다운로드"},
|
||||
{
|
||||
"type": "plugin",
|
||||
"plugin": "bot_downloader_ktv",
|
||||
"name": "봇 다운로드 - TV",
|
||||
},
|
||||
{
|
||||
"type": "plugin",
|
||||
"plugin": "bot_downloader_movie",
|
||||
"name": "봇 다운로드 - 영화",
|
||||
},
|
||||
{
|
||||
"type": "plugin",
|
||||
"plugin": "bot_downloader_av",
|
||||
"name": "봇 다운로드 - AV",
|
||||
},
|
||||
{"type": "divider"},
|
||||
{"type": "plugin", "plugin": "offcloud2", "name": "Offcloud2"},
|
||||
{"type": "plugin", "plugin": "torrent_info", "name": "토렌트 정보"},
|
||||
],
|
||||
"count": 0,
|
||||
},
|
||||
{
|
||||
"category": "VOD",
|
||||
"name": "vod",
|
||||
"type": "plugin",
|
||||
"position": "left",
|
||||
"list": [
|
||||
{"type": "plugin", "plugin": "ffmpeg", "name": "FFMPEG"},
|
||||
{"type": "divider"},
|
||||
{"type": "plugin", "plugin": "wavve", "name": "웨이브"},
|
||||
{"type": "plugin", "plugin": "tving", "name": "티빙"},
|
||||
{"type": "plugin", "plugin": "nsearch", "name": "검색"},
|
||||
{"type": "divider"},
|
||||
{"type": "plugin", "plugin": "ani24", "name": "애니24"},
|
||||
{"type": "plugin", "plugin": "youtube-dl", "name": "youtube-dl"},
|
||||
],
|
||||
"count": 0,
|
||||
},
|
||||
{
|
||||
"category": "파일처리",
|
||||
"name": "fileprocess",
|
||||
"type": "plugin",
|
||||
"position": "left",
|
||||
"list": [
|
||||
{"type": "plugin", "plugin": "ktv", "name": "국내방송"},
|
||||
{"type": "plugin", "plugin": "fileprocess_movie", "name": "영화"},
|
||||
{"type": "plugin", "plugin": "fileprocess_av", "name": "AV"},
|
||||
{"type": "plugin", "plugin": "musicProc", "name": "음악"},
|
||||
{"type": "divider"},
|
||||
{"type": "plugin", "plugin": "smi2srt", "name": "SMI to SRT"},
|
||||
{"type": "plugin", "plugin": "synoindex", "name": "Synoindex"},
|
||||
],
|
||||
"count": 0,
|
||||
},
|
||||
{
|
||||
"category": "PLEX",
|
||||
"name": "plex",
|
||||
"type": "plugin",
|
||||
"position": "left",
|
||||
"list": [
|
||||
{"type": "plugin", "plugin": "plex", "name": "PLEX"},
|
||||
{"type": "divider"},
|
||||
{"type": "plugin", "plugin": "gdrive_scan", "name": "GDrive 스캔"},
|
||||
{"type": "divider"},
|
||||
{"type": "plugin", "plugin": "av_agent", "name": "AV Agent"},
|
||||
],
|
||||
"count": 0,
|
||||
},
|
||||
{
|
||||
"category": "TV",
|
||||
"name": "tv",
|
||||
"type": "plugin",
|
||||
"position": "left",
|
||||
"list": [
|
||||
{"type": "plugin", "plugin": "klive", "name": "KLive"},
|
||||
{"type": "plugin", "plugin": "tvheadend", "name": "Tvheadend"},
|
||||
{"type": "plugin", "plugin": "hdhomerun", "name": "HDHomerun"},
|
||||
{"type": "divider"},
|
||||
{"type": "plugin", "plugin": "epg", "name": "EPG"},
|
||||
],
|
||||
"count": 0,
|
||||
},
|
||||
{
|
||||
"category": "서비스",
|
||||
"name": "service",
|
||||
"type": "plugin",
|
||||
"position": "left",
|
||||
"list": [
|
||||
{"type": "plugin", "plugin": "kthoom", "name": "kthoom"},
|
||||
{"type": "plugin", "plugin": "manamoa", "name": "manamoa"},
|
||||
{
|
||||
"type": "plugin",
|
||||
"plugin": "webtoon_naver",
|
||||
"name": "webtoon_naver",
|
||||
},
|
||||
{
|
||||
"type": "plugin",
|
||||
"plugin": "webtoon_daum",
|
||||
"name": "webtoon_daum",
|
||||
},
|
||||
{"type": "divider"},
|
||||
{
|
||||
"type": "plugin",
|
||||
"plugin": "podcast_rss_maker",
|
||||
"name": "podcast_rss_maker",
|
||||
},
|
||||
{
|
||||
"type": "plugin",
|
||||
"plugin": "gd_share_client",
|
||||
"name": "gd_share_client",
|
||||
},
|
||||
],
|
||||
"count": 0,
|
||||
},
|
||||
{
|
||||
"category": "툴",
|
||||
"name": "tool",
|
||||
"type": "plugin",
|
||||
"position": "left",
|
||||
"list": [
|
||||
{"type": "plugin", "plugin": "rclone", "name": "Rclone"},
|
||||
{"type": "plugin", "plugin": "vnStat", "name": "vnStat"},
|
||||
{"type": "plugin", "plugin": "aria2", "name": "aria2"},
|
||||
{"type": "divider"},
|
||||
{"type": "plugin", "plugin": "daum_tv", "name": "Daum TV"},
|
||||
],
|
||||
"count": 0,
|
||||
},
|
||||
{
|
||||
"category": "런처",
|
||||
"name": "launcher",
|
||||
"type": "plugin",
|
||||
"position": "left",
|
||||
"list": [],
|
||||
"count": 0,
|
||||
},
|
||||
{
|
||||
"category": "베타",
|
||||
"name": "beta",
|
||||
"type": "plugin",
|
||||
"position": "left",
|
||||
"list": [],
|
||||
"count": 0,
|
||||
},
|
||||
{
|
||||
"category": "Custom",
|
||||
"name": "custom",
|
||||
"type": "custom",
|
||||
"position": "left",
|
||||
"list": [],
|
||||
"count": 0,
|
||||
},
|
||||
{
|
||||
"category": "링크",
|
||||
"name": "link",
|
||||
"type": "link",
|
||||
"position": "right",
|
||||
"list": [
|
||||
{"type": "link", "name": "PLEX", "link": "https://app.plex.tv"},
|
||||
{"type": "divider"},
|
||||
{
|
||||
"type": "link",
|
||||
"name": "나스당",
|
||||
"link": "https://www.clien.net/service/board/cm_nas",
|
||||
},
|
||||
{
|
||||
"type": "link",
|
||||
"name": "mk802카페",
|
||||
"link": "https://cafe.naver.com/mk802",
|
||||
},
|
||||
],
|
||||
"count": 0,
|
||||
},
|
||||
{
|
||||
"category": "시스템",
|
||||
"name": "system",
|
||||
"type": "system",
|
||||
"position": "right",
|
||||
"list": [
|
||||
{"type": "plugin", "plugin": "system", "name": "설정"},
|
||||
# {'type':'direct', 'name' : u'설정', 'link':'/system/setting'},
|
||||
# {'type':'direct', 'name' : u'플러그인', 'link':'/system/plugin'},
|
||||
# {'type':'direct', 'name' : u'정보', 'link':'/system/information'},
|
||||
{"type": "divider"},
|
||||
{"type": "plugin", "plugin": "mod", "name": "모듈"},
|
||||
{"type": "plugin", "plugin": "command", "name": "Command"},
|
||||
{"type": "divider"},
|
||||
{"type": "link", "name": "Terminal", "link": "/terminal"},
|
||||
{"type": "direct", "name": "파일 매니저", "link": "/flaskfilemanager"},
|
||||
{"type": "direct", "name": "편집기", "link": "/flaskcode"},
|
||||
{"type": "divider"},
|
||||
# {'type':'link', 'name' : u'FileManager', 'link':'/iframe/file_manager'},
|
||||
# {'type':'system_value', 'name' : u'FileBrowser.xyz', 'link':'url_filebrowser'},
|
||||
# {'type':'system_value', 'name' : u'Celery Monitoring', 'link':'url_celery_monitoring'},
|
||||
# {'type':'divider'},
|
||||
# {'type':'link', 'name':u'위키', 'link':'https://sjva.me/wiki/public/start'},
|
||||
# {'type':'divider'},
|
||||
{"type": "direct", "name": "로그아웃", "link": "/logout"},
|
||||
{"type": "direct", "name": "재시작(업데이트)", "link": "/system/restart"},
|
||||
{
|
||||
"type": "direct",
|
||||
"name": "종료",
|
||||
"link": "javascript:shutdown_confirm();",
|
||||
},
|
||||
],
|
||||
"count": 0,
|
||||
},
|
||||
]
|
||||
|
||||
DEFINE_MENU_MAP = copy.deepcopy(MENU_MAP)
|
||||
|
||||
|
||||
def init_menu(plugin_menus):
|
||||
global MENU_MAP
|
||||
from framework import logger
|
||||
|
||||
for plugin_menu in plugin_menus:
|
||||
find = False
|
||||
for category in MENU_MAP:
|
||||
for category_child in category["list"]:
|
||||
if category_child["type"] != "plugin":
|
||||
continue
|
||||
if category_child["plugin"] == plugin_menu["main"][0]:
|
||||
find = True
|
||||
category_child["name"] = plugin_menu["main"][1]
|
||||
category_child["sub"] = plugin_menu["sub"]
|
||||
category_child["sub2"] = (
|
||||
plugin_menu["sub2"] if "sub2" in plugin_menu else None
|
||||
)
|
||||
category_child["exist"] = True
|
||||
category["count"] += 1
|
||||
break
|
||||
if find:
|
||||
break
|
||||
else:
|
||||
if (
|
||||
"category" in plugin_menu
|
||||
and plugin_menu["category"] == category["name"]
|
||||
):
|
||||
cc = {}
|
||||
cc["type"] = "plugin"
|
||||
cc["plugin"] = plugin_menu["main"][0]
|
||||
cc["name"] = plugin_menu["main"][1]
|
||||
cc["sub"] = plugin_menu["sub"]
|
||||
cc["sub2"] = (
|
||||
plugin_menu["sub2"] if "sub2" in plugin_menu else None
|
||||
)
|
||||
cc["exist"] = True
|
||||
category["count"] += 1
|
||||
category["list"].append(cc)
|
||||
find = True
|
||||
|
||||
if find:
|
||||
continue
|
||||
else:
|
||||
# 카테고리를 발견하지 못하였다면..
|
||||
c = MENU_MAP[9]
|
||||
cc = {}
|
||||
cc["type"] = "plugin"
|
||||
cc["plugin"] = plugin_menu["main"][0]
|
||||
cc["name"] = plugin_menu["main"][1]
|
||||
cc["sub"] = plugin_menu["sub"]
|
||||
cc["sub2"] = plugin_menu["sub2"] if "sub2" in plugin_menu else None
|
||||
cc["exist"] = True
|
||||
c["count"] += 1
|
||||
c["list"].append(cc)
|
||||
|
||||
tmp = copy.deepcopy(MENU_MAP)
|
||||
MENU_MAP = []
|
||||
for category in tmp:
|
||||
if category["type"] in ["link"]:
|
||||
MENU_MAP.append(category)
|
||||
elif category["type"] in ["system"]:
|
||||
from system.model import ModelSetting as SystemModelSetting
|
||||
|
||||
for t in category["list"]:
|
||||
if t["type"] == "system_value":
|
||||
t["type"] = "link"
|
||||
t["link"] = SystemModelSetting.get(t["link"])
|
||||
MENU_MAP.append(category)
|
||||
else:
|
||||
if category["count"] > 0:
|
||||
MENU_MAP.append(category)
|
||||
|
||||
for category in MENU_MAP:
|
||||
if category["category"] in ["system", "link", "custom"]:
|
||||
continue
|
||||
flag_custom = False
|
||||
total_plugin_count = 0
|
||||
exist_plugin_count = 0
|
||||
for category_child in category["list"]:
|
||||
total_plugin_count += 1
|
||||
if category_child["type"] == "plugin":
|
||||
if (
|
||||
"exist" not in category_child
|
||||
or category_child["exist"] == False
|
||||
):
|
||||
flag_custom = True
|
||||
else:
|
||||
exist_plugin_count += 1
|
||||
if exist_plugin_count == 0:
|
||||
# 올수없다
|
||||
|
||||
continue
|
||||
|
||||
if flag_custom:
|
||||
tmp = copy.deepcopy(category["list"])
|
||||
category["list"] = []
|
||||
for category_child in tmp:
|
||||
if category_child["type"] != "plugin":
|
||||
category["list"].append(category_child)
|
||||
if (
|
||||
"exist" in category_child
|
||||
and category_child["exist"] == True
|
||||
):
|
||||
category["list"].append(category_child)
|
||||
|
||||
try:
|
||||
import flaskfilemanager
|
||||
except Exception:
|
||||
# del MENU_MAP[-1]['list'][2]
|
||||
try:
|
||||
index = -1
|
||||
for idx, item in enumerate(MENU_MAP[-1]["list"]):
|
||||
if "link" in item and item["link"] == "/flaskfilemanager" != -1:
|
||||
item["link"] = "/system/plugin?install=flaskfilemanager"
|
||||
break
|
||||
if index != -1:
|
||||
del MENU_MAP[-1]["list"][index]
|
||||
except Exception as exception:
|
||||
import traceback
|
||||
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
try:
|
||||
import flaskcode
|
||||
except Exception:
|
||||
# del MENU_MAP[-1]['list'][2]
|
||||
try:
|
||||
index = -1
|
||||
for idx, item in enumerate(MENU_MAP[-1]["list"]):
|
||||
if "link" in item and item["link"] == "/flaskcode" != -1:
|
||||
item["link"] = "/system/plugin?install=flaskcode"
|
||||
break
|
||||
if index != -1:
|
||||
del MENU_MAP[-1]["list"][index]
|
||||
except Exception as exception:
|
||||
import traceback
|
||||
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
import platform
|
||||
|
||||
if platform.system() == "Windows":
|
||||
try:
|
||||
index = -1
|
||||
for idx, item in enumerate(MENU_MAP[-1]["list"]):
|
||||
if "link" in item and item["link"] == "/terminal":
|
||||
index = idx
|
||||
break
|
||||
if index != -1:
|
||||
del MENU_MAP[-1]["list"][index]
|
||||
except Exception as exception:
|
||||
import traceback
|
||||
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
try:
|
||||
## 선 제거
|
||||
for category in MENU_MAP:
|
||||
new_category = []
|
||||
flag = -1
|
||||
first = False
|
||||
for idx, item in enumerate(category["list"]):
|
||||
if (idx == 0 or idx == len(category["list"]) - 1) and item[
|
||||
"type"
|
||||
] == "divider":
|
||||
continue
|
||||
if first == False and item["type"] == "divider":
|
||||
continue
|
||||
if item["type"] == "divider":
|
||||
if flag == 1:
|
||||
continue
|
||||
else:
|
||||
flag = 1
|
||||
else:
|
||||
first = True
|
||||
flag = 0
|
||||
new_category.append(item)
|
||||
if new_category[-1]["type"] == "divider":
|
||||
new_category = new_category[:-1]
|
||||
category["list"] = new_category
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def get_menu_map():
|
||||
global MENU_MAP
|
||||
return MENU_MAP
|
||||
|
||||
|
||||
def get_plugin_menu(plugin_name):
|
||||
global MENU_MAP
|
||||
for category in MENU_MAP:
|
||||
for category_child in category["list"]:
|
||||
if category_child["type"] != "plugin":
|
||||
continue
|
||||
if category_child["plugin"] == plugin_name:
|
||||
return category_child
|
||||
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
import Queue as py_queue
|
||||
import urllib2 as py_urllib2 # Request, urlopen
|
||||
import urllib as py_urllib # quote, unuote, urlencode
|
||||
|
||||
py_reload = reload
|
||||
|
||||
def py_unicode(v):
|
||||
return unicode(v)
|
||||
|
||||
else:
|
||||
import queue as py_queue
|
||||
import urllib.request as py_urllib2
|
||||
import urllib.parse as py_urllib # urlencode
|
||||
from importlib import reload as py_reload
|
||||
|
||||
def py_unicode(v):
|
||||
return str(v)
|
||||
@@ -0,0 +1,35 @@
|
||||
import os
|
||||
|
||||
|
||||
class User:
|
||||
def __init__(
|
||||
self, user_id, email=None, passwd_hash=None, authenticated=True
|
||||
):
|
||||
self.user_id = user_id
|
||||
self.email = email
|
||||
self.passwd_hash = passwd_hash
|
||||
self.authenticated = authenticated
|
||||
|
||||
def __repr__(self):
|
||||
r = {
|
||||
"user_id": self.user_id,
|
||||
"email": self.email,
|
||||
"passwd_hash": self.passwd_hash,
|
||||
"authenticated": self.authenticated,
|
||||
}
|
||||
return str(r)
|
||||
|
||||
def can_login(self, passwd_hash):
|
||||
return self.passwd_hash == passwd_hash
|
||||
|
||||
def is_active(self):
|
||||
return True
|
||||
|
||||
def get_id(self):
|
||||
return self.user_id
|
||||
|
||||
def is_authenticated(self):
|
||||
return self.authenticated
|
||||
|
||||
def is_anonymous(self):
|
||||
return False
|
||||
@@ -0,0 +1,217 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#########################################################
|
||||
# python
|
||||
import os
|
||||
import json
|
||||
import traceback
|
||||
import platform
|
||||
import subprocess
|
||||
|
||||
# third-party
|
||||
from sqlalchemy.ext.declarative import DeclarativeMeta
|
||||
|
||||
# sjva 공용
|
||||
from framework import app, logger
|
||||
|
||||
#########################################################
|
||||
|
||||
|
||||
class Util(object):
|
||||
@staticmethod
|
||||
def sizeof_fmt(num, suffix="Bytes"):
|
||||
"""
|
||||
파일크기, 다운로드 속도 표시시 사용
|
||||
"""
|
||||
for unit in ["", "K", "M", "G", "T", "P", "E", "Z"]:
|
||||
if abs(num) < 1024.0:
|
||||
return "%3.1f%s%s" % (num, unit, suffix)
|
||||
num /= 1024.0
|
||||
return "%.1f%s%s" % (num, "Y", suffix)
|
||||
|
||||
@staticmethod
|
||||
def db_list_to_dict(db_list):
|
||||
"""
|
||||
세팅DB에서 사용, (key, value) dict로 변환
|
||||
"""
|
||||
ret = {}
|
||||
for item in db_list:
|
||||
ret[item.key] = item.value
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def db_to_dict(db_list):
|
||||
ret = []
|
||||
for item in db_list:
|
||||
ret.append(item.as_dict())
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def get_paging_info(count, current_page, page_size):
|
||||
try:
|
||||
paging = {}
|
||||
paging["prev_page"] = True
|
||||
paging["next_page"] = True
|
||||
if current_page <= 10:
|
||||
paging["prev_page"] = False
|
||||
|
||||
paging["total_page"] = int(count / page_size) + 1
|
||||
if count % page_size == 0:
|
||||
paging["total_page"] -= 1
|
||||
paging["start_page"] = int((current_page - 1) / 10) * 10 + 1
|
||||
paging["last_page"] = (
|
||||
paging["total_page"]
|
||||
if paging["start_page"] + 9 > paging["total_page"]
|
||||
else paging["start_page"] + 9
|
||||
)
|
||||
if paging["last_page"] == paging["total_page"]:
|
||||
paging["next_page"] = False
|
||||
paging["current_page"] = current_page
|
||||
paging["count"] = count
|
||||
logger.debug(
|
||||
"paging : c:%s %s %s %s %s %s",
|
||||
count,
|
||||
paging["total_page"],
|
||||
paging["prev_page"],
|
||||
paging["next_page"],
|
||||
paging["start_page"],
|
||||
paging["last_page"],
|
||||
)
|
||||
return paging
|
||||
except Exception as exception:
|
||||
logger.debug("Exception:%s", exception)
|
||||
logger.debug(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def get_list_except_empty(source):
|
||||
tmp = []
|
||||
for _ in source:
|
||||
if _.strip().startswith("#"):
|
||||
continue
|
||||
if _.strip() != "":
|
||||
tmp.append(_.strip())
|
||||
return tmp
|
||||
|
||||
@staticmethod
|
||||
def save_from_dict_to_json(d, filename):
|
||||
from tool_base import ToolUtil
|
||||
|
||||
ToolUtil.save_dict(d, filename)
|
||||
|
||||
# list형태
|
||||
@staticmethod
|
||||
def execute_command(command):
|
||||
from tool_base import ToolSubprocess
|
||||
|
||||
return ToolSubprocess.execute_command_return(command)
|
||||
|
||||
@staticmethod
|
||||
def change_text_for_use_filename(text):
|
||||
from tool_base import ToolBaseFile
|
||||
|
||||
return ToolBaseFile.text_for_filename(text)
|
||||
|
||||
# 토렌트 인포에서 최대 크기 파일과 폴더명을 리턴한다
|
||||
@staticmethod
|
||||
def get_max_size_fileinfo(torrent_info):
|
||||
try:
|
||||
ret = {}
|
||||
max_size = -1
|
||||
max_filename = None
|
||||
for t in torrent_info["files"]:
|
||||
if t["size"] > max_size:
|
||||
max_size = t["size"]
|
||||
max_filename = str(t["path"])
|
||||
t = max_filename.split("/")
|
||||
ret["filename"] = t[-1]
|
||||
if len(t) == 1:
|
||||
ret["dirname"] = ""
|
||||
elif len(t) == 2:
|
||||
ret["dirname"] = t[0]
|
||||
else:
|
||||
ret["dirname"] = max_filename.replace(
|
||||
"/%s" % ret["filename"], ""
|
||||
)
|
||||
ret["max_size"] = max_size
|
||||
return ret
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
# 압축할 폴더 경로를 인자로 받음. 폴더명.zip 생성
|
||||
@staticmethod
|
||||
def makezip(zip_path, zip_extension="zip"):
|
||||
import zipfile
|
||||
|
||||
try:
|
||||
if os.path.isdir(zip_path):
|
||||
zipfilename = os.path.join(
|
||||
os.path.dirname(zip_path),
|
||||
"%s.%s" % (os.path.basename(zip_path), zip_extension),
|
||||
)
|
||||
fantasy_zip = zipfile.ZipFile(zipfilename, "w")
|
||||
for f in os.listdir(zip_path):
|
||||
# if f.endswith('.jpg') or f.endswith('.png'):
|
||||
src = os.path.join(zip_path, f)
|
||||
fantasy_zip.write(
|
||||
src,
|
||||
os.path.basename(src),
|
||||
compress_type=zipfile.ZIP_DEFLATED,
|
||||
)
|
||||
fantasy_zip.close()
|
||||
import shutil
|
||||
|
||||
shutil.rmtree(zip_path)
|
||||
return True
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def make_apikey(url):
|
||||
from framework import SystemModelSetting
|
||||
|
||||
url = url.format(ddns=SystemModelSetting.get("ddns"))
|
||||
if SystemModelSetting.get_bool("auth_use_apikey"):
|
||||
if url.find("?") == -1:
|
||||
url += "?"
|
||||
else:
|
||||
url += "&"
|
||||
url += "apikey=%s" % SystemModelSetting.get("auth_apikey")
|
||||
return url
|
||||
|
||||
|
||||
class SingletonClass(object):
|
||||
__instance = None
|
||||
|
||||
@classmethod
|
||||
def __getInstance(cls):
|
||||
return cls.__instance
|
||||
|
||||
@classmethod
|
||||
def instance(cls, *args, **kargs):
|
||||
cls.__instance = cls(*args, **kargs)
|
||||
cls.instance = cls.__getInstance
|
||||
return cls.__instance
|
||||
|
||||
|
||||
class AlchemyEncoder(json.JSONEncoder):
|
||||
def default(self, obj):
|
||||
if isinstance(obj.__class__, DeclarativeMeta):
|
||||
# an SQLAlchemy class
|
||||
fields = {}
|
||||
for field in [
|
||||
x for x in dir(obj) if not x.startswith("_") and x != "metadata"
|
||||
]:
|
||||
data = obj.__getattribute__(field)
|
||||
try:
|
||||
json.dumps(
|
||||
data
|
||||
) # this will fail on non-encodable values, like other classes
|
||||
fields[field] = data
|
||||
except TypeError:
|
||||
fields[field] = None
|
||||
# a json-encodable dict
|
||||
return fields
|
||||
|
||||
return json.JSONEncoder.default(self, obj)
|
||||
Reference in New Issue
Block a user