first commit
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
from .plugin import (
|
||||
blueprint,
|
||||
menu,
|
||||
plugin_load,
|
||||
plugin_unload,
|
||||
restart,
|
||||
shutdown,
|
||||
)
|
||||
from .logic import SystemLogic
|
||||
from .model import ModelSetting
|
||||
|
||||
|
||||
from .logic_plugin import LogicPlugin
|
||||
from .logic_selenium import SystemLogicSelenium
|
||||
from .logic_command import SystemLogicCommand
|
||||
|
||||
from .logic_site import SystemLogicSite
|
||||
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,586 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#########################################################
|
||||
# python
|
||||
import os
|
||||
import traceback
|
||||
import logging
|
||||
from datetime import datetime
|
||||
import string
|
||||
import random
|
||||
import json
|
||||
|
||||
# third-party
|
||||
import requests
|
||||
from flask import (
|
||||
Blueprint,
|
||||
request,
|
||||
Response,
|
||||
send_file,
|
||||
render_template,
|
||||
redirect,
|
||||
jsonify,
|
||||
)
|
||||
from flask_login import login_user, logout_user, current_user, login_required
|
||||
|
||||
# gommi 공용
|
||||
from framework.logger import get_logger, set_level
|
||||
from framework import (
|
||||
app,
|
||||
db,
|
||||
scheduler,
|
||||
version,
|
||||
path_app_root,
|
||||
path_data,
|
||||
USERS,
|
||||
)
|
||||
from framework.util import Util
|
||||
|
||||
from framework import USERS
|
||||
from framework.user import User
|
||||
from framework import db, scheduler
|
||||
from framework.job import Job
|
||||
|
||||
# 패키지
|
||||
from .model import ModelSetting
|
||||
import system
|
||||
|
||||
# 로그
|
||||
package_name = __name__.split(".")[0]
|
||||
logger = get_logger(package_name)
|
||||
#########################################################
|
||||
|
||||
|
||||
class SystemLogic(object):
|
||||
point = 0
|
||||
db_default = {
|
||||
"db_version": "1",
|
||||
"port": "7771",
|
||||
"ddns": "http://localhost:7771",
|
||||
#'url_filebrowser' : 'http://localhost:9998',
|
||||
#'url_celery_monitoring' : 'http://localhost:9997',
|
||||
"id": "sjva",
|
||||
"pw": "sjva",
|
||||
"system_start_time": "",
|
||||
"repeat": "",
|
||||
"auto_restart_hour": "12",
|
||||
#'unique' : '',
|
||||
"theme": "Default",
|
||||
"log_level": "10",
|
||||
"use_login": "False",
|
||||
"link_json": '[{"type":"link","title":"위키","url":"https://sjva.me/wiki/public/start"}]',
|
||||
"plugin_dev_path": "",
|
||||
"plugin_tving_level2": "False",
|
||||
"web_title": "GOMMI Agent",
|
||||
"my_ip": "",
|
||||
"wavve_guid": "",
|
||||
# 번역
|
||||
"trans_type": "0",
|
||||
"trans_google_api_key": "",
|
||||
"trans_papago_key": "",
|
||||
# 인증
|
||||
"auth_use_apikey": "False",
|
||||
"auth_apikey": "",
|
||||
"hide_menu": "True",
|
||||
# Selenium
|
||||
"selenium_remote_url": "",
|
||||
"selenium_remote_default_option": "--no-sandbox\n--disable-gpu",
|
||||
"selenium_binary_default_option": "",
|
||||
# notify
|
||||
"notify_telegram_use": "False",
|
||||
"notify_telegram_token": "",
|
||||
"notify_telegram_chat_id": "",
|
||||
"notify_telegram_disable_notification": "False",
|
||||
"notify_discord_use": "False",
|
||||
"notify_discord_webhook": "",
|
||||
"notify_advaned_use": "False",
|
||||
"notify_advaned_policy": "# 각 플러그인 설정 설명에 명시되어 있는 ID = 형식\n# DEFAULT 부터 주석(#) 제거 후 작성\n\n# DEFAULT = ",
|
||||
# telegram
|
||||
"telegram_bot_token": "",
|
||||
"telegram_bot_auto_start": "False",
|
||||
"telegram_resend": "False",
|
||||
"telegram_resend_chat_id": "",
|
||||
# 홈페이지 연동 2020-06-07
|
||||
"sjva_me_user_id": "",
|
||||
"auth_status": "",
|
||||
"sjva_id": "",
|
||||
# site
|
||||
"site_daum_interval": "0 4 */3 * *",
|
||||
"site_daum_auto_start": "False",
|
||||
"site_daum_cookie": "TIARA=gaXEIPluo-wWAFlwZN6l8gN3yzhkoo_piP.Kymhuy.6QBt4Q6.cRtxbKDaWpWajcyteRHzrlTVpJRxLjwLoMvyYLVi_7xJ1L",
|
||||
"site_daum_test": "나쁜 녀석들",
|
||||
"site_daum_proxy": "",
|
||||
"site_wavve_id": "",
|
||||
"site_wavve_pw": "",
|
||||
"site_wavve_credential": "",
|
||||
"site_wavve_use_proxy": "False",
|
||||
"site_wavve_proxy_url": "",
|
||||
"site_tving_id": "",
|
||||
"site_tving_pw": "",
|
||||
"site_tving_login_type": "0",
|
||||
"site_tving_token": "",
|
||||
"site_tving_deviceid": "",
|
||||
"site_tving_use_proxy": "False",
|
||||
"site_tving_proxy_url": "",
|
||||
"site_tving_uuid": "",
|
||||
# memo
|
||||
"memo": "",
|
||||
# tool - decrypt
|
||||
"tool_crypt_use_user_key": "False",
|
||||
"tool_crypt_user_key": "",
|
||||
"tool_crypt_encrypt_word": "",
|
||||
"tool_crypt_decrypt_word": "",
|
||||
"use_beta": "False",
|
||||
}
|
||||
|
||||
db_default2 = {
|
||||
"use_category_vod": "True",
|
||||
"use_category_file_process": "True",
|
||||
"use_category_plex": "True",
|
||||
"use_category_tool": "True",
|
||||
}
|
||||
|
||||
db_default3 = {
|
||||
"use_plugin_ffmpeg": "False",
|
||||
"use_plugin_ktv": "False",
|
||||
"use_plugin_fileprocess_movie": "False",
|
||||
"use_plugin_plex": "False",
|
||||
"use_plugin_gdrive_scan": "False",
|
||||
"use_plugin_rclone": "False",
|
||||
"use_plugin_daum_tv": "False",
|
||||
}
|
||||
|
||||
recent_version = None
|
||||
|
||||
@staticmethod
|
||||
def plugin_load():
|
||||
try:
|
||||
SystemLogic.db_init()
|
||||
SystemLogic.init()
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def db_init():
|
||||
try:
|
||||
logger.debug(
|
||||
"setting count : %s",
|
||||
db.session.query(ModelSetting).filter_by().count(),
|
||||
)
|
||||
is_first = False
|
||||
for key, value in SystemLogic.db_default.items():
|
||||
if db.session.query(ModelSetting).filter_by(key=key).count() == 0:
|
||||
if key == "port":
|
||||
is_first = True
|
||||
if key == "sjva_id" or key == "auth_apikey":
|
||||
value = "".join(
|
||||
random.choice(string.ascii_uppercase + string.digits)
|
||||
for _ in range(10)
|
||||
)
|
||||
db.session.add(ModelSetting(key, value))
|
||||
db.session.commit()
|
||||
# 기존...사람들을 위해 토큰이 있는 사용자면 추가할때 True로 해준다
|
||||
for key, value in SystemLogic.db_default2.items():
|
||||
if db.session.query(ModelSetting).filter_by(key=key).count() == 0:
|
||||
tmp = value
|
||||
if is_first is False:
|
||||
tmp = "True"
|
||||
db.session.add(ModelSetting(key, tmp))
|
||||
db.session.commit()
|
||||
# db.session.commit()
|
||||
|
||||
for key, value in SystemLogic.db_default3.items():
|
||||
if db.session.query(ModelSetting).filter_by(key=key).count() == 0:
|
||||
tmp = value
|
||||
if is_first is False:
|
||||
tmp = "True"
|
||||
db.session.add(ModelSetting(key, tmp))
|
||||
db.session.commit()
|
||||
|
||||
# for key, value in SystemLogic.db_default_etc.items():
|
||||
# if db.session.query(ModelSetting).filter_by(key=key).count() == 0:
|
||||
# db.session.add(ModelSetting(key, value))
|
||||
# db.session.commit()
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def init():
|
||||
try:
|
||||
if (
|
||||
app.config["config"]["repeat"] == 0
|
||||
or SystemLogic.get_setting_value("system_start_time") == ""
|
||||
):
|
||||
item = (
|
||||
db.session.query(ModelSetting)
|
||||
.filter_by(key="system_start_time")
|
||||
.with_for_update()
|
||||
.first()
|
||||
)
|
||||
item.value = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
db.session.commit()
|
||||
|
||||
item = (
|
||||
db.session.query(ModelSetting)
|
||||
.filter_by(key="repeat")
|
||||
.with_for_update()
|
||||
.first()
|
||||
)
|
||||
item.value = str(app.config["config"]["repeat"])
|
||||
db.session.commit()
|
||||
username = db.session.query(ModelSetting).filter_by(key="id").first().value
|
||||
passwd = db.session.query(ModelSetting).filter_by(key="pw").first().value
|
||||
USERS[username] = User(username, passwd_hash=passwd)
|
||||
|
||||
SystemLogic.set_restart_scheduler()
|
||||
# SystemLogic.set_statistics_scheduler()
|
||||
SystemLogic.set_scheduler_check_scheduler()
|
||||
SystemLogic.get_recent_version()
|
||||
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def get_recent_version():
|
||||
try:
|
||||
import requests
|
||||
|
||||
url = f"{app.config['DEFINE']['MAIN_SERVER_URL']}/version"
|
||||
if ModelSetting.get("ddns") == app.config["DEFINE"]["MAIN_SERVER_URL"]:
|
||||
url = "https://dev.soju6jan.com/version"
|
||||
SystemLogic.recent_version = requests.get(url).text
|
||||
return True
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def restart():
|
||||
import system
|
||||
|
||||
system.restart()
|
||||
|
||||
@staticmethod
|
||||
def get_info():
|
||||
info = {}
|
||||
import platform
|
||||
|
||||
info["platform"] = platform.platform()
|
||||
info["processor"] = platform.processor()
|
||||
|
||||
import sys
|
||||
|
||||
info["python_version"] = sys.version
|
||||
info["version"] = version
|
||||
info["recent_version"] = SystemLogic.recent_version
|
||||
info["path_app_root"] = path_app_root
|
||||
info["running_type"] = "%s. 비동기 작업 : %s" % (
|
||||
app.config["config"]["running_type"],
|
||||
"사용" if app.config["config"]["use_celery"] else "미사용",
|
||||
)
|
||||
import system
|
||||
|
||||
info["auth"] = app.config["config"]["auth_desc"]
|
||||
info["cpu_percent"] = "not supported"
|
||||
info["memory"] = "not supported"
|
||||
info["disk"] = "not supported"
|
||||
if app.config["config"]["running_type"] != "termux":
|
||||
try:
|
||||
import psutil
|
||||
from framework.util import Util
|
||||
|
||||
print("here")
|
||||
info["cpu_percent"] = "%s %%" % psutil.cpu_percent()
|
||||
tmp = psutil.virtual_memory()
|
||||
# info['memory'] = [Util.sizeof_fmt(tmp[0], suffix='B'), Util.sizeof_fmt(tmp[3]), Util.sizeof_fmt(tmp[1]), tmp[2]]
|
||||
info["memory"] = "전체 : %s 사용량 : %s 남은량 : %s (%s%%)" % (
|
||||
Util.sizeof_fmt(tmp[0], suffix="B"),
|
||||
Util.sizeof_fmt(tmp[3], suffix="B"),
|
||||
Util.sizeof_fmt(tmp[1], suffix="B"),
|
||||
tmp[2],
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
import platform
|
||||
|
||||
if platform.system() == "Windows":
|
||||
s = os.path.splitdrive(path_app_root)
|
||||
root = s[0]
|
||||
else:
|
||||
root = "/"
|
||||
tmp = psutil.disk_usage(root)
|
||||
info["disk"] = "전체 : %s 사용량 : %s 남은량 : %s (%s%%) - 드라이브 (%s)" % (
|
||||
Util.sizeof_fmt(tmp[0], suffix="B"),
|
||||
Util.sizeof_fmt(tmp[1], suffix="B"),
|
||||
Util.sizeof_fmt(tmp[2], suffix="B"),
|
||||
tmp[3],
|
||||
root,
|
||||
)
|
||||
except Exception as exception:
|
||||
pass
|
||||
try:
|
||||
tmp = SystemLogic.get_setting_value("system_start_time")
|
||||
# logger.debug('SYSTEM_START_TIME:%s', tmp)
|
||||
tmp_datetime = datetime.strptime(tmp, "%Y-%m-%d %H:%M:%S")
|
||||
timedelta = datetime.now() - tmp_datetime
|
||||
info["time"] = "시작 : %s 경과 : %s 재시작 : %s" % (
|
||||
tmp,
|
||||
str(timedelta).split(".")[0],
|
||||
app.config["config"]["repeat"],
|
||||
)
|
||||
except Exception as exception:
|
||||
info["time"] = str(exception)
|
||||
return info
|
||||
|
||||
@staticmethod
|
||||
def setting_save_system(req):
|
||||
try:
|
||||
for key, value in req.form.items():
|
||||
logger.debug("Key:%s Value:%s", key, value)
|
||||
entity = (
|
||||
db.session.query(ModelSetting)
|
||||
.filter_by(key=key)
|
||||
.with_for_update()
|
||||
.first()
|
||||
)
|
||||
entity.value = value
|
||||
# if key == 'theme':
|
||||
# SystemLogic.change_theme(value)
|
||||
db.session.commit()
|
||||
lists = ModelSetting.query.all()
|
||||
SystemLogic.setting_list = Util.db_list_to_dict(lists)
|
||||
USERS[
|
||||
db.session.query(ModelSetting).filter_by(key="id").first().value
|
||||
] = User(
|
||||
db.session.query(ModelSetting).filter_by(key="id").first().value,
|
||||
passwd_hash=db.session.query(ModelSetting)
|
||||
.filter_by(key="pw")
|
||||
.first()
|
||||
.value,
|
||||
)
|
||||
SystemLogic.set_restart_scheduler()
|
||||
set_level(
|
||||
int(
|
||||
db.session.query(ModelSetting)
|
||||
.filter_by(key="log_level")
|
||||
.first()
|
||||
.value
|
||||
)
|
||||
)
|
||||
|
||||
return True
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def setting_save_after():
|
||||
try:
|
||||
USERS[ModelSetting.get("id")] = User(
|
||||
ModelSetting.get("id"), passwd_hash=ModelSetting.get("pw")
|
||||
)
|
||||
SystemLogic.set_restart_scheduler()
|
||||
set_level(
|
||||
int(
|
||||
db.session.query(ModelSetting)
|
||||
.filter_by(key="log_level")
|
||||
.first()
|
||||
.value
|
||||
)
|
||||
)
|
||||
from .logic_site import SystemLogicSite
|
||||
|
||||
SystemLogicSite.get_daum_cookies(force=True)
|
||||
SystemLogicSite.create_tving_instance()
|
||||
return True
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def change_theme(theme):
|
||||
try:
|
||||
source = os.path.join(
|
||||
path_app_root,
|
||||
"static",
|
||||
"css",
|
||||
"theme",
|
||||
"%s_bootstrap.min.css" % theme,
|
||||
)
|
||||
target = os.path.join(path_app_root, "static", "css", "bootstrap.min.css")
|
||||
os.remove(target)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_setting_value(key):
|
||||
try:
|
||||
# logger.debug('get_setting_value:%s', key)
|
||||
entity = db.session.query(ModelSetting).filter_by(key=key).first()
|
||||
if entity is None:
|
||||
return None
|
||||
else:
|
||||
return entity.value
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
logger.error("error key : %s", key)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def set_restart_scheduler():
|
||||
name = "%s_restart" % (package_name)
|
||||
if scheduler.is_include(name):
|
||||
scheduler.remove_job(name)
|
||||
interval = ModelSetting.get("auto_restart_hour")
|
||||
if interval != "0":
|
||||
if len(interval.split(" ")) == 1:
|
||||
interval = "%s" % (int(interval) * 60)
|
||||
job_instance = Job(
|
||||
package_name,
|
||||
name,
|
||||
interval,
|
||||
SystemLogic.restart,
|
||||
"자동 재시작",
|
||||
True,
|
||||
)
|
||||
scheduler.add_job_instance(job_instance, run=False)
|
||||
|
||||
"""
|
||||
@staticmethod
|
||||
def set_statistics_scheduler():
|
||||
try:
|
||||
name = '%s_statistics' % (package_name)
|
||||
if scheduler.is_include(name):
|
||||
scheduler.remove_job(name)
|
||||
|
||||
job_instance = Job(package_name, name, 59, SystemLogic.statistics_scheduler_function, u"Update Check", True)
|
||||
scheduler.add_job_instance(job_instance, run=True)
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def set_scheduler_check_scheduler():
|
||||
try:
|
||||
name = "scheduler_check"
|
||||
if scheduler.is_include(name):
|
||||
scheduler.remove_job(name)
|
||||
|
||||
job_instance = Job(
|
||||
package_name,
|
||||
name,
|
||||
2,
|
||||
scheduler.first_run_check_thread_function,
|
||||
"Scheduler Check",
|
||||
True,
|
||||
)
|
||||
scheduler.add_job_instance(job_instance, run=False)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def command_run(command_text):
|
||||
try:
|
||||
ret = {}
|
||||
tmp = command_text.strip().split(" ")
|
||||
if not tmp:
|
||||
ret["ret"] = "success"
|
||||
ret["log"] = "Empty.."
|
||||
return ret
|
||||
if tmp[0] == "set":
|
||||
if len(tmp) == 3:
|
||||
if tmp[1] == "token":
|
||||
tmp[1] = "unique"
|
||||
entity = (
|
||||
db.session.query(ModelSetting)
|
||||
.filter_by(key=tmp[1])
|
||||
.with_for_update()
|
||||
.first()
|
||||
)
|
||||
if entity is None:
|
||||
ret["ret"] = "fail"
|
||||
ret["log"] = "%s not exist" % tmp[1]
|
||||
return ret
|
||||
entity.value = tmp[2] if tmp[2] != "EMPTY" else ""
|
||||
db.session.commit()
|
||||
ret["ret"] = "success"
|
||||
ret["log"] = "%s - %s" % (tmp[1], tmp[2])
|
||||
return ret
|
||||
|
||||
if tmp[0] == "set2":
|
||||
if tmp[1] == "klive":
|
||||
from klive import ModelSetting as KLiveModelSetting
|
||||
|
||||
if KLiveModelSetting.get(tmp[2]) is not None:
|
||||
KLiveModelSetting.set(tmp[2], tmp[3])
|
||||
ret["ret"] = "success"
|
||||
ret["log"] = f"KLive 설정 값 변경 : {tmp[2]} - {tmp[3]}"
|
||||
return ret
|
||||
|
||||
ret["ret"] = "fail"
|
||||
ret["log"] = "wrong command"
|
||||
return ret
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
ret["ret"] = "fail"
|
||||
ret["log"] = str(exception)
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def link_save(link_data_str):
|
||||
try:
|
||||
data = json.loads(link_data_str)
|
||||
entity = (
|
||||
db.session.query(ModelSetting)
|
||||
.filter_by(key="link_json")
|
||||
.with_for_update()
|
||||
.first()
|
||||
)
|
||||
entity.value = link_data_str
|
||||
db.session.commit()
|
||||
SystemLogic.apply_menu_link()
|
||||
return True
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def apply_menu_link():
|
||||
try:
|
||||
link_data_str = SystemLogic.get_setting_value("link_json")
|
||||
data = json.loads(link_data_str)
|
||||
from framework.menu import get_menu_map
|
||||
|
||||
menu_map = get_menu_map()
|
||||
for link_category in menu_map:
|
||||
if link_category["type"] == "link":
|
||||
break
|
||||
link_category["list"] = []
|
||||
for item in data:
|
||||
entity = {}
|
||||
entity["type"] = item["type"]
|
||||
if item["type"] == "link":
|
||||
entity["name"] = item["title"]
|
||||
entity["link"] = item["url"]
|
||||
link_category["list"].append(entity)
|
||||
return True
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
@@ -0,0 +1,86 @@
|
||||
import os
|
||||
import traceback
|
||||
import random
|
||||
import json
|
||||
import string
|
||||
import codecs
|
||||
|
||||
# third-party
|
||||
import requests
|
||||
from flask import (
|
||||
Blueprint,
|
||||
request,
|
||||
Response,
|
||||
send_file,
|
||||
render_template,
|
||||
redirect,
|
||||
jsonify,
|
||||
)
|
||||
|
||||
# gommi 공용
|
||||
from framework.logger import get_logger
|
||||
from framework import path_app_root, app
|
||||
from framework.util import Util
|
||||
|
||||
# 패키지
|
||||
from .plugin import package_name, logger
|
||||
from .model import ModelSetting
|
||||
|
||||
|
||||
class SystemLogicAuth(object):
|
||||
@staticmethod
|
||||
def process_ajax(sub, req):
|
||||
logger.debug(sub)
|
||||
try:
|
||||
if sub == "apikey_generate":
|
||||
ret = SystemLogicAuth.apikey_generate()
|
||||
return jsonify(ret)
|
||||
elif sub == "do_auth":
|
||||
ret = SystemLogicAuth.do_auth()
|
||||
return jsonify(ret)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def get_ip():
|
||||
import socket
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
try:
|
||||
# doesn't even have to be reachable
|
||||
s.connect(("10.255.255.255", 1))
|
||||
IP = s.getsockname()[0]
|
||||
except Exception:
|
||||
IP = "127.0.0.1"
|
||||
finally:
|
||||
s.close()
|
||||
logger.debug("IP:%s", IP)
|
||||
return IP
|
||||
|
||||
@staticmethod
|
||||
def do_auth():
|
||||
try:
|
||||
# ret = {"ret": False, "msg": "", "level": 0, "point": 0}
|
||||
ret = {"ret": True, "msg": "", "level": 100, "point": 100}
|
||||
return ret
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
ret["msg"] = "인증 실패"
|
||||
ret["level"] = -1
|
||||
ret["point"] = -1
|
||||
ModelSetting.set("auth_status", "auth_fail")
|
||||
|
||||
return ret
|
||||
|
||||
@staticmethod
|
||||
def get_auth_status(retry=True):
|
||||
try:
|
||||
value = ModelSetting.get("auth_status")
|
||||
ret = {"ret": False, "desc": "", "level": 0, "point": 0}
|
||||
|
||||
return ret
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
Executable
+248
@@ -0,0 +1,248 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#########################################################
|
||||
# python
|
||||
import os
|
||||
import traceback
|
||||
import logging
|
||||
import platform
|
||||
import subprocess
|
||||
import threading
|
||||
import sys
|
||||
import io
|
||||
import time
|
||||
import json
|
||||
# third-party
|
||||
|
||||
# sjva 공용
|
||||
from framework.logger import get_logger
|
||||
from framework import path_app_root, socketio, py_queue, app
|
||||
|
||||
# 패키지
|
||||
|
||||
# 로그
|
||||
package_name = __name__.split('.')[0]
|
||||
logger = get_logger(package_name)
|
||||
#########################################################
|
||||
|
||||
class SystemLogicCommand(object):
|
||||
|
||||
commands = None
|
||||
process = None
|
||||
stdout_queue = None
|
||||
thread = None
|
||||
send_to_ui_thread = None
|
||||
return_log = None
|
||||
@staticmethod
|
||||
def start(title, commands, clear=True, wait=False, show_modal=True):
|
||||
try:
|
||||
if show_modal:
|
||||
if clear:
|
||||
socketio.emit("command_modal_clear", None, namespace='/framework', broadcast=True)
|
||||
SystemLogicCommand.return_log = []
|
||||
SystemLogicCommand.title = title
|
||||
SystemLogicCommand.commands = commands
|
||||
SystemLogicCommand.thread = threading.Thread(target=SystemLogicCommand.execute_thread_function, args=(show_modal,))
|
||||
SystemLogicCommand.thread.setDaemon(True)
|
||||
SystemLogicCommand.thread.start()
|
||||
if wait:
|
||||
time.sleep(1)
|
||||
SystemLogicCommand.thread.join()
|
||||
return SystemLogicCommand.return_log
|
||||
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def execute_thread_function(show_modal):
|
||||
try:
|
||||
#if wait:
|
||||
if show_modal:
|
||||
socketio.emit("loading_hide", None, namespace='/framework', broadcast=True)
|
||||
for command in SystemLogicCommand.commands:
|
||||
#logger.debug('Command :%s', command)
|
||||
if command[0] == 'msg':
|
||||
if show_modal:
|
||||
socketio.emit("command_modal_add_text", '%s\n\n' % command[1], namespace='/framework', broadcast=True)
|
||||
elif command[0] == 'system':
|
||||
if show_modal:
|
||||
socketio.emit("command_modal_add_text", '$ %s\n\n' % command[1], namespace='/framework', broadcast=True)
|
||||
os.system(command[1])
|
||||
else:
|
||||
show_command = True
|
||||
if command[0] == 'hide':
|
||||
show_command = False
|
||||
command = command[1:]
|
||||
#SystemLogicCommand.process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, bufsize=1)
|
||||
SystemLogicCommand.process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, encoding='utf8')
|
||||
SystemLogicCommand.start_communicate(command, show_command=show_command)
|
||||
SystemLogicCommand.send_queue_start(show_modal)
|
||||
if SystemLogicCommand.process is not None:
|
||||
SystemLogicCommand.process.wait()
|
||||
time.sleep(1)
|
||||
|
||||
except Exception as exception:
|
||||
#logger.error('Exception:%s', exception)
|
||||
#logger.error(traceback.format_exc())
|
||||
if show_modal:
|
||||
socketio.emit("command_modal_show", SystemLogicCommand.title, namespace='/framework', broadcast=True)
|
||||
socketio.emit("command_modal_add_text", str(exception), namespace='/framework', broadcast=True)
|
||||
socketio.emit("command_modal_add_text", str(traceback.format_exc()), namespace='/framework', broadcast=True)
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def start_communicate(current_command, show_command=True):
|
||||
SystemLogicCommand.stdout_queue = py_queue.Queue()
|
||||
if show_command:
|
||||
SystemLogicCommand.stdout_queue.put('$ %s\n' % ' '.join(current_command))
|
||||
sout = io.open(SystemLogicCommand.process.stdout.fileno(), 'rb', closefd=False)
|
||||
#serr = io.open(process.stderr.fileno(), 'rb', closefd=False)
|
||||
|
||||
def Pump(stream):
|
||||
queue = py_queue.Queue()
|
||||
|
||||
def rdr():
|
||||
logger.debug('START RDR')
|
||||
while True:
|
||||
buf = SystemLogicCommand.process.stdout.read(1)
|
||||
if buf:
|
||||
queue.put( buf )
|
||||
else:
|
||||
queue.put( None )
|
||||
break
|
||||
logger.debug('END RDR')
|
||||
queue.put( None )
|
||||
time.sleep(1)
|
||||
|
||||
#Logic.command_close()
|
||||
def clct():
|
||||
active = True
|
||||
logger.debug('START clct')
|
||||
while active:
|
||||
r = queue.get()
|
||||
if r is None:
|
||||
break
|
||||
try:
|
||||
while True:
|
||||
r1 = queue.get(timeout=0.005)
|
||||
if r1 is None:
|
||||
active = False
|
||||
break
|
||||
else:
|
||||
r += r1
|
||||
except:
|
||||
pass
|
||||
if r is not None:
|
||||
try:
|
||||
r = r.decode('utf-8')
|
||||
except Exception as exception:
|
||||
#logger.error('Exception:%s', e)
|
||||
#logger.error(traceback.format_exc())
|
||||
try:
|
||||
r = r.decode('cp949')
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
try:
|
||||
r = r.decode('euc-kr')
|
||||
except:
|
||||
pass
|
||||
|
||||
SystemLogicCommand.stdout_queue.put(r)
|
||||
#SystemLogicCommand.return_log.append(r)
|
||||
SystemLogicCommand.return_log += r.split('\n')
|
||||
logger.debug('IN:%s', r)
|
||||
SystemLogicCommand.stdout_queue.put('<END>')
|
||||
logger.debug('END clct')
|
||||
#Logic.command_close()
|
||||
for tgt in [rdr, clct]:
|
||||
th = threading.Thread(target=tgt)
|
||||
th.setDaemon(True)
|
||||
th.start()
|
||||
Pump(sout)
|
||||
#Pump(serr, 'stderr')
|
||||
|
||||
@staticmethod
|
||||
def send_queue_start(show_modal):
|
||||
def send_to_ui_thread_function():
|
||||
logger.debug('send_queue_thread_function START')
|
||||
if show_modal:
|
||||
socketio.emit("command_modal_show", SystemLogicCommand.title, namespace='/framework', broadcast=True)
|
||||
while SystemLogicCommand.stdout_queue:
|
||||
line = SystemLogicCommand.stdout_queue.get()
|
||||
logger.debug('Send to UI :%s', line)
|
||||
if line == '<END>':
|
||||
if show_modal:
|
||||
socketio.emit("command_modal_add_text", "\n", namespace='/framework', broadcast=True)
|
||||
break
|
||||
else:
|
||||
if show_modal:
|
||||
socketio.emit("command_modal_add_text", line, namespace='/framework', broadcast=True)
|
||||
SystemLogicCommand.send_to_ui_thread = None
|
||||
SystemLogicCommand.stdout_queue = None
|
||||
SystemLogicCommand.process = None
|
||||
logger.debug('send_to_ui_thread_function END')
|
||||
|
||||
if SystemLogicCommand.send_to_ui_thread is None:
|
||||
SystemLogicCommand.send_to_ui_thread = threading.Thread(target=send_to_ui_thread_function, args=())
|
||||
SystemLogicCommand.send_to_ui_thread.start()
|
||||
|
||||
@staticmethod
|
||||
def plugin_unload():
|
||||
try:
|
||||
if SystemLogicCommand.process is not None and SystemLogicCommand.process.poll() is None:
|
||||
import psutil
|
||||
process = psutil.Process(SystemLogicCommand.process.pid)
|
||||
for proc in SystemLogicCommand.process.children(recursive=True):
|
||||
proc.kill()
|
||||
SystemLogicCommand.process.kill()
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
##################################
|
||||
# 외부 호출
|
||||
@staticmethod
|
||||
def execute_command_return(command, format=None, force_log=False):
|
||||
from tool_base import ToolSubprocess
|
||||
return ToolSubprocess.execute_command_return(command, format=format, force_log=force_log)
|
||||
"""
|
||||
try:
|
||||
logger.debug('execute_command_return : %s', ' '.join(command))
|
||||
|
||||
if app.config['config']['running_type'] == 'windows':
|
||||
command = ' '.join(command)
|
||||
|
||||
process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, encoding='utf8')
|
||||
ret = []
|
||||
with process.stdout:
|
||||
for line in iter(process.stdout.readline, ''):
|
||||
ret.append(line.strip())
|
||||
if force_log:
|
||||
logger.debug(ret[-1])
|
||||
process.wait() # wait for the subprocess to exit
|
||||
|
||||
|
||||
if format is None:
|
||||
ret2 = '\n'.join(ret)
|
||||
elif format == 'json':
|
||||
try:
|
||||
index = 0
|
||||
for idx, tmp in enumerate(ret):
|
||||
#logger.debug(tmp)
|
||||
if tmp.startswith('{') or tmp.startswith('['):
|
||||
index = idx
|
||||
break
|
||||
ret2 = json.loads(''.join(ret[index:]))
|
||||
except:
|
||||
ret2 = None
|
||||
|
||||
return ret2
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
logger.error('command : %s', command)
|
||||
"""
|
||||
|
||||
Executable
+208
@@ -0,0 +1,208 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#########################################################
|
||||
# python
|
||||
import os
|
||||
import traceback
|
||||
import logging
|
||||
import platform
|
||||
import subprocess
|
||||
import threading
|
||||
import sys
|
||||
import io
|
||||
import time
|
||||
import json
|
||||
# third-party
|
||||
|
||||
# sjva 공용
|
||||
from framework.logger import get_logger
|
||||
from framework import path_app_root, socketio, logger, py_queue, app
|
||||
|
||||
# 패키지
|
||||
|
||||
# 로그
|
||||
package_name = __name__.split('.')[0]
|
||||
#logger = get_logger(package_name)
|
||||
#########################################################
|
||||
|
||||
class SystemLogicCommand2(object):
|
||||
instance_list = []
|
||||
|
||||
def __init__(self, title, commands, clear=True, wait=False, show_modal=True):
|
||||
self.title = title
|
||||
self.commands = commands
|
||||
self.clear = clear
|
||||
self.wait = wait
|
||||
self.show_modal = show_modal
|
||||
|
||||
self.process = None
|
||||
self.stdout_queue = None
|
||||
self.thread = None
|
||||
self.send_to_ui_thread = None
|
||||
self.return_log = []
|
||||
SystemLogicCommand2.instance_list.append(self)
|
||||
|
||||
|
||||
def start(self):
|
||||
try:
|
||||
if self.show_modal:
|
||||
if self.clear:
|
||||
socketio.emit("command_modal_clear", None, namespace='/framework', broadcast=True)
|
||||
|
||||
self.thread = threading.Thread(target=self.execute_thread_function, args=())
|
||||
self.thread.setDaemon(True)
|
||||
self.thread.start()
|
||||
if self.wait:
|
||||
time.sleep(1)
|
||||
self.thread.join()
|
||||
return self.return_log
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
def execute_thread_function(self):
|
||||
try:
|
||||
#if wait:
|
||||
if self.show_modal:
|
||||
socketio.emit("command_modal_show", self.title, namespace='/framework', broadcast=True)
|
||||
socketio.emit("loading_hide", None, namespace='/framework', broadcast=True)
|
||||
|
||||
for command in self.commands:
|
||||
if command[0] == 'msg':
|
||||
if self.show_modal:
|
||||
socketio.emit("command_modal_add_text", '%s\n\n' % command[1], namespace='/framework', broadcast=True)
|
||||
elif command[0] == 'system':
|
||||
if self.show_modal:
|
||||
socketio.emit("command_modal_add_text", '$ %s\n\n' % command[1], namespace='/framework', broadcast=True)
|
||||
os.system(command[1])
|
||||
else:
|
||||
show_command = True
|
||||
if command[0] == 'hide':
|
||||
show_command = False
|
||||
command = command[1:]
|
||||
#self.process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, bufsize=1)
|
||||
self.process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, encoding='utf8')
|
||||
self.start_communicate(command, show_command=show_command)
|
||||
self.send_queue_start()
|
||||
if self.process is not None:
|
||||
self.process.wait()
|
||||
time.sleep(1)
|
||||
except Exception as exception:
|
||||
if self.show_modal:
|
||||
socketio.emit("command_modal_show", self.title, namespace='/framework', broadcast=True)
|
||||
socketio.emit("command_modal_add_text", str(exception), namespace='/framework', broadcast=True)
|
||||
socketio.emit("command_modal_add_text", str(traceback.format_exc()), namespace='/framework', broadcast=True)
|
||||
|
||||
|
||||
def start_communicate(self, current_command, show_command=True):
|
||||
self.stdout_queue = py_queue.Queue()
|
||||
if show_command:
|
||||
self.stdout_queue.put('$ %s\n' % ' '.join(current_command))
|
||||
sout = io.open(self.process.stdout.fileno(), 'rb', closefd=False)
|
||||
#serr = io.open(process.stderr.fileno(), 'rb', closefd=False)
|
||||
|
||||
def Pump(stream):
|
||||
queue = py_queue.Queue()
|
||||
|
||||
def rdr():
|
||||
#logger.debug('START RDR')
|
||||
while True:
|
||||
buf = self.process.stdout.read(1)
|
||||
if buf:
|
||||
queue.put( buf )
|
||||
else:
|
||||
queue.put( None )
|
||||
break
|
||||
#logger.debug('END RDR')
|
||||
queue.put( None )
|
||||
time.sleep(1)
|
||||
|
||||
#Logic.command_close()
|
||||
def clct():
|
||||
active = True
|
||||
#logger.debug('START clct')
|
||||
while active:
|
||||
r = queue.get()
|
||||
if r is None:
|
||||
break
|
||||
try:
|
||||
while True:
|
||||
r1 = queue.get(timeout=0.005)
|
||||
if r1 is None:
|
||||
active = False
|
||||
break
|
||||
else:
|
||||
r += r1
|
||||
except:
|
||||
pass
|
||||
if r is not None:
|
||||
if app.config['config']['is_py2']:
|
||||
try:
|
||||
r = r.decode('utf-8')
|
||||
except Exception as exception:
|
||||
#logger.error('Exception:%s', e)
|
||||
#logger.error(traceback.format_exc())
|
||||
try:
|
||||
r = r.decode('cp949')
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
try:
|
||||
r = r.decode('euc-kr')
|
||||
except:
|
||||
pass
|
||||
|
||||
self.stdout_queue.put(r)
|
||||
self.return_log += r.split('\n')
|
||||
#logger.debug('IN:%s', r)
|
||||
self.stdout_queue.put('<END>')
|
||||
#logger.debug('END clct')
|
||||
#Logic.command_close()
|
||||
for tgt in [rdr, clct]:
|
||||
th = threading.Thread(target=tgt)
|
||||
th.setDaemon(True)
|
||||
th.start()
|
||||
Pump(sout)
|
||||
#Pump(serr, 'stderr')
|
||||
|
||||
def send_queue_start(self):
|
||||
def send_to_ui_thread_function():
|
||||
#logger.debug('send_queue_thread_function START')
|
||||
if self.show_modal:
|
||||
socketio.emit("command_modal_show", self.title, namespace='/framework', broadcast=True)
|
||||
while self.stdout_queue:
|
||||
line = self.stdout_queue.get()
|
||||
#logger.debug('Send to UI :%s', line)
|
||||
if line == '<END>':
|
||||
if self.show_modal:
|
||||
socketio.emit("command_modal_add_text", "\n", namespace='/framework', broadcast=True)
|
||||
break
|
||||
else:
|
||||
if self.show_modal:
|
||||
socketio.emit("command_modal_add_text", line, namespace='/framework', broadcast=True)
|
||||
self.send_to_ui_thread = None
|
||||
self.stdout_queue = None
|
||||
self.process = None
|
||||
#logger.debug('send_to_ui_thread_function END')
|
||||
|
||||
if self.send_to_ui_thread is None:
|
||||
self.send_to_ui_thread = threading.Thread(target=send_to_ui_thread_function, args=())
|
||||
self.send_to_ui_thread.start()
|
||||
|
||||
|
||||
@classmethod
|
||||
def plugin_unload(cls):
|
||||
for instance in cls.instance_list:
|
||||
try:
|
||||
if instance.process is not None and instance.process.poll() is None:
|
||||
import psutil
|
||||
process = psutil.Process(instance.process.pid)
|
||||
for proc in instance.process.children(recursive=True):
|
||||
proc.kill()
|
||||
instance.process.kill()
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
finally:
|
||||
try: instance.process.kill()
|
||||
except: pass
|
||||
@@ -0,0 +1,346 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#########################################################
|
||||
# python
|
||||
import os
|
||||
import traceback
|
||||
import logging
|
||||
import json
|
||||
import zipfile
|
||||
import time
|
||||
import platform
|
||||
|
||||
# third-party
|
||||
import requests
|
||||
from flask import (
|
||||
Blueprint,
|
||||
request,
|
||||
Response,
|
||||
send_file,
|
||||
render_template,
|
||||
redirect,
|
||||
jsonify,
|
||||
)
|
||||
from flask_login import login_user, logout_user, current_user, login_required
|
||||
|
||||
# gommi 공용
|
||||
from framework.logger import get_logger, set_level
|
||||
from framework import (
|
||||
app,
|
||||
db,
|
||||
scheduler,
|
||||
version,
|
||||
path_app_root,
|
||||
path_data,
|
||||
USERS,
|
||||
)
|
||||
from framework.util import Util
|
||||
|
||||
# 패키지
|
||||
from .model import ModelSetting
|
||||
import system
|
||||
|
||||
# 로그
|
||||
package_name = __name__.split(".")[0]
|
||||
logger = get_logger(package_name)
|
||||
#########################################################
|
||||
|
||||
|
||||
class LogicPlugin(object):
|
||||
plugin_loading = False
|
||||
|
||||
current_loading_plugin_list = {}
|
||||
|
||||
"""
|
||||
custom_plugin_list = []
|
||||
@staticmethod
|
||||
def loading():
|
||||
try:
|
||||
custom_path = os.path.join(path_data, 'custom')
|
||||
plugin_list = os.listdir(custom_path)
|
||||
logger.debug(plugin_list)
|
||||
for name in plugin_list:
|
||||
try:
|
||||
p = {}
|
||||
p['name'] = name
|
||||
p['plugin_name'] = name
|
||||
mod = __import__('%s' % (p['plugin_name']), fromlist=[])
|
||||
p['local_info'] = getattr(mod, 'plugin_info')
|
||||
p['status'] = 'latest'
|
||||
LogicPlugin.custom_plugin_list.append(p)
|
||||
except Exception as exception:
|
||||
logger.error('NO Exception:%s', exception)
|
||||
logger.debug('plunin not import : %s', p['plugin_name'])
|
||||
p['local_info'] = None
|
||||
p['status'] = 'no'
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_plugin_list():
|
||||
return LogicPlugin.current_loading_plugin_list
|
||||
"""
|
||||
try:
|
||||
if not LogicPlugin.plugin_loading:
|
||||
LogicPlugin.loading()
|
||||
LogicPlugin.plugin_loading = True
|
||||
return LogicPlugin.custom_plugin_list
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_plugin_info(plugin_name):
|
||||
try:
|
||||
lists = LogicPlugin.get_plugin_list()
|
||||
for key, value in lists.items():
|
||||
if key == plugin_name:
|
||||
return value["info"]
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
"""
|
||||
@staticmethod
|
||||
def plugin_install(plugin_name):
|
||||
logger.debug('plugin_name : %s', plugin_name)
|
||||
try:
|
||||
plugin_info = LogicPlugin.get_plugin_info(plugin_name)
|
||||
|
||||
custom_path = os.path.join(path_data, 'custom')
|
||||
|
||||
if 'platform' in plugin_info:
|
||||
if platform.system() not in plugin_info['platform']:
|
||||
return 'not_support_os'
|
||||
if 'running_type' in plugin_info:
|
||||
if app.config['config']['running_type'] not in plugin_info['running_type']:
|
||||
return 'not_support_running_type'
|
||||
git_clone_flag = True
|
||||
if git_clone_flag:
|
||||
# git clone
|
||||
command = ['git', '-C', custom_path, 'clone', plugin_info['git'], '--depth', '1']
|
||||
ret = Util.execute_command(command)
|
||||
return 'success'
|
||||
except Exception as exception:
|
||||
logger.error('Exception:%s', exception)
|
||||
logger.error(traceback.format_exc())
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def plugin_uninstall(plugin_name):
|
||||
logger.debug("plugin_name : %s", plugin_name)
|
||||
try:
|
||||
mod = __import__("%s" % (plugin_name), fromlist=[])
|
||||
mod_plugin_unload = getattr(mod, "plugin_unload")
|
||||
mod_plugin_unload()
|
||||
time.sleep(1)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
try:
|
||||
custom_path = os.path.join(path_data, "custom")
|
||||
plugin_path = os.path.join(custom_path, plugin_name)
|
||||
if os.path.exists(plugin_path):
|
||||
try:
|
||||
import framework.common.celery as celery_task
|
||||
|
||||
celery_task.rmtree(plugin_path)
|
||||
except Exception as exception:
|
||||
try:
|
||||
logger.debug("plugin_uninstall")
|
||||
os.system('rmdir /S /Q "%s"' % plugin_path)
|
||||
except Exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
if os.path.exists(plugin_path):
|
||||
return "fail"
|
||||
else:
|
||||
return "success"
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def custom_plugin_update():
|
||||
try:
|
||||
if os.environ.get("UPDATE_STOP") == "true":
|
||||
return
|
||||
if os.environ.get("PLUGIN_UPDATE_FROM_PYTHON") == "false":
|
||||
return
|
||||
custom_path = os.path.join(path_data, "custom")
|
||||
tmps = os.listdir(custom_path)
|
||||
for t in tmps:
|
||||
plugin_path = os.path.join(custom_path, t)
|
||||
try:
|
||||
if t == "torrent_info":
|
||||
os.remove(os.path.join(plugin_path, "info.json"))
|
||||
except Exception:
|
||||
pass
|
||||
if t.startswith("_"):
|
||||
continue
|
||||
if os.path.exists(os.path.join(plugin_path, ".git")):
|
||||
command = [
|
||||
"git",
|
||||
"-C",
|
||||
plugin_path,
|
||||
"reset",
|
||||
"--hard",
|
||||
"HEAD",
|
||||
]
|
||||
ret = Util.execute_command(command)
|
||||
command = ["git", "-C", plugin_path, "pull"]
|
||||
ret = Util.execute_command(command)
|
||||
logger.debug("%s\n%s", plugin_path, ret)
|
||||
else:
|
||||
logger.debug(f"{plugin_path} is not git repo")
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def plugin_install_by_api(plugin_git, zip_url, zip_filename):
|
||||
logger.debug("plugin_git : %s", plugin_git)
|
||||
logger.debug("zip_url : %s", zip_url)
|
||||
logger.debug("zip_filename : %s", zip_filename)
|
||||
|
||||
is_git = True if plugin_git != None and plugin_git != "" else False
|
||||
ret = {}
|
||||
try:
|
||||
if is_git:
|
||||
name = plugin_git.split("/")[-1]
|
||||
else:
|
||||
name = zip_filename.split(".")[0]
|
||||
|
||||
custom_path = os.path.join(path_data, "custom")
|
||||
plugin_path = os.path.join(custom_path, name)
|
||||
logger.debug(plugin_path)
|
||||
plugin_info = None
|
||||
if os.path.exists(plugin_path):
|
||||
ret["ret"] = "already_exist"
|
||||
ret["log"] = "이미 설치되어 있습니다."
|
||||
else:
|
||||
if plugin_git and plugin_git.startswith("http"):
|
||||
for tag in ["main", "master"]:
|
||||
try:
|
||||
info_url = (
|
||||
plugin_git.replace(
|
||||
"github.com", "raw.githubusercontent.com"
|
||||
)
|
||||
+ "/%s/info.json" % tag
|
||||
)
|
||||
plugin_info = requests.get(info_url).json()
|
||||
if plugin_info is not None:
|
||||
break
|
||||
except:
|
||||
pass
|
||||
if zip_filename and zip_filename != "":
|
||||
import zipfile
|
||||
from tool_base import ToolBaseFile
|
||||
|
||||
zip_filepath = os.path.join(path_data, "tmp", zip_filename)
|
||||
extract_filepath = os.path.join(path_data, "tmp", name)
|
||||
logger.error(zip_url)
|
||||
logger.warning(zip_filepath)
|
||||
if ToolBaseFile.download(zip_url, zip_filepath):
|
||||
# logger.warning(os.path.exists(zip_filepath))
|
||||
with zipfile.ZipFile(zip_filepath, "r") as zip_ref:
|
||||
zip_ref.extractall(extract_filepath)
|
||||
plugin_info_filepath = os.path.join(
|
||||
extract_filepath, "info.json"
|
||||
)
|
||||
if os.path.exists(plugin_info_filepath):
|
||||
plugin_info = ToolBaseFile.read_json(
|
||||
plugin_info_filepath
|
||||
)
|
||||
if plugin_info == None:
|
||||
plugin_info = {}
|
||||
flag = True
|
||||
if "platform" in plugin_info:
|
||||
if platform.system() not in plugin_info["platform"]:
|
||||
ret["ret"] = "not_support_os"
|
||||
ret["log"] = "설치 가능한 OS가 아닙니다."
|
||||
flag = False
|
||||
if flag and "running_type" in plugin_info:
|
||||
if (
|
||||
app.config["config"]["running_type"]
|
||||
not in plugin_info["running_type"]
|
||||
):
|
||||
ret["ret"] = "not_support_running_type"
|
||||
ret["log"] = "설치 가능한 실행타입이 아닙니다."
|
||||
flag = False
|
||||
if flag and "policy_level" in plugin_info:
|
||||
if (
|
||||
plugin_info["policy_level"]
|
||||
> app.config["config"]["level"]
|
||||
):
|
||||
ret["ret"] = "policy_level"
|
||||
ret["log"] = "설치 가능 회원등급보다 낮습니다."
|
||||
flag = False
|
||||
if flag and "policy_point" in plugin_info:
|
||||
if (
|
||||
plugin_info["policy_level"]
|
||||
> app.config["config"]["point"]
|
||||
):
|
||||
ret["ret"] = "policy_level"
|
||||
ret["log"] = "설치 가능 포인트보다 낮습니다."
|
||||
flag = False
|
||||
|
||||
if flag:
|
||||
if plugin_git and plugin_git.startswith("http"):
|
||||
command = [
|
||||
"git",
|
||||
"-C",
|
||||
custom_path,
|
||||
"clone",
|
||||
plugin_git + ".git",
|
||||
"--depth",
|
||||
"1",
|
||||
]
|
||||
log = Util.execute_command(command)
|
||||
if zip_filename and zip_filename != "":
|
||||
import shutil
|
||||
|
||||
if os.path.exists(plugin_path) == False:
|
||||
shutil.move(extract_filepath, plugin_path)
|
||||
else:
|
||||
for tmp in os.listdir(extract_filepath):
|
||||
shutil.move(
|
||||
os.path.join(extract_filepath, tmp),
|
||||
plugin_path,
|
||||
)
|
||||
log = ""
|
||||
logger.debug(plugin_info)
|
||||
# 2021-12-31
|
||||
if "dependency" in plugin_info:
|
||||
for dep in plugin_info["dependency"]:
|
||||
for (
|
||||
key,
|
||||
value,
|
||||
) in LogicPlugin.get_plugin_list().items():
|
||||
if key == dep["name"]:
|
||||
logger.debug(
|
||||
f"Dependency 설치 - 이미 설치됨 : {dep['name']}"
|
||||
)
|
||||
break
|
||||
else:
|
||||
logger.debug(f"Dependency 설치 : {dep['home']}")
|
||||
LogicPlugin.plugin_install_by_api(
|
||||
dep["home"],
|
||||
dep.get("zip_url"),
|
||||
dep.get("zip_filename"),
|
||||
)
|
||||
# command = ['git', '-C', custom_path, 'clone', dep['home'], '--depth', '1']
|
||||
# ret = Util.execute_command(command)
|
||||
ret["ret"] = "success"
|
||||
ret["log"] = ["정상적으로 설치하였습니다. 재시작시 적용됩니다.", log]
|
||||
ret["log"] = "<br>".join(ret["log"])
|
||||
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
ret["ret"] = "exception"
|
||||
ret["log"] = str(exception)
|
||||
|
||||
return ret
|
||||
Executable
+445
@@ -0,0 +1,445 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#########################################################
|
||||
# python
|
||||
import os
|
||||
import traceback
|
||||
import logging
|
||||
import platform
|
||||
import time
|
||||
import base64
|
||||
|
||||
# third-party
|
||||
from flask import (
|
||||
Blueprint,
|
||||
request,
|
||||
Response,
|
||||
send_file,
|
||||
render_template,
|
||||
redirect,
|
||||
jsonify,
|
||||
)
|
||||
|
||||
try:
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from PIL import Image
|
||||
|
||||
Image.MAX_IMAGE_PIXELS = None
|
||||
except:
|
||||
pass
|
||||
from io import BytesIO
|
||||
|
||||
# sjva 공용
|
||||
from framework.logger import get_logger
|
||||
from framework import path_app_root, path_data
|
||||
|
||||
# 패키지
|
||||
from .plugin import logger, package_name
|
||||
from .model import ModelSetting
|
||||
|
||||
#########################################################
|
||||
# apk --no-cache add --repository http://dl-cdn.alpinelinux.org/alpine/edge/testing firefox
|
||||
# https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-linux64.tar.gz
|
||||
# curl -s -L "$url" | tar -xz
|
||||
|
||||
|
||||
class SystemLogicSelenium(object):
|
||||
chrome_driver = None
|
||||
chrome_driver_list = []
|
||||
|
||||
@staticmethod
|
||||
def process_ajax(sub, req):
|
||||
try:
|
||||
if sub == "selenium_test_go":
|
||||
driver = SystemLogicSelenium.get_driver()
|
||||
driver.get(req.form["url"])
|
||||
return jsonify("success")
|
||||
elif sub == "capture":
|
||||
driver = SystemLogicSelenium.get_driver()
|
||||
img = Image.open(BytesIO((driver.get_screenshot_as_png())))
|
||||
|
||||
timestamp = time.time()
|
||||
timestamp = str(timestamp).split(".")[0]
|
||||
tmp = os.path.join(path_data, "tmp", "%s.png" % timestamp)
|
||||
img.save(tmp)
|
||||
from system.model import ModelSetting as SystemModelSetting
|
||||
|
||||
ddns = SystemModelSetting.get("ddns")
|
||||
url = "%s/open_file%s" % (ddns, tmp)
|
||||
logger.debug(url)
|
||||
ret = {}
|
||||
ret["ret"] = "success"
|
||||
ret["data"] = url
|
||||
return jsonify(ret)
|
||||
elif sub == "full_capture":
|
||||
driver = SystemLogicSelenium.get_driver()
|
||||
img = SystemLogicSelenium.full_screenshot(driver)
|
||||
|
||||
timestamp = time.time()
|
||||
timestamp = str(timestamp).split(".")[0]
|
||||
tmp = os.path.join(path_data, "tmp", "%s.png" % timestamp)
|
||||
img.save(tmp)
|
||||
return send_file(tmp, mimetype="image/png")
|
||||
elif sub == "cookie":
|
||||
driver = SystemLogicSelenium.get_driver()
|
||||
data = driver.get_cookies()
|
||||
return jsonify(data)
|
||||
elif sub == "daum_capcha":
|
||||
daum_capcha = req.form["daum_capcha"]
|
||||
driver = SystemLogicSelenium.get_driver()
|
||||
# driver.find_element_by_xpath('//div[@class="secret_viewer"]/p/img').screenshot("captcha.png")
|
||||
driver.find_element_by_xpath('//input[@id="answer"]').send_keys(
|
||||
daum_capcha
|
||||
)
|
||||
driver.find_element_by_xpath(
|
||||
'//input[@value="%s"]' % "확인"
|
||||
).click()
|
||||
return jsonify({"ret": "success"})
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return jsonify("exception")
|
||||
|
||||
@staticmethod
|
||||
def get_pagesoruce_by_selenium(url, wait_xpath, retry=True):
|
||||
try:
|
||||
logger.debug("get_pagesoruce_by_selenium:%s %s", url, wait_xpath)
|
||||
driver = SystemLogicSelenium.get_driver()
|
||||
# logger.debug('driver : %s', driver)
|
||||
driver.get(url)
|
||||
|
||||
WebDriverWait(driver, 10).until(
|
||||
lambda driver: driver.find_element_by_xpath(wait_xpath)
|
||||
)
|
||||
# import time
|
||||
# driver.save_screenshot('%s.png' % time.time())
|
||||
# logger.debug('return page_source')
|
||||
return driver.page_source
|
||||
except Exception as exception:
|
||||
# logger.debug(driver.page_source)
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
SystemLogicSelenium.close_driver()
|
||||
# SystemLogicSelenium.chrome_driver = None
|
||||
if retry:
|
||||
return SystemLogicSelenium.get_pagesoruce_by_selenium(
|
||||
url, wait_xpath, retry=False
|
||||
)
|
||||
|
||||
# 1회성
|
||||
@staticmethod
|
||||
def get_driver(chrome_options=None):
|
||||
try:
|
||||
if SystemLogicSelenium.chrome_driver is None:
|
||||
SystemLogicSelenium.chrome_driver = (
|
||||
SystemLogicSelenium.inner_create_driver(chrome_options)
|
||||
)
|
||||
return SystemLogicSelenium.chrome_driver
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
# 플러그인이 점유
|
||||
@staticmethod
|
||||
def create_driver(chrome_options=None):
|
||||
try:
|
||||
driver = SystemLogicSelenium.inner_create_driver(chrome_options)
|
||||
if driver is not None:
|
||||
SystemLogicSelenium.chrome_driver_list.append(driver)
|
||||
return driver
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def close_driver():
|
||||
try:
|
||||
# if SystemLogicSelenium.chrome_driver is not None:
|
||||
# SystemLogicSelenium.chrome_driver.quit()
|
||||
# SystemLogicSelenium.chrome_driver = None
|
||||
if SystemLogicSelenium.chrome_driver is not None:
|
||||
try:
|
||||
SystemLogicSelenium.chrome_driver.close()
|
||||
except Exception:
|
||||
pass
|
||||
time.sleep(2)
|
||||
try:
|
||||
SystemLogicSelenium.chrome_driver.quit()
|
||||
except Exception:
|
||||
pass
|
||||
SystemLogicSelenium.chrome_driver = None
|
||||
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def inner_create_driver(chrome_options):
|
||||
try:
|
||||
driver = None
|
||||
remote_url = ModelSetting.get("selenium_remote_url")
|
||||
if remote_url.endswith("/wd/hub/"):
|
||||
remote_url = remote_url[:-1]
|
||||
if remote_url != "":
|
||||
if chrome_options is None:
|
||||
chrome_options = webdriver.ChromeOptions()
|
||||
tmp = ModelSetting.get_list(
|
||||
"selenium_remote_default_option"
|
||||
)
|
||||
for t in tmp:
|
||||
chrome_options.add_argument(t)
|
||||
driver = webdriver.Remote(
|
||||
command_executor=remote_url,
|
||||
desired_capabilities=chrome_options.to_capabilities(),
|
||||
)
|
||||
driver.set_window_size(1920, 1080)
|
||||
logger.debug("Using Remote :%s", driver)
|
||||
else:
|
||||
path_chrome = os.path.join(
|
||||
path_app_root, "bin", platform.system(), "chromedriver"
|
||||
)
|
||||
if platform.system() == "Windows":
|
||||
path_chrome += ".exe"
|
||||
if chrome_options is None:
|
||||
chrome_options = webdriver.ChromeOptions()
|
||||
tmp = ModelSetting.get_list(
|
||||
"selenium_binary_default_option"
|
||||
)
|
||||
for t in tmp:
|
||||
chrome_options.add_argument(t)
|
||||
driver = webdriver.Chrome(
|
||||
path_chrome, chrome_options=chrome_options
|
||||
)
|
||||
logger.debug("Using local bin :%s", driver)
|
||||
if driver is not None:
|
||||
return driver
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def plugin_unload():
|
||||
try:
|
||||
SystemLogicSelenium.close_driver()
|
||||
# logger.debug(SystemLogicSelenium.chrome_driver)
|
||||
# if SystemLogicSelenium.chrome_driver is not None:
|
||||
# SystemLogicSelenium.chrome_driver.quit()
|
||||
# logger.debug(SystemLogicSelenium.chrome_driver)
|
||||
|
||||
for tmp in SystemLogicSelenium.chrome_driver_list:
|
||||
if tmp is not None:
|
||||
tmp.quit()
|
||||
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def get_text_excluding_children(driver, element):
|
||||
return driver.execute_script(
|
||||
"""
|
||||
return jQuery(arguments[0]).contents().filter(function() {
|
||||
return this.nodeType == Node.TEXT_NODE;
|
||||
}).text();
|
||||
""",
|
||||
element,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def full_screenshot(driver, low_offset=0):
|
||||
try:
|
||||
# initiate value
|
||||
# save_path = save_path + '.png' if save_path[-4::] != '.png' else save_path
|
||||
img_li = [] # to store image fragment
|
||||
offset = 0 # where to start
|
||||
|
||||
# js to get height
|
||||
height = driver.execute_script(
|
||||
"return Math.max("
|
||||
"document.documentElement.clientHeight, window.innerHeight);"
|
||||
)
|
||||
# height = height - low_offset
|
||||
# js to get the maximum scroll height
|
||||
# Ref--> https://stackoverflow.com/questions/17688595/finding-the-maximum-scroll-position-of-a-page
|
||||
max_window_height = driver.execute_script(
|
||||
"return Math.max("
|
||||
"document.body.scrollHeight, "
|
||||
"document.body.offsetHeight, "
|
||||
"document.documentElement.clientHeight, "
|
||||
"document.documentElement.scrollHeight, "
|
||||
"document.documentElement.offsetHeight);"
|
||||
)
|
||||
|
||||
# looping from top to bottom, append to img list
|
||||
# Ref--> https://gist.github.com/fabtho/13e4a2e7cfbfde671b8fa81bbe9359fb
|
||||
|
||||
while offset < max_window_height:
|
||||
|
||||
# Scroll to height
|
||||
driver.execute_script(
|
||||
"""
|
||||
window.scrollTo(0, arguments[0]);
|
||||
""",
|
||||
offset,
|
||||
)
|
||||
img = Image.open(BytesIO((driver.get_screenshot_as_png())))
|
||||
|
||||
if low_offset != 0:
|
||||
img = img.crop(
|
||||
(0, 0, img.width, img.height - low_offset)
|
||||
) # defines crop points
|
||||
|
||||
img_li.append(img)
|
||||
offset += height
|
||||
logger.debug("offset : %s / %s", offset, max_window_height)
|
||||
|
||||
# Stitch image into one
|
||||
# Set up the full screen frame
|
||||
img_frame_height = sum([img_frag.size[1] for img_frag in img_li])
|
||||
img_frame = Image.new("RGB", (img_li[0].size[0], img_frame_height))
|
||||
offset = 0
|
||||
for img_frag in img_li:
|
||||
img_frame.paste(img_frag, (0, offset))
|
||||
offset += img_frag.size[1]
|
||||
logger.debug("paste offset : %s ", offset)
|
||||
# img_frame.save(save_path)
|
||||
# return
|
||||
return img_frame
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def remove_element(driver, element):
|
||||
driver.execute_script(
|
||||
"""
|
||||
var element = arguments[0];
|
||||
element.parentNode.removeChild(element);
|
||||
""",
|
||||
element,
|
||||
)
|
||||
|
||||
######################################################################
|
||||
@staticmethod
|
||||
def __get_downloaded_files(driver=None):
|
||||
if driver is None:
|
||||
driver = SystemLogicSelenium.get_driver()
|
||||
if not driver.current_url.startswith("chrome://downloads"):
|
||||
driver.get("chrome://downloads/")
|
||||
# driver.implicitly_wait(4)
|
||||
return driver.execute_script(
|
||||
"return downloads.Manager.get().items_ "
|
||||
" .filter(e => e.state === 'COMPLETE') "
|
||||
" .map(e => e.filePath || e.file_path); "
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_file_content(path, driver=None):
|
||||
if driver is None:
|
||||
driver = SystemLogicSelenium.get_driver()
|
||||
|
||||
elem = driver.execute_script(
|
||||
"var input = window.document.createElement('INPUT'); "
|
||||
"input.setAttribute('type', 'file'); "
|
||||
"input.hidden = true; "
|
||||
"input.onchange = function (e) { e.stopPropagation() }; "
|
||||
"return window.document.documentElement.appendChild(input); "
|
||||
)
|
||||
|
||||
elem._execute("sendKeysToElement", {"value": [path], "text": path})
|
||||
|
||||
result = driver.execute_async_script(
|
||||
"var input = arguments[0], callback = arguments[1]; "
|
||||
"var reader = new FileReader(); "
|
||||
"reader.onload = function (ev) { callback(reader.result) }; "
|
||||
"reader.onerror = function (ex) { callback(ex.message) }; "
|
||||
"reader.readAsDataURL(input.files[0]); "
|
||||
"input.remove(); ",
|
||||
elem,
|
||||
)
|
||||
|
||||
if not result.startswith("data:"):
|
||||
raise Exception("Failed to get file content: %s" % result)
|
||||
|
||||
return base64.b64decode(result[result.find("base64,") + 7 :])
|
||||
|
||||
@staticmethod
|
||||
def get_downloaded_files(driver=None):
|
||||
if driver is None:
|
||||
driver = SystemLogicSelenium.get_driver()
|
||||
|
||||
# files = WebDriverWait(driver, 20, 1).until(SystemLogicSelenium.__get_downloaded_files)
|
||||
files = SystemLogicSelenium.__get_downloaded_files()
|
||||
return files
|
||||
|
||||
@staticmethod
|
||||
def waitUntilDownloadCompleted(maxTime=600, driver=None):
|
||||
if driver is None:
|
||||
driver = SystemLogicSelenium.get_driver()
|
||||
driver.execute_script("window.open()")
|
||||
# switch to new tab
|
||||
driver.switch_to.window(driver.window_handles[-1])
|
||||
# navigate to chrome downloads
|
||||
driver.get("chrome://downloads")
|
||||
# define the endTime
|
||||
endTime = time.time() + maxTime
|
||||
while True:
|
||||
try:
|
||||
# get the download percentage
|
||||
downloadPercentage = driver.execute_script(
|
||||
"return document.querySelector('downloads-manager').shadowRoot.querySelector('#downloadsList downloads-item').shadowRoot.querySelector('#progress').value"
|
||||
)
|
||||
# check if downloadPercentage is 100 (otherwise the script will keep waiting)
|
||||
if downloadPercentage == 100:
|
||||
# exit the method once it's completed
|
||||
return downloadPercentage
|
||||
except:
|
||||
pass
|
||||
# wait for 1 second before checking the percentage next time
|
||||
time.sleep(1)
|
||||
# exit method if the download not completed with in MaxTime.
|
||||
if time.time() > endTime:
|
||||
break
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
driver = webdriver.Chrome(desired_capabilities=capabilities_chrome)
|
||||
#driver = webdriver.Remote('http://127.0.0.1:5555/wd/hub', capabilities_chrome)
|
||||
|
||||
# download a pdf file
|
||||
driver.get("https://www.mozilla.org/en-US/foundation/documents")
|
||||
driver.find_element_by_css_selector("[href$='.pdf']").click()
|
||||
|
||||
# list all the completed remote files (waits for at least one)
|
||||
files = WebDriverWait(driver, 20, 1).until(get_downloaded_files)
|
||||
|
||||
# get the content of the first file remotely
|
||||
content = get_file_content(driver, files[0])
|
||||
|
||||
# save the content in a local file in the working directory
|
||||
with open(os.path.basename(files[0]), 'wb') as f:
|
||||
f.write(content)
|
||||
|
||||
|
||||
capabilities_chrome = { \
|
||||
'browserName': 'chrome',
|
||||
# 'proxy': { \
|
||||
# 'proxyType': 'manual',
|
||||
# 'sslProxy': '50.59.162.78:8088',
|
||||
# 'httpProxy': '50.59.162.78:8088'
|
||||
# },
|
||||
'goog:chromeOptions': { \
|
||||
'args': [
|
||||
],
|
||||
'prefs': { \
|
||||
# 'download.default_directory': "",
|
||||
# 'download.directory_upgrade': True,
|
||||
'download.prompt_for_download': False,
|
||||
'plugins.always_open_pdf_externally': True,
|
||||
'safebrowsing_for_trusted_sources_enabled': False
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
@@ -0,0 +1,100 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#########################################################
|
||||
# python
|
||||
import os
|
||||
import traceback
|
||||
import logging
|
||||
import platform
|
||||
import time
|
||||
|
||||
# third-party
|
||||
from flask import (
|
||||
Blueprint,
|
||||
request,
|
||||
Response,
|
||||
send_file,
|
||||
render_template,
|
||||
redirect,
|
||||
jsonify,
|
||||
)
|
||||
|
||||
|
||||
# sjva 공용
|
||||
from framework.logger import get_logger
|
||||
from framework import path_app_root, path_data, socketio, scheduler
|
||||
from framework.job import Job
|
||||
from tool_base import d
|
||||
|
||||
|
||||
# 패키지
|
||||
from .plugin import logger, package_name
|
||||
from .model import ModelSetting
|
||||
|
||||
|
||||
class SystemLogicSite(object):
|
||||
# pass
|
||||
daum_cookie = None
|
||||
|
||||
@staticmethod
|
||||
def process_ajax(sub, req):
|
||||
try:
|
||||
ret = {}
|
||||
if sub == "site_daum_test":
|
||||
pass
|
||||
elif sub == "scheduler":
|
||||
go = req.form["scheduler"]
|
||||
if go == "true":
|
||||
SystemLogicSite.scheduler_start()
|
||||
else:
|
||||
SystemLogicSite.scheduler_stop()
|
||||
return jsonify(go)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
ret["ret"] = False
|
||||
ret["log"] = str(traceback.format_exc())
|
||||
return jsonify(ret)
|
||||
|
||||
@staticmethod
|
||||
def plugin_load():
|
||||
|
||||
SystemLogicSite.get_daum_cookies(force=True)
|
||||
if ModelSetting.get_bool("site_daum_auto_start"):
|
||||
SystemLogicSite.scheduler_start()
|
||||
|
||||
@staticmethod
|
||||
def scheduler_function():
|
||||
try:
|
||||
data = SystemLogicSite.get_daum_cookie_by_selenium()
|
||||
if data["ret"]:
|
||||
ModelSetting.set("site_daum_cookie", data["data"])
|
||||
SystemLogicSite.get_daum_cookies(force=True)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def get_daum_cookies(force=False):
|
||||
try:
|
||||
if SystemLogicSite.daum_cookie is None or force:
|
||||
ret = {}
|
||||
tmp = ModelSetting.get("site_daum_cookie")
|
||||
tmps = tmp.split(";")
|
||||
for t in tmps:
|
||||
t2 = t.split("=")
|
||||
if len(t2) == 2:
|
||||
ret[t2[0]] = t2[1]
|
||||
SystemLogicSite.daum_cookie = ret
|
||||
return SystemLogicSite.daum_cookie
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return {
|
||||
"TIARA": "gaXEIPluo-wWAFlwZN6l8gN3yzhkoo_piP.Kymhuy.6QBt4Q6.cRtxbKDaWpWajcyteRHzrlTVpJRxLjwLoMvyYLVi_7xJ1L"
|
||||
}
|
||||
|
||||
daum_headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36",
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
|
||||
"Accept-Language": "ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7",
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# =========================================================
|
||||
# python
|
||||
import traceback
|
||||
|
||||
# third-party
|
||||
import requests
|
||||
from flask import (
|
||||
Blueprint,
|
||||
request,
|
||||
Response,
|
||||
send_file,
|
||||
render_template,
|
||||
redirect,
|
||||
jsonify,
|
||||
)
|
||||
|
||||
# gommi 공용
|
||||
from framework.logger import get_logger
|
||||
from framework import app, db, scheduler
|
||||
from framework.util import Util
|
||||
|
||||
# 패키지
|
||||
|
||||
# 로그
|
||||
package_name = __name__.split(".")[0]
|
||||
logger = get_logger(package_name)
|
||||
# ==============================================================
|
||||
|
||||
|
||||
class ModelSetting(db.Model):
|
||||
__tablename__ = "system_setting"
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
key = db.Column(db.String(100), unique=True, nullable=False)
|
||||
value = db.Column(db.String(100), nullable=False)
|
||||
|
||||
def __init__(self, key, value):
|
||||
self.key = key
|
||||
self.value = value
|
||||
|
||||
def __repr__(self):
|
||||
return "<SystemSetting(id:%s, key:%s, value:%s)>" % (
|
||||
self.id,
|
||||
self.key,
|
||||
self.value,
|
||||
)
|
||||
|
||||
def as_dict(self):
|
||||
return {x.name: getattr(self, x.name) for x in self.__table__.columns}
|
||||
|
||||
@staticmethod
|
||||
def get(key):
|
||||
try:
|
||||
ret = db.session.query(ModelSetting).filter_by(key=key).first()
|
||||
if ret is not None:
|
||||
return ret.value.strip()
|
||||
else:
|
||||
return ""
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s %s", exception, key)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def get_int(key):
|
||||
try:
|
||||
return int(ModelSetting.get(key))
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s %s", exception, key)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def get_bool(key):
|
||||
try:
|
||||
return ModelSetting.get(key) == "True"
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s %s", exception, key)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def set(key, value):
|
||||
try:
|
||||
logger.debug(key)
|
||||
item = (
|
||||
db.session.query(ModelSetting)
|
||||
.filter_by(key=key)
|
||||
.with_for_update()
|
||||
.first()
|
||||
)
|
||||
if item is not None:
|
||||
item.value = value.strip() if value is not None else value
|
||||
db.session.commit()
|
||||
else:
|
||||
db.session.add(ModelSetting(key, value.strip()))
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s %s", exception, key)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def to_dict():
|
||||
try:
|
||||
from framework.util import Util
|
||||
|
||||
arg = Util.db_list_to_dict(db.session.query(ModelSetting).all())
|
||||
arg["package_name"] = package_name
|
||||
return arg
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
@staticmethod
|
||||
def setting_save(req):
|
||||
try:
|
||||
for key, value in req.form.items():
|
||||
if key in ["scheduler", "is_running"]:
|
||||
continue
|
||||
if key.startswith("tmp_"):
|
||||
continue
|
||||
logger.debug("Key:%s Value:%s", key, value)
|
||||
entity = (
|
||||
db.session.query(ModelSetting)
|
||||
.filter_by(key=key)
|
||||
.with_for_update()
|
||||
.first()
|
||||
)
|
||||
entity.value = value
|
||||
db.session.commit()
|
||||
return True
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
logger.debug("Error Key:%s Value:%s", key, value)
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_list(key):
|
||||
try:
|
||||
value = ModelSetting.get(key)
|
||||
values = [
|
||||
x.strip().replace(" ", "").strip()
|
||||
for x in value.replace("\n", "|").split("|")
|
||||
]
|
||||
values = Util.get_list_except_empty(values)
|
||||
return values
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
logger.error("Error Key:%s Value:%s", key, value)
|
||||
|
||||
@staticmethod
|
||||
def make_apikey(url):
|
||||
url = url.format(ddns=ModelSetting.get("ddns"))
|
||||
if ModelSetting.get_bool("auth_use_apikey"):
|
||||
if url.find("?") == -1:
|
||||
url += "?"
|
||||
else:
|
||||
url += "&"
|
||||
url += "apikey=%s" % ModelSetting.get("auth_apikey")
|
||||
return url
|
||||
@@ -0,0 +1,440 @@
|
||||
# third-party
|
||||
import requests
|
||||
import traceback
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
import json
|
||||
import os
|
||||
|
||||
from flask import (
|
||||
Blueprint,
|
||||
request,
|
||||
Response,
|
||||
send_file,
|
||||
render_template,
|
||||
redirect,
|
||||
jsonify,
|
||||
stream_with_context,
|
||||
)
|
||||
|
||||
# gommi 공용
|
||||
from framework.logger import get_logger
|
||||
from framework import (
|
||||
app,
|
||||
db,
|
||||
scheduler,
|
||||
socketio,
|
||||
check_api,
|
||||
path_app_root,
|
||||
path_data,
|
||||
) # , celery
|
||||
from flask_login import login_user, logout_user, current_user, login_required
|
||||
from framework.util import Util, SingletonClass
|
||||
|
||||
# 로그
|
||||
package_name = __name__.split(".")[0]
|
||||
logger = get_logger(package_name)
|
||||
|
||||
# package
|
||||
from .logic import SystemLogic
|
||||
from .model import ModelSetting
|
||||
from .logic_plugin import LogicPlugin
|
||||
from .logic_selenium import SystemLogicSelenium
|
||||
from .logic_command import SystemLogicCommand
|
||||
from .logic_command2 import SystemLogicCommand2
|
||||
from .logic_auth import SystemLogicAuth
|
||||
|
||||
# celery 때문에 import
|
||||
from .logic_site import SystemLogicSite
|
||||
|
||||
|
||||
# ========================================================================
|
||||
# plugin public
|
||||
# ========================================================================
|
||||
blueprint = Blueprint(
|
||||
package_name,
|
||||
package_name,
|
||||
url_prefix="/%s" % package_name,
|
||||
template_folder="templates",
|
||||
)
|
||||
menu = {
|
||||
"main": [package_name, "설정"],
|
||||
"sub": [
|
||||
["setting", "일반설정"],
|
||||
["plugin", "플러그인"],
|
||||
["tool", "Tool"],
|
||||
["log", "로그"],
|
||||
],
|
||||
"sub2": {
|
||||
"setting": [
|
||||
["basic", "기본"],
|
||||
["auth", "인증"],
|
||||
["env", "시스템"],
|
||||
["notify", "알림"],
|
||||
["telegram_bot", "텔레그램 봇"],
|
||||
["selenium", "Selenium"],
|
||||
["trans", "번역"],
|
||||
["site", "Site"],
|
||||
["memo", "메모"],
|
||||
["terminal", "Terminal"],
|
||||
],
|
||||
"rss": [["setting", "설정"], ["job", "작업"], ["list", "목록"]],
|
||||
"cache": [["setting", "설정"], ["list", "목록"]],
|
||||
"tool": [["crypt", "암호화"]],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def plugin_load():
|
||||
logger.debug("plugin_load:%s", package_name)
|
||||
SystemLogic.plugin_load()
|
||||
# SystemLogicTelegramBot.plugin_load()
|
||||
SystemLogicSite.plugin_load()
|
||||
|
||||
|
||||
def plugin_unload():
|
||||
logger.debug("plugin_load:%s", package_name)
|
||||
SystemLogicSelenium.plugin_unload()
|
||||
SystemLogicCommand.plugin_unload()
|
||||
SystemLogicCommand2.plugin_unload()
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Web menu
|
||||
# ============================================================
|
||||
@blueprint.route("/")
|
||||
def normal():
|
||||
return redirect("/%s/setting" % package_name)
|
||||
|
||||
|
||||
@login_required
|
||||
def home():
|
||||
return render_template("info.html", arg=None)
|
||||
|
||||
|
||||
@blueprint.route("/<sub>", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def first_menu(sub):
|
||||
logger.debug("System SUB:%s", sub)
|
||||
arg = None
|
||||
if sub == "home":
|
||||
return render_template("%s_%s.html" % (package_name, sub), arg=None)
|
||||
elif sub == "setting":
|
||||
return redirect("/%s/%s/basic" % (package_name, sub))
|
||||
elif sub == "restart":
|
||||
restart()
|
||||
return render_template(
|
||||
"system_restart.html",
|
||||
sub=sub,
|
||||
referer=request.headers.get("Referer"),
|
||||
)
|
||||
elif sub == "plugin":
|
||||
arg = ModelSetting.to_dict()
|
||||
logger.debug = f"arg:: {arg}"
|
||||
arg["install"] = request.args.get("install", "")
|
||||
|
||||
return render_template("system_plugin.html", arg=arg)
|
||||
|
||||
return render_template("sample.html", title="%s - %s" % (package_name, sub))
|
||||
|
||||
|
||||
@blueprint.route("/<sub>/<sub2>")
|
||||
@login_required
|
||||
def second_menu(sub, sub2):
|
||||
try:
|
||||
if sub == "setting":
|
||||
arg = ModelSetting.to_dict()
|
||||
arg["sub"] = sub2
|
||||
if sub2 == "basic":
|
||||
arg["point"] = SystemLogic.point
|
||||
return render_template(
|
||||
"%s_%s_%s.html" % (package_name, sub, sub2), arg=arg
|
||||
)
|
||||
elif sub2 == "auth":
|
||||
arg["auth_result"] = SystemLogicAuth.get_auth_status()
|
||||
return render_template(
|
||||
"%s_%s_%s.html" % (package_name, sub, sub2), arg=arg
|
||||
)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
#########################################################
|
||||
# For UI
|
||||
#########################################################
|
||||
@blueprint.route("/ajax/<sub>/<sub2>", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def second_ajax(sub, sub2):
|
||||
logger.debug("System AJAX sub:%s", sub)
|
||||
try:
|
||||
if sub == "trans":
|
||||
from .logic_trans import SystemLogicTrans
|
||||
|
||||
return SystemLogicTrans.process_ajax(sub2, request)
|
||||
elif sub == "auth":
|
||||
from .logic_auth import SystemLogicAuth
|
||||
|
||||
return SystemLogicAuth.process_ajax(sub2, request)
|
||||
elif sub == "selenium":
|
||||
return SystemLogicSelenium.process_ajax(sub2, request)
|
||||
elif sub == "notify":
|
||||
return SystemLogicNotify.process_ajax(sub2, request)
|
||||
elif sub == "telegram_bot":
|
||||
return SystemLogicTelegramBot.process_ajax(sub2, request)
|
||||
elif sub == "env":
|
||||
return SystemLogicEnv.process_ajax(sub2, request)
|
||||
elif sub == "site":
|
||||
return SystemLogicSite.process_ajax(sub2, request)
|
||||
elif sub == "crypt":
|
||||
return SystemLogicToolDecrypt.process_ajax(sub2, request)
|
||||
elif sub == "terminal":
|
||||
return SystemLogicTerminal.process_ajax(sub2, request)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@blueprint.route("/ajax/<sub>", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def ajax(sub):
|
||||
# logger.debug('System AJAX sub:%s', sub)
|
||||
try:
|
||||
if sub == "info":
|
||||
try:
|
||||
ret = {}
|
||||
ret["system"] = SystemLogic.get_info()
|
||||
ret["scheduler"] = scheduler.get_job_list_info()
|
||||
|
||||
# logger.debug(ret)
|
||||
return jsonify(ret)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return jsonify()
|
||||
elif sub == "setting_save_system":
|
||||
try:
|
||||
ret = SystemLogic.setting_save_system(request)
|
||||
return jsonify(ret)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
elif sub == "setting_save":
|
||||
ret = ModelSetting.setting_save(request)
|
||||
SystemLogic.setting_save_after()
|
||||
return jsonify(ret)
|
||||
elif sub == "ddns_test":
|
||||
try:
|
||||
url = request.form["ddns"] + "/version"
|
||||
res = requests.get(url)
|
||||
data = res.text
|
||||
# data = res.json()
|
||||
# logger.debug(data)
|
||||
return jsonify(data)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
return jsonify("fail")
|
||||
elif sub == "celery_test":
|
||||
try:
|
||||
# result = add_together.delay(10, 20)
|
||||
# print(result.wait())
|
||||
# return 'Welcome to my app!'
|
||||
try:
|
||||
import framework
|
||||
|
||||
framework.exit_code = 1
|
||||
socketio.stop()
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
# os.environ['SJVA_REPEAT_TYPE'] = 'update'
|
||||
|
||||
return jsonify()
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
elif sub == "command_run":
|
||||
try:
|
||||
command_text = request.form["command_text"]
|
||||
ret = SystemLogic.command_run(command_text)
|
||||
return jsonify(ret)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
elif sub == "get_link_list":
|
||||
try:
|
||||
link_json = SystemLogic.get_setting_value("link_json")
|
||||
j = json.loads(link_json)
|
||||
return jsonify(j)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
elif sub == "link_save":
|
||||
try:
|
||||
link_data_str = request.form["link_data"]
|
||||
# j = json.loads(link_data)
|
||||
ret = SystemLogic.link_save(link_data_str)
|
||||
return jsonify(ret)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
elif sub == "plugin_list":
|
||||
try:
|
||||
return jsonify(LogicPlugin.get_plugin_list())
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
elif sub == "plugin_install":
|
||||
try:
|
||||
# plugin_name = request.form['plugin_name']
|
||||
plugin_git = request.form["plugin_git"]
|
||||
return jsonify(
|
||||
LogicPlugin.plugin_install_by_api(
|
||||
plugin_git,
|
||||
request.form.get("zip_url"),
|
||||
request.form.get("zip_filename"),
|
||||
)
|
||||
)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
elif sub == "plugin_uninstall":
|
||||
try:
|
||||
plugin_name = request.form["plugin_name"]
|
||||
return jsonify(LogicPlugin.plugin_uninstall(plugin_name))
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
elif sub == "recent_version":
|
||||
ret = SystemLogic.get_recent_version()
|
||||
ret = {"ret": ret, "version": SystemLogic.recent_version}
|
||||
return jsonify(ret)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
def restart():
|
||||
try:
|
||||
try:
|
||||
import framework
|
||||
|
||||
framework.exit_code = 1
|
||||
app_close()
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
def shutdown():
|
||||
try:
|
||||
try:
|
||||
nginx_kill = "/app/data/custom/nginx/files/kill.sh"
|
||||
if os.path.exists(nginx_kill):
|
||||
SystemLogicCommand.execute_command_return([nginx_kill])
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
import framework
|
||||
|
||||
framework.exit_code = 0
|
||||
app_close()
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
def app_close():
|
||||
try:
|
||||
from framework.init_plugin import plugin_unload
|
||||
|
||||
plugin_unload()
|
||||
socketio.stop()
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@socketio.on("connect", namespace="/%s" % package_name)
|
||||
def connect():
|
||||
try:
|
||||
InfoProcess.instance().connect(request.sid)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@socketio.on("disconnect", namespace="/%s" % package_name)
|
||||
def disconnect():
|
||||
try:
|
||||
InfoProcess.instance().disconnect(request.sid)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@socketio.on("connect", namespace="/system_restart")
|
||||
def connect_system_restart():
|
||||
try:
|
||||
socketio.emit(
|
||||
"on_connect", "restart", namespace="/system_restart", broadcast=True
|
||||
)
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
@socketio.on("disconnect", namespace="/system_restart")
|
||||
def disconnect_system_restart():
|
||||
try:
|
||||
pass
|
||||
except Exception as exception:
|
||||
logger.error("Exception:%s", exception)
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
class InfoThread(threading.Thread):
|
||||
def __init__(self):
|
||||
super(InfoThread, self).__init__()
|
||||
self.stop_flag = False
|
||||
self.daemon = True
|
||||
|
||||
def stop(self):
|
||||
self.stop_flag = True
|
||||
|
||||
def run(self) -> None:
|
||||
while not self.stop_flag:
|
||||
ret = {}
|
||||
ret["system"] = SystemLogic.get_info()
|
||||
ret["scheduler"] = scheduler.get_job_list_info()
|
||||
socketio.emit(
|
||||
"status", ret, namespace="/system", broadcast=True
|
||||
)
|
||||
# logger.debug('InfoThread')
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
class InfoProcess(SingletonClass):
|
||||
sid_list = []
|
||||
thread = None
|
||||
|
||||
@classmethod
|
||||
def connect(cls, sid):
|
||||
logger.debug("Info connect:%s", InfoProcess.sid_list)
|
||||
if not InfoProcess.sid_list:
|
||||
InfoProcess.thread = InfoThread()
|
||||
InfoProcess.thread.start()
|
||||
InfoProcess.sid_list.append(sid)
|
||||
|
||||
@classmethod
|
||||
def disconnect(cls, sid):
|
||||
logger.debug("Info disconnect:%s", InfoProcess.sid_list)
|
||||
InfoProcess.sid_list.remove(sid)
|
||||
if not InfoProcess.sid_list:
|
||||
InfoProcess.thread.stop()
|
||||
Executable
+153
@@ -0,0 +1,153 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
|
||||
<script type="text/javascript">
|
||||
document.getElementById("plugin_first_menu").innerHTML = '';
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h3>시스템</h3>
|
||||
<hr>
|
||||
{{ macros.info_text('python_version', 'Python 버전') }}
|
||||
{{ macros.info_text('platform', 'Platform') }}
|
||||
{{ macros.info_text('processor', 'Processor') }}
|
||||
{{ macros.info_text_and_buttons('version', 'SJVA 버전', [['changelog_btn', '업데이트 내역'], ['recent_version_btn', '최신버전 확인']]) }}
|
||||
{{ macros.info_text('path_app_root', 'SJVA 폴더') }}
|
||||
{{ macros.info_text('running_type', '실행타입') }}
|
||||
{{ macros.info_text('auth', '인증') }}
|
||||
<div class="d-inline-block"></div>
|
||||
|
||||
<h3>모니터링</h3>
|
||||
<hr>
|
||||
{{ macros.info_text('time', '시간') }}
|
||||
{{ macros.info_text('cpu_percent', 'CPU 사용량') }}
|
||||
{{ macros.info_text('memory', '메모리') }}
|
||||
{{ macros.info_text('disk', '디스크') }}
|
||||
<div class="d-inline-block"></div>
|
||||
|
||||
<h3>스케쥴러</h3>
|
||||
<hr>
|
||||
<div id="scheduler_list_div"></div>
|
||||
</div> <!--전체-->
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
|
||||
|
||||
function make_scheduler_list(data) {
|
||||
|
||||
str = m_row_start(p='0');
|
||||
str += m_col(1, '<strong>NO</strong>');
|
||||
str += m_col(2, '<strong>플러그인 & ID</strong>');
|
||||
//str += m_col(2, '<strong>생성 & 다음 실행</strong>');
|
||||
str += m_col(2, '<strong>다음 실행 (남은시간)</strong>');
|
||||
str += m_col(1, '<strong>이전소요/횟수</strong>');
|
||||
str += m_col(2, '<strong>Interval & Cron</strong>');
|
||||
str += m_col(1, '<strong>상태</strong>');
|
||||
str += m_col(3, '<strong>설 명</strong>');
|
||||
str += m_row_end();
|
||||
str += m_hr();
|
||||
|
||||
for(var i in data) {
|
||||
if (data[i].is_running) {
|
||||
str += m_row_start_color2(0);
|
||||
} else {
|
||||
str += m_row_start(p='0');
|
||||
}
|
||||
|
||||
str += m_col(1, data[i].no);
|
||||
tmp = '<strong>'+data[i].plugin+'</strong><br>' + data[i].id;
|
||||
str += m_col(2, tmp);
|
||||
|
||||
//tmp = ''+''+'' + data[i].make_time + '<br>';
|
||||
//tmp += ''+''+'' + data[i].next_run_time + '<br>';
|
||||
tmp = ''+''+'' + data[i].next_run_time + '<br>';
|
||||
if (data[i].remain_time != '') {
|
||||
tmp += '('+data[i].remain_time+')';
|
||||
}
|
||||
str += m_col(2, tmp);
|
||||
tmp = ''+''+'' + data[i].running_timedelta + '초 / ';
|
||||
tmp += ''+''+'' + data[i].count + '회';
|
||||
str += m_col(1, tmp);
|
||||
|
||||
tmp = ''+''+'' + data[i].interval + ' <br>';
|
||||
str += m_col(2, tmp);
|
||||
tmp = ''+''+'' + ((data[i].is_running) ?'실행중':'대기중') + '';
|
||||
if (data[i].run == false) {
|
||||
tmp += '(F)'
|
||||
}
|
||||
str += m_col(1, tmp);
|
||||
str += m_col(3, data[i].description);
|
||||
str += m_row_end();
|
||||
str += m_hr();
|
||||
}
|
||||
document.getElementById("scheduler_list_div").innerHTML = str;
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
|
||||
$.ajax({
|
||||
url: '/system/ajax/info',
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data:{},
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
make_system(data);
|
||||
}
|
||||
});
|
||||
|
||||
var protocol = window.location.protocol;
|
||||
var socket = io.connect(protocol + "//" + document.domain + ":" + location.port + "/system")
|
||||
socket.on('status', function(data) {
|
||||
make_system(data);
|
||||
make_scheduler_list(data.scheduler);
|
||||
});
|
||||
});
|
||||
|
||||
$("body").on('click', '#changelog_btn', function(e){
|
||||
e.preventDefault();
|
||||
//window.location.href='/system/information';
|
||||
window.open('https://sjva.me/wiki/public/changelog', "_blank");
|
||||
});
|
||||
|
||||
$("body").on('click', '#recent_version_btn', function(e){
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
url: '/system/ajax/recent_version',
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data:{},
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (data.ret)
|
||||
$.notify('<strong>최신버전 : '+data.version+'</strong>', {type: 'success'});
|
||||
else
|
||||
$.notify('<strong>확인 실패</strong>', {type: 'warning'});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function make_system(data) {
|
||||
for(var key in data.system) {
|
||||
if (key == 'auth' && data.system[key].startsWith('인증')==false)
|
||||
document.getElementById(key).innerHTML = '<span style="color:red;font-weight: bold;font-size: xx-large ;">' + data.system[key]+ '</span>';
|
||||
else {
|
||||
if (document.getElementById(key) != null)
|
||||
document.getElementById(key).innerHTML = data.system[key];
|
||||
}
|
||||
tmp = data.system['version']
|
||||
if (data.system['version'] == data.system['recent_version']) {
|
||||
tmp += ' <span style="color:red;font-weight: bold;">(최신버전)</span>';
|
||||
} else {
|
||||
tmp += ' <span style="color:red;font-weight: bold;">(최신버전이 아닙니다. 최신:'+data.system['recent_version']+')</span>';
|
||||
}
|
||||
document.getElementById('version').innerHTML = tmp;
|
||||
}
|
||||
//console.log(document.getElementById('version'))
|
||||
//document.getElementById('version').innerHTML = data.system.version + data.system.recent_version;
|
||||
}
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1 @@
|
||||
{% extends "base.html" %} {% block content %} hihi {% endblock %}
|
||||
Executable
+184
@@ -0,0 +1,184 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<style type="text/css">
|
||||
.my_hover:hover{
|
||||
background-color: #ffff00;
|
||||
transition: all 0.01s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div>
|
||||
<nav>
|
||||
{{ macros.m_tab_head_start() }}
|
||||
{{ macros.m_tab_head2('normal', '일반', false) }}
|
||||
{{ macros.m_tab_head2('list', '플러그인 목록', true) }}
|
||||
{{ macros.m_tab_head_end() }}
|
||||
</nav>
|
||||
|
||||
<div class="tab-content" id="nav-tabContent">
|
||||
|
||||
{{ macros.m_tab_content_start('normal', false) }}
|
||||
<form id='setting' name='setting'>
|
||||
{{ macros.setting_input_text('plugin_dev_path', '개발용 플러그인 경로', value=arg['plugin_dev_path'], desc=['플러그인을 개발할 때 사용하는 경로'], col='9') }}
|
||||
{{ macros.setting_button([['setting_save', '저장']]) }}
|
||||
|
||||
</form>
|
||||
</form>
|
||||
{{ macros.m_tab_content_end() }}
|
||||
|
||||
{{ macros.m_tab_content_start('list', true) }}
|
||||
{{ macros.setting_input_text_and_buttons('plugin_git', '플러그인 수동 설치', [['plugin_install_btn', '설치']], value='https://github.com/', desc=['SJVA.ME 플러그인 게시판에 있는 링크 주소를 입력하세요.']) }}
|
||||
|
||||
{{ macros.m_hr_head_top() }}
|
||||
{{ macros.m_row_start('0') }}
|
||||
{{ macros.m_col(3, macros.m_strong('Name')) }}
|
||||
{{ macros.m_col(1, macros.m_strong('Dev.')) }}
|
||||
{{ macros.m_col(1, macros.m_strong('Category')) }}
|
||||
{{ macros.m_col(1, macros.m_strong('Version')) }}
|
||||
{{ macros.m_col(6, macros.m_strong('Description')) }}
|
||||
{{ macros.m_row_end() }}
|
||||
{{ macros.m_hr_head_bottom() }}
|
||||
<div id="plugin_list_div"></div>
|
||||
{{ macros.m_tab_content_end() }}
|
||||
</div><!--tab-content-->
|
||||
</div> <!--전체-->
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
var package_name = 'system';
|
||||
var current_data;
|
||||
var install = "{{arg['install']}}";
|
||||
|
||||
$(document).ready(function(){
|
||||
$.ajax({
|
||||
url: '/' + package_name + '/ajax/plugin_list',
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data: {},
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
current_data = data
|
||||
make_plugin_list();
|
||||
if (install != '') {
|
||||
$('#plugin_git').val(install);
|
||||
//notify('플러그인이 설치되어 있지 않습니다.', 'danger');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
//설정 저장
|
||||
$("#setting_save").click(function(e) {
|
||||
e.preventDefault();
|
||||
var formData = get_formdata('#setting');
|
||||
setting_save_func(formData, true)
|
||||
//
|
||||
});
|
||||
|
||||
|
||||
function setting_save_func(formData, noti) {
|
||||
$.ajax({
|
||||
url: '/' + package_name + '/ajax/setting_save',
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data: formData,
|
||||
dataType: "json",
|
||||
success: function (ret) {
|
||||
if (ret) {
|
||||
if (noti) {
|
||||
$.notify('<strong>설정을 저장하였습니다.</strong>', {
|
||||
type: 'success'
|
||||
});
|
||||
} else {
|
||||
window.location.href = "/"
|
||||
}
|
||||
} else {
|
||||
$.notify('<strong>설정 저장에 실패하였습니다.</strong>', {
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function make_plugin_list() {
|
||||
str = ''
|
||||
console.log(current_data)
|
||||
for (i in current_data) {
|
||||
console.log(i)
|
||||
console.log(current_data[i])
|
||||
str += m_row_start();
|
||||
str += m_col(3, i)
|
||||
|
||||
if (current_data[i].info != null) {
|
||||
str += m_col(1, current_data[i].info.developer);
|
||||
str += m_col(1, current_data[i].info.category);
|
||||
str += m_col(1, current_data[i].info.version);
|
||||
tmp = ''
|
||||
tmp += m_button('plugin_uninstall_btn', '삭제', [{'key':'plugin_name', 'value':current_data[i].info.name}]);
|
||||
if (current_data[i].info.local_info != null) {
|
||||
tmp += m_button('global_link_btn', 'GIT', [{'key':'url', 'value':current_data[i].info.local_info.home}]);
|
||||
if (current_data[i].info.local_info.home != current_data[i].info.local_info.more && current_data[i].info.local_info.more.startsWith('http'))
|
||||
tmp += m_button('global_link_btn', 'MORE', [{'key':'url', 'value':current_data[i].info.local_info.more}]);
|
||||
}
|
||||
|
||||
tmp = m_button_group(tmp)
|
||||
str += m_col(6, current_data[i].info.description +'<br><br>'+ tmp)
|
||||
}
|
||||
str += m_row_end();
|
||||
if (i != current_data.length -1) str += m_hr(0);
|
||||
}
|
||||
document.getElementById("plugin_list_div").innerHTML = str;
|
||||
}
|
||||
|
||||
|
||||
|
||||
$("body").on('click', '#plugin_install_btn', function(e){
|
||||
e.preventDefault();
|
||||
plugin_git = document.getElementById("plugin_git").value
|
||||
|
||||
console.log(plugin_git)
|
||||
|
||||
$.ajax({
|
||||
url: '/' + package_name + '/ajax/plugin_install',
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data:{"plugin_git": plugin_git},
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
t = (data.ret == 'success') ? 'success' : 'warning'
|
||||
$.notify('<strong>'+data.log+'</strong>', {
|
||||
type: t
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$("body").on('click', '#plugin_uninstall_btn', function(e){
|
||||
e.preventDefault();
|
||||
plugin_name = $(this).data('plugin_name')
|
||||
$.ajax({
|
||||
url: '/' + package_name + '/ajax/plugin_uninstall',
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data:{plugin_name:plugin_name},
|
||||
success: function (data) {
|
||||
if (data == 'success') {
|
||||
$.notify('<strong>재시작시 적용됩니다.</strong>', {
|
||||
type: 'success'
|
||||
});
|
||||
} else {
|
||||
$.notify('<strong>실패하였습니다.</strong>', {
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div>
|
||||
<h4>
|
||||
{% if sub == 'restart' %}
|
||||
시스템 재시작 중입니다. <br>
|
||||
완료시 이전 페이지로 이동합니다.
|
||||
|
||||
{% elif sub == 'shutdown' %}
|
||||
시스템이 종료되었습니다.
|
||||
{% endif %}
|
||||
</h4>
|
||||
</div>
|
||||
{% if sub == 'restart' %}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
var referer = "{{referer}}"
|
||||
$('#loading').show();
|
||||
setTimeout(function(){
|
||||
// 1초 후 작동해야할 코드
|
||||
}, 2000);
|
||||
|
||||
var protocol = window.location.protocol;
|
||||
var socket2 = io.connect(protocol + "//" + document.domain + ":" + location.port + "/system_restart");
|
||||
socket2.on('on_connect', function(data){
|
||||
//window.location.href = protocol + "//" + document.domain + ":" + location.port;
|
||||
window.location.href = referer;
|
||||
});
|
||||
})
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div>
|
||||
{{ macros.m_button_group([['global_setting_save_btn', '설정 저장']])}}
|
||||
{{ macros.m_row_start('5') }}
|
||||
{{ macros.m_row_end() }}
|
||||
<nav>
|
||||
{{ macros.m_tab_head_start() }}
|
||||
{{ macros.m_tab_head2('normal', '웹 로그인', true) }}
|
||||
{{ macros.m_tab_head2('api', 'API & 홈페이지 인증', false) }}
|
||||
{{ macros.m_tab_head_end() }}
|
||||
</nav>
|
||||
<form id='setting' name='setting'>
|
||||
<div class="tab-content" id="nav-tabContent">
|
||||
{{ macros.m_tab_content_start('normal', true) }}
|
||||
{{ macros.setting_checkbox('use_login', '로그인 사용', value=arg['use_login']) }}
|
||||
<div id="use_login_div" class="collapse">
|
||||
{{ macros.setting_input_text('id', '로그인 ID', value=arg['id'], desc=['초기값은 sjva입니다.'], col='3') }}
|
||||
{{ macros.setting_input_text('pw', '로그인 암호', value=arg['pw'], col='3', type='password', desc=['초기값은 sjva입니다.']) }}
|
||||
{{ macros.setting_checkbox('hide_menu', '로그인 화면 메뉴 표시 안함', value=arg['hide_menu']) }}
|
||||
</div>
|
||||
{{ macros.m_tab_content_end() }}
|
||||
|
||||
{{ macros.m_tab_content_start('api', false) }}
|
||||
{{ macros.setting_checkbox('auth_use_apikey', 'APIKEY 사용', value=arg['auth_use_apikey'], desc=['On : 모든 API 요청시 apikey 값을 입력해야합니다.', '없거나 틀릴 경우 에러코드 403리턴']) }}
|
||||
{{ macros.setting_input_text_and_buttons('auth_apikey', 'APIKEY', [['generate_btn', '자동생성']], col='4', value=arg['auth_apikey']) }}
|
||||
{{ macros.m_hr() }}
|
||||
{{ macros.setting_input_text('sjva_me_user_id', 'sjva.me 홈페이지 ID', value=arg['sjva_me_user_id'], desc=['ID변경, APIKEY 변경시 재인증해야합니다.'], col='3') }}
|
||||
{{ macros.info_text('sjva_id', 'SJVA ID', value=arg['sjva_id'], desc=['SJVA별로 자동 생성되는 값입니다. 수정불가'] ) }}
|
||||
{{ macros.info_text_and_buttons('auth_result', '인증상태', [['do_auth_btn', '인증하기']], value=arg['auth_result']['desc'], desc=['APIKEY, 홈페이지ID 값을 저장한 후에 시도하세요.']) }}
|
||||
|
||||
{{ macros.m_tab_content_end() }}
|
||||
|
||||
</div><!--tab-content-->
|
||||
</form>
|
||||
</div> <!--전체-->
|
||||
|
||||
<script type="text/javascript">
|
||||
var package_name = "{{arg['package_name']}}";
|
||||
var sub = "{{arg['sub'] }}";
|
||||
|
||||
$(document).ready(function(){
|
||||
use_collapse("use_login");
|
||||
use_collapse("auth_use_apikey");
|
||||
});
|
||||
|
||||
$('#use_login').change(function() {
|
||||
use_collapse('use_login');
|
||||
});
|
||||
|
||||
$("body").on('click', '#generate_btn', function(e) {
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
url: '/' + package_name + '/ajax/'+sub+'/apikey_generate',
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data: {},
|
||||
dataType: "json",
|
||||
success: function (ret) {
|
||||
document.getElementById("auth_apikey").value = ret
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("body").on('click', '#do_auth_btn', function(e) {
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
url: '/' + package_name + '/ajax/'+sub+'/do_auth',
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data: {},
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (data.ret) {
|
||||
$.notify('<strong>인증되었습니다. 재시작하세요.<br>'+data.msg+'</strong><br>', {type: 'success'});
|
||||
} else {
|
||||
$.notify('<strong>인증에 실패하였습니다.<br>'+data.msg+'</strong><br>', {type: 'warning'});
|
||||
$.notify('<strong>설정을 먼저 저장하고 시도하세요.</strong><br>', {type: 'warning'});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
+420
@@ -0,0 +1,420 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<style type="text/css">
|
||||
.my_hover:hover{
|
||||
background-color: #ffff00;
|
||||
transition: all 0.01s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div>
|
||||
<nav>
|
||||
{{ macros.m_tab_head_start() }}
|
||||
{{ macros.m_tab_head2('normal', '일반', true) }}
|
||||
{{ macros.m_tab_head2('web', '웹', false) }}
|
||||
{{ macros.m_tab_head2('menu', '메뉴', false) }}
|
||||
{{ macros.m_tab_head2('link', '링크', false) }}
|
||||
{{ macros.m_tab_head2('download', '다운로드', false) }}
|
||||
{{ macros.m_tab_head_end() }}
|
||||
</nav>
|
||||
|
||||
<div class="tab-content" id="nav-tabContent">
|
||||
{{ macros.m_tab_content_start('normal', true) }}
|
||||
<form id='setting' name='setting'>
|
||||
{{ macros.setting_input_int('port', 'Port', value=arg['port'], min='1', placeholder='Port', desc=['포트 번호입니다.', '네이티브 설치 혹은 도커 네트워크 타입이 호스트일 경우 반영됩니다.', '도커 브릿지 모드인 경우는 docker run -p 옵션에서 변경하시기 바랍니다.', '경고 : -p 브릿지 모드로 사용중 일 경우 9999번을 절대 변경하지 마세요.']) }}
|
||||
{{ macros.setting_input_text_and_buttons('ddns', 'DDNS', [['ddns_test_btn', '테스트']], value=arg['ddns'], desc=['외부에서 접근시 사용할 DDNS. http:// 나 https:// 로 시작해야합니다.', 'RSS, Plex Callback, KLive 등에서 URL생성시 사용합니다.', '테스트 버튼 클릭 후 버전을 확인 할 수 있어야 합니다.']) }}
|
||||
{{ macros.setting_input_text('auto_restart_hour', '자동 재시작 시간', value=arg['auto_restart_hour'], col='3', desc=['자동 재시작 간격(시간단위)이나 Cron 설정을 입력합니다.', '0이면 재시작 안함.']) }}
|
||||
{{ macros.setting_select('log_level', '로그 레벨', [['10', 'DEBUG'],['20', 'INFO'],['30', 'WARNING'],['40', 'ERROR'], ['50', 'CRITICAL'] ], value=arg['log_level'], col='3') }}
|
||||
{{ macros.setting_button([['setting_save', '저장']]) }}
|
||||
</form>
|
||||
</form>
|
||||
{{ macros.m_hr() }}
|
||||
{{ macros.setting_input_text_and_buttons('command_text', 'Command', [['command_run_btn', 'Run']], value='', desc='') }}
|
||||
{{ macros.m_tab_content_end() }}
|
||||
|
||||
|
||||
{{ macros.m_tab_content_start('web', false) }}
|
||||
<form id='setting2' name='setting2'>
|
||||
{{ macros.setting_select('theme', '테마 선택', [['Default','Default'], ['Cerulean','Cerulean'], ['Cosmo','Cosmo'], ['Cyborg','Cyborg'], ['Darkly','Darkly'], ['Flatly','Flatly'], ['Journal','Journal'], ['Litera','Litera'], ['Lumen','Lumen'], ['Lux','Lux'], ['Materia','Materia'], ['Minty','Minty'], ['Morph','Morph'],['Pulse','Pulse'], ['Quartz','Quartz'], ['Sandstone','Sandstone'], ['Simplex','Simplex'], ['Sketchy','Sketchy'], ['Slate','Slate'], ['Solar','Solar'], ['Spacelab','Spacelab'], ['Superhero','Superhero'], ['United','United'], ['Vapor','Vapor'], ['Yeti','Yeti'], ['Zephyr','Zephyr']], value=arg['theme'], desc=['https://bootswatch.com'], col='6') }}
|
||||
{{ macros.setting_input_text('web_title', '웹 타이틀', value=arg['web_title']) }}
|
||||
{{ macros.setting_button([['setting_save2', '저장']]) }}
|
||||
</form>
|
||||
{{ macros.m_tab_content_end() }}
|
||||
|
||||
|
||||
{{ macros.m_tab_content_start('menu', false) }}
|
||||
<form id='setting3' name='setting3'>
|
||||
{% if arg['use_category_vod'] == 'True' %}
|
||||
{{ macros.m_hr() }}
|
||||
{{ macros.setting_button_with_info([['menu_toggle_btn', 'Toggle', [{'key':'category', 'value':'vod'}]]], left='VOD', desc=None) }}
|
||||
<div id="menu_vod_div" class="collapse">
|
||||
{{ macros.setting_checkbox('use_plugin_ffmpeg', 'FFMPEG', value=arg['use_plugin_ffmpeg']) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if arg['use_category_file_process'] == 'True' %}
|
||||
{{ macros.m_hr() }}
|
||||
{{ macros.setting_button_with_info([['menu_toggle_btn', 'Toggle', [{'key':'category', 'value':'file_process'}]]], left='파일처리', desc=None) }}
|
||||
<div id="menu_file_process_div" class="collapse">
|
||||
{{ macros.setting_checkbox('use_plugin_ktv', '국내TV', value=arg['use_plugin_ktv']) }}
|
||||
{{ macros.setting_checkbox('use_plugin_fileprocess_movie', '영화', value=arg['use_plugin_fileprocess_movie']) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if arg['use_category_plex'] == 'True' %}
|
||||
{{ macros.m_hr() }}
|
||||
{{ macros.setting_button_with_info([['menu_toggle_btn', 'Toggle', [{'key':'category', 'value':'plex'}]]], left='PLEX', desc=None) }}
|
||||
<div id="menu_plex_div" class="collapse">
|
||||
{{ macros.setting_checkbox('use_plugin_plex', 'PLEX', value=arg['use_plugin_plex']) }}
|
||||
{{ macros.setting_checkbox('use_plugin_gdrive_scan', 'GDrive 스캔', value=arg['use_plugin_gdrive_scan']) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if arg['use_category_tool'] == 'True' %}
|
||||
{{ macros.m_hr() }}
|
||||
{{ macros.setting_button_with_info([['menu_toggle_btn', 'Toggle', [{'key':'category', 'value':'tool'}]]], left='툴', desc=None) }}
|
||||
<div id="menu_tool_div" class="collapse">
|
||||
{{ macros.setting_checkbox('use_plugin_rclone', 'RClone', value=arg['use_plugin_rclone']) }}
|
||||
{{ macros.setting_checkbox('use_plugin_daum_tv', 'Daum TV', value=arg['use_plugin_daum_tv']) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{{ macros.setting_button([['setting_save3', '저장']]) }}
|
||||
</form>
|
||||
{{ macros.m_tab_content_end() }}
|
||||
|
||||
|
||||
{{ macros.m_tab_content_start('link', false) }}
|
||||
{{ macros.m_button_group([['link_add_btn', '추가'], ['link_add_divider_btn', 'Divider Line 추가'], ['link_save_btn', '저장'], ['link_reset_btn', '초기화']])}}
|
||||
{{ macros.m_row_start('0') }}
|
||||
{{ macros.m_row_end() }}
|
||||
|
||||
{{ macros.m_hr_head_top() }}
|
||||
{{ macros.m_row_start('0') }}
|
||||
{{ macros.m_col(1, macros.m_strong('Idx')) }}
|
||||
{{ macros.m_col(4, macros.m_strong('Title')) }}
|
||||
{{ macros.m_col(4, macros.m_strong('URL')) }}
|
||||
{{ macros.m_col(3, macros.m_strong('Action')) }}
|
||||
{{ macros.m_row_end() }}
|
||||
{{ macros.m_hr_head_bottom() }}
|
||||
<form id="link_form" name="link_form">
|
||||
<div id="link_list_div"></div>
|
||||
</form>
|
||||
{{ macros.m_tab_content_end() }}
|
||||
|
||||
{{ macros.m_tab_content_start('download', false) }}
|
||||
{{ macros.setting_button_with_info([['global_link_btn', '다운로드', [{'key':'url', 'value':'https://github.com/soju6jan/soju6jan.github.io/blob/master/etc/hdhomerun_scan_191214.zip'}]], ['global_link_btn', '매뉴얼', [{'key':'url', 'value':'.'}]]], left='HDHomerun Scan Tool', desc=['HDHomerun 스캔하여 TVH용 프리셋 파일을 만들어주는 Windows용 프로그램', '8VSB 지원 케이블용']) }}
|
||||
<!--
|
||||
{{ macros.setting_button_with_info([['global_link_btn', '다운로드', [{'key':'url', 'value':'https://github.com/soju6jan/soju6jan.github.io/raw/master/etc/sjva_lc_0.1.1.apk'}]], ['global_link_btn', '매뉴얼', [{'key':'url', 'value':'.'}]]], left='SJVA for Live Channels', desc=['Android TV Live Channels 앱에 채널 소스를 제공하는 앱.', 'Klive, Plex 지원']) }}
|
||||
{{ macros.setting_button_with_info([['global_link_btn', '티빙 애드온', [{'key':'url', 'value':'https://github.com/soju6jan/soju6jan.github.io/blob/master/kodi_plugin/plugin.video.tving.zip'}]]], left='KODI', desc=None) }}
|
||||
-->
|
||||
{{ macros.m_tab_content_end() }}
|
||||
|
||||
</div><!--tab-content-->
|
||||
</div> <!--전체-->
|
||||
|
||||
<!-- 링크 모달 -->
|
||||
{{ macros.m_modal_start('link_edit_modal', '링크', 'modal-lg') }}
|
||||
<form id="link_form">
|
||||
<input type="hidden" id="link_edit_index" name="link_edit_index"/>
|
||||
{{ macros.setting_input_text('link_edit_title', '제목') }}
|
||||
{{ macros.setting_input_text('link_edit_url', 'URL') }}
|
||||
{{ macros.setting_button([['link_edit_confirm_btn', '확인'], ['link_edit_cancel_btn', '취소']]) }}
|
||||
</form>
|
||||
{{ macros.m_modal_end() }}
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
var package_name = 'system';
|
||||
var current_data;
|
||||
var link_data;
|
||||
|
||||
$(document).ready(function(){
|
||||
$(function() {
|
||||
});
|
||||
|
||||
$.ajax({
|
||||
url: '/' + package_name + '/ajax/get_link_list',
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data: {},
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
link_data = data
|
||||
make_link_data();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function setting_save_func(formData, noti) {
|
||||
$.ajax({
|
||||
url: '/' + package_name + '/ajax/setting_save_system',
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data: formData,
|
||||
dataType: "json",
|
||||
success: function (ret) {
|
||||
if (ret) {
|
||||
if (noti) {
|
||||
$.notify('<strong>설정을 저장하였습니다.</strong>', {
|
||||
type: 'success'
|
||||
});
|
||||
} else {
|
||||
window.location.href = "/"
|
||||
}
|
||||
} else {
|
||||
$.notify('<strong>설정 저장에 실패하였습니다.</strong>', {
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//설정 저장
|
||||
$("#setting_save").click(function(e) {
|
||||
e.preventDefault();
|
||||
var formData = get_formdata('#setting');
|
||||
setting_save_func(formData, true)
|
||||
});
|
||||
|
||||
//설정 저장
|
||||
$("#setting_save2").click(function(e) {
|
||||
e.preventDefault();
|
||||
var formData = get_formdata('#setting2');
|
||||
setting_save_func(formData, false)
|
||||
});
|
||||
|
||||
$("#setting_save4").click(function(e) {
|
||||
e.preventDefault();
|
||||
var formData = get_formdata('#setting4');
|
||||
setting_save_func(formData, true)
|
||||
});
|
||||
|
||||
$("#setting_save3").click(function(e) {
|
||||
e.preventDefault();
|
||||
var formData = get_formdata('#setting3');
|
||||
setting_save_func(formData, true)
|
||||
$.notify('<strong>재시작해야 적용됩니다.</strong>', {
|
||||
type: 'success'
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$("body").on('click', '#ddns_test_btn', function(e){
|
||||
e.preventDefault();
|
||||
ddns = document.getElementById('ddns').value;
|
||||
$.ajax({
|
||||
url: '/' + package_name + '/ajax/ddns_test',
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data:{ddns:ddns},
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
console.log(data)
|
||||
if (data == 'fail') {
|
||||
$.notify('<strong>접속에 실패하였습니다.</strong>', {
|
||||
type: 'warning'
|
||||
});
|
||||
} else {
|
||||
$.notify('<strong>Version:'+ data+'</strong>', {
|
||||
type: 'success'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
$("body").on('click', '#menu_toggle_btn', function(e){
|
||||
e.preventDefault();
|
||||
category = $(this).data('category')
|
||||
var div_name = '#menu_'+category+'_div'
|
||||
$(div_name).collapse('toggle')
|
||||
});
|
||||
|
||||
$("body").on('click', '#command_run_btn', function(e){
|
||||
e.preventDefault();
|
||||
command_text = document.getElementById('command_text').value;
|
||||
$.ajax({
|
||||
url: '/' + package_name + '/ajax/command_run',
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data:{command_text:command_text},
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (data.ret == 'success') {
|
||||
$.notify('<strong>성공 : '+ data.log +'</strong>', {
|
||||
type: 'success'
|
||||
});
|
||||
} else {
|
||||
$.notify('<strong>실패 : ' + data.log+'</strong>', {
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// 링크
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 화면 상단 버튼 START
|
||||
$("body").on('click', '#link_add_btn', function(e){
|
||||
e.preventDefault();
|
||||
document.getElementById("link_edit_index").value = -1;
|
||||
document.getElementById('link_edit_title').value = '';
|
||||
document.getElementById('link_edit_url').value = '';
|
||||
$('#link_edit_modal').modal();
|
||||
});
|
||||
|
||||
$("body").on('click', '#link_add_divider_btn', function(e){
|
||||
e.preventDefault();
|
||||
tmp = {}
|
||||
tmp['type'] = 'divider'
|
||||
link_data.splice(link_data.length, 0, tmp);
|
||||
make_link_data()
|
||||
});
|
||||
|
||||
$("body").on('click', '#link_save_btn', function(e){
|
||||
e.preventDefault();
|
||||
$.ajax({
|
||||
url: '/' + package_name + '/ajax/link_save',
|
||||
type: "POST",
|
||||
cache: false,
|
||||
data:{link_data:JSON.stringify(link_data)},
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (data) {
|
||||
$.notify('<strong>저장 후 적용하였습니다.</strong>', {
|
||||
type: 'success'
|
||||
});
|
||||
} else {
|
||||
$.notify('<strong>실패하였습니다.</strong>', {
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("body").on('click', '#link_reset_btn', function(e){
|
||||
e.preventDefault();
|
||||
link_data = []
|
||||
make_link_data()
|
||||
});
|
||||
// 화면 상단 버튼 END
|
||||
|
||||
// 리스트 각 항목 별 버튼 START
|
||||
$("body").on('click', '#link_item_up_btn', function(e){
|
||||
e.preventDefault();
|
||||
target_id = $(this).data('index')
|
||||
target = link_data[target_id]
|
||||
if (target_id != 0) {
|
||||
link_data.splice(target_id, 1);
|
||||
link_data.splice(target_id-1, 0, target);
|
||||
}
|
||||
make_link_data()
|
||||
});
|
||||
|
||||
$("body").on('click', '#link_item_down_btn', function(e){
|
||||
e.preventDefault();
|
||||
target_id = $(this).data('index')
|
||||
target = link_data[target_id]
|
||||
if (link_data.length -1 != target_id) {
|
||||
link_data.splice(target_id, 1);
|
||||
link_data.splice(target_id+1, 0, target);
|
||||
}
|
||||
make_link_data()
|
||||
});
|
||||
|
||||
$("body").on('click', '#link_item_delete_btn', function(e){
|
||||
e.preventDefault();
|
||||
target_id = $(this).data('index')
|
||||
target = link_data[target_id]
|
||||
link_data.splice(target_id, 1);
|
||||
make_link_data()
|
||||
});
|
||||
|
||||
$("body").on('click', '#link_item_edit_btn', function(e){
|
||||
e.preventDefault();
|
||||
target_id = $(this).data('index')
|
||||
target = link_data[target_id]
|
||||
document.getElementById('link_edit_index').value = target_id
|
||||
document.getElementById('link_edit_title').value = target.title
|
||||
document.getElementById('link_edit_url').value = target.url
|
||||
$('#link_edit_modal').modal();
|
||||
});
|
||||
// 리스트 각 항목 별 버튼 END
|
||||
|
||||
// START 모달 버튼
|
||||
$("body").on('click', '#link_edit_confirm_btn', function(e){
|
||||
e.preventDefault();
|
||||
edit_index = parseInt(document.getElementById('link_edit_index').value)
|
||||
tmp = {}
|
||||
tmp['type'] = 'link'
|
||||
tmp['title'] = document.getElementById('link_edit_title').value
|
||||
tmp['url'] = document.getElementById('link_edit_url').value
|
||||
if (edit_index == -1) {
|
||||
link_data.splice(link_data.length, 0, tmp);
|
||||
} else {
|
||||
link_data.splice(target_id, 1);
|
||||
link_data.splice(target_id, 0, tmp);
|
||||
}
|
||||
make_link_data()
|
||||
$('#link_edit_modal').modal('hide');
|
||||
});
|
||||
|
||||
$("body").on('click', '#link_edit_cancel_btn', function(e){
|
||||
e.preventDefault();
|
||||
$('#link_edit_modal').modal('hide');
|
||||
});
|
||||
// END 모달 버튼
|
||||
|
||||
|
||||
|
||||
function make_link_data() {
|
||||
str = ''
|
||||
for (i in link_data) {
|
||||
//console.log(link_data[i])
|
||||
str += m_row_start_hover();
|
||||
str += m_col(1, parseInt(i)+1);
|
||||
|
||||
if (link_data[i].type == 'link') {
|
||||
str += m_col(4, link_data[i].title)
|
||||
str += m_col(4, link_data[i].url)
|
||||
} else {
|
||||
str += m_col(8, '---Divider Line---')
|
||||
}
|
||||
tmp = ''
|
||||
tmp += m_button('link_item_up_btn', 'UP', [{'key':'index', 'value':i}]);
|
||||
tmp += m_button('link_item_down_btn', 'DOWN', [{'key':'index', 'value':i}]);
|
||||
tmp += m_button('link_item_delete_btn', '삭제', [{'key':'index', 'value':i}]);
|
||||
if (link_data[i].type == 'link') {
|
||||
tmp += m_button('link_item_edit_btn', '편집', [{'key':'index', 'value':i}]);
|
||||
tmp += m_button('global_link_btn', 'Go', [{'key':'url', 'value':link_data[i].url}]);
|
||||
}
|
||||
tmp = m_button_group(tmp)
|
||||
str += m_col(3, tmp)
|
||||
str += m_row_end();
|
||||
if (i != link_data.length -1) str += m_hr(0);
|
||||
}
|
||||
document.getElementById("link_list_div").innerHTML = str;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
$("body").on('click', '#go_filebrowser_btn', function(e){
|
||||
e.preventDefault();
|
||||
url = document.getElementById('url_filebrowser').value
|
||||
window.open(url, "_blank");
|
||||
});
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user