update
This commit is contained in:
@@ -43,4 +43,4 @@ class CodeEncode:
|
|||||||
if __name__== "__main__":
|
if __name__== "__main__":
|
||||||
CodeEncode().process_args()
|
CodeEncode().process_args()
|
||||||
|
|
||||||
# python C:\work\FlaskFarm\flaskfarm\lib\cli\code_encode.py --source=C:\work\FlaskFarm\data\LOADING\klive_plus\source_spotv.py
|
# python C:\work\FlaskFarm\flaskfarm\cli\code_encode.py --source=C:\work\FlaskFarm\data\LOADING\sjva
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ class Framework:
|
|||||||
|
|
||||||
|
|
||||||
def __initialize(self):
|
def __initialize(self):
|
||||||
|
os.environ["PYTHONUNBUFFERED"] = "1"
|
||||||
os.environ['FF'] = "true"
|
os.environ['FF'] = "true"
|
||||||
self.__config_initialize("first")
|
self.__config_initialize("first")
|
||||||
self.__make_default_dir()
|
self.__make_default_dir()
|
||||||
@@ -118,7 +119,9 @@ class Framework:
|
|||||||
for package_name in plugins:
|
for package_name in plugins:
|
||||||
db_path = os.path.join(self.config['path_data'], 'db', f'{package_name}.db')
|
db_path = os.path.join(self.config['path_data'], 'db', f'{package_name}.db')
|
||||||
self.app.config['SQLALCHEMY_BINDS'][package_name] = f'sqlite:///{db_path}'
|
self.app.config['SQLALCHEMY_BINDS'][package_name] = f'sqlite:///{db_path}'
|
||||||
self.db = SQLAlchemy(self.app, session_options={"autoflush": False})
|
self.db = SQLAlchemy(self.app, session_options={"autoflush": False, "expire_on_commit": False})
|
||||||
|
#with self.app.app_context():
|
||||||
|
# self.db.session.expunge_all()
|
||||||
|
|
||||||
|
|
||||||
def __init_celery(self):
|
def __init_celery(self):
|
||||||
@@ -222,6 +225,7 @@ class Framework:
|
|||||||
from . import init_route, log_viewer
|
from . import init_route, log_viewer
|
||||||
|
|
||||||
self.__make_default_logger()
|
self.__make_default_logger()
|
||||||
|
self.__config_initialize("last")
|
||||||
self.logger.info('### LAST')
|
self.logger.info('### LAST')
|
||||||
self.logger.info(f"### PORT: {self.config.get('port')}")
|
self.logger.info(f"### PORT: {self.config.get('port')}")
|
||||||
self.logger.info('### Now you can access App by webbrowser!!')
|
self.logger.info('### Now you can access App by webbrowser!!')
|
||||||
@@ -266,6 +270,18 @@ class Framework:
|
|||||||
self.app.config['JSON_AS_ASCII'] = False
|
self.app.config['JSON_AS_ASCII'] = False
|
||||||
elif mode == 'system_loading_after':
|
elif mode == 'system_loading_after':
|
||||||
pass
|
pass
|
||||||
|
elif mode == 'last':
|
||||||
|
db_foder = os.path.join(self.config['path_data'], 'db')
|
||||||
|
for name in os.listdir(db_foder):
|
||||||
|
if name.endswith('.db'):
|
||||||
|
db_filepath = os.path.join(db_foder, name)
|
||||||
|
try:
|
||||||
|
if os.stat(db_filepath).st_size == 0:
|
||||||
|
os.remove(db_filepath)
|
||||||
|
self.logger.debug(f"REMOVE {db_filepath}")
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def __init_define(self):
|
def __init_define(self):
|
||||||
@@ -372,7 +388,7 @@ class Framework:
|
|||||||
###################################################
|
###################################################
|
||||||
# 로그
|
# 로그
|
||||||
###################################################
|
###################################################
|
||||||
def get_logger(self, name):
|
def get_logger(self, name, from_command=False):
|
||||||
logger = logging.getLogger(name)
|
logger = logging.getLogger(name)
|
||||||
if not logger.handlers:
|
if not logger.handlers:
|
||||||
level = logging.DEBUG
|
level = logging.DEBUG
|
||||||
@@ -396,25 +412,26 @@ class Framework:
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
logger.setLevel(level)
|
logger.setLevel(level)
|
||||||
file_formatter = logging.Formatter(u'[%(asctime)s|%(levelname)s|%(filename)s:%(lineno)s] %(message)s')
|
|
||||||
def customTime(*args):
|
def customTime(*args):
|
||||||
utc_dt = utc.localize(datetime.utcnow())
|
utc_dt = utc.localize(datetime.utcnow())
|
||||||
my_tz = timezone("Asia/Seoul")
|
my_tz = timezone("Asia/Seoul")
|
||||||
converted = utc_dt.astimezone(my_tz)
|
converted = utc_dt.astimezone(my_tz)
|
||||||
return converted.timetuple()
|
return converted.timetuple()
|
||||||
|
|
||||||
|
if from_command == False:
|
||||||
|
file_formatter = logging.Formatter(u'[%(asctime)s|%(levelname)s|%(filename)s:%(lineno)s] %(message)s')
|
||||||
|
else:
|
||||||
|
file_formatter = logging.Formatter(u'[%(asctime)s] %(message)s')
|
||||||
|
|
||||||
file_formatter.converter = customTime
|
file_formatter.converter = customTime
|
||||||
file_max_bytes = 1 * 1024 * 1024
|
file_max_bytes = 1 * 1024 * 1024
|
||||||
fileHandler = logging.handlers.RotatingFileHandler(filename=os.path.join(self.path_data, 'log', f'{name}.log'), maxBytes=file_max_bytes, backupCount=5, encoding='utf8', delay=True)
|
fileHandler = logging.handlers.RotatingFileHandler(filename=os.path.join(self.path_data, 'log', f'{name}.log'), maxBytes=file_max_bytes, backupCount=5, encoding='utf8', delay=True)
|
||||||
streamHandler = logging.StreamHandler()
|
|
||||||
|
|
||||||
# handler에 fommater 세팅
|
|
||||||
fileHandler.setFormatter(file_formatter)
|
fileHandler.setFormatter(file_formatter)
|
||||||
streamHandler.setFormatter(CustomFormatter())
|
|
||||||
|
|
||||||
# Handler를 logging에 추가
|
|
||||||
logger.addHandler(fileHandler)
|
logger.addHandler(fileHandler)
|
||||||
logger.addHandler(streamHandler)
|
if from_command == False:
|
||||||
|
streamHandler = logging.StreamHandler()
|
||||||
|
streamHandler.setFormatter(CustomFormatter())
|
||||||
|
logger.addHandler(streamHandler)
|
||||||
return logger
|
return logger
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from support import SupportYaml
|
from support import SupportYaml, d
|
||||||
|
|
||||||
from framework import F
|
from framework import F
|
||||||
|
|
||||||
@@ -89,4 +89,5 @@ class MenuManager:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_menu_map(cls):
|
def get_menu_map(cls):
|
||||||
|
#F.logger.warning(d(cls.menu_map))
|
||||||
return cls.menu_map
|
return cls.menu_map
|
||||||
|
|||||||
@@ -128,4 +128,3 @@ def videojs():
|
|||||||
def connect():
|
def connect():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ $.notify({
|
|||||||
},
|
},
|
||||||
offset: 20,
|
offset: 20,
|
||||||
spacing: 10,
|
spacing: 10,
|
||||||
z_index: 1031,
|
z_index: 3000,
|
||||||
delay: 10000,
|
delay: 10000,
|
||||||
timer: 1000,
|
timer: 1000,
|
||||||
url_target: '_blank',
|
url_target: '_blank',
|
||||||
@@ -81,7 +81,7 @@ $.notify({
|
|||||||
|
|
||||||
|
|
||||||
function notify(msg, type) {
|
function notify(msg, type) {
|
||||||
$.notify('<strong>' + msg + '</strong>', {type: type});
|
$.notify('<strong>' + msg + '</strong>', {type: type, z_index: 3000});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 메뉴 제거
|
// 메뉴 제거
|
||||||
@@ -289,8 +289,8 @@ $.extend(
|
|||||||
{
|
{
|
||||||
var form = '';
|
var form = '';
|
||||||
$.each( args, function( key, value ) {
|
$.each( args, function( key, value ) {
|
||||||
//console.log(key);
|
console.log(key);
|
||||||
//console.log(value);
|
console.log(value);
|
||||||
value = value.split('"').join('\"')
|
value = value.split('"').join('\"')
|
||||||
form += '<input type="hidden" name="'+key+'" value="'+value+'">';
|
form += '<input type="hidden" name="'+key+'" value="'+value+'">';
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// global socketio
|
// global socketio
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
|
ResizeTextArea();
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
@@ -23,6 +24,7 @@ frameSocket.on('notify', function(data){
|
|||||||
target: '_self'
|
target: '_self'
|
||||||
},{
|
},{
|
||||||
type: data['type'],
|
type: data['type'],
|
||||||
|
z_index: 2000,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -317,5 +319,17 @@ $("body").on('click', '#command_modal_input_btn', function(e) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$(window).resize(function() {
|
||||||
|
ResizeTextArea();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function ResizeTextArea() {
|
||||||
|
ClientHeight = window.innerHeight
|
||||||
|
$("#command_modal").height(ClientHeight-100);
|
||||||
|
$("#command_modal_textarea").height(ClientHeight-380);
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
function m_button_group(h) {
|
function j_button_group(h) {
|
||||||
var str = '<div class="btn-group btn-group-sm flex-wrap mr-2" role="group">';
|
var str = '<div class="btn-group btn-group-sm flex-wrap mr-2" role="group">';
|
||||||
str += h
|
str += h
|
||||||
str += '</div>';
|
str += '</div>';
|
||||||
@@ -6,7 +6,7 @@ function m_button_group(h) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// primary, secondary, success, danger, warning, info, light, dark, white
|
// primary, secondary, success, danger, warning, info, light, dark, white
|
||||||
function m_button(id, text, data={}, color='success', outline=true, small=false) {
|
function j_button(id, text, data={}, color='success', outline=true, small=false) {
|
||||||
var str = '<button id="'+id+'" name="'+id+'" class="btn btn-sm btn';
|
var str = '<button id="'+id+'" name="'+id+'" class="btn btn-sm btn';
|
||||||
if (outline) {
|
if (outline) {
|
||||||
str += '-outline';
|
str += '-outline';
|
||||||
@@ -24,18 +24,48 @@ function m_button(id, text, data={}, color='success', outline=true, small=false)
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
function m_button_small(id, text, data={}, color='success', outline=true) {
|
function j_button_small(id, text, data={}, color='success', outline=true) {
|
||||||
return m_button(id, text, data, color, outline, true);
|
return j_button(id, text, data, color, outline, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function j_row_start(padding='10', align='center') {
|
||||||
|
var str = '<div class="row" style="padding-top: '+padding+'px; padding-bottom:'+padding+'px; align-items:'+align+';">';
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
function j_col(w, h, align='left') {
|
||||||
|
var str = '<div class="col-sm-' + w + ' " style="text-align: '+align+'; word-break:break-all;">';
|
||||||
|
str += h;
|
||||||
|
str += '</div>';
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function j_col_wide(w, h, align='left') {
|
||||||
|
var str = '<div class="col-sm-' + w + ' " style="padding:0px; margin:0px; text-align: '+align+'; word-break:break-all;">';
|
||||||
|
str += h;
|
||||||
|
str += '</div>';
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function j_row_end() {
|
||||||
|
var str = '</div>';
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function j_hr(margin='5') {
|
||||||
|
var str = '<hr style="width: 100%; margin:'+margin+'px;" />';
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function j_hr_black() {
|
||||||
|
var str = '<hr style="width: 100%; color: black; height: 2px; background-color:black;" />';
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -119,10 +149,9 @@ function m_table(id, heads) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
function m_row_start(padding='10', align='center') {
|
|
||||||
var str = '<div class="row" style="padding-top: '+padding+'px; padding-bottom:'+padding+'px; align-items:'+align+';">';
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
function m_row_start_hover(padding='10', align='center') {
|
function m_row_start_hover(padding='10', align='center') {
|
||||||
var str = '<div class="row my_hover" style="padding-top: '+padding+'px; padding-bottom:'+padding+'px; align-items:'+align+';">';
|
var str = '<div class="row my_hover" style="padding-top: '+padding+'px; padding-bottom:'+padding+'px; align-items:'+align+';">';
|
||||||
return str;
|
return str;
|
||||||
@@ -139,39 +168,17 @@ function m_row_start_color2(padding='10', align='center') {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
function m_row_end() {
|
|
||||||
var str = '</div>';
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
//border
|
//border
|
||||||
function m_col(w, h, align='left') {
|
|
||||||
var str = '<div class="col-sm-' + w + ' " style="text-align: '+align+'; word-break:break-all;">';
|
|
||||||
str += h
|
|
||||||
str += '</div>';
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
function m_col_wide(w, h, align='left') {
|
|
||||||
var str = '<div class="col-sm-' + w + ' " style="padding:0px; margin:0px; text-align: '+align+'; word-break:break-all;">';
|
|
||||||
str += h
|
|
||||||
str += '</div>';
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function m_hr(margin='5') {
|
|
||||||
var str = '<hr style="width: 100%; margin:'+margin+'px;" />';
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
function m_hr_black() {
|
|
||||||
var str = '<hr style="width: 100%; color: black; height: 2px; background-color:black;" />';
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
// 체크박스는 자바로 하면 on/off 스크립트가 안먹힘.
|
// 체크박스는 자바로 하면 on/off 스크립트가 안먹힘.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,14 +11,7 @@ function m_row_start_hover(padding='10', align='center') {
|
|||||||
function m_row_start_top(padding='10') {
|
function m_row_start_top(padding='10') {
|
||||||
return m_row_start(padding, 'top');
|
return m_row_start(padding, 'top');
|
||||||
}
|
}
|
||||||
function m_row_start_color(padding='10', align='center', color='') {
|
|
||||||
var str = '<div class="row" style="padding-top: '+padding+'px; padding-bottom:'+padding+'px; align-items:'+align+'; background-color:'+color+'">';
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
function m_row_start_color2(padding='10', align='center') {
|
|
||||||
var str = '<div class="row bg-dark text-white" style="padding-top: '+padding+'px; padding-bottom:'+padding+'px; align-items:'+align+';">';
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
function m_row_end() {
|
function m_row_end() {
|
||||||
var str = '</div>';
|
var str = '</div>';
|
||||||
|
|||||||
@@ -36,17 +36,17 @@
|
|||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
setWide();
|
setWide();
|
||||||
$('#loading').show();
|
$('#loading').show();
|
||||||
ResizeTextArea()
|
ResizeTextAreaLog()
|
||||||
})
|
})
|
||||||
|
|
||||||
function ResizeTextArea() {
|
function ResizeTextAreaLog() {
|
||||||
ClientHeight = window.innerHeight
|
ClientHeight = window.innerHeight
|
||||||
$("#log").height(ClientHeight-240);
|
$("#log").height(ClientHeight-240);
|
||||||
$("#add").height(ClientHeight-260);
|
$("#add").height(ClientHeight-260);
|
||||||
}
|
}
|
||||||
|
|
||||||
$(window).resize(function() {
|
$(window).resize(function() {
|
||||||
ResizeTextArea();
|
ResizeTextAreaLog();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -140,6 +140,65 @@
|
|||||||
{{ setting_bottom(desc) }}
|
{{ setting_bottom(desc) }}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro setting_input_textarea_wide(id, left, value='', col='12', row='3', desc='', disabled=False, padding='10') %}
|
||||||
|
<div class='row' style="padding-top: {{padding}}px; padding-bottom:{{padding}}px; align-items: center;">
|
||||||
|
<div class='col-sm-12'>
|
||||||
|
{% if left != '' %}
|
||||||
|
<div class="input-group col-sm-{{col}}">
|
||||||
|
<strong>{{ left }}</strong>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="input-group col-sm-{{col}}">
|
||||||
|
<textarea id="{{id}}" name="{{id}}" class="col-md-12" rows="{{row}}"
|
||||||
|
{% if disabled %}
|
||||||
|
disabled
|
||||||
|
{% endif %}
|
||||||
|
>{{ value }}</textarea>
|
||||||
|
</div>
|
||||||
|
{% if desc is not none %}
|
||||||
|
<div style="padding-left:20px; padding-top:{{padding_top}}}px;">
|
||||||
|
<em>
|
||||||
|
{% if desc is string %}
|
||||||
|
{{ desc }}
|
||||||
|
{% elif desc is iterable %}
|
||||||
|
{% for d in desc %}
|
||||||
|
{{ d }}<br>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</em>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
|
||||||
|
{% macro setting_input_textarea_and_buttons(id, left, buttons, value='', col='9', row='3', desc='', disabled=False) %}
|
||||||
|
{{ setting_top(left) }}
|
||||||
|
<div class="input-group col-sm-{{col}}">
|
||||||
|
<textarea id="{{id}}" name="{{id}}" class="col-md-12" rows="{{row}}"
|
||||||
|
{% if disabled %}
|
||||||
|
disabled
|
||||||
|
{% endif %}
|
||||||
|
>{{ value }}</textarea>
|
||||||
|
<div class="btn-group btn-group-sm flex-wrap mr-2" role="group" style="padding-left:5px; padding-top:0px">
|
||||||
|
{% for b in buttons %}
|
||||||
|
<button id="{{b[0]}}" class="btn btn-sm btn-outline-primary"
|
||||||
|
{% if b|length > 2 %}
|
||||||
|
{% for d in b[2] %}
|
||||||
|
data-{{d[0]}}="{{d[1]}}""
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
>{{b[1]}}</button>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ setting_bottom(desc) }}
|
||||||
|
{% endmacro %}
|
||||||
|
<!-- TEXTAREA -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- 라디오버튼 -->
|
<!-- 라디오버튼 -->
|
||||||
{% macro setting_radio_with_value(id, title, radios, value=None, desc=None, disabled=False) %}
|
{% macro setting_radio_with_value(id, title, radios, value=None, desc=None, disabled=False) %}
|
||||||
{{ setting_top(title) }}
|
{{ setting_top(title) }}
|
||||||
@@ -635,62 +694,8 @@ macros.setting_button_with_info([['toggle_btn', 'Toggle', [{'key':'category', 'v
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% macro setting_input_textarea_wide(id, left, value='', col='12', row='3', desc='', disabled=False, padding='10') %}
|
|
||||||
<div class='row' style="padding-top: {{padding}}px; padding-bottom:{{padding}}px; align-items: center;">
|
|
||||||
<div class='col-sm-12'>
|
|
||||||
{% if left != '' %}
|
|
||||||
<div class="input-group col-sm-{{col}}">
|
|
||||||
<strong>{{ left }}</strong>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<div class="input-group col-sm-{{col}}">
|
|
||||||
<textarea id="{{id}}" name="{{id}}" class="col-md-12" rows="{{row}}"
|
|
||||||
{% if disabled %}
|
|
||||||
disabled
|
|
||||||
{% endif %}
|
|
||||||
>{{ value }}</textarea>
|
|
||||||
</div>
|
|
||||||
{% if desc is not none %}
|
|
||||||
<div style="padding-left:20px; padding-top:{{padding_top}}}px;">
|
|
||||||
<em>
|
|
||||||
{% if desc is string %}
|
|
||||||
{{ desc }}
|
|
||||||
{% elif desc is iterable %}
|
|
||||||
{% for d in desc %}
|
|
||||||
{{ d }}<br>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
</em>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
|
|
||||||
{% macro setting_input_textarea_and_buttons(id, left, buttons, value='', col='9', row='3', desc='', disabled=False) %}
|
|
||||||
{{ setting_top(left) }}
|
|
||||||
<div class="input-group col-sm-{{col}}">
|
|
||||||
<textarea id="{{id}}" name="{{id}}" class="col-md-12" rows="{{row}}"
|
|
||||||
{% if disabled %}
|
|
||||||
disabled
|
|
||||||
{% endif %}
|
|
||||||
>{{ value }}</textarea>
|
|
||||||
<div class="btn-group btn-group-sm flex-wrap mr-2" role="group" style="padding-left:5px; padding-top:0px">
|
|
||||||
{% for b in buttons %}
|
|
||||||
<button id="{{b[0]}}" class="btn btn-sm btn-outline-primary"
|
|
||||||
{% if b|length > 2 %}
|
|
||||||
{% for d in b[2] %}
|
|
||||||
data-{{d[0]}}="{{d[1]}}""
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
>{{b[1]}}</button>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{ setting_bottom(desc) }}
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!--progress-bar-striped progress-bar-animated-->
|
<!--progress-bar-striped progress-bar-animated-->
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
{% for category in menu_map %}
|
{% for category in menu_map %}
|
||||||
{% if 'uri' in category and category['uri'].startswith('http') %}
|
{% if 'uri' in category and category['uri'].startswith('http') %}
|
||||||
<li class="nav-item"> <a class="nav-link" href="{{ category['uri']}}" target="_blank">{{category['name']}}</a></li>
|
<li class="nav-item"> <a class="nav-link" href="{{ category['uri']}}" target="_blank">{{category['name']}}</a></li>
|
||||||
|
{% elif 'uri' in category and category['uri'].startswith('http') == False %}
|
||||||
|
<li class="nav-item"> <a class="nav-link" href="{{ category['uri']}}">{{category['name']}}</a></li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<!--{{ category }}-->
|
<!--{{ category }}-->
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
VERSION="4.0.26"
|
VERSION="4.0.27"
|
||||||
@@ -241,3 +241,15 @@ class Logic(object):
|
|||||||
self.P.logger.error(traceback.format_exc())
|
self.P.logger.error(traceback.format_exc())
|
||||||
ret = {'ret' : 'danger', 'msg':str(e)}
|
ret = {'ret' : 'danger', 'msg':str(e)}
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def arg_to_dict(self, arg):
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
tmp = urllib.parse.unquote(arg)
|
||||||
|
tmps = tmp.split('&')
|
||||||
|
ret = {}
|
||||||
|
for tmp in tmps:
|
||||||
|
_ = tmp.split('=')
|
||||||
|
ret[_[0]] = _[1]
|
||||||
|
return ret
|
||||||
|
|||||||
@@ -19,8 +19,10 @@ class ModelBase(F.db.Model):
|
|||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
try:
|
try:
|
||||||
F.db.session.add(self)
|
with F.app.app_context():
|
||||||
F.db.session.commit()
|
F.db.session.add(self)
|
||||||
|
F.db.session.commit()
|
||||||
|
return self
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.logger.error(f'Exception:{str(e)}')
|
self.logger.error(f'Exception:{str(e)}')
|
||||||
self.logger.error(traceback.format_exc())
|
self.logger.error(traceback.format_exc())
|
||||||
@@ -43,55 +45,59 @@ class ModelBase(F.db.Model):
|
|||||||
paging['next_page'] = False
|
paging['next_page'] = False
|
||||||
paging['current_page'] = current_page
|
paging['current_page'] = current_page
|
||||||
paging['count'] = count
|
paging['count'] = count
|
||||||
cls.logger.debug('paging : c:%s %s %s %s %s %s', count, paging['total_page'], paging['prev_page'], paging['next_page'] , paging['start_page'], paging['last_page'])
|
F.logger.debug('paging : c:%s %s %s %s %s %s', count, paging['total_page'], paging['prev_page'], paging['next_page'] , paging['start_page'], paging['last_page'])
|
||||||
return paging
|
return paging
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
cls.logger.error(f'Exception:{str(e)}')
|
F.logger.error(f'Exception:{str(e)}')
|
||||||
cls.logger.error(traceback.format_exc())
|
F.logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_by_id(cls, id):
|
def get_by_id(cls, id):
|
||||||
try:
|
try:
|
||||||
return F.db.session.query(cls).filter_by(id=id).first()
|
with F.app.app_context():
|
||||||
|
return F.db.session.query(cls).filter_by(id=int(id)).first()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
cls.logger.error(f'Exception:{str(e)}')
|
F.logger.error(f'Exception:{str(e)}')
|
||||||
cls.logger.error(traceback.format_exc())
|
F.logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_list(cls, by_dict=False):
|
def get_list(cls, by_dict=False):
|
||||||
try:
|
try:
|
||||||
tmp = F.db.session.query(cls).all()
|
with F.app.app_context():
|
||||||
if by_dict:
|
tmp = F.db.session.query(cls).all()
|
||||||
tmp = [x.as_dict() for x in tmp]
|
if by_dict:
|
||||||
return tmp
|
tmp = [x.as_dict() for x in tmp]
|
||||||
|
return tmp
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
cls.logger.error(f'Exception:{str(e)}')
|
F.logger.error(f'Exception:{str(e)}')
|
||||||
cls.logger.error(traceback.format_exc())
|
F.logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def delete_by_id(cls, id):
|
def delete_by_id(cls, id):
|
||||||
try:
|
try:
|
||||||
F.db.session.query(cls).filter_by(id=id).delete()
|
with F.app.app_context():
|
||||||
F.db.session.commit()
|
F.db.session.query(cls).filter_by(id=int(id)).delete()
|
||||||
return True
|
F.db.session.commit()
|
||||||
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
cls.logger.error(f'Exception:{str(e)}')
|
F.logger.error(f'Exception:{str(e)}')
|
||||||
cls.logger.error(traceback.format_exc())
|
F.logger.error(traceback.format_exc())
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def delete_all(cls):
|
def delete_all(cls):
|
||||||
try:
|
try:
|
||||||
F.db.session.query(cls).delete()
|
with F.app.app_context():
|
||||||
F.db.session.commit()
|
F.db.session.query(cls).delete()
|
||||||
return True
|
F.db.session.commit()
|
||||||
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
cls.logger.error(f'Exception:{str(e)}')
|
F.logger.error(f'Exception:{str(e)}')
|
||||||
cls.logger.error(traceback.format_exc())
|
F.logger.error(traceback.format_exc())
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@@ -113,7 +119,7 @@ class ModelBase(F.db.Model):
|
|||||||
query = cls.make_query(order=order, search=search, option1=option1, option2=option2)
|
query = cls.make_query(order=order, search=search, option1=option1, option2=option2)
|
||||||
count = query.count()
|
count = query.count()
|
||||||
query = query.limit(page_size).offset((page-1)*page_size)
|
query = query.limit(page_size).offset((page-1)*page_size)
|
||||||
cls.logger.debug('cls count:%s', count)
|
F.logger.debug('cls count:%s', count)
|
||||||
lists = query.all()
|
lists = query.all()
|
||||||
ret['list'] = [item.as_dict() for item in lists]
|
ret['list'] = [item.as_dict() for item in lists]
|
||||||
ret['paging'] = cls.get_paging_info(count, page, page_size)
|
ret['paging'] = cls.get_paging_info(count, page, page_size)
|
||||||
@@ -121,18 +127,19 @@ class ModelBase(F.db.Model):
|
|||||||
if cls.model_setting is not None and cls.__tablename__ is not None:
|
if cls.model_setting is not None and cls.__tablename__ is not None:
|
||||||
cls.model_setting.set(f'{cls.__tablename__}_last_list_option', f'{order}|{page}|{search}|{option1}|{option2}')
|
cls.model_setting.set(f'{cls.__tablename__}_last_list_option', f'{order}|{page}|{search}|{option1}|{option2}')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
cls.logger.error('Exception:%s', e)
|
F.logger.error('Exception:%s', e)
|
||||||
cls.logger.error(traceback.format_exc())
|
F.logger.error(traceback.format_exc())
|
||||||
cls.logger.error(f'{cls.__tablename__}_last_list_option ERROR!' )
|
F.logger.error(f'{cls.__tablename__}_last_list_option ERROR!' )
|
||||||
return ret
|
return ret
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
cls.logger.error('Exception:%s', e)
|
F.logger.error('Exception:%s', e)
|
||||||
cls.logger.error(traceback.format_exc())
|
F.logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
# 오버라이딩
|
# 오버라이딩
|
||||||
@classmethod
|
@classmethod
|
||||||
def make_query(cls, order='desc', search='', option1='all', option2='all'):
|
def make_query(cls, order='desc', search='', option1='all', option2='all'):
|
||||||
query = F.db.session.query(cls)
|
with F.app.app_context():
|
||||||
return query
|
query = F.db.session.query(cls)
|
||||||
|
return query
|
||||||
|
|
||||||
@@ -136,7 +136,7 @@ def default_route(P):
|
|||||||
@login_required
|
@login_required
|
||||||
def sub_ajax(module_name, page_name, command):
|
def sub_ajax(module_name, page_name, command):
|
||||||
try:
|
try:
|
||||||
ins_module = P.get_module(module_name)
|
ins_module = P.logic.get_module(module_name)
|
||||||
ins_page = ins_module.get_page(page_name)
|
ins_page = ins_module.get_page(page_name)
|
||||||
if ins_page != None:
|
if ins_page != None:
|
||||||
if command == 'scheduler':
|
if command == 'scheduler':
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ class SupportSubprocess(object):
|
|||||||
instance_list = []
|
instance_list = []
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, command, print_log=False, shell=False, env=None, timeout=None, uid=None, gid=None, stdout_callback=None):
|
def __init__(self, command, print_log=False, shell=False, env=None, timeout=None, uid=None, gid=None, stdout_callback=None, call_id=None):
|
||||||
self.command = command
|
self.command = command
|
||||||
self.print_log = print_log
|
self.print_log = print_log
|
||||||
self.shell = shell
|
self.shell = shell
|
||||||
@@ -101,6 +101,8 @@ class SupportSubprocess(object):
|
|||||||
self.stdout_callback = stdout_callback
|
self.stdout_callback = stdout_callback
|
||||||
self.process = None
|
self.process = None
|
||||||
self.stdout_queue = None
|
self.stdout_queue = None
|
||||||
|
self.call_id = call_id
|
||||||
|
self.timestamp = time.time()
|
||||||
|
|
||||||
|
|
||||||
def start(self, join=True):
|
def start(self, join=True):
|
||||||
@@ -127,13 +129,14 @@ class SupportSubprocess(object):
|
|||||||
tmp.append(f'"{x}"')
|
tmp.append(f'"{x}"')
|
||||||
self.command = ' '.join(tmp)
|
self.command = ' '.join(tmp)
|
||||||
|
|
||||||
|
logger.debug(f"{self.command=}")
|
||||||
if platform.system() == 'Windows':
|
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')
|
self.process = subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=self.shell, env=self.env, encoding='utf8', bufsize=0)
|
||||||
else:
|
else:
|
||||||
if self.uid == None:
|
if self.uid == None:
|
||||||
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')
|
self.process = subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=self.shell, env=self.env, encoding='utf8', bufsize=0)
|
||||||
else:
|
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')
|
self.process = subprocess.Popen(self.command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=self.shell, env=self.env, preexec_fn=demote(self.uid, self.gid), encoding='utf8', bufsize=0)
|
||||||
SupportSubprocess.instance_list.append(self)
|
SupportSubprocess.instance_list.append(self)
|
||||||
self.start_communicate()
|
self.start_communicate()
|
||||||
self.start_send_callback()
|
self.start_send_callback()
|
||||||
@@ -207,11 +210,14 @@ class SupportSubprocess(object):
|
|||||||
else:
|
else:
|
||||||
if self.stdout_callback != None:
|
if self.stdout_callback != None:
|
||||||
self.stdout_callback('log', line)
|
self.stdout_callback('log', line)
|
||||||
|
self.remove_instance(self)
|
||||||
|
|
||||||
th = threading.Thread(target=func, args=())
|
th = threading.Thread(target=func, args=())
|
||||||
th.setDaemon(True)
|
th.setDaemon(True)
|
||||||
th.start()
|
th.start()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def process_close(self):
|
def process_close(self):
|
||||||
try:
|
try:
|
||||||
if self.process is not None and self.process.poll() is None:
|
if self.process is not None and self.process.poll() is None:
|
||||||
@@ -228,6 +234,8 @@ class SupportSubprocess(object):
|
|||||||
#self.stdout_queue = None
|
#self.stdout_queue = None
|
||||||
self.process.kill()
|
self.process.kill()
|
||||||
except: pass
|
except: pass
|
||||||
|
|
||||||
|
self.remove_instance(self)
|
||||||
|
|
||||||
def input_command(self, cmd):
|
def input_command(self, cmd):
|
||||||
if self.process != None:
|
if self.process != None:
|
||||||
@@ -241,4 +249,25 @@ class SupportSubprocess(object):
|
|||||||
instance.process_close()
|
instance.process_close()
|
||||||
cls.instance_list = []
|
cls.instance_list = []
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def remove_instance(cls, remove_instance):
|
||||||
|
new = []
|
||||||
|
for instance in cls.instance_list:
|
||||||
|
if remove_instance.timestamp == instance.timestamp:
|
||||||
|
continue
|
||||||
|
new.append(instance)
|
||||||
|
cls.instance_list = new
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def print(cls):
|
||||||
|
for instance in cls.instance_list:
|
||||||
|
logger.info(instance.command)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_instance_by_call_id(cls, call_id):
|
||||||
|
for instance in cls.instance_list:
|
||||||
|
if instance.call_id == call_id:
|
||||||
|
return instance
|
||||||
|
|
||||||
@@ -23,6 +23,11 @@ class ModuleLog(PluginModuleBase):
|
|||||||
arg['log_list'] = '|'.join(log_list)
|
arg['log_list'] = '|'.join(log_list)
|
||||||
arg['all_list'] = '|'.join(log_files)
|
arg['all_list'] = '|'.join(log_files)
|
||||||
arg['filename'] = 'framework.log'
|
arg['filename'] = 'framework.log'
|
||||||
|
print(request.form)
|
||||||
|
print(request.form)
|
||||||
|
print(request.form)
|
||||||
|
print(request.form)
|
||||||
|
|
||||||
if 'filename' in request.form:
|
if 'filename' in request.form:
|
||||||
arg['filename'] = request.form['filename']
|
arg['filename'] = request.form['filename']
|
||||||
return render_template(f'{__package__}_{name}.html', arg=arg)
|
return render_template(f'{__package__}_{name}.html', arg=arg)
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
from support import SupportFile
|
from support import SupportFile
|
||||||
|
|
||||||
|
from .page_command import PageCommand
|
||||||
from .setup import *
|
from .setup import *
|
||||||
|
|
||||||
name = 'tool'
|
name = 'tool'
|
||||||
|
|
||||||
class ModuleTool(PluginModuleBase):
|
class ModuleTool(PluginModuleBase):
|
||||||
def __init__(self, P):
|
def __init__(self, P):
|
||||||
super(ModuleTool, self).__init__(P, name=name, first_menu='upload')
|
super(ModuleTool, self).__init__(P, name=name, first_menu='command')
|
||||||
self.set_page_list([PageUpload, PageCrypt])
|
|
||||||
|
self.set_page_list([PageUpload, PageCrypt, PagePython, PageCommand])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -23,8 +24,6 @@ class PageUpload(PluginPageBase):
|
|||||||
|
|
||||||
|
|
||||||
class PageCrypt(PluginPageBase):
|
class PageCrypt(PluginPageBase):
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, P, parent):
|
def __init__(self, P, parent):
|
||||||
super(PageCrypt, self).__init__(P, parent, name='crypt')
|
super(PageCrypt, self).__init__(P, parent, name='crypt')
|
||||||
|
|
||||||
@@ -32,4 +31,9 @@ class PageCrypt(PluginPageBase):
|
|||||||
f'{self.parent.name}_{self.name}_use_user_key': 'False',
|
f'{self.parent.name}_{self.name}_use_user_key': 'False',
|
||||||
f'{self.parent.name}_{self.name}_user_key': '',
|
f'{self.parent.name}_{self.name}_user_key': '',
|
||||||
f'{self.parent.name}_{self.name}_user_key': '',
|
f'{self.parent.name}_{self.name}_user_key': '',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PagePython(PluginPageBase):
|
||||||
|
def __init__(self, P, parent):
|
||||||
|
super(PagePython, self).__init__(P, parent, name='python')
|
||||||
|
|
||||||
|
|||||||
291
lib/system/page_command.py
Normal file
291
lib/system/page_command.py
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
import queue
|
||||||
|
|
||||||
|
from support import SupportSubprocess
|
||||||
|
from tool import ToolModalCommand
|
||||||
|
|
||||||
|
from .setup import *
|
||||||
|
|
||||||
|
|
||||||
|
class PageCommand(PluginPageBase):
|
||||||
|
|
||||||
|
def __init__(self, P, parent):
|
||||||
|
super(PageCommand, self).__init__(P, parent, name='command')
|
||||||
|
self.db_default = {
|
||||||
|
f'{self.parent.name}_{self.name}_recent': '',
|
||||||
|
}
|
||||||
|
|
||||||
|
def process_menu(self, req):
|
||||||
|
arg = self.P.ModelSetting.to_dict()
|
||||||
|
arg['path_data'] = F.config['path_data']
|
||||||
|
return render_template(f'{self.P.package_name}_{self.parent.name}_{self.name}.html', arg=arg)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def process_command(self, command, arg1, arg2, arg3, req):
|
||||||
|
ret = {'ret':'success'}
|
||||||
|
if command == 'foreground_command':
|
||||||
|
P.ModelSetting.set(f'{self.parent.name}_{self.name}_recent', arg1)
|
||||||
|
self.__foreground_execute(arg1, arg1.split(' '))
|
||||||
|
|
||||||
|
return jsonify('')
|
||||||
|
elif command == 'job_new':
|
||||||
|
db_item = ModelCommand.job_new(arg1)
|
||||||
|
ret['msg'] = f"ID:{db_item.id} 작업을 생성하였습니다."
|
||||||
|
elif command == 'job_list':
|
||||||
|
ret['data'] = ModelCommand.job_list()
|
||||||
|
|
||||||
|
elif command == 'job_save':
|
||||||
|
data = P.logic.arg_to_dict(arg1)
|
||||||
|
db_item = ModelCommand.get_by_id(data['job_id'])
|
||||||
|
db_item.set_command(data['job_command'])
|
||||||
|
db_item.args = data['job_command_args']
|
||||||
|
db_item.description = data['job_description']
|
||||||
|
db_item.schedule_mode = data['job_schedule_mode']
|
||||||
|
db_item.schedule_auto_start = (data.get('job_schedule_auto_start', 'False') == 'True')
|
||||||
|
db_item.schedule_interval = data.get('job_schedule_interval', '')
|
||||||
|
db_item.save()
|
||||||
|
ret['msg'] = '수정하였습니다.'
|
||||||
|
elif command == 'job_remove':
|
||||||
|
if ModelCommand.delete_by_id(arg1):
|
||||||
|
ret['msg'] = '삭제하였습니다.'
|
||||||
|
else:
|
||||||
|
ret['ret'] = 'danger'
|
||||||
|
ret['msg'] = '삭제에 실패하였습니다.'
|
||||||
|
elif command == 'job_fore_execute':
|
||||||
|
db_item = ModelCommand.get_by_id(arg1)
|
||||||
|
cmd = (db_item.command + ' ' + db_item.args).strip()
|
||||||
|
self.__foreground_execute(f"Command ID: {db_item.id}", cmd.split(' '), db_item.id)
|
||||||
|
elif command == 'job_back_execute':
|
||||||
|
self.execute_thread_start(arg1)
|
||||||
|
ret['msg'] = "실행 요청을 하였습니다.<br>로그를 확인하세요."
|
||||||
|
elif command == 'job_log':
|
||||||
|
ret['filename'] = f"command_{arg1}.log"
|
||||||
|
if os.path.exists(os.path.join(F.config['path_data'], 'log', f"command_{arg1}.log")) == False:
|
||||||
|
ret['ret'] = 'danger'
|
||||||
|
ret['msg'] = "로그 파일이 없습니다."
|
||||||
|
elif command == 'task_sched':
|
||||||
|
job_id = req.form['arg1']
|
||||||
|
flag = (req.form['arg2'] == 'true')
|
||||||
|
scheduler_id = f'command_{job_id}'
|
||||||
|
if flag and F.scheduler.is_include(scheduler_id):
|
||||||
|
ret['msg'] = '이미 스케쥴러에 등록되어 있습니다.'
|
||||||
|
elif flag and F.scheduler.is_include(scheduler_id) == False:
|
||||||
|
result = self.__sched_add(job_id)
|
||||||
|
ret['msg'] = '스케쥴러에 추가하였습니다.'
|
||||||
|
elif flag == False and scheduler.is_include(scheduler_id):
|
||||||
|
result = scheduler.remove_job(scheduler_id)
|
||||||
|
ret['msg'] = '스케쥴링 취소'
|
||||||
|
elif flag == False and scheduler.is_include(scheduler_id) == False:
|
||||||
|
ret['msg'] = '등록되어 있지 않습니다.'
|
||||||
|
elif command == 'job_process_stop':
|
||||||
|
process_ins = SupportSubprocess.get_instance_by_call_id(f"command_{arg1}")
|
||||||
|
if process_ins == None:
|
||||||
|
ret['msg'] = "실행중인 Process가 없습니다."
|
||||||
|
else:
|
||||||
|
process_ins.process_close()
|
||||||
|
ret['msg'] = "Process를 중지하였습니다."
|
||||||
|
return jsonify(ret)
|
||||||
|
|
||||||
|
|
||||||
|
def __foreground_execute(self, title, command, job_id=None):
|
||||||
|
|
||||||
|
if command[0] != 'LOAD':
|
||||||
|
ToolModalCommand.start(title, [command])
|
||||||
|
else:
|
||||||
|
F.socketio.emit("command_modal_show", title, namespace='/framework', broadcast=True)
|
||||||
|
def start_communicate_load(load_log_list):
|
||||||
|
def func():
|
||||||
|
while True:
|
||||||
|
logs = load_log_list.getvalue()
|
||||||
|
load_log_list.truncate(0)
|
||||||
|
if logs:
|
||||||
|
P.logger.error(logs)
|
||||||
|
F.socketio.emit("command_modal_add_text", logs.strip() + '\n', namespace='/framework', broadcast=True)
|
||||||
|
if logs == '<<END>>':
|
||||||
|
break
|
||||||
|
time.sleep(0.3)
|
||||||
|
th = threading.Thread(target=func)
|
||||||
|
th.setDaemon(True)
|
||||||
|
th.start()
|
||||||
|
|
||||||
|
def func():
|
||||||
|
import io
|
||||||
|
from contextlib import redirect_stdout
|
||||||
|
load_log_list = io.StringIO()
|
||||||
|
with redirect_stdout(self.load_log_list):
|
||||||
|
start_communicate_load(load_log_list)
|
||||||
|
if job_id is not None:
|
||||||
|
command_logger = get_logger(f'command_{job_id}')
|
||||||
|
else:
|
||||||
|
command_logger = P.logger
|
||||||
|
self.__module_load(command, logger=command_logger)
|
||||||
|
load_log_list.write("<<END>>")
|
||||||
|
load_log_list.flush()
|
||||||
|
th = threading.Thread(target=func, args=())
|
||||||
|
th.setDaemon(True)
|
||||||
|
th.start()
|
||||||
|
return 'success'
|
||||||
|
|
||||||
|
|
||||||
|
def __module_load(self, command, **kwargs):
|
||||||
|
try:
|
||||||
|
python_filename = command[1]
|
||||||
|
python_sys_path = os.path.dirname(python_filename)
|
||||||
|
if python_sys_path not in sys.path:
|
||||||
|
sys.path.append(python_sys_path)
|
||||||
|
module_name = os.path.basename(python_filename).split('.py')[0]
|
||||||
|
if module_name not in sys.path:
|
||||||
|
sys.path.append(module_name)
|
||||||
|
import importlib
|
||||||
|
mod = importlib.import_module(module_name)
|
||||||
|
importlib.reload(mod)
|
||||||
|
args = command
|
||||||
|
mod_command_load = getattr(mod, 'main')
|
||||||
|
if mod_command_load:
|
||||||
|
ret = mod_command_load(*args, **kwargs)
|
||||||
|
return ret
|
||||||
|
except Exception as e:
|
||||||
|
P.logger.error(f'Exception:{str(e)}')
|
||||||
|
P.logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def execute_thread_start(self, job_id):
|
||||||
|
th = threading.Thread(target=self.execute_thread_function_by_job_id, args=(job_id,))
|
||||||
|
th.setDaemon(True)
|
||||||
|
th.start()
|
||||||
|
|
||||||
|
|
||||||
|
def execute_thread_function_by_job_id(self, *args, **kwargs):
|
||||||
|
P.logger.error(d(args))
|
||||||
|
P.logger.error(d(kwargs))
|
||||||
|
db_item = ModelCommand.get_by_id(args[0])
|
||||||
|
kwargs['id'] = args[0]
|
||||||
|
self.execute_thread_function((db_item.command + ' ' + db_item.args).strip(), **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_thread_function(self, command, **kwargs):
|
||||||
|
try:
|
||||||
|
cmd = command.split(' ')
|
||||||
|
|
||||||
|
if cmd[0] == 'LOAD':
|
||||||
|
command_logger = F.get_logger(f"command_{kwargs['id']}")
|
||||||
|
kwargs['logger'] = command_logger
|
||||||
|
return self.__module_load(cmd, **kwargs)
|
||||||
|
else:
|
||||||
|
class LogReceiver:
|
||||||
|
def __init__(self, logger):
|
||||||
|
self.logger = logger
|
||||||
|
|
||||||
|
def stdout_callback(self, mode, text):
|
||||||
|
if mode == 'log':
|
||||||
|
self.logger.debug(text)
|
||||||
|
else:
|
||||||
|
self.logger.debug(mode)
|
||||||
|
command_logger = F.get_logger(f"command_{kwargs['id']}", from_command=True)
|
||||||
|
receiver = LogReceiver(command_logger)
|
||||||
|
process = SupportSubprocess(cmd, stdout_callback=receiver.stdout_callback, call_id=f"command_{kwargs['id']}")
|
||||||
|
process.start()
|
||||||
|
except Exception as e:
|
||||||
|
P.logger.error(f'Exception:{str(e)}')
|
||||||
|
P.logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
|
def plugin_load(self):
|
||||||
|
def plugin_load_thread():
|
||||||
|
try:
|
||||||
|
db_items = ModelCommand.get_list()
|
||||||
|
for db_item in db_items:
|
||||||
|
if db_item.schedule_mode == 'startup':
|
||||||
|
self.execute_thread_start(db_item.id)
|
||||||
|
elif db_item.schedule_mode == 'scheduler' and db_item.schedule_auto_start:
|
||||||
|
self.__sched_add(db_item.id, db_item=db_item)
|
||||||
|
except Exception as exception:
|
||||||
|
logger.error('Exception:%s', exception)
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
try:
|
||||||
|
th = threading.Thread(target=plugin_load_thread)
|
||||||
|
th.setDaemon(True)
|
||||||
|
th.start()
|
||||||
|
except Exception as e:
|
||||||
|
P.logger.error(f'Exception:{str(e)}')
|
||||||
|
P.logger.error(traceback.format_exc())
|
||||||
|
|
||||||
|
|
||||||
|
def __sched_add(self, id, db_item=None):
|
||||||
|
try:
|
||||||
|
if db_item is None:
|
||||||
|
db_item = ModelCommand.get_by_id(id)
|
||||||
|
job_id = f"command_{db_item.id}"
|
||||||
|
if scheduler.is_include(job_id):
|
||||||
|
return
|
||||||
|
job = Job(self.P.package_name, job_id, db_item.schedule_interval, self.execute_thread_function_by_job_id, db_item.description, args=db_item.id)
|
||||||
|
scheduler.add_job_instance(job)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
P.logger.error(f'Exception:{str(e)}')
|
||||||
|
P.logger.error(traceback.format_exc())
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ModelCommand(ModelBase):
|
||||||
|
__tablename__ = 'command_job'
|
||||||
|
__table_args__ = {'mysql_collate': 'utf8_general_ci'}
|
||||||
|
__bind_key__ = 'system'
|
||||||
|
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
command = db.Column(db.String)
|
||||||
|
filepath = db.Column(db.String)
|
||||||
|
args = db.Column(db.String)
|
||||||
|
description = db.Column(db.String)
|
||||||
|
schedule_mode = db.Column(db.String) # none, startup, scheduler
|
||||||
|
schedule_auto_start = db.Column(db.Boolean) # 시작시 스케쥴링 등록
|
||||||
|
schedule_interval = db.Column(db.String) # 주기
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, command):
|
||||||
|
self.args = ''
|
||||||
|
self.description = ''
|
||||||
|
self.schedule_mode = 'none'
|
||||||
|
self.schedule_auto_start = False
|
||||||
|
self.schedule_interval = ''
|
||||||
|
self.set_command(command)
|
||||||
|
|
||||||
|
def set_command(self, command):
|
||||||
|
self.command = command
|
||||||
|
tmp = command.split(' ')
|
||||||
|
for t in tmp:
|
||||||
|
for ext in ['.py', '.sh', '.bat']:
|
||||||
|
if t.endswith(ext):
|
||||||
|
self.filepath = t
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def job_new(cls, command):
|
||||||
|
item = ModelCommand(command)
|
||||||
|
return item.save()
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def job_list(cls):
|
||||||
|
try:
|
||||||
|
data = cls.get_list(by_dict=True)
|
||||||
|
for item in data:
|
||||||
|
item['scheduler_is_include'] = F.scheduler.is_include(f"command_{item['id']}")
|
||||||
|
item['scheduler_is_running'] = F.scheduler.is_running(f"command_{item['id']}")
|
||||||
|
|
||||||
|
item['process'] = (SupportSubprocess.get_instance_by_call_id(f"command_{item['id']}") != None)
|
||||||
|
|
||||||
|
return data
|
||||||
|
except Exception as exception:
|
||||||
|
logger.error('Exception:%s', exception)
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
@@ -29,6 +29,7 @@ __menu = {
|
|||||||
'uri': 'tool',
|
'uri': 'tool',
|
||||||
'name': '시스템 툴',
|
'name': '시스템 툴',
|
||||||
'list': [
|
'list': [
|
||||||
|
{'uri': 'command', 'name': 'Command'},
|
||||||
{'uri': 'upload', 'name': '업로드'},
|
{'uri': 'upload', 'name': '업로드'},
|
||||||
{'uri': 'python', 'name': 'Python'},
|
{'uri': 'python', 'name': 'Python'},
|
||||||
{'uri': 'db', 'name': 'DB'},
|
{'uri': 'db', 'name': 'DB'},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div>
|
<div>
|
||||||
{{ macros.setting_select_empty('log_select1', '로그 파일 선택 (.log)') }}
|
{{ macros.setting_select_empty('log_select1', '로그 파일 선택 (.log)') }}
|
||||||
{{ macros.setting_select_empty('log_select2', '로그 파일 선택 (.logX)') }}
|
<!--{{ macros.setting_select_empty('log_select2', '로그 파일 선택 (.logX)') }}-->
|
||||||
<nav>
|
<nav>
|
||||||
{{ macros.m_tab_head_start() }}
|
{{ macros.m_tab_head_start() }}
|
||||||
{{ macros.m_tab_head('이전', true) }}
|
{{ macros.m_tab_head('이전', true) }}
|
||||||
@@ -42,7 +42,7 @@ var start_filename = "{{arg['filename']}}";
|
|||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('#main_container').attr('class', 'container-fluid');
|
$('#main_container').attr('class', 'container-fluid');
|
||||||
ResizeTextArea()
|
ResizeTextAreaAllLog()
|
||||||
make_form()
|
make_form()
|
||||||
if (start_filename != '')
|
if (start_filename != '')
|
||||||
document.getElementById('log_select').value = start_filename;
|
document.getElementById('log_select').value = start_filename;
|
||||||
@@ -56,13 +56,14 @@ function make_form() {
|
|||||||
str += '<option value="' + data[i] + '">' + data[i] + '</option>';
|
str += '<option value="' + data[i] + '">' + data[i] + '</option>';
|
||||||
}
|
}
|
||||||
$("#log_select1_div").html(str);
|
$("#log_select1_div").html(str);
|
||||||
|
/*
|
||||||
str = '<select id="log_select" name="log_select" class="form-control form-control-sm">';
|
str = '<select id="log_select" name="log_select" class="form-control form-control-sm">';
|
||||||
data = all_list.split('|')
|
data = all_list.split('|')
|
||||||
for(var i in data) {
|
for(var i in data) {
|
||||||
str += '<option value="' + data[i] + '">' + data[i] + '</option>';
|
str += '<option value="' + data[i] + '">' + data[i] + '</option>';
|
||||||
}
|
}
|
||||||
$("#log_select2_div").html(str);
|
$("#log_select2_div").html(str);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
$("body").on('change', '#log_select', function(e){
|
$("body").on('change', '#log_select', function(e){
|
||||||
@@ -75,14 +76,14 @@ $("body").on('change', '#log_select', function(e){
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
function ResizeTextArea() {
|
function ResizeTextAreaAllLog() {
|
||||||
ClientHeight = window.innerHeight
|
ClientHeight = window.innerHeight
|
||||||
$("#log").height(ClientHeight-300);
|
$("#log").height(ClientHeight-300);
|
||||||
$("#add").height(ClientHeight-320);
|
$("#add").height(ClientHeight-320);
|
||||||
}
|
}
|
||||||
|
|
||||||
$(window).resize(function() {
|
$(window).resize(function() {
|
||||||
ResizeTextArea();
|
ResizeTextAreaAllLog();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -159,13 +159,13 @@ function make_scheduler_list(data) {
|
|||||||
TD_STR = '<td scope="col" style="width:10%; text-align:center;">';
|
TD_STR = '<td scope="col" style="width:10%; text-align:center;">';
|
||||||
for(var i in data) {
|
for(var i in data) {
|
||||||
if (data[i].is_running) {
|
if (data[i].is_running) {
|
||||||
str += '<tr class="bg-light">';
|
str += '<tr class="bg-dark text-white">';
|
||||||
} else {
|
} else {
|
||||||
str += '<tr>';
|
str += '<tr>';
|
||||||
}
|
}
|
||||||
str += '<td scope="col" style="width:5%; text-align:center;">' + (parseInt(i)+1) + '</td>';
|
str += '<td scope="col" style="width:5%; text-align:center;">' + (parseInt(i)+1) + '</td>';
|
||||||
str += '<td scope="col" style="width:10%; text-align:center;">' + (data[i].plugin) + '<br>' + (data[i].id) + '</td>';
|
str += '<td scope="col" style="width:10%; text-align:center;">' + (data[i].plugin) + '<br>' + (data[i].id) + '</td>';
|
||||||
str += '<td scope="col" style="width:10%; text-align:center;">' + ((data[i].is_running) ?'실행중':'대기중') + '</td>';
|
str += '<td scope="col" style="width:10%; text-align:center;">' + ((data[i].is_running) ? '실행중':'대기중') + '</td>';
|
||||||
str += '<td scope="col" style="width:10%; text-align:center;">' + (data[i].next_run_time) + '</td>';
|
str += '<td scope="col" style="width:10%; text-align:center;">' + (data[i].next_run_time) + '</td>';
|
||||||
str += '<td scope="col" style="width:10%; text-align:center;">' + (data[i].remain_time) + '</td>';
|
str += '<td scope="col" style="width:10%; text-align:center;">' + (data[i].remain_time) + '</td>';
|
||||||
|
|
||||||
@@ -180,54 +180,6 @@ function make_scheduler_list(data) {
|
|||||||
|
|
||||||
$("#scheduler_list_div").html(str);
|
$("#scheduler_list_div").html(str);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -36,39 +36,39 @@ function make_plugin_list(data) {
|
|||||||
console.log(data)
|
console.log(data)
|
||||||
for (i in data) {
|
for (i in data) {
|
||||||
console.log(data[i]);
|
console.log(data[i]);
|
||||||
str += m_row_start();
|
str += j_row_start();
|
||||||
str += m_col_wide(1, (parseInt(i)+1), 'center')
|
str += j_col_wide(1, (parseInt(i)+1), 'center')
|
||||||
if (data[i].title == null) {
|
if (data[i].title == null) {
|
||||||
str += m_col_wide(2, '');
|
str += j_col_wide(2, '');
|
||||||
str += m_col_wide(2, data[i].package_name);
|
str += j_col_wide(2, data[i].package_name);
|
||||||
str += m_col_wide(5, '');
|
str += j_col_wide(5, '');
|
||||||
tmp = m_button('uninstall_btn', '삭제', {'package_name':data[i].package_name}, 'danger', false, true);
|
tmp = j_button('uninstall_btn', '삭제', {'package_name':data[i].package_name}, 'danger', false, true);
|
||||||
} else {
|
} else {
|
||||||
str += m_col_wide(2, data[i].title);
|
str += j_col_wide(2, data[i].title);
|
||||||
|
|
||||||
str += m_col_wide(2, data[i].package_name);
|
str += j_col_wide(2, data[i].package_name);
|
||||||
str += m_col_wide(1, data[i].developer);
|
str += j_col_wide(1, data[i].developer);
|
||||||
str += m_col_wide(1, data[i].version);
|
str += j_col_wide(1, data[i].version);
|
||||||
if (data[i].loading == false) {
|
if (data[i].loading == false) {
|
||||||
tmp = data[i].description + '<br>' + text_color('[로딩 실패] ') + data[i].status;
|
tmp = data[i].description + '<br>' + text_color('[로딩 실패] ') + data[i].status;
|
||||||
str += m_col_wide(3, tmp);
|
str += j_col_wide(3, tmp);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
str += m_col_wide(3, data[i].description);
|
str += j_col_wide(3, data[i].description);
|
||||||
}
|
}
|
||||||
|
|
||||||
tmp = ''
|
tmp = ''
|
||||||
|
|
||||||
tmp += m_button_small('globalOpenBtn', '홈페이지', {'url':data[i].home}, 'primary', false, true);
|
tmp += j_button_small('globalOpenBtn', '홈페이지', {'url':data[i].home}, 'primary', false, true);
|
||||||
tmp += m_button_small('uninstall_btn', '삭제', {'package_name':data[i].package_name, 'title':data[i].title}, 'danger', false, true);
|
tmp += j_button_small('uninstall_btn', '삭제', {'package_name':data[i].package_name, 'title':data[i].title}, 'danger', false, true);
|
||||||
tmp += m_button_small('json_btn', 'JSON', {'idx':i}, 'info', false, true);
|
tmp += j_button_small('json_btn', 'JSON', {'idx':i}, 'info', false, true);
|
||||||
}
|
}
|
||||||
tmp = m_button_group(tmp)
|
tmp = j_button_group(tmp)
|
||||||
str += m_col_wide(2, tmp, 'right')
|
str += j_col_wide(2, tmp, 'right')
|
||||||
str += m_row_end();
|
str += j_row_end();
|
||||||
if (i != current_data.length -1) str += m_hr(0);
|
if (i != current_data.length -1) str += j_hr(0);
|
||||||
}
|
}
|
||||||
document.getElementById("plugin_list_div").innerHTML = str;
|
$("#plugin_list_div").html(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
$("body").on('click', '#json_btn', function(e){
|
$("body").on('click', '#json_btn', function(e){
|
||||||
|
|||||||
@@ -16,9 +16,7 @@
|
|||||||
{{ macros.m_hr() }}
|
{{ macros.m_hr() }}
|
||||||
|
|
||||||
{{ macros.setting_checkbox('use_apikey', 'APIKEY 사용', value=arg['use_apikey'], desc=['On : 모든 API 요청 시 apikey 값을 입력해야 합니다.', '없거나 틀릴 경우 에러코드 403리턴']) }}
|
{{ macros.setting_checkbox('use_apikey', 'APIKEY 사용', value=arg['use_apikey'], desc=['On : 모든 API 요청 시 apikey 값을 입력해야 합니다.', '없거나 틀릴 경우 에러코드 403리턴']) }}
|
||||||
<div id="use_apikey_div" class="collapse">
|
{{ macros.setting_input_text_and_buttons('apikey', 'APIKEY', [['apikey_generate_btn', '자동생성']], col='4', value=arg['apikey']) }}
|
||||||
{{ macros.setting_input_text_and_buttons('apikey', 'APIKEY', [['apikey_generate_btn', '자동생성']], col='4', value=arg['apikey']) }}
|
|
||||||
</div>
|
|
||||||
{{ macros.m_hr() }}
|
{{ macros.m_hr() }}
|
||||||
</form>
|
</form>
|
||||||
</div> <!--전체-->
|
</div> <!--전체-->
|
||||||
@@ -27,17 +25,12 @@
|
|||||||
|
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
use_collapse("use_login");
|
use_collapse("use_login");
|
||||||
use_collapse("use_apikey");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#use_login').change(function() {
|
$('#use_login').change(function() {
|
||||||
use_collapse('use_login');
|
use_collapse('use_login');
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#use_apikey').change(function() {
|
|
||||||
use_collapse('use_apikey');
|
|
||||||
});
|
|
||||||
|
|
||||||
$("body").on('click', '#apikey_generate_btn', function(e) {
|
$("body").on('click', '#apikey_generate_btn', function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
globalSendCommand('apikey_generate', null, null, null, null, function(ret){
|
globalSendCommand('apikey_generate', null, null, null, null, function(ret){
|
||||||
|
|||||||
310
lib/system/templates/system_tool_command.html
Normal file
310
lib/system/templates/system_tool_command.html
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{{ macros.m_button_group([['foreground_command_btn', 'Foreground 실행'], ['job_new_btn', '저장'], ['select_file_btn', '파일선택', [['path_data', arg['path_data']]] ]])}}
|
||||||
|
|
||||||
|
{{ macros.setting_input_textarea_wide('command', 'Command', row='3', value=arg['tool_command_recent'], desc=['예) cmd, bssh, sh, python test.py, LOAD test.py, curl ifconfig.me']) }}
|
||||||
|
|
||||||
|
<div id="list_div"></div>
|
||||||
|
|
||||||
|
{{ macros.m_modal_start('job_modal', '', 'modal-lg') }}
|
||||||
|
<form id='item_setting' name='item_setting'>
|
||||||
|
<input type='hidden' id="job_id" name="job_id">
|
||||||
|
{{ macros.setting_input_textarea_wide('job_command', 'Command', row=5, desc=['LOAD형으로 실행할 경우 python 대신 LOAD로 시작']) }}
|
||||||
|
{{ macros.setting_input_text('job_command_args', 'ARGS', desc=['Command에 덧붙여 전달할 값. API로 변경 가능']) }}
|
||||||
|
|
||||||
|
{{ macros.setting_input_text('job_description', 'Description') }}
|
||||||
|
{{ macros.setting_radio_with_value('job_schedule_mode', '스케쥴링 타입', [['none', '없음'], ['startup', '시작시 한번 실행'], ['scheduler', '스케쥴링']]) }}
|
||||||
|
{{ macros.setting_input_text('job_schedule_interval', '스케쥴링 정보', desc=['Interval(minute 단위)이나 Cron 설정']) }}
|
||||||
|
{{ macros.setting_checkbox('job_schedule_auto_start', '시작시 스케쥴링 등록', desc=['On : 시작시 자동으로 스케쥴러에 등록됩니다.']) }}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
{{ macros.m_button_group([['job_save_btn', '저장'], ['job_remove_btn', '삭제'], ['modal_hide_btn', '닫기']])}}
|
||||||
|
</div>
|
||||||
|
</div></div></div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="file_modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title" id="modal_title">Site </h4>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" id="modal_body" style="word-break:break-all;">
|
||||||
|
<form id="file_form" name="file_form">
|
||||||
|
<div class="input-group col-sm-12">
|
||||||
|
<textarea id="file_textarea" name="file_textarea" class="col-md-12" rows="50"></textarea>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="file_job_id" id="file_job_id" value="-1">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button id="file_save_btn" type="button" class="btn btn-primary">저장</button>
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">닫기</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(document).ready(function(){
|
||||||
|
request_list();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////// 상단 버튼
|
||||||
|
$("body").on('click', '#foreground_command_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
globalSendCommandPage('foreground_command', $('#command').val());
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$("body").on('click', '#job_new_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
globalSendCommandPage('job_new', $('#command').val(), null, null, null, function(ret){
|
||||||
|
request_list();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on('click', '#select_file_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
path_data = $(this).data('path_data');
|
||||||
|
globalSelectLocalFile("파일 선택", path_data, function(ret){
|
||||||
|
$('#command').val(ret);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
////////////////////////////////// 상단 버튼 END
|
||||||
|
|
||||||
|
|
||||||
|
function request_list() {
|
||||||
|
globalSendCommandPage('job_list', null, null, null, null, function(ret){
|
||||||
|
make_list(ret.data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_list(data) {
|
||||||
|
current_data = data;
|
||||||
|
str = '';
|
||||||
|
str = '<table id="result_table" class="table table-sm" ><thead class="thead-dark"><tr> \
|
||||||
|
<th style="width:5%; text-align:center;">ID</th> \
|
||||||
|
<th style="width:60%; text-align:center;">Command & arg & Desc</th> \
|
||||||
|
<th style="width:5%; text-align:center;">자동</th> \
|
||||||
|
<th colspan="2" style="width:20%; text-align:center;">스케쥴 상태</th> \
|
||||||
|
<th style="width:10%; text-align:center;">스케쥴</th> \
|
||||||
|
</tr></thead><tbody id="list">';
|
||||||
|
|
||||||
|
if (data.length == 0) str += '<tr><td colspan="6"><h4>작업이 없습니다.</h4></td></tr>';
|
||||||
|
|
||||||
|
for(i in data) {
|
||||||
|
console.log(data[i]);
|
||||||
|
str += '<tr class="chover" style="cursor: pointer;" data-toggle="collapse" data-target="#collapse_' + i + '" aria-expanded="true" >';
|
||||||
|
|
||||||
|
str += '<td rowspan="2" scope="col" style="width:5%; text-align:center;">'+ (data[i].id) + '</td>';
|
||||||
|
|
||||||
|
|
||||||
|
// command
|
||||||
|
tmp = '';
|
||||||
|
tmp += text_color(data[i].command, 'blue') + '<br>';
|
||||||
|
tmp += data[i].args + '<br>';
|
||||||
|
tmp += data[i].description + '<br>';
|
||||||
|
|
||||||
|
str += '<td scope="col" style="width:60%; text-align:left;">'+ tmp + '</td>';
|
||||||
|
|
||||||
|
|
||||||
|
tmp = (data[i].schedule_auto_start) ? text_color("ON", 'blue') : "OFF";
|
||||||
|
str += '<td scope="col" style="width:5%; text-align:center;">'+ tmp + '</td>';
|
||||||
|
|
||||||
|
tmp2 = null;
|
||||||
|
if (data[i].schedule_mode == 'none') {
|
||||||
|
tmp1 = "없음";
|
||||||
|
} else if (data[i].schedule_mode == 'startup') {
|
||||||
|
tmp1 = "시작시 한번 실행";
|
||||||
|
} else if (data[i].schedule_mode == 'scheduler') {
|
||||||
|
tmp1 = "스케쥴링";
|
||||||
|
tmp2 = '<input id="use_checkbox|'+data[i].id+'" type="checkbox" data-id='+data[i].id+' data-toggle="toggle" data-on="On" data-off="Off" data-onstyle="info" data-offstyle="danger" data-size="small" ' + ((data[i].scheduler_is_include) ? 'checked' : '') + '>';
|
||||||
|
if (data[i].scheduler_is_include) {
|
||||||
|
tmp2 += (data[i].scheduler_is_running) ? "<br>실행중" : "<br>대기중";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tmp2 == null) {
|
||||||
|
str += '<td scope="col" colspan="2" style="width:20%; text-align:center;">'+ tmp1 + '</td>';
|
||||||
|
} else {
|
||||||
|
str += '<td scope="col" style="width:10%; text-align:right;">'+ tmp1 + '</td>';
|
||||||
|
str += '<td scope="col" style="width:10%; text-align:left;">'+ tmp2 + '</td>';
|
||||||
|
}
|
||||||
|
str += '<td scope="col" style="width:10%; text-align:center;">'+ data[i].schedule_interval + '</td>';
|
||||||
|
str += '</tr>'
|
||||||
|
|
||||||
|
str += '<tr>'
|
||||||
|
tmp = j_row_start(0);
|
||||||
|
tmp += j_col('0', '');
|
||||||
|
btn = j_button('job_edit_btn', '작업 편집', {'idx':i}, 'secondary', false, true);
|
||||||
|
btn += j_button('job_remove_from_table_btn', '삭제', {'id':data[i].id}, 'danger', true, true);
|
||||||
|
btn += j_button('job_fore_execute_btn', 'Foreground 실행', {'id':data[i].id}, 'primary', true, true);
|
||||||
|
btn += j_button('job_back_execute_btn', 'Background 실행', {'id':data[i].id}, 'primary', true, true);
|
||||||
|
btn += j_button('job_log_btn', '로그', {'id':data[i].id}, 'info', true, true);
|
||||||
|
btn += j_button('job_cmd_input_btn', 'Command에 입력', {'idx':i}, 'info', true, true);
|
||||||
|
if ( data[i].filepath != '' && data[i].filepath != null )
|
||||||
|
btn += j_button('globalEditBtn', '파일 수정', {'file':data[i].filepath}, 'info', false, true);
|
||||||
|
if ( data[i].process )
|
||||||
|
btn += j_button('job_process_stop_btn', '실행중인 Process 중지', {'id':data[i].id}, 'danger', false, true);
|
||||||
|
tmp += j_col('12', j_button_group(btn));
|
||||||
|
tmp += j_row_end();
|
||||||
|
str += '<td colspan="1" scope="col" style="width:5%; text-align:center;">'+ tmp + '</td>';
|
||||||
|
str += '</tr>'
|
||||||
|
}
|
||||||
|
str += '</table>';
|
||||||
|
document.getElementById("list_div").innerHTML = str;
|
||||||
|
$('input[id^="use_checkbox|"]').bootstrapToggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$('input[type=radio][name=job_schedule_mode]').change(function() {
|
||||||
|
set_schedule_mode(this.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function set_schedule_mode(mode) {
|
||||||
|
$('input:radio[name="job_schedule_mode"][value="'+mode+'"]').attr('checked',true);
|
||||||
|
|
||||||
|
if ( mode == 'none' || mode == 'startup') {
|
||||||
|
$("#job_schedule_interval").attr('disabled', true);
|
||||||
|
$("#job_schedule_auto_start").attr('disabled', true);
|
||||||
|
} else {
|
||||||
|
$("#job_schedule_interval").attr('disabled', false);
|
||||||
|
$("#job_schedule_auto_start").attr('disabled', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 아이템 저장 버튼
|
||||||
|
$("body").on('click', '#job_save_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
//tmp = document.getElementById("schedule_radio2").getAttribute("checked");
|
||||||
|
schedule_mode = $('input[name=job_schedule_mode]:checked').val();
|
||||||
|
console.log(tmp);
|
||||||
|
|
||||||
|
schedule_interval = $("#job_schedule_interval").val();
|
||||||
|
|
||||||
|
if (schedule_mode == 'scheduler' && schedule_interval == '') {
|
||||||
|
notify("스케쥴링 정보를 입력하세요", 'warning');
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var formData = getFormdata('#item_setting');
|
||||||
|
globalSendCommandPage('job_save', formData, null, null, null, function(ret){
|
||||||
|
if (ret.ret == 'success') {
|
||||||
|
$('#job_modal').modal('hide');
|
||||||
|
request_list();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on('click', '#modal_hide_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
$('#job_modal').modal('hide');
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on('click', '#job_remove_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
remove_job($("#job_id").val());
|
||||||
|
});
|
||||||
|
|
||||||
|
function remove_job(job_id) {
|
||||||
|
globalSendCommandPage('job_remove', job_id, null, null, null, function(ret){
|
||||||
|
if (ret.ret == 'success') {
|
||||||
|
$('#job_modal').modal('hide');
|
||||||
|
request_list();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////
|
||||||
|
// JOB 테이블 제어
|
||||||
|
$("body").on('click', '#job_edit_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
idx = parseInt($(this).data('idx'));
|
||||||
|
item = current_data[idx];
|
||||||
|
$("#job_id").val(item.id);
|
||||||
|
$("#job_modal_title").html('ID: ' + item.id + ' Command ID: ' + item.command_id);
|
||||||
|
$("#job_command").val(item.command);
|
||||||
|
$("#job_description").val(item.description);
|
||||||
|
$("#job_command_args").val(item.args);
|
||||||
|
set_schedule_mode(item.schedule_mode);
|
||||||
|
if ( item.schedule_mode == 'scheduler') {
|
||||||
|
$("#scheduler_swtich_btn").attr('disabled', false);
|
||||||
|
} else {
|
||||||
|
$("#scheduler_swtich_btn").attr('disabled', true);
|
||||||
|
}
|
||||||
|
$("#job_schedule_interval").val(item.schedule_interval);
|
||||||
|
if (item.schedule_auto_start) {
|
||||||
|
$("#job_schedule_auto_start").val('on');
|
||||||
|
$('#job_schedule_auto_start').bootstrapToggle('on')
|
||||||
|
} else {
|
||||||
|
$("#job_schedule_auto_start").val('off');
|
||||||
|
$('#job_schedule_auto_start').bootstrapToggle('off')
|
||||||
|
}
|
||||||
|
$("#job_modal").modal();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$("body").on('click', '#job_remove_from_table_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
remove_job($(this).data('id'));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$("body").on('click', '#job_fore_execute_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
globalSendCommandPage('job_fore_execute', $(this).data('id'));
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on('click', '#job_back_execute_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
globalSendCommandPage('job_back_execute', $(this).data('id'), null, null, null, function(e) {
|
||||||
|
request_list();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on('click', '#job_log_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
globalSendCommandPage('job_log', $(this).data('id'), null, null, null, function(data){
|
||||||
|
if (data.ret == 'success') {
|
||||||
|
redirect = '/system/all_log/list';
|
||||||
|
$.redirectPost(redirect, {filename: data.filename});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$("body").on('click', '#job_cmd_input_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
idx = parseInt($(this).data('idx'));
|
||||||
|
$("#command").val((current_data[idx].command + ' ' + current_data[idx].args).trim());
|
||||||
|
window.scrollTo(0,0);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$("body").on('change', 'input[id^="use_checkbox|"]', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
globalSendCommandPage('task_sched', $(this).data('id'), $(this).prop('checked'), null, null, function(e) {
|
||||||
|
request_list();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on('click', '#job_process_stop_btn', function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
globalSendCommandPage('job_process_stop', $(this).data('id'), null, null, null, function(e) {
|
||||||
|
request_list();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
76
lib/system/templates/system_tool_python.html
Normal file
76
lib/system/templates/system_tool_python.html
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% block content %}
|
||||||
|
<div>
|
||||||
|
{{ macros.m_button_group([['globalSettingSaveBtn', '설정 저장']])}}
|
||||||
|
{{ macros.m_row_start('5') }}
|
||||||
|
{{ macros.m_row_end() }}
|
||||||
|
<nav>
|
||||||
|
{{ macros.m_tab_head_start() }}
|
||||||
|
{{ macros.m_tab_head2('normal', '일반', true) }}
|
||||||
|
|
||||||
|
{{ 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('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">
|
||||||
|
{{ macros.setting_input_text('tool_crypt_user_key', '암호화 키', value=arg['tool_crypt_user_key'], desc=['16진수(숫자, a~e)로 이루어진 32글자. 미만시 앞을 0으로 채움. 초과시 뒤에 무시']) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{ macros.setting_input_text_and_buttons('tool_crypt_encrypt_word', '암호화', [['tool_crypt_encrypt_word_btn', '암호화']], value=arg['tool_crypt_encrypt_word']) }}
|
||||||
|
{{ macros.setting_input_text('tool_crypt_encrypt_word_result', '', disabled=True) }}
|
||||||
|
|
||||||
|
{{ macros.setting_input_text_and_buttons('tool_crypt_decrypt_word', '평문화', [['tool_crypt_decrypt_word_btn', '평문화']], value=arg['tool_crypt_decrypt_word']) }}
|
||||||
|
{{ macros.setting_input_text('tool_crypt_decrypt_word_result', '', disabled=True) }}
|
||||||
|
{{ 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("tool_crypt_use_user_key");
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on('change', '#tool_crypt_use_user_key', function(e){
|
||||||
|
use_collapse('tool_crypt_use_user_key');
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on('click', '#tool_crypt_encrypt_word_btn', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
word = document.getElementById("tool_crypt_encrypt_word").value
|
||||||
|
crypt_test('encrypt', word);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on('click', '#tool_crypt_decrypt_word_btn', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
word = document.getElementById("tool_crypt_decrypt_word").value
|
||||||
|
crypt_test('decrypt', word);
|
||||||
|
});
|
||||||
|
|
||||||
|
function crypt_test(mode, word) {
|
||||||
|
$.ajax({
|
||||||
|
url: '/' + package_name + '/ajax/'+sub+'/crypt_test',
|
||||||
|
type: "POST",
|
||||||
|
cache: false,
|
||||||
|
data: {mode:mode, word:word},
|
||||||
|
dataType: "json",
|
||||||
|
success: function (ret) {
|
||||||
|
if (ret.ret == 'success') {
|
||||||
|
if (mode == "encrypt")
|
||||||
|
document.getElementById("tool_crypt_encrypt_word_result").value = ret.data;
|
||||||
|
else
|
||||||
|
document.getElementById("tool_crypt_decrypt_word_result").value = ret.data;
|
||||||
|
} else {
|
||||||
|
notify(ret.log, 'warning');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user