diff --git a/.gitignore b/.gitignore index 32b616d5ba..a79627090a 100644 --- a/.gitignore +++ b/.gitignore @@ -151,8 +151,10 @@ data/system.db-journal data/hook_*.json data/letsencrypt.json data/basic_auth.json +data/notify.json data/api.json data/bind_domain.pl +data/unauthorized_status.pl plugins/vip_* plugins/own_* @@ -176,4 +178,5 @@ plugins/file_search debug.out -data/unauthorized_status.pl + + diff --git a/README.md b/README.md index 6d21dcba1f..2cbbb83744 100644 --- a/README.md +++ b/README.md @@ -99,13 +99,15 @@ docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443: ``` -### 版本更新 0.13.1 - -* 关闭issue,建立bbs.midoks.me。问题集中处理,发挥群众的力量。 -* 添加站操作回调钩子site_cb。 网站统计和OP防火墙不再手动重载配置 -* 修复ubuntu20 pureftp 安装时,端口没有加入安全里 -* 增加global_static hook关键字,并优化menu的文件加载hook -* 各种细节优化。 +### 版本更新 0.13.2 + +* supervisor添加重启功能。 +* 加入滚动写入日志 mw.writeFileLog方法。 +* mysql导出优化优化。 +* 添加tgbot插件[仅测试]。 +* TelegramBot机器人通知-加入配置。 +* mariadb更新版本。 +* 在线文件编辑优化。 ### JSDelivr安装地址 diff --git a/class/core/config_api.py b/class/core/config_api.py index 737f53ecb9..2a5aa69c4e 100755 --- a/class/core/config_api.py +++ b/class/core/config_api.py @@ -27,7 +27,7 @@ class config_api: - __version = '0.13.1' + __version = '0.13.2' __api_addr = 'data/api.json' def __init__(self): @@ -661,6 +661,60 @@ def setStatusCodeApi(self): mw.writeLog('面板设置', '将未授权响应状态码设置为:{}'.format(status_code)) return mw.returnJson(True, '设置成功!') + def getNotifyApi(self): + # 获取 + data = mw.getNotifyData(True) + return mw.returnData(True, 'ok', data) + + def setNotifyApi(self): + tag = request.form.get('tag', '').strip() + data = request.form.get('data', '').strip() + + cfg = mw.getNotifyData(False) + + crypt_data = mw.enDoubleCrypt(tag, data) + if tag in cfg: + cfg[tag]['cfg'] = crypt_data + else: + t = {'cfg': crypt_data} + cfg[tag] = t + + mw.writeNotify(cfg) + return mw.returnData(True, '设置成功') + + def setNotifyTestApi(self): + # 异步通知验证 + tag = request.form.get('tag', '').strip() + tag_data = request.form.get('data', '').strip() + + if tag == 'tgbot': + t = json.loads(tag_data) + test_bool = mw.tgbotNotifyTest(t['app_token'], t['chat_id']) + if test_bool: + return mw.returnData(True, '验证成功') + return mw.returnData(False, '验证失败') + + return mw.returnData(False, '暂时未支持该验证') + + def setNotifyEnableApi(self): + # 异步通知验证 + tag = request.form.get('tag', '').strip() + tag_enable = request.form.get('enable', '').strip() + + data = mw.getNotifyData(False) + op_enable = True + op_action = '开启' + if tag_enable != 'true': + op_enable = False + op_action = '关闭' + + if tag in data: + data[tag]['enable'] = op_enable + + mw.writeNotify(data) + + return mw.returnData(True, op_action + '成功') + def getPanelTokenApi(self): api_file = self.__api_addr tmp = mw.readFile(api_file) @@ -853,4 +907,14 @@ def get(self): else: data['hook_global_static'] = [] + # notiy config + notify_data = mw.getNotifyData(True) + notify_tag_list = ['tgbot', 'email'] + for tag in notify_tag_list: + new_tag = 'notify_' + tag + '_enable' + data[new_tag] = '' + if tag in notify_data and 'enable' in notify_data[tag]: + if notify_data[tag]['enable']: + data[new_tag] = 'checked' + return data diff --git a/class/core/mw.py b/class/core/mw.py index 8e4bb0e99b..503c0699b6 100755 --- a/class/core/mw.py +++ b/class/core/mw.py @@ -82,6 +82,10 @@ def getPanelDataDir(): return getRunDir() + '/data' +def getPanelTmp(): + return getRunDir() + '/tmp' + + def getServerDir(): return getRootDir() + '/server' @@ -538,6 +542,14 @@ def getDate(): return time.strftime('%Y-%m-%d %X', time.localtime()) +def getDateFromNow(tf_format="%Y-%m-%d %H:%M:%S", time_zone="Asia/Shanghai"): + # 取格式时间 + import time + os.environ['TZ'] = time_zone + time.tzset() + return time.strftime(tf_format, time.localtime()) + + def getDataFromInt(val): time_format = '%Y-%m-%d %H:%M:%S' time_str = time.localtime(val) @@ -553,7 +565,7 @@ def writeLog(stype, msg, args=()): uid = session['uid'] except Exception as e: pass - # print(getTracebackInfo()) + # writeFileLog(getTracebackInfo()) return writeDbLog(stype, msg, args, uid) @@ -704,7 +716,7 @@ def enCrypt(key, strings): result = f.encrypt(strings) return result.decode('utf-8') except: - print(getTracebackInfo()) + writeFileLog(getTracebackInfo()) return strings @@ -723,7 +735,7 @@ def deCrypt(key, strings): result = f.decrypt(strings).decode('utf-8') return result except: - print(getTracebackInfo()) + writeFileLog(getTracebackInfo()) return strings @@ -742,7 +754,7 @@ def enDoubleCrypt(key, strings): result = f.encrypt(strings) return result.decode('utf-8') except: - print(getTracebackInfo()) + writeFileLog(getTracebackInfo()) return strings @@ -760,7 +772,7 @@ def deDoubleCrypt(key, strings): result = f.decrypt(strings).decode('utf-8') return result except: - print(getTracebackInfo()) + writeFileLog(getTracebackInfo()) return strings @@ -1564,7 +1576,7 @@ def getCertName(certPath): result['notAfter'], "%Y-%m-%d")) - time.time()) / 86400) return result except Exception as e: - # print(getTracebackInfo()) + writeFileLog(getTracebackInfo()) return None @@ -1662,6 +1674,141 @@ def getMyORMDb(): o = ormDb.ORM() return o +##################### notify start ######################################### + + +def initNotifyConfig(): + p = getNotifyPath() + if not os.path.exists(p): + writeFile(p, '{}') + return True + + +def getNotifyPath(): + path = 'data/notify.json' + return path + + +def getNotifyData(is_parse=False): + initNotifyConfig() + notify_file = getNotifyPath() + notify_data = readFile(notify_file) + + data = json.loads(notify_data) + + if is_parse: + tag_list = ['tgbot'] + for t in tag_list: + if t in data and 'cfg' in data[t]: + data[t]['data'] = json.loads(deDoubleCrypt(t, data[t]['cfg'])) + return data + + +def writeNotify(data): + p = getNotifyPath() + return writeFile(p, json.dumps(data)) + + +def tgbotNotifyChatID(): + data = getNotifyData(True) + if 'tgbot' in data and 'enable' in data['tgbot']: + if data['tgbot']['enable']: + t = data['tgbot']['data'] + return t['chat_id'] + return '' + + +def tgbotNotifyObject(): + data = getNotifyData(True) + if 'tgbot' in data and 'enable' in data['tgbot']: + if data['tgbot']['enable']: + t = data['tgbot']['data'] + import telebot + bot = telebot.TeleBot(app_token) + return True, bot + return False, None + + +def tgbotNotifyMessage(app_token, chat_id, msg): + import telebot + bot = telebot.TeleBot(app_token) + try: + data = bot.send_message(chat_id, msg) + return True + except Exception as e: + writeFileLog(str(e)) + return False + + +def tgbotNotifyHttpPost(app_token, chat_id, msg): + try: + url = 'https://api.telegram.org/bot' + app_token + '/sendMessage' + post_data = { + 'chat_id': chat_id, + 'text': msg, + } + rdata = httpPost(url, post_data) + return True + except Exception as e: + writeFileLog(str(e)) + return False + + +def tgbotNotifyTest(app_token, chat_id): + msg = 'MW-通知验证测试OK' + return tgbotNotifyHttpPost(app_token, chat_id, msg) + + +def notifyMessageTry(msg, stype='common', trigger_time=300, is_write_log=True): + + lock_file = getPanelTmp() + '/notify_lock.json' + if not os.path.exists(lock_file): + writeFile(lock_file, '{}') + + lock_data = json.loads(readFile(lock_file)) + if stype in lock_data: + diff_time = time.time() - lock_data[stype]['do_time'] + if diff_time >= trigger_time: + lock_data[stype]['do_time'] = time.time() + else: + return False + else: + lock_data[stype] = {'do_time': time.time()} + + writeFile(lock_file, json.dumps(lock_data)) + + if is_write_log: + writeLog("通知管理[" + stype + "]", msg) + + data = getNotifyData(True) + # tag_list = ['tgbot', 'email'] + # tagbot + do_notify = False + if 'tgbot' in data and 'enable' in data['tgbot']: + if data['tgbot']['enable']: + t = data['tgbot']['data'] + i = sys.version_info + + # telebot 在python小于3.7无法使用 + if i[0] < 3 or i[1] < 7: + do_notify = tgbotNotifyHttpPost( + t['app_token'], t['chat_id'], msg) + else: + do_notify = tgbotNotifyMessage( + t['app_token'], t['chat_id'], msg) + return do_notify + + +def notifyMessage(msg, stype='common', trigger_time=300, is_write_log=True): + try: + return notifyMessageTry(msg, stype, trigger_time, is_write_log) + except Exception as e: + writeFileLog(getTracebackInfo()) + return False + + +##################### notify end ######################################### + ##################### ssh start ######################################### def getSshDir(): diff --git a/plugins/mariadb/info.json b/plugins/mariadb/info.json index aa6364b5c0..a0767c8238 100755 --- a/plugins/mariadb/info.json +++ b/plugins/mariadb/info.json @@ -8,7 +8,7 @@ "uninstall_pre_inspection":true, "checks": "server/mariadb", "path": "server/mariadb", - "versions":["10.6","10.7","10.8","10.9"], + "versions":["10.6","10.7","10.8","10.9","10.11","11.0"], "shell":"install.sh", "checks":"server/mariadb", "path":"server/mariadb", diff --git a/plugins/mariadb/versions/10.11/install.sh b/plugins/mariadb/versions/10.11/install.sh new file mode 100755 index 0000000000..44ed9fe5f8 --- /dev/null +++ b/plugins/mariadb/versions/10.11/install.sh @@ -0,0 +1,119 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH + +#https://dev.mysql.com/downloads/mysql/5.5.html#downloads +#https://dev.mysql.com/downloads/file/?id=480541 + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") +sysName=`uname` + +install_tmp=${rootPath}/tmp/mw_install.pl +mariadbDir=${serverPath}/source/mariadb + +MY_VER=10.11.2 + +Install_app() +{ + mkdir -p ${mariadbDir} + echo '正在安装脚本文件...' > $install_tmp + + if id mysql &> /dev/null ;then + echo "mysql uid is `id -u www`" + echo "mysql shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `" + else + groupadd mysql + useradd -g mysql mysql + fi + + if [ "$sysName" != "Darwin" ];then + mkdir -p /var/log/mariadb + touch /var/log/mariadb/mariadb.log + fi + + # ----- cpu start ------ + if [ -z "${cpuCore}" ]; then + cpuCore="1" + fi + + if [ -f /proc/cpuinfo ];then + cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` + fi + + MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') + if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then + if [ "${cpuCore}" -gt "${MEM_INFO}" ];then + cpuCore="${MEM_INFO}" + fi + else + cpuCore="1" + fi + + if [ "$cpuCore" -gt "2" ];then + cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'` + else + cpuCore="1" + fi + # ----- cpu end ------ + + # if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then + # wget --no-check-certificate -O ${mariadbDir}/mariadb-${MY_VER}.tar.gz --tries=3 https://mirrors.aliyun.com/mariadb/mariadb-${MY_VER}/source/mariadb-${MY_VER}.tar.gz + # fi + + # https://downloads.mariadb.org/interstitial/mariadb-10.9.1/source/mariadb-10.9.1.tar.gz + if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then + wget --no-check-certificate -O ${mariadbDir}/mariadb-${MY_VER}.tar.gz --tries=3 https://archive.mariadb.org/mariadb-${MY_VER}/source/mariadb-${MY_VER}.tar.gz + fi + + if [ ! -d ${mariadbDir}/mariadb-${MY_VER} ];then + cd ${mariadbDir} && tar -zxvf ${mariadbDir}/mariadb-${MY_VER}.tar.gz + fi + + + if [ ! -d $serverPath/mariadb ];then + cd ${mariadbDir}/mariadb-${MY_VER} && cmake \ + -DCMAKE_INSTALL_PREFIX=$serverPath/mariadb \ + -DMYSQL_DATADIR=$serverPath/mariadb/data/ \ + -DMYSQL_USER=mysql \ + -DMYSQL_UNIX_ADDR=$serverPath/mariadb/mysql.sock \ + -DWITH_MYISAM_STORAGE_ENGINE=1 \ + -DWITH_INNOBASE_STORAGE_ENGINE=1 \ + -DWITH_MEMORY_STORAGE_ENGINE=1 \ + -DENABLED_LOCAL_INFILE=1 \ + -DWITH_PARTITION_STORAGE_ENGINE=1 \ + -DEXTRA_CHARSETS=all \ + -DDEFAULT_CHARSET=utf8mb4 \ + -DDEFAULT_COLLATION=utf8mb4_general_ci \ + -DCMAKE_C_COMPILER=/usr/bin/gcc \ + -DCMAKE_CXX_COMPILER=/usr/bin/g++ + make -j${cpuCore} && make install && make clean + + if [ -d $serverPath/mariadb ];then + echo '10.11' > $serverPath/mariadb/version.pl + echo '安装完成' > $install_tmp + else + echo '安装失败' > $install_tmp + echo 'install fail'>&2 + exit 1 + fi + fi + + rm -rf ${mariadbDir}/mariadb-${MY_VER} + rm -rf ${mariadbDir}/mariadb-${MY_VER}.tar.gz +} + +Uninstall_app() +{ + rm -rf $serverPath/mariadb + echo '卸载完成' > $install_tmp +} + +action=$1 +if [ "${1}" == 'install' ];then + Install_app +else + Uninstall_app +fi diff --git a/plugins/mariadb/versions/10.6/install.sh b/plugins/mariadb/versions/10.6/install.sh index 9b7e522dcd..9772e8c0a7 100755 --- a/plugins/mariadb/versions/10.6/install.sh +++ b/plugins/mariadb/versions/10.6/install.sh @@ -13,7 +13,7 @@ sysName=`uname` install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb -MY_VER=10.6.9 +MY_VER=10.6.12 Install_app() { diff --git a/plugins/mariadb/versions/10.7/install.sh b/plugins/mariadb/versions/10.7/install.sh index ea02e3c165..2a8112c3fe 100755 --- a/plugins/mariadb/versions/10.7/install.sh +++ b/plugins/mariadb/versions/10.7/install.sh @@ -14,7 +14,7 @@ sysName=`uname` install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb -MY_VER=10.7.4 +MY_VER=10.7.8 Install_app() { diff --git a/plugins/mariadb/versions/10.8/install.sh b/plugins/mariadb/versions/10.8/install.sh index 7c3a880a3c..895a767d42 100755 --- a/plugins/mariadb/versions/10.8/install.sh +++ b/plugins/mariadb/versions/10.8/install.sh @@ -14,7 +14,7 @@ sysName=`uname` install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb -MY_VER=10.8.5 +MY_VER=10.8.7 Install_app() { diff --git a/plugins/mariadb/versions/10.9/install.sh b/plugins/mariadb/versions/10.9/install.sh index 6cfe763ce7..61299f769e 100755 --- a/plugins/mariadb/versions/10.9/install.sh +++ b/plugins/mariadb/versions/10.9/install.sh @@ -14,7 +14,7 @@ sysName=`uname` install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb -MY_VER=10.9.2 +MY_VER=10.9.5 Install_app() { diff --git a/plugins/mariadb/versions/11.0/install.sh b/plugins/mariadb/versions/11.0/install.sh new file mode 100755 index 0000000000..7335bb11ba --- /dev/null +++ b/plugins/mariadb/versions/11.0/install.sh @@ -0,0 +1,119 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH + +#https://dev.mysql.com/downloads/mysql/5.5.html#downloads +#https://dev.mysql.com/downloads/file/?id=480541 + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") +sysName=`uname` + +install_tmp=${rootPath}/tmp/mw_install.pl +mariadbDir=${serverPath}/source/mariadb + +MY_VER=11.0.1 + +Install_app() +{ + mkdir -p ${mariadbDir} + echo '正在安装脚本文件...' > $install_tmp + + if id mysql &> /dev/null ;then + echo "mysql uid is `id -u www`" + echo "mysql shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `" + else + groupadd mysql + useradd -g mysql mysql + fi + + if [ "$sysName" != "Darwin" ];then + mkdir -p /var/log/mariadb + touch /var/log/mariadb/mariadb.log + fi + + # ----- cpu start ------ + if [ -z "${cpuCore}" ]; then + cpuCore="1" + fi + + if [ -f /proc/cpuinfo ];then + cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l` + fi + + MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}') + if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then + if [ "${cpuCore}" -gt "${MEM_INFO}" ];then + cpuCore="${MEM_INFO}" + fi + else + cpuCore="1" + fi + + if [ "$cpuCore" -gt "2" ];then + cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'` + else + cpuCore="1" + fi + # ----- cpu end ------ + + # if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then + # wget --no-check-certificate -O ${mariadbDir}/mariadb-${MY_VER}.tar.gz --tries=3 https://mirrors.aliyun.com/mariadb/mariadb-${MY_VER}/source/mariadb-${MY_VER}.tar.gz + # fi + + # https://downloads.mariadb.org/interstitial/mariadb-10.9.1/source/mariadb-10.9.1.tar.gz + if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then + wget --no-check-certificate -O ${mariadbDir}/mariadb-${MY_VER}.tar.gz --tries=3 https://archive.mariadb.org/mariadb-${MY_VER}/source/mariadb-${MY_VER}.tar.gz + fi + + if [ ! -d ${mariadbDir}/mariadb-${MY_VER} ];then + cd ${mariadbDir} && tar -zxvf ${mariadbDir}/mariadb-${MY_VER}.tar.gz + fi + + + if [ ! -d $serverPath/mariadb ];then + cd ${mariadbDir}/mariadb-${MY_VER} && cmake \ + -DCMAKE_INSTALL_PREFIX=$serverPath/mariadb \ + -DMYSQL_DATADIR=$serverPath/mariadb/data/ \ + -DMYSQL_USER=mysql \ + -DMYSQL_UNIX_ADDR=$serverPath/mariadb/mysql.sock \ + -DWITH_MYISAM_STORAGE_ENGINE=1 \ + -DWITH_INNOBASE_STORAGE_ENGINE=1 \ + -DWITH_MEMORY_STORAGE_ENGINE=1 \ + -DENABLED_LOCAL_INFILE=1 \ + -DWITH_PARTITION_STORAGE_ENGINE=1 \ + -DEXTRA_CHARSETS=all \ + -DDEFAULT_CHARSET=utf8mb4 \ + -DDEFAULT_COLLATION=utf8mb4_general_ci \ + -DCMAKE_C_COMPILER=/usr/bin/gcc \ + -DCMAKE_CXX_COMPILER=/usr/bin/g++ + make -j${cpuCore} && make install && make clean + + if [ -d $serverPath/mariadb ];then + echo '10.11' > $serverPath/mariadb/version.pl + echo '安装完成' > $install_tmp + else + echo '安装失败' > $install_tmp + echo 'install fail'>&2 + exit 1 + fi + fi + + rm -rf ${mariadbDir}/mariadb-${MY_VER} + rm -rf ${mariadbDir}/mariadb-${MY_VER}.tar.gz +} + +Uninstall_app() +{ + rm -rf $serverPath/mariadb + echo '卸载完成' > $install_tmp +} + +action=$1 +if [ "${1}" == 'install' ];then + Install_app +else + Uninstall_app +fi diff --git a/plugins/redis/index.py b/plugins/redis/index.py index 62a8686c24..334ab4d195 100755 --- a/plugins/redis/index.py +++ b/plugins/redis/index.py @@ -214,7 +214,8 @@ def initdStatus(): if mw.isAppleSystem(): return "Apple Computer does not support" - shell_cmd = 'systemctl status redis | grep loaded | grep "enabled;"' + shell_cmd = 'systemctl status ' + \ + getPluginName() + ' | grep loaded | grep "enabled;"' data = mw.execShell(shell_cmd) if data[0] == '': return 'fail' @@ -225,7 +226,7 @@ def initdInstall(): if mw.isAppleSystem(): return "Apple Computer does not support" - mw.execShell('systemctl enable redis') + mw.execShell('systemctl enable ' + getPluginName()) return 'ok' @@ -233,7 +234,7 @@ def initdUinstall(): if mw.isAppleSystem(): return "Apple Computer does not support" - mw.execShell('systemctl disable redis') + mw.execShell('systemctl disable ' + getPluginName()) return 'ok' diff --git a/plugins/redis/js/redis.js b/plugins/redis/js/redis.js index 047328f153..01da0647c4 100755 --- a/plugins/redis/js/redis.js +++ b/plugins/redis/js/redis.js @@ -1,14 +1,3 @@ - -function str2Obj(str){ - var data = {}; - kv = str.split('&'); - for(i in kv){ - v = kv[i].split('='); - data[v[0]] = v[1]; - } - return data; -} - function redisPost(method, version, args,callback){ var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); @@ -18,7 +7,7 @@ function redisPost(method, version, args,callback){ req_data['version'] = version; if (typeof(args) == 'string'){ - req_data['args'] = JSON.stringify(str2Obj(args)); + req_data['args'] = JSON.stringify(toArrayObject(args)); } else { req_data['args'] = JSON.stringify(args); } @@ -46,7 +35,7 @@ function redisPostCallbak(method, version, args,callback){ args['version'] = version; if (typeof(args) == 'string'){ - req_data['args'] = JSON.stringify(str2Obj(args)); + req_data['args'] = JSON.stringify(toArrayObject(args)); } else { req_data['args'] = JSON.stringify(args); } diff --git a/plugins/supervisor/index.html b/plugins/supervisor/index.html index 798116a54d..f58df6ecc6 100755 --- a/plugins/supervisor/index.html +++ b/plugins/supervisor/index.html @@ -23,7 +23,7 @@ .code span {display: block;margin-left: 15px;margin-bottom: 0;} \ No newline at end of file diff --git a/plugins/tgbot/index.py b/plugins/tgbot/index.py new file mode 100755 index 0000000000..cd040d32f2 --- /dev/null +++ b/plugins/tgbot/index.py @@ -0,0 +1,245 @@ +# coding:utf-8 + +import sys +import io +import os +import time +import re +import json +import base64 + +sys.path.append(os.getcwd() + "/class/core") +import mw + +app_debug = False +if mw.isAppleSystem(): + app_debug = True + + +def getPluginName(): + return 'tgbot' + + +def getPluginDir(): + return mw.getPluginDir() + '/' + getPluginName() + + +def getServerDir(): + return mw.getServerDir() + '/' + getPluginName() + + +def getInitDFile(): + if app_debug: + return '/tmp/' + getPluginName() + return '/etc/init.d/' + getPluginName() + + +def getConfigData(): + cfg_path = getServerDir() + "/data.cfg" + if not os.path.exists(cfg_path): + mw.writeFile(cfg_path, '{}') + t = mw.readFile(cfg_path) + return json.loads(t) + + +def writeConf(data): + cfg_path = getServerDir() + "/data.cfg" + mw.writeFile(cfg_path, json.dumps(data)) + return True + + +def getInitDTpl(): + path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl" + return path + + +def getArgs(): + args = sys.argv[2:] + tmp = {} + args_len = len(args) + if args_len == 1: + t = args[0].strip('{').strip('}') + if t.strip() == '': + tmp = [] + else: + t = t.split(':') + tmp[t[0]] = t[1] + tmp[t[0]] = t[1] + elif args_len > 1: + for i in range(len(args)): + t = args[i].split(':') + tmp[t[0]] = t[1] + return tmp + + +def checkArgs(data, ck=[]): + for i in range(len(ck)): + if not ck[i] in data: + return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!')) + return (True, mw.returnJson(True, 'ok')) + + +def status(): + data = mw.execShell( + "ps -ef|grep tgbot |grep -v grep | grep -v mdserver-web | awk '{print $2}'") + if data[0] == '': + return 'stop' + return 'start' + + +def initDreplace(): + + file_tpl = getInitDTpl() + service_path = mw.getServerDir() + app_path = service_path + '/' + getPluginName() + + initD_path = getServerDir() + '/init.d' + if not os.path.exists(initD_path): + os.mkdir(initD_path) + file_bin = initD_path + '/' + getPluginName() + + # initd replace + # if not os.path.exists(file_bin): + content = mw.readFile(file_tpl) + content = content.replace('{$SERVER_PATH}', service_path + '/mdserver-web') + content = content.replace('{$APP_PATH}', app_path) + + mw.writeFile(file_bin, content) + mw.execShell('chmod +x ' + file_bin) + + # systemd + systemDir = mw.systemdCfgDir() + systemService = systemDir + '/tgbot.service' + systemServiceTpl = getPluginDir() + '/init.d/tgbot.service.tpl' + if os.path.exists(systemDir) and not os.path.exists(systemService): + service_path = mw.getServerDir() + se_content = mw.readFile(systemServiceTpl) + se_content = se_content.replace('{$APP_PATH}', app_path) + mw.writeFile(systemService, se_content) + mw.execShell('systemctl daemon-reload') + + return file_bin + + +def tbOp(method): + file = initDreplace() + + if not mw.isAppleSystem(): + data = mw.execShell('systemctl ' + method + ' ' + getPluginName()) + if data[1] == '': + return 'ok' + return data[1] + + data = mw.execShell(file + ' ' + method) + print(data) + # if data[1] == '': + # return 'ok' + return 'ok' + + +def start(): + return tbOp('start') + + +def stop(): + return tbOp('stop') + + +def restart(): + status = tbOp('restart') + return status + + +def reload(): + + tgbot_tpl = getPluginDir() + '/startup/tgbot.py' + tgbot_dst = getServerDir() + '/tgbot.py' + + content = mw.readFile(tgbot_tpl) + mw.writeFile(tgbot_dst, content) + + return tbOp('reload') + + +def initdStatus(): + if mw.isAppleSystem(): + return "Apple Computer does not support" + + shell_cmd = 'systemctl status ' + \ + getPluginName() + ' | grep loaded | grep "enabled;"' + data = mw.execShell(shell_cmd) + if data[0] == '': + return 'fail' + return 'ok' + + +def initdInstall(): + if mw.isAppleSystem(): + return "Apple Computer does not support" + + mw.execShell('systemctl enable ' + getPluginName()) + return 'ok' + + +def initdUinstall(): + if mw.isAppleSystem(): + return "Apple Computer does not support" + + mw.execShell('systemctl disable ' + getPluginName()) + return 'ok' + + +def getBotConf(): + data = getConfigData() + if 'bot' in data: + + return mw.returnJson(True, 'ok', data['bot']) + return mw.returnJson(False, 'ok', {}) + + +def setBotConf(): + args = getArgs() + data_args = checkArgs(args, ['app_token']) + if not data_args[0]: + return data_args[1] + + data = getConfigData() + args['app_token'] = base64.b64decode(args['app_token']).decode('ascii') + data['bot'] = args + writeConf(data) + + return mw.returnJson(True, '保存成功!', []) + + +def runLog(): + p = getServerDir() + '/task.log' + return p + + +if __name__ == "__main__": + func = sys.argv[1] + if func == 'status': + print(status()) + elif func == 'start': + print(start()) + elif func == 'stop': + print(stop()) + elif func == 'restart': + print(restart()) + elif func == 'reload': + print(reload()) + elif func == 'initd_status': + print(initdStatus()) + elif func == 'initd_install': + print(initdInstall()) + elif func == 'initd_uninstall': + print(initdUinstall()) + elif func == 'get_bot_conf': + print(getBotConf()) + elif func == 'set_bot_conf': + print(setBotConf()) + elif func == 'run_log': + print(runLog()) + + else: + print('error') diff --git a/plugins/tgbot/info.json b/plugins/tgbot/info.json new file mode 100755 index 0000000000..266bbfb6be --- /dev/null +++ b/plugins/tgbot/info.json @@ -0,0 +1,17 @@ +{ + "sort": 7, + "ps": "[DEV]简单Telegram机器人", + "name": "tgbot", + "title": "tgbot", + "shell": "install.sh", + "versions":["0.1"], + "tip": "soft", + "checks": "server/tgbot", + "path": "server/tgbot", + "display": 1, + "author": "Zend", + "date": "2023-03-06", + "home": "https://core.telegram.org/bots/api", + "type": 0, + "pid": "5" +} \ No newline at end of file diff --git a/plugins/tgbot/init.d/tgbot.service.tpl b/plugins/tgbot/init.d/tgbot.service.tpl new file mode 100644 index 0000000000..04ee1b9526 --- /dev/null +++ b/plugins/tgbot/init.d/tgbot.service.tpl @@ -0,0 +1,14 @@ +[Unit] +Description=Tgbot Service +After=network.target + +[Service] +Type=forking +ExecStart={$APP_PATH}/init.d/tgbot start +ExecStop={$APP_PATH}/init.d/tgbot stop +ExecReload={$APP_PATH}/init.d/tgbot reload +KillMode=process +Restart=on-failure + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/plugins/tgbot/init.d/tgbot.tpl b/plugins/tgbot/init.d/tgbot.tpl new file mode 100644 index 0000000000..a7c2aaea5d --- /dev/null +++ b/plugins/tgbot/init.d/tgbot.tpl @@ -0,0 +1,85 @@ +#!/bin/sh +# chkconfig: 2345 55 25 +# description: Tgbot Service + +### BEGIN INIT INFO +# Provides: Tgbot +# Required-Start: $all +# Required-Stop: $all +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: starts Tgbot +# Description: starts the MDW-Web +### END INIT INFO + +# Simple Tgbot init.d script conceived to work on Linux systems +# as it does use of the /proc filesystem. + +PATH=/usr/local/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export LANG=en_US.UTF-8 + + +mw_path={$SERVER_PATH} +PATH=$PATH:$mw_path/bin + +if [ -f $mw_path/bin/activate ];then + source $mw_path/bin/activate +fi + +tg_start(){ + + isStart=`ps -ef|grep 'tgbot.py' |grep -v grep | awk '{print $2}'` + if [ "$isStart" == '' ];then + echo -e "starting tgbot... \c" + cd $mw_path + python3 {$APP_PATH}/tgbot.py >> {$APP_PATH}/task.log & + isStart="" + while [[ "$isStart" == "" ]]; + do + echo -e ".\c" + sleep 0.5 + isStart=`ps -ef|grep 'tgbot.py' |grep -v grep | awk '{print $2}'` + let n+=1 + if [ $n -gt 20 ];then + break; + fi + done + if [ "$isStart" == '' ];then + echo -e "\033[31mfailed\033[0m" + echo -e "\033[31mError: tgbot service startup failed.\033[0m" + return; + fi + echo -e "\033[32mdone\033[0m" + else + echo "starting tgbot...(pid $(echo $isStart)) already running" + fi +} + + +tg_stop(){ + echo -e "stopping tgbot ... \c"; + arr=`ps aux|grep 'tgbot.py'|grep -v grep|awk '{print $2}'` + for p in ${arr[@]} + do + kill -9 $p > /dev/null 2>&1 + done + echo -e "\033[32mdone\033[0m" +} + +case "$1" in + start) + tg_start + ;; + stop) + tg_stop + ;; + restart|reload) + tg_stop + sleep 0.3 + tg_start + ;; + *) + echo "Please use start or stop as first argument" + ;; +esac + diff --git a/plugins/tgbot/install.sh b/plugins/tgbot/install.sh new file mode 100755 index 0000000000..cec3fab831 --- /dev/null +++ b/plugins/tgbot/install.sh @@ -0,0 +1,59 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") + + +install_tmp=${rootPath}/tmp/mw_install.pl + +VERSION=$2 + +# pip3 install ccxt +if [ -f ${rootPath}/bin/activate ];then + source ${rootPath}/bin/activate +fi + +pip3 install pyTelegramBotAPI +pip3 install telebot + +Install_App() +{ + echo '正在安装脚本文件...' > $install_tmp + mkdir -p $serverPath/source + mkdir -p $serverPath/tgbot + echo "${VERSION}" > $serverPath/tgbot/version.pl + + cp -rf ${rootPath}/plugins/tgbot/startup/* $serverPath/tgbot + + cd ${rootPath} && python3 ${rootPath}/plugins/tgbot/index.py start + cd ${rootPath} && python3 ${rootPath}/plugins/tgbot/index.py initd_install + echo '安装完成' > $install_tmp +} + +Uninstall_App() +{ + if [ -f /usr/lib/systemd/system/tgbot.service ];then + systemctl stop tgbot + systemctl disable tgbot + rm -rf /usr/lib/systemd/system/tgbot.service + systemctl daemon-reload + fi + + if [ -f $serverPath/tgbot/initd/tgbot ];then + $serverPath/tgbot/initd/tgbot stop + fi + + rm -rf $serverPath/tgbot + echo "Uninstall_redis" > $install_tmp +} + +action=$1 +if [ "${1}" == 'install' ];then + Install_App +else + Uninstall_App +fi diff --git a/plugins/tgbot/js/tgbot.js b/plugins/tgbot/js/tgbot.js new file mode 100755 index 0000000000..04c1643ed9 --- /dev/null +++ b/plugins/tgbot/js/tgbot.js @@ -0,0 +1,78 @@ +function appPost(method, args,callback){ + var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); + + var req_data = {}; + req_data['name'] = 'tgbot'; + req_data['func'] = method; + + if (typeof(args) == 'string'){ + req_data['args'] = JSON.stringify(toArrayObject(args)); + } else { + req_data['args'] = JSON.stringify(args); + } + + $.post('/plugins/run', req_data, function(data) { + layer.close(loadT); + if(typeof(callback) == 'function'){ + callback(data); + } + },'json'); +} + +function appPostCallbak(method, args,callback){ + var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); + + var req_data = {}; + req_data['name'] = 'tgbot'; + req_data['func'] = method; + + if (typeof(args) == 'string'){ + req_data['args'] = JSON.stringify(toArrayObject(args)); + } else { + req_data['args'] = JSON.stringify(args); + } + + $.post('/plugins/callback', req_data, function(data) { + layer.close(loadT); + if (!data.status){ + layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']}); + return; + } + + if(typeof(callback) == 'function'){ + callback(data); + } + },'json'); +} + +function botConf(){ + appPost('get_bot_conf','',function(data){ + var rdata = $.parseJSON(data.data); + var app_token = 'app_token'; + if(rdata['status']){ + db_data = rdata['data']; + app_token = db_data['app_token']; + + } + + var mlist = ''; + mlist += '

app_token必填写

' + var option = '\ +
\ + ' + mlist + '\ +
\ + \ +
\ +
'; + $(".soft-man-con").html(option); + }); +} + +function submitBotConf(){ + var pull_data = {}; + pull_data['app_token'] = base64_encode($('input[name="app_token"]').val()); + appPost('set_bot_conf',pull_data,function(data){ + var rdata = $.parseJSON(data.data); + layer.msg(rdata['msg'],{icon:rdata['status']?1:2,time:2000,shade: [0.3, '#000']}); + }); +} diff --git a/plugins/tgbot/startup/tgbot.py b/plugins/tgbot/startup/tgbot.py new file mode 100644 index 0000000000..c763e8c1c1 --- /dev/null +++ b/plugins/tgbot/startup/tgbot.py @@ -0,0 +1,81 @@ + +# coding:utf-8 + +import sys +import io +import os +import time +import re +import json +import base64 + +sys.path.append(os.getcwd() + "/class/core") +import mw + +import telebot + + +def getPluginName(): + return 'tgbot' + + +def getPluginDir(): + return mw.getPluginDir() + '/' + getPluginName() + + +def getServerDir(): + return mw.getServerDir() + '/' + getPluginName() + + +def getConfigData(): + cfg_path = getServerDir() + "/data.cfg" + if not os.path.exists(cfg_path): + mw.writeFile(cfg_path, '{}') + t = mw.readFile(cfg_path) + return json.loads(t) + + +def writeConf(data): + cfg_path = getServerDir() + "/data.cfg" + mw.writeFile(cfg_path, json.dumps(data)) + return True + + +def writeLog(log_str): + if __name__ == "__main__": + print(log_str) + + log_file = getServerDir() + '/task.log' + mw.writeFileLog(log_str, log_file, limit_size=5 * 1024) + return True + +# start tgbot +cfg = getConfigData() +while True: + cfg = getConfigData() + if 'bot' in cfg and 'app_token' in cfg['bot']: + if cfg['bot']['app_token'] != '' and cfg['bot']['app_token'] != 'app_token': + break + writeLog('等待输入配置,填写app_token') + time.sleep(3) + +bot = telebot.TeleBot(cfg['bot']['app_token']) + + +# from telebot.async_telebot import AsyncTeleBot +# import asyncio +# bot = AsyncTeleBot(cfg['bot']['app_token']) + + +@bot.message_handler(commands=['start', 'help']) +def hanle_start_help(message): + bot.reply_to(message, "hello world") + + +@bot.message_handler(commands=['chat_id']) +def hanle_get_chat_id(message): + bot.reply_to(message, message.chat.id) + +writeLog('启动成功') +bot.polling() +# asyncio.run(bot.polling()) diff --git a/requirements.txt b/requirements.txt index deb9e8f357..211761a52f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -29,3 +29,5 @@ flask-caching>=1.10.1 bcrypt==3.1.3 PyMySQL==1.0.2 whitenoise==5.3.0 +pyTelegramBotAPI +telebot diff --git a/route/static/app/config.js b/route/static/app/config.js index 83a8a8c679..949234cf09 100755 --- a/route/static/app/config.js +++ b/route/static/app/config.js @@ -354,6 +354,78 @@ function setPanelSSL(){ }); } +function setNotifyApi(tag, obj){ + var enable = $(obj).prop("checked"); + console.log(tag,obj,enable); + $.post('/config/set_notify_enable', {'tag':tag, 'enable':enable},function(rdata){ + showMsg(rdata.msg, function(){ + if (rdata.status){} + } ,{icon:rdata.status?1:2}, 1000); + },'json'); +} + +function getTgbot(){ + var loadT = layer.msg('正在获取TgBot信息...',{icon:16,time:0,shade: [0.3, '#000']}); + $.post('/config/get_notify',{},function(data){ + layer.close(loadT); + + var app_token = ''; + var chat_id = ''; + + if (data.status){ + if (typeof(data['data']['tgbot']) !='undefined'){ + app_token = data['data']['tgbot']['data']['app_token']; + chat_id = data['data']['tgbot']['data']['chat_id']; + } + } + + layer.open({ + type: 1, + area: "500px", + title: '配置TgBot配置', + closeBtn: 1, + shift: 5, + btn:["确定","关闭","验证"], + shadeClose: false, + content: "
\ +
\ + APP_TOKEN\ +
\ +
\ +
\ + CHAT_ID\ +
\ +
\ +
", + yes:function(index){ + var pdata = {}; + pdata['app_token'] = $('input[name="app_token"]').val(); + pdata['chat_id'] = $('input[name="chat_id"]').val(); + $.post('/config/set_notify',{'tag':'tgbot', 'data':JSON.stringify(pdata)},function(rdata){ + showMsg(rdata.msg, function(){ + if (rdata.status){ + layer.close(index); + } + },{icon:rdata.status?1:2},2000); + }); + }, + + btn3:function(index){ + var pdata = {}; + pdata['app_token'] = $('input[name="app_token"]').val(); + pdata['chat_id'] = $('input[name="chat_id"]').val(); + $.post('/config/set_notify_test',{'tag':'tgbot', 'data':JSON.stringify(pdata)},function(rdata){ + showMsg(rdata.msg, function(){ + if (rdata.status){ + layer.close(index); + } + },{icon:rdata.status?1:2},2000); + }); + return false; + } + }); + }); +} function getPanelSSL(){ var loadT = layer.msg('正在获取证书信息...',{icon:16,time:0,shade: [0.3, '#000']}); diff --git a/route/static/app/public.js b/route/static/app/public.js index 5f65bcce8c..305161b976 100755 --- a/route/static/app/public.js +++ b/route/static/app/public.js @@ -515,29 +515,24 @@ function openPath(a) { window.location.href = "/files/" } -function onlineEditFile(k, f) { +function onlineEditFile(k, f, callback) { if(k != 0) { var l = $("#PathPlace input").val(); var h = encodeURIComponent($("#textBody").val()); var a = $("select[name=encoding]").val(); - var loadT = layer.msg(lan.bt.save_file, { - icon: 16, - time: 0 - }); - $.post("/files/save_body", "data=" + h + "&path=" + encodeURIComponent(f) + "&encoding=" + a, function(m) { + var loadT = layer.msg('正在保存,请稍候...', {icon: 16,time: 0}); + $.post("/files/save_body", "data=" + h + "&path=" + encodeURIComponent(f) + "&encoding=" + a, function(data) { if(k == 1) { layer.close(loadT); } - layer.msg(m.msg, { - icon: m.status ? 1 : 2 - }); + layer.msg(data.msg, {icon: data.status ? 1 : 2}); + if (data.status && typeof(callback) == 'function'){ + callback(k, f); + } },'json'); return } - var e = layer.msg(lan.bt.read_file, { - icon: 16, - time: 0 - }); + var e = layer.msg('正在读取文件,请稍候...', {icon: 16,time: 0}); var g = f.split("."); var b = g[g.length - 1]; var d; @@ -595,15 +590,12 @@ function onlineEditFile(k, f) { default: var j = { name: "htmlmixed", - scriptTypes: [{ - matches: /\/x-handlebars-template|\/x-mustache/i, - mode: null - }, { - matches: /(text|application)\/(x-)?vb(a|script)/i, - mode: "vbscript" - }] + scriptTypes: [ + {matches: /\/x-handlebars-template|\/x-mustache/i,mode: null}, + {matches: /(text|application)\/(x-)?vb(a|script)/i,mode: "vbscript"} + ] }; - d = j + d = j; } $.post("/files/get_body", "path=" + encodeURIComponent(f), function(s) { if(s.status === false){ @@ -619,55 +611,57 @@ function onlineEditFile(k, f) { m = s.data.encoding == u[p] ? "selected" : ""; n += '"; } + var code_mirror = null; var r = layer.open({ type: 1, shift: 5, closeBtn: 1, area: ["90%", "90%"], - title: lan.bt.edit_title+"[" + f + "]", - content: '
\ + btn:['保存','关闭'], + title: "在线编辑[" + f + "]", + content: '\
\ -

' + lan.bt.edit_ps + '\ +

提示:Ctrl+F 搜索关键字,Ctrl+G 查找下一个,Ctrl+S 保存,Ctrl+Shift+R 查找替换!\ \

\ \ -
'; + + + var h = parseInt($('.bt-w-menu').css('height')) - 40; + var ebody = ''; $(".soft-man-con").html(ebody); var ob = document.getElementById('info_log'); ob.scrollTop = ob.scrollHeight; diff --git a/route/templates/default/config.html b/route/templates/default/config.html index 7429ff58a8..b3e67d46ef 100755 --- a/route/templates/default/config.html +++ b/route/templates/default/config.html @@ -102,7 +102,6 @@

安全

-

@@ -148,6 +147,21 @@ 为非管理员临时提供面板访问权限

+ + +

通知

+ +
+ +

+ TG机器人通知 + + + + Telegram Bot机器人通知【国内可能无法使用 +

+ +
diff --git a/task.py b/task.py index 65ad933c7c..651a0dcaec 100755 --- a/task.py +++ b/task.py @@ -256,6 +256,13 @@ def systemTask(): tmp = {} # 取当前CPU Io tmp['used'] = psutil.cpu_percent(interval=1) + if tmp['used'] > 80: + panel_title = mw.getConfig('title') + ip = mw.getHostAddr() + now_time = mw.getDateFromNow() + msg = now_time + '|节点[' + panel_title + ':' + ip + \ + ']处于高负载[' + str(tmp['used']) + '],请排查原因!' + mw.notifyMessage(msg, '面板监控', 600) if not cpuInfo: tmp['mem'] = sm.getMemUsed()