update
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -140,4 +140,5 @@ export.sh
|
|||||||
run.sh
|
run.sh
|
||||||
pre_start.sh
|
pre_start.sh
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
false/
|
false
|
||||||
|
*copy.py
|
||||||
@@ -61,9 +61,7 @@ class Framework:
|
|||||||
self.__make_default_dir()
|
self.__make_default_dir()
|
||||||
|
|
||||||
self.logger = self.get_logger(__package__)
|
self.logger = self.get_logger(__package__)
|
||||||
|
import support
|
||||||
from support import set_logger
|
|
||||||
set_logger(self.logger)
|
|
||||||
|
|
||||||
self.__prepare_starting()
|
self.__prepare_starting()
|
||||||
self.app = Flask(__name__)
|
self.app = Flask(__name__)
|
||||||
@@ -462,6 +460,8 @@ class Framework:
|
|||||||
|
|
||||||
def __app_close(self):
|
def __app_close(self):
|
||||||
try:
|
try:
|
||||||
|
from support import SupportSubprocess
|
||||||
|
SupportSubprocess.all_process_close()
|
||||||
from .init_plugin import PluginManager
|
from .init_plugin import PluginManager
|
||||||
PluginManager.plugin_unload()
|
PluginManager.plugin_unload()
|
||||||
self.socketio.stop()
|
self.socketio.stop()
|
||||||
|
|||||||
@@ -40,6 +40,15 @@ def global_ajax(sub):
|
|||||||
return jsonify(ret)
|
return jsonify(ret)
|
||||||
except:
|
except:
|
||||||
return jsonify({'ret':False})
|
return jsonify({'ret':False})
|
||||||
|
elif sub == 'command_modal_hide':
|
||||||
|
from tool import ToolModalCommand
|
||||||
|
ToolModalCommand.modal_close()
|
||||||
|
return jsonify('')
|
||||||
|
elif sub == 'command_modal_input':
|
||||||
|
from tool import ToolModalCommand
|
||||||
|
cmd = request.form['cmd']
|
||||||
|
ToolModalCommand.input_command(cmd)
|
||||||
|
return jsonify('')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ $(window).on("load resize", function (event) {
|
|||||||
$body.css("padding-top", $navbar.outerHeight());
|
$body.css("padding-top", $navbar.outerHeight());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#command_modal').on('show.bs.modal', function (event) {
|
||||||
|
console.log('111111111')
|
||||||
|
console.log(event);
|
||||||
|
})
|
||||||
|
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
// 사용 미확인
|
// 사용 미확인
|
||||||
|
|||||||
@@ -29,19 +29,6 @@ frameSocket.on('modal', function(data){
|
|||||||
m_modal(data.data, data.title, false);
|
m_modal(data.data, data.title, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
frameSocket.on('command_modal_add_text', function(data){
|
|
||||||
document.getElementById("command_modal_textarea").innerHTML += data ;
|
|
||||||
document.getElementById("command_modal_textarea").scrollTop = document.getElementById("command_modal_textarea").scrollHeight;
|
|
||||||
});
|
|
||||||
|
|
||||||
frameSocket.on('command_modal_show', function(data){
|
|
||||||
command_modal_show(data)
|
|
||||||
});
|
|
||||||
|
|
||||||
frameSocket.on('command_modal_clear', function(data){
|
|
||||||
document.getElementById("command_modal_textarea").innerHTML = ""
|
|
||||||
});
|
|
||||||
|
|
||||||
frameSocket.on('loading_hide', function(data){
|
frameSocket.on('loading_hide', function(data){
|
||||||
$('#loading').hide();
|
$('#loading').hide();
|
||||||
});
|
});
|
||||||
@@ -52,6 +39,16 @@ frameSocket.on('refresh', function(data){
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$('#command_modal').on('hide.bs.modal', function (event) {
|
||||||
|
$.ajax({
|
||||||
|
url: `/global/ajax/command_modal_hide`,
|
||||||
|
type: 'POST',
|
||||||
|
cache: false,
|
||||||
|
data: {},
|
||||||
|
dataType: 'json'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -250,3 +247,50 @@ let listdir = (path = '/', only_dir = true) => {
|
|||||||
// 파일 선택 모달 End
|
// 파일 선택 모달 End
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////
|
||||||
|
// Command MODAL
|
||||||
|
///////////////////////////////////////
|
||||||
|
|
||||||
|
frameSocket.on('command_modal_add_text', function(data){
|
||||||
|
document.getElementById("command_modal_textarea").innerHTML += data ;
|
||||||
|
document.getElementById("command_modal_textarea").scrollTop = document.getElementById("command_modal_textarea").scrollHeight;
|
||||||
|
});
|
||||||
|
|
||||||
|
frameSocket.on('command_modal_input_disable', function(data){
|
||||||
|
$('#command_modal_input').attr('disabled', true);
|
||||||
|
});
|
||||||
|
|
||||||
|
frameSocket.on('command_modal_show', function(data){
|
||||||
|
command_modal_show(data)
|
||||||
|
});
|
||||||
|
|
||||||
|
frameSocket.on('command_modal_clear', function(data){
|
||||||
|
document.getElementById("command_modal_textarea").innerHTML = ""
|
||||||
|
});
|
||||||
|
|
||||||
|
function command_modal_show(title) {
|
||||||
|
ClientHeight = window.innerHeight
|
||||||
|
document.getElementById("command_modal_title").innerHTML = title
|
||||||
|
$("#command_modal").height(ClientHeight+50);
|
||||||
|
$("#command_modal_textarea").height(ClientHeight-380);
|
||||||
|
$("#command_modal").modal({backdrop: 'static', keyboard: false}, 'show');
|
||||||
|
$('#command_modal_input').attr('disabled', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
$("body").on('click', '#command_modal_input_btn', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$.ajax({
|
||||||
|
url: '/global/ajax/command_modal_input',
|
||||||
|
type: "POST",
|
||||||
|
cache: false,
|
||||||
|
data: {cmd:$('#command_modal_input').val()},
|
||||||
|
dataType: "json",
|
||||||
|
success: function (ret) {
|
||||||
|
$('#command_modal_input').val('');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
///////////////////////////////////////
|
||||||
|
|
||||||
|
|||||||
@@ -66,13 +66,7 @@ $("body").on('click', '#global_downloader_add_btn', function(e){
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function command_modal_show(title) {
|
|
||||||
ClientHeight = window.innerHeight
|
|
||||||
document.getElementById("command_modal_title").innerHTML = title
|
|
||||||
$("#command_modal").height(ClientHeight-100);
|
|
||||||
$("#command_modal_textarea").height(ClientHeight-380);
|
|
||||||
$("#command_modal").modal();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 토렌트 프로그램에 다운로드 추가할 결과를 보여주는
|
// 토렌트 프로그램에 다운로드 추가할 결과를 보여주는
|
||||||
|
|||||||
@@ -116,8 +116,14 @@
|
|||||||
<!--command modal-->
|
<!--command modal-->
|
||||||
{{ macros.m_modal_start('command_modal', '', 'modal-lg') }}
|
{{ macros.m_modal_start('command_modal', '', 'modal-lg') }}
|
||||||
<div>
|
<div>
|
||||||
<textarea id="command_modal_textarea" class="col-md-12" rows="30" disabled style="visibility:visible"></textarea>
|
<textarea id="command_modal_textarea" class="col-md-12" rows="30" disabled style="background-color:#ffffff;visibility:visible"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
<form id='command_modal_form' name='command_form'>
|
||||||
|
<div class="form-inline input-group">
|
||||||
|
<input id="command_modal_input" name="command_modal_input" type="text" class="form-control form-control-sm" placeholder="">
|
||||||
|
<button id="command_modal_input_btn" class="btn btn-sm btn-outline-success">입력</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
{{ macros.m_modal_end() }}
|
{{ macros.m_modal_end() }}
|
||||||
<!--command modal end-->
|
<!--command modal end-->
|
||||||
<!-- Modal end -->
|
<!-- Modal end -->
|
||||||
|
|||||||
@@ -8,32 +8,10 @@ def d(data):
|
|||||||
else:
|
else:
|
||||||
return str(data)
|
return str(data)
|
||||||
|
|
||||||
def load():
|
|
||||||
from .base.aes import SupportAES
|
|
||||||
from .base.discord import SupportDiscord
|
|
||||||
from .base.file import SupportFile
|
|
||||||
from .base.process import SupportProcess
|
|
||||||
from .base.string import SupportString
|
|
||||||
from .base.subprocess import SupportSubprocess
|
|
||||||
from .base.telegram import SupportTelegram
|
|
||||||
from .base.util import (AlchemyEncoder, SingletonClass, SupportUtil,
|
|
||||||
default_headers, pt)
|
|
||||||
from .base.yaml import SupportYaml
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
logger = None
|
|
||||||
|
|
||||||
if os.environ.get('FF') == 'true':
|
|
||||||
def set_logger(l):
|
|
||||||
global logger
|
|
||||||
logger = l
|
|
||||||
|
|
||||||
else:
|
|
||||||
from .logger import get_logger
|
from .logger import get_logger
|
||||||
|
|
||||||
logger = get_logger()
|
logger = get_logger()
|
||||||
|
|
||||||
|
|
||||||
from .base.aes import SupportAES
|
from .base.aes import SupportAES
|
||||||
from .base.discord import SupportDiscord
|
from .base.discord import SupportDiscord
|
||||||
from .base.file import SupportFile
|
from .base.file import SupportFile
|
||||||
@@ -44,7 +22,3 @@ from .base.telegram import SupportTelegram
|
|||||||
from .base.util import (AlchemyEncoder, SingletonClass, SupportUtil,
|
from .base.util import (AlchemyEncoder, SingletonClass, SupportUtil,
|
||||||
default_headers, pt)
|
default_headers, pt)
|
||||||
from .base.yaml import SupportYaml
|
from .base.yaml import SupportYaml
|
||||||
|
|
||||||
# 일반 cli 사용 겸용이다.
|
|
||||||
# set_logger 로 인한 진입이 아니고 import가 되면 기본 경로로 로그파일을
|
|
||||||
# 생성하기 때문에, set_logger 전에 import가 되지 않도록 주의.
|
|
||||||
|
|||||||
@@ -1,12 +1,4 @@
|
|||||||
from support import d, logger
|
from support import d, logger
|
||||||
|
|
||||||
from .aes import SupportAES
|
import support
|
||||||
from .discord import SupportDiscord
|
logger = support.logger
|
||||||
from .ffmpeg import SupportFfmpeg
|
|
||||||
from .file import SupportFile
|
|
||||||
from .image import SupportImage
|
|
||||||
from .process import SupportProcess
|
|
||||||
from .string import SupportString
|
|
||||||
from .util import (AlchemyEncoder, SingletonClass, SupportUtil,
|
|
||||||
default_headers, pt)
|
|
||||||
from .yaml import SupportYaml
|
|
||||||
|
|||||||
@@ -1,23 +1,29 @@
|
|||||||
|
import io
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
|
import queue
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from . import logger
|
from . import logger
|
||||||
|
|
||||||
|
|
||||||
|
def demote(user_uid, user_gid):
|
||||||
|
def result():
|
||||||
|
os.setgid(user_gid)
|
||||||
|
os.setuid(user_uid)
|
||||||
|
return result
|
||||||
|
|
||||||
class SupportSubprocess(object):
|
class SupportSubprocess(object):
|
||||||
|
|
||||||
# 2021-10-25
|
# 2021-10-25
|
||||||
# timeout 적용
|
# timeout 적용
|
||||||
@classmethod
|
@classmethod
|
||||||
def execute_command_return(cls, command, format=None, force_log=False, shell=False, env=None, timeout=None, uid=0, gid=0):
|
def execute_command_return(cls, command, format=None, force_log=False, shell=False, env=None, timeout=None, uid=0, gid=0):
|
||||||
def demote(user_uid, user_gid):
|
|
||||||
def result():
|
|
||||||
os.setgid(user_gid)
|
|
||||||
os.setuid(user_uid)
|
|
||||||
return result
|
|
||||||
try:
|
try:
|
||||||
if platform.system() == 'Windows':
|
if platform.system() == 'Windows':
|
||||||
tmp = []
|
tmp = []
|
||||||
@@ -74,3 +80,162 @@ class SupportSubprocess(object):
|
|||||||
logger.error('Exception:%s', exception)
|
logger.error('Exception:%s', exception)
|
||||||
logger.error(traceback.format_exc())
|
logger.error(traceback.format_exc())
|
||||||
logger.error('command : %s', command)
|
logger.error('command : %s', command)
|
||||||
|
|
||||||
|
|
||||||
|
instance_list = []
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, command, print_log=False, shell=False, env=None, timeout=None, uid=0, gid=0, stdout_callback=None):
|
||||||
|
self.command = command
|
||||||
|
self.print_log = print_log
|
||||||
|
self.shell = shell
|
||||||
|
self.env = env
|
||||||
|
self.timeout = timeout
|
||||||
|
self.uid = uid
|
||||||
|
self.gid = gid
|
||||||
|
self.stdout_callback = stdout_callback
|
||||||
|
self.process = None
|
||||||
|
self.stdout_queue = None
|
||||||
|
|
||||||
|
|
||||||
|
def start(self, join=True):
|
||||||
|
try:
|
||||||
|
self.thread = threading.Thread(target=self.execute_thread_function, args=())
|
||||||
|
self.thread.setDaemon(True)
|
||||||
|
self.thread.start()
|
||||||
|
if join:
|
||||||
|
self.thread.join()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'Exception:{str(e)}')
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
|
def execute_thread_function(self):
|
||||||
|
try:
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
tmp = []
|
||||||
|
if type(self.command) == type([]):
|
||||||
|
for x in self.command:
|
||||||
|
if x.find(' ') == -1:
|
||||||
|
tmp.append(x)
|
||||||
|
else:
|
||||||
|
tmp.append(f'"{x}"')
|
||||||
|
self.command = ' '.join(tmp)
|
||||||
|
|
||||||
|
iter_arg = ''
|
||||||
|
if platform.system() == 'Windows':
|
||||||
|
self.process = subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=self.shell, env=self.env, encoding='utf8')
|
||||||
|
else:
|
||||||
|
self.process = subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, shell=self.shell, env=self.env, preexec_fn=demote(self.uid, self.gid), encoding='utf8')
|
||||||
|
SupportSubprocess.instance_list.append(self)
|
||||||
|
self.start_communicate()
|
||||||
|
self.start_send_callback()
|
||||||
|
if self.process is not None:
|
||||||
|
self.process.wait()
|
||||||
|
#logger.info(f"{self.command} 정상 종료")
|
||||||
|
if self.stdout_queue != None:
|
||||||
|
self.stdout_queue.put('<END>')
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'Exception:{str(e)}')
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def start_communicate(self):
|
||||||
|
self.stdout_queue = queue.Queue()
|
||||||
|
sout = io.open(self.process.stdout.fileno(), 'rb', closefd=False)
|
||||||
|
|
||||||
|
def Pump(stream):
|
||||||
|
_queue = queue.Queue()
|
||||||
|
|
||||||
|
def rdr():
|
||||||
|
while True:
|
||||||
|
if self.process == None:
|
||||||
|
break
|
||||||
|
buf = self.process.stdout.read(1)
|
||||||
|
if buf:
|
||||||
|
_queue.put( buf )
|
||||||
|
else:
|
||||||
|
_queue.put( None )
|
||||||
|
break
|
||||||
|
_queue.put( None )
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
def clct():
|
||||||
|
active = True
|
||||||
|
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 self.stdout_queue != None:
|
||||||
|
self.stdout_queue.put(r)
|
||||||
|
if self.stdout_queue != None: # 사용자 중지
|
||||||
|
self.stdout_queue.put('<END>')
|
||||||
|
for tgt in [rdr, clct]:
|
||||||
|
th = threading.Thread(target=tgt)
|
||||||
|
th.setDaemon(True)
|
||||||
|
th.start()
|
||||||
|
Pump(sout)
|
||||||
|
|
||||||
|
|
||||||
|
def start_send_callback(self):
|
||||||
|
def func():
|
||||||
|
while self.stdout_queue:
|
||||||
|
line = self.stdout_queue.get()
|
||||||
|
if line == '<END>':
|
||||||
|
if self.stdout_callback != None:
|
||||||
|
self.stdout_callback('end', None)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if self.stdout_callback != None:
|
||||||
|
self.stdout_callback('log', line)
|
||||||
|
self.send_to_ui_thread = None
|
||||||
|
self.stdout_queue = None
|
||||||
|
self.process = None
|
||||||
|
|
||||||
|
th = threading.Thread(target=func, args=())
|
||||||
|
th.setDaemon(True)
|
||||||
|
th.start()
|
||||||
|
|
||||||
|
|
||||||
|
def process_close(self):
|
||||||
|
try:
|
||||||
|
if self.process is not None and self.process.poll() is None:
|
||||||
|
#import psutil
|
||||||
|
#process = psutil.Process(instance.process.pid)
|
||||||
|
#for proc in instance.process.children(recursive=True):
|
||||||
|
# proc.kill()
|
||||||
|
self.process.kill()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'Exception:{str(e)}')
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
self.stdout_queue = None
|
||||||
|
self.process.kill()
|
||||||
|
except: pass
|
||||||
|
|
||||||
|
def input_command(self, cmd):
|
||||||
|
if self.process != None:
|
||||||
|
self.process.stdin.write(f'{cmd}\n')
|
||||||
|
self.process.stdin.flush()
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def all_process_close(cls):
|
||||||
|
for instance in cls.instance_list:
|
||||||
|
instance.process_close()
|
||||||
|
cls.instance_list = []
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +1,11 @@
|
|||||||
import os, sys, logging, logging.handlers
|
import logging
|
||||||
|
import logging.handlers
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from pytz import timezone, utc
|
from pytz import timezone, utc
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ConsoleColor.Black => "\x1B[30m",
|
ConsoleColor.Black => "\x1B[30m",
|
||||||
ConsoleColor.DarkRed => "\x1B[31m",
|
ConsoleColor.DarkRed => "\x1B[31m",
|
||||||
@@ -48,6 +53,8 @@ class CustomFormatter(logging.Formatter):
|
|||||||
|
|
||||||
|
|
||||||
def get_logger(name=None, log_path=None):
|
def get_logger(name=None, log_path=None):
|
||||||
|
if os.environ.get('FF') == 'true':
|
||||||
|
name = 'framework'
|
||||||
if name == None:
|
if name == None:
|
||||||
name = sys.argv[0].rsplit('.', 1)[0]
|
name = sys.argv[0].rsplit('.', 1)[0]
|
||||||
logger = logging.getLogger(name)
|
logger = logging.getLogger(name)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import random
|
|||||||
import string
|
import string
|
||||||
|
|
||||||
from support import SupportDiscord, SupportFile, SupportTelegram
|
from support import SupportDiscord, SupportFile, SupportTelegram
|
||||||
|
from tool.modal_command import ToolModalCommand
|
||||||
|
|
||||||
from .setup import *
|
from .setup import *
|
||||||
|
|
||||||
@@ -33,6 +34,10 @@ class ModuleSetting(PluginModuleBase):
|
|||||||
'notify_advaned_use' : 'False',
|
'notify_advaned_use' : 'False',
|
||||||
'notify.yaml': '', #직접 사용하지 않으나 저장 편의상.
|
'notify.yaml': '', #직접 사용하지 않으나 저장 편의상.
|
||||||
'command_text': '',
|
'command_text': '',
|
||||||
|
'celery_start_by_web': 'False', #웹 실행시 celery 실행
|
||||||
|
'celery_start_command': "celery -A flaskfarm.main.celery worker --loglevel=info --pool=gevent --concurrency=2 --config_filepath={F.config['config_filepath']} --running_type=native",
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, P):
|
def __init__(self, P):
|
||||||
@@ -57,6 +62,9 @@ class ModuleSetting(PluginModuleBase):
|
|||||||
elif page == 'notify':
|
elif page == 'notify':
|
||||||
arg['notify_yaml_filepath'] = F.config['notify_yaml_filepath']
|
arg['notify_yaml_filepath'] = F.config['notify_yaml_filepath']
|
||||||
arg['notify.yaml'] = SupportFile.read_file(arg['notify_yaml_filepath'])
|
arg['notify.yaml'] = SupportFile.read_file(arg['notify_yaml_filepath'])
|
||||||
|
elif page == 'celery':
|
||||||
|
arg['use_celery'] = F.config['use_celery']
|
||||||
|
arg['running_type'] = F.config['running_type']
|
||||||
|
|
||||||
return render_template(f'{__package__}_{name}_{page}.html', arg=arg)
|
return render_template(f'{__package__}_{name}_{page}.html', arg=arg)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -113,12 +121,21 @@ class ModuleSetting(PluginModuleBase):
|
|||||||
elif command == 'command_run':
|
elif command == 'command_run':
|
||||||
ret['msg'] = arg1
|
ret['msg'] = arg1
|
||||||
pass
|
pass
|
||||||
|
elif command == 'celery_execute':
|
||||||
|
tmp = arg1.replace("{F.config['config_filepath']}", F.config['config_filepath']).replace('{F.config["config_filepath"]}', F.config['config_filepath'])
|
||||||
|
cmd = [
|
||||||
|
['msg', f'명령 : {tmp}'],
|
||||||
|
['msg', ''],
|
||||||
|
tmp.split(' '),
|
||||||
|
]
|
||||||
|
ToolModalCommand.start("Celery 실행", cmd)
|
||||||
return jsonify(ret)
|
return jsonify(ret)
|
||||||
|
|
||||||
|
|
||||||
def plugin_load(self):
|
def plugin_load(self):
|
||||||
try:
|
try:
|
||||||
if F.config['run_flask']:
|
if F.config['run_flask'] == False:
|
||||||
|
return
|
||||||
F.logger.info(f"arg_repeat : {F.config['arg_repeat']}")
|
F.logger.info(f"arg_repeat : {F.config['arg_repeat']}")
|
||||||
F.logger.info(f"arg_repeat : {F.config['arg_repeat']}")
|
F.logger.info(f"arg_repeat : {F.config['arg_repeat']}")
|
||||||
|
|
||||||
@@ -148,6 +165,8 @@ class ModuleSetting(PluginModuleBase):
|
|||||||
P.logger.error(f'Exception:{str(e)}')
|
P.logger.error(f'Exception:{str(e)}')
|
||||||
P.logger.error(traceback.format_exc())
|
P.logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
def plugin_unload(self):
|
||||||
|
ToolModalCommand.process_close()
|
||||||
|
|
||||||
def setting_save_after(self, change_list):
|
def setting_save_after(self, change_list):
|
||||||
if 'theme' in change_list:
|
if 'theme' in change_list:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ class ModuleTool(PluginModuleBase):
|
|||||||
|
|
||||||
|
|
||||||
def __init__(self, P):
|
def __init__(self, P):
|
||||||
super(ModuleTool, self).__init__(P, name=name, first_menu='celery')
|
super(ModuleTool, self).__init__(P, name=name, first_menu='upload')
|
||||||
|
|
||||||
|
|
||||||
def process_menu(self, page, req):
|
def process_menu(self, page, req):
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ __menu = {
|
|||||||
{'uri': 'menu', 'name': '메뉴 구성'},
|
{'uri': 'menu', 'name': '메뉴 구성'},
|
||||||
{'uri': 'config', 'name': 'config.yaml 파일'},
|
{'uri': 'config', 'name': 'config.yaml 파일'},
|
||||||
{'uri': 'export', 'name': 'export.sh 파일'},
|
{'uri': 'export', 'name': 'export.sh 파일'},
|
||||||
|
{'uri': 'celery', 'name': '비동기 작업(celery)'},
|
||||||
{'uri': 'notify', 'name': '알림'},
|
{'uri': 'notify', 'name': '알림'},
|
||||||
|
|
||||||
],
|
],
|
||||||
@@ -22,6 +23,7 @@ __menu = {
|
|||||||
'list': [
|
'list': [
|
||||||
{'uri': 'setting', 'name': '설정'},
|
{'uri': 'setting', 'name': '설정'},
|
||||||
{'uri': 'list', 'name': '로딩 플러그인'},
|
{'uri': 'list', 'name': '로딩 플러그인'},
|
||||||
|
{'uri': 'all', 'name': '플러그인 목록'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -29,7 +31,6 @@ __menu = {
|
|||||||
'name': '시스템 툴',
|
'name': '시스템 툴',
|
||||||
'list': [
|
'list': [
|
||||||
{'uri': 'upload', 'name': '업로드'},
|
{'uri': 'upload', 'name': '업로드'},
|
||||||
{'uri': 'celery', 'name': 'celery 테스트'},
|
|
||||||
{'uri': 'python', 'name': 'Python'},
|
{'uri': 'python', 'name': 'Python'},
|
||||||
{'uri': 'db', 'name': 'DB'},
|
{'uri': 'db', 'name': 'DB'},
|
||||||
{'uri': 'crypt', 'name': '암호화'},
|
{'uri': 'crypt', 'name': '암호화'},
|
||||||
|
|||||||
123
lib/system/templates/system_setting_celery.html
Normal file
123
lib/system/templates/system_setting_celery.html
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block content %}
|
||||||
|
<div>
|
||||||
|
{{ macros.m_button_group([['globalSettingSaveBtn', '설정 저장'], ['celery_test_btn', 'Celery 테스트']])}}
|
||||||
|
{{ macros.m_row_start('5') }}
|
||||||
|
{{ macros.m_row_end() }}
|
||||||
|
{{ macros.m_hr() }}
|
||||||
|
|
||||||
|
<div class="tab-content" id="nav-tabContent">
|
||||||
|
{{ macros.info_text('use_celery', 'use_celery 값', arg['use_celery']) }}
|
||||||
|
{{ macros.info_text('running_type', 'running_type 값', arg['running_type']) }}
|
||||||
|
{{ macros.info_text('_tmp', '설명', "Docker는 celery가 서비스로 동작하기 때문에 설정이 불필요하며 '테스트' 버튼으로 작동 여부 확인만 가능합니다.", desc=['','native로 동작하는 경우 celery 실행을 따로 하지 않고 한번에 실행하기 위한 설정', 'Redis는 설정된 Port로 동작중인 상태여야 함.']) }}
|
||||||
|
{{ macros.m_hr() }}
|
||||||
|
<form id='setting' name='setting'>
|
||||||
|
{{ macros.setting_checkbox('celery_start_by_web', '시작시 celery 실행', value=arg['celery_start_by_web']) }}
|
||||||
|
|
||||||
|
{{ macros.setting_input_textarea('celery_start_command', 'celery 실행 명령', desc=['',
|
||||||
|
|
||||||
|
'예: celery -A flaskfarm.main.celery worker --loglevel=info --pool=gevent --concurrency=2 --config_filepath="config.yaml파일경로" --running_type=native',
|
||||||
|
'패키지로 실행시 : -A flaskfarm.main.celery',
|
||||||
|
'GIT 소스로 실행시 : -A main.celery (현재 작업폴더가 flaskfarm)',
|
||||||
|
'Linux는 사용자에 따라 export C_FORCE_ROOT=true 필요'
|
||||||
|
],
|
||||||
|
value=arg['celery_start_command'], row='5') }}
|
||||||
|
</form>
|
||||||
|
{{ macros.setting_buttons([['celery_excute_btn', '실행 테스트']]) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$("body").on('click', '#celery_excute_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
globalSendCommand('celery_execute', $('#celery_start_command').val());
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$("body").on('click', '#shutdown_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
shutdown_confirm();
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on('click', '#celery_test_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
$.ajax({
|
||||||
|
url: '/' + package_name + '/ajax/' + sub + '/celery_test',
|
||||||
|
type: "POST",
|
||||||
|
cache: false,
|
||||||
|
data: {},
|
||||||
|
dataType: "json",
|
||||||
|
success: function (ret) {
|
||||||
|
if (ret.ret == 'success') {
|
||||||
|
$.notify('<strong>'+ ret.data+'</strong>', {
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
} else if (ret.ret == 'timeout' || ret.ret == 'no_celery') {
|
||||||
|
$.notify('<strong>'+ ret.data+'</strong>', {
|
||||||
|
type: 'warning'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//m_modal(ret)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$("body").on('click', '#ps_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
$.ajax({
|
||||||
|
url: '/' + package_name + '/ajax/' + sub + '/ps',
|
||||||
|
type: "POST",
|
||||||
|
cache: false,
|
||||||
|
data: {},
|
||||||
|
dataType: "json",
|
||||||
|
success: function (ret) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on('click', '#worker_start_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
$.ajax({
|
||||||
|
url: '/' + package_name + '/ajax/' + sub + '/worker_start',
|
||||||
|
type: "POST",
|
||||||
|
cache: false,
|
||||||
|
data: {},
|
||||||
|
dataType: "json",
|
||||||
|
success: function (ret) {
|
||||||
|
if (ret.ret == 'success') {
|
||||||
|
$.notify('<strong>'+ ret.data+'</strong>', {
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
} else if (ret.ret == 'timeout' || ret.ret == 'no_celery' || ret.ret == 'not_registered') {
|
||||||
|
$.notify('<strong>'+ ret.data+'</strong>', {
|
||||||
|
type: 'warning'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//m_modal(ret)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<form id='setting' name='setting'>
|
<form id='setting' name='setting'>
|
||||||
<div class="tab-content" id="nav-tabContent">
|
<div class="tab-content" id="nav-tabContent">
|
||||||
{{ macros.m_tab_content_start('normal', true) }}
|
{{ macros.m_tab_content_start('normal', true) }}
|
||||||
{{ macros.setting_checkbox('tool_crypt_use_user_key', '암호화 키 본인키 사용', value=arg['tool_crypt_use_user_key'], desc=['On : 본인 키 사용', '주의) 변경 후 일반설정-인증-로그인 암호를 새로 저장해야 합니다.', 'Off : 앱 고정 키 사용']) }}
|
{{ macros.setting_checkbox('tool_crypt_use_user_key', '암호화 키 직접 입력', value=arg['tool_crypt_use_user_key'], desc=['On : 본인 키 사용', '주의) 변경 후 일반설정-인증-로그인 암호를 새로 저장해야 합니다.', 'Off : 앱 고정 키 사용']) }}
|
||||||
<div id="tool_crypt_use_user_key_div" class="collapse">
|
<div id="tool_crypt_use_user_key_div" class="collapse">
|
||||||
{{ macros.setting_input_text('tool_crypt_user_key', '암호화 키', value=arg['tool_crypt_user_key'], desc=['16진수(숫자, a~e)로 이루어진 32글자. 미만시 앞을 0으로 채움. 초과시 뒤에 무시']) }}
|
{{ macros.setting_input_text('tool_crypt_user_key', '암호화 키', value=arg['tool_crypt_user_key'], desc=['16진수(숫자, a~e)로 이루어진 32글자. 미만시 앞을 0으로 채움. 초과시 뒤에 무시']) }}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
from framework import logger
|
from framework import logger
|
||||||
|
|
||||||
|
from .modal_command import ToolModalCommand
|
||||||
from .notify import ToolNotify
|
from .notify import ToolNotify
|
||||||
|
|||||||
113
lib/tool/modal_command.py
Normal file
113
lib/tool/modal_command.py
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import os
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
from framework import F
|
||||||
|
from support import SupportSubprocess
|
||||||
|
|
||||||
|
|
||||||
|
class ToolModalCommand(object):
|
||||||
|
__thread = None
|
||||||
|
__title = None
|
||||||
|
__commands = None
|
||||||
|
__clear = None
|
||||||
|
__show_modal = None
|
||||||
|
__wait = None
|
||||||
|
__ss_process = None
|
||||||
|
__abort = None
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def start(cls, title, commands, clear=True, wait=False, show_modal=True):
|
||||||
|
if cls.__thread != None:
|
||||||
|
cls.__abort = True
|
||||||
|
cls.__thread = None
|
||||||
|
if cls.__ss_process != None:
|
||||||
|
cls.__ss_process.process_close()
|
||||||
|
cls.__ss_process = None
|
||||||
|
cls.__title = title
|
||||||
|
cls.__commands = commands
|
||||||
|
cls.__clear = clear
|
||||||
|
cls.__wait = wait
|
||||||
|
cls.__show_modal = show_modal
|
||||||
|
cls.__thread = None
|
||||||
|
cls.__abort = False
|
||||||
|
cls.__start()
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __start(cls):
|
||||||
|
try:
|
||||||
|
if cls.__show_modal:
|
||||||
|
if cls.__clear:
|
||||||
|
F.socketio.emit("command_modal_clear", None, namespace='/framework', broadcast=True)
|
||||||
|
cls.__thread = threading.Thread(target=cls.__execute_thread_function, args=())
|
||||||
|
cls.__thread.setDaemon(True)
|
||||||
|
cls.__thread.start()
|
||||||
|
if cls.__wait:
|
||||||
|
time.sleep(1)
|
||||||
|
cls.__thread.join()
|
||||||
|
except Exception as exception:
|
||||||
|
F.logger.error('Exception:%s', exception)
|
||||||
|
F.logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __execute_thread_function(cls):
|
||||||
|
try:
|
||||||
|
if cls.__show_modal:
|
||||||
|
F.socketio.emit("command_modal_show", cls.__title, namespace='/framework', broadcast=True)
|
||||||
|
F.socketio.emit("loading_hide", None, namespace='/framework', broadcast=True)
|
||||||
|
|
||||||
|
for command in cls.__commands:
|
||||||
|
if cls.__abort:
|
||||||
|
return
|
||||||
|
if command[0] == 'msg':
|
||||||
|
if cls.__show_modal:
|
||||||
|
F.socketio.emit("command_modal_add_text", '%s\n\n' % command[1], namespace='/framework', broadcast=True)
|
||||||
|
elif command[0] == 'system':
|
||||||
|
if cls.__show_modal:
|
||||||
|
F.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:]
|
||||||
|
cls.__ss_process = SupportSubprocess(command, stdout_callback=cls.process_callback)
|
||||||
|
cls.__ss_process.start()
|
||||||
|
cls.__ss_process.process_close()
|
||||||
|
cls.__ss_process = None
|
||||||
|
time.sleep(1)
|
||||||
|
except Exception as exception:
|
||||||
|
if cls.__show_modal:
|
||||||
|
F.socketio.emit("command_modal_show", cls.__title, namespace='/framework', broadcast=True)
|
||||||
|
F.socketio.emit("command_modal_add_text", str(exception), namespace='/framework', broadcast=True)
|
||||||
|
F.socketio.emit("command_modal_add_text", str(traceback.format_exc()), namespace='/framework', broadcast=True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def process_callback(cls, mode, text):
|
||||||
|
if cls.__show_modal == False:
|
||||||
|
return
|
||||||
|
if mode == 'end':
|
||||||
|
F.socketio.emit("command_modal_add_text", "\n\n<<프로세스 종료>>", namespace='/framework', broadcast=True)
|
||||||
|
F.socketio.emit("command_modal_input_disable", "", namespace='/framework', broadcast=True)
|
||||||
|
|
||||||
|
else:
|
||||||
|
F.socketio.emit("command_modal_add_text", text, namespace='/framework', broadcast=True)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def modal_close(cls):
|
||||||
|
if cls.__thread != None:
|
||||||
|
cls.__abort = True
|
||||||
|
|
||||||
|
if cls.__ss_process != None:
|
||||||
|
cls.__ss_process.process_close()
|
||||||
|
cls.__ss_process = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def input_command(cls, cmd):
|
||||||
|
if cls.__ss_process != None:
|
||||||
|
cls.__ss_process.input_command(cmd)
|
||||||
|
|
||||||
Reference in New Issue
Block a user