Compare commits

...
108 Commits
Author SHA1 Message Date
Mr Chen b1a8c5c02f Merge pull request #228 from midoks/dev
0.10.2
2022-10-31 02:59:12 +08:00
midoks 3ed0ceed70 Update crontab.js 2022-10-31 02:57:58 +08:00
midoks 6948d503c0 Update crontab.js 2022-10-31 02:49:22 +08:00
midoks 39bca95e62 Update crontab.js 2022-10-31 02:39:12 +08:00
midoks 063bc092e6 0.10.2
* 修复上次更新的UI问题。
* `FTP存储空间` 备份数据库修复。
* 修复计划任务执行Python脚本的编码问题。
* 添加,修改计划任务参数强校验。
2022-10-31 02:30:55 +08:00
midoks aa97982f6f Update crontab_api.py 2022-10-31 00:12:45 +08:00
midoks af73ce7af8 优化 2022-10-30 23:34:18 +08:00
midoks e46ed828d9 Update index.py 2022-10-30 23:24:54 +08:00
midoks f5ec3bba3d ftp备份数据库优化 2022-10-30 22:34:18 +08:00
midoks 2f7ec60e89 Update index.html 2022-10-30 21:16:41 +08:00
midoks 5df4b9ee57 Update mw.py 2022-10-30 18:04:42 +08:00
midoks 71dbfbf5d8 登录界面统一显示设置面板名 2022-10-30 18:03:31 +08:00
Mr Chen 93fcdd2e27 Merge pull request #227 from midoks/dev
OP防火墙安装校验
2022-10-30 13:22:53 +08:00
midoks a725220577 Update info.json 2022-10-30 13:17:09 +08:00
midoks 783c4ebc60 Update firewall_api.py 2022-10-30 11:56:37 +08:00
midoks a06db0e8c3 Update firewall_api.py 2022-10-30 11:46:49 +08:00
Mr Chen d7c4dcda66 Merge pull request #226 from midoks/dev
小问题优化
2022-10-30 11:28:50 +08:00
midoks 2866e1a602 优化 2022-10-30 10:27:58 +08:00
midoks 77e4ee1053 debian首先同步时间再安装 2022-10-30 00:27:10 +08:00
Mr Chen 7589c14f81 Merge pull request #224 from midoks/dev
0.10.1
2022-10-29 23:23:23 +08:00
midoks 4344d774c1 Update config_api.py 2022-10-29 23:21:26 +08:00
midoks 4859792096 Update README.md 2022-10-29 22:00:36 +08:00
midoks e55c40e973 优化文件多选的bug 2022-10-29 21:58:29 +08:00
midoks 103beb62a4 Update files_api.py 2022-10-29 20:59:00 +08:00
midoks 76a34578ad Update index.py 2022-10-29 20:36:37 +08:00
midoks f82fdbf939 Update README.md 2022-10-29 20:19:18 +08:00
midoks 4eaf9eb36c phpmyadmin+路径随机 2022-10-29 20:12:01 +08:00
midoks 9c9f9fc42c 初始化卸载脚本 2022-10-29 16:31:59 +08:00
midoks 8301ad6f62 Update ubuntu.sh 2022-10-29 16:19:53 +08:00
midoks d42fb48749 Update mw.tpl 2022-10-29 16:13:23 +08:00
midoks fa49a3dbeb Update README.md 2022-10-29 15:24:28 +08:00
midoks dd1147c891 Update debian.sh 2022-10-29 14:39:06 +08:00
midoks 67b015a50b up 2022-10-29 13:46:16 +08:00
midoks 57831168a3 debian 9 兼容 2022-10-29 13:35:12 +08:00
midoks f6f7cabddc Update debian.sh 2022-10-29 12:18:07 +08:00
midoks dfec241c3f Update README.md 2022-10-29 11:54:58 +08:00
midoks 8a78dff571 Update ubuntu.sh 2022-10-29 11:38:27 +08:00
midoks a9486d4fbe Update ubuntu.sh 2022-10-29 11:38:08 +08:00
midoks 78d3a091cd openresty增加性能调整功能 2022-10-29 02:46:27 +08:00
midoks 314005e478 Update openresty.js 2022-10-28 19:06:27 +08:00
midoks 55fa911485 Update mysql.js 2022-10-28 17:52:43 +08:00
midoks a72046462c Update index.py 2022-10-28 17:49:30 +08:00
midoks 0b8fe15b65 Update index.py 2022-10-28 16:47:53 +08:00
midoks 34f5d12ad9 mysql 部分操作优化 2022-10-28 14:20:12 +08:00
midoks e88544d290 Update mysql.js 2022-10-28 14:08:56 +08:00
midoks a211746de7 mysql导入功能 2022-10-28 14:08:50 +08:00
midoks c417b02844 Update install.sh 2022-10-27 23:44:40 +08:00
Mr Chen 034aaedf48 Merge pull request #223 from midoks/dev
小优化
2022-10-27 15:51:03 +08:00
midoks af0ec79072 统计一下用户量,让我的开发更有激情 2022-10-27 15:44:42 +08:00
midoks 195ccf409e openresty 默认加入ipv6支持 2022-10-27 14:38:44 +08:00
Mr Chen 7cec26b8a3 Merge pull request #222 from midoks/dev
优化logo
2022-10-26 23:47:01 +08:00
midoks aacb816b11 优化logo 2022-10-26 23:40:48 +08:00
Mr Chen 411798f4bf Merge pull request #221 from midoks/dev
0.10.0
2022-10-26 23:25:55 +08:00
midoks 2179db5f8b 0.10.0 2022-10-26 23:24:33 +08:00
midoks dcced8c0fb Update crontab_api.py 2022-10-26 22:08:29 +08:00
midoks 3e0e42d8a1 优化 2022-10-26 19:41:51 +08:00
midoks e5355d3e2f Update ftp_client.py 2022-10-26 18:56:57 +08:00
midoks 0a22e25611 Update ftp_client.py 2022-10-26 18:49:05 +08:00
midoks 5f7a3e5bf6 Update index.py 2022-10-26 18:43:48 +08:00
midoks 3518548c61 backup ftp 80% 2022-10-26 17:47:40 +08:00
midoks 08c4b0845d Update crontab_api.py 2022-10-26 11:34:52 +08:00
midoks 8f5fae8e79 Update index.py 2022-10-26 02:50:11 +08:00
midoks a151db1c30 Update install.sh 2022-10-26 02:48:08 +08:00
midoks 628c69ed22 Update index.html 2022-10-26 02:48:05 +08:00
midoks 13bcbc0f3e Update soft.js 2022-10-26 02:47:57 +08:00
midoks c94838462c backup_ftp 50% 2022-10-26 02:42:10 +08:00
midoks a147ef6962 Update centos.sh 2022-10-25 22:06:32 +08:00
midoks 4ccda15933 Update ftp.js 2022-10-25 14:11:49 +08:00
midoks 01074c743d Update pure-ftpd.conf 2022-10-25 14:10:46 +08:00
midoks 0871fa1763 up 2022-10-25 13:51:24 +08:00
midoks aade0ccd98 Update install.sh 2022-10-25 12:28:57 +08:00
midoks 4aed2f91de Update ftp.js 2022-10-25 12:28:49 +08:00
midoks d49dd9f2ac Update install.sh 2022-10-25 03:37:48 +08:00
midoks cff29194f4 Revert "安装优化"
This reverts commit ecbfb2500f.
2022-10-25 03:34:00 +08:00
midoks ecbfb2500f 安装优化 2022-10-25 03:20:35 +08:00
midoks 9c43170d4b Update install.sh 2022-10-25 03:11:06 +08:00
midoks 1239a2807f up 2022-10-25 02:53:19 +08:00
midoks 1c0a130b49 up 2022-10-25 01:43:29 +08:00
midoks ffb1eb34e0 waf 0.2.3 2022-10-25 00:41:27 +08:00
midoks 82874d1f7a Update url_white.json 2022-10-24 23:42:26 +08:00
midoks 4e05f29fcd OP防火墙-0.2.3 2022-10-24 23:12:45 +08:00
midoks d5f3b0d442 初始安装 ipv6 优化 2022-10-24 21:55:44 +08:00
midoks 81eec04037 Update mw.tpl 2022-10-24 21:44:34 +08:00
midoks 5a5dc43e17 Update mw.tpl 2022-10-24 21:38:10 +08:00
midoks b4eb2b3192 Update mw.tpl 2022-10-24 21:20:04 +08:00
midoks 2d1c070e1b Update mw.tpl 2022-10-24 21:16:17 +08:00
midoks 81364fdfa3 Update common.lua 2022-10-24 21:12:41 +08:00
midoks 9d0d88d465 Update init.lua 2022-10-24 20:56:18 +08:00
midoks 7e2eef8bbf Update init.lua 2022-10-24 20:54:41 +08:00
midoks 8d1315e5be Update init.lua 2022-10-24 20:39:07 +08:00
midoks fe6a51540e Update init.lua 2022-10-24 20:38:24 +08:00
midoks 6e35683505 Update init.lua 2022-10-24 20:35:39 +08:00
midoks 713c0d1a3c up 2022-10-24 20:18:34 +08:00
midoks 8323e0ec73 up 2022-10-24 20:06:38 +08:00
midoks 6eae94658f up 2022-10-24 19:55:09 +08:00
midoks 07d173d2cf up 2022-10-24 19:11:48 +08:00
midoks 23f2b9efac Update index.py 2022-10-24 15:14:14 +08:00
midoks 9b2ab9276c Update common.lua 2022-10-24 14:53:54 +08:00
midoks 7903e1a286 Update common.lua 2022-10-24 14:53:39 +08:00
midoks 662e05511f Update common.lua 2022-10-24 14:51:58 +08:00
midoks 1f8785acbc Update init.lua 2022-10-24 14:49:20 +08:00
midoks 3eb87cf953 Update init.lua 2022-10-24 11:57:46 +08:00
midoks 124b24885e Update op_waf.js 2022-10-24 11:23:35 +08:00
midoks 2196983455 Update ip_white.json 2022-10-24 11:22:39 +08:00
midoks 80a67c4f58 Update webstats_log.lua 2022-10-24 11:10:23 +08:00
midoks ff06f7ef91 Update webstats_common.lua 2022-10-24 11:06:35 +08:00
midoks b16506c8bc #211 优化
phpmyadmin安全设置处修改端口保存后当前用修改后的端口能打开,一旦停止phpmyadmin后,再重启phpmyadmin端口又变为888,需要手工再次改变
2022-10-23 14:07:18 +08:00
midoks 2d3801dcc0 up 2022-10-23 13:49:08 +08:00
70 changed files with 2225 additions and 403 deletions
+17 -4
View File
@@ -92,11 +92,12 @@ docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:
```
### 版本更新 0.9.14
### 版本更新 0.10.2
* 优化网站统计插件,提搞速度和并发数。
* mysql8安装地址更新。
* OP防火墙优化。
* 修复上次更新的UI问题。
* `FTP存储空间` 备份数据库修复。
* 修复计划任务执行Python脚本的编码问题。
* 添加,修改计划任务参数强校验。
### JSDelivr安装地址
@@ -112,6 +113,12 @@ curl -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/instal
curl -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/update.sh | bash
```
- 卸载脚本
```
curl -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/uninstall.sh | bash
```
### 备用地址
- 初始安装
@@ -127,6 +134,12 @@ curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/update.sh | bash
```
- 卸载脚本
```
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/uninstall.sh | bash
```
### 通用软件安装[命令行安装]
+1 -1
View File
@@ -85,7 +85,7 @@ def initInitD():
mw.execShell('mkdir -p /etc/init.d')
# initd
if os.path.exists("/etc/init.d"):
if os.path.exists('/etc/init.d'):
initd_bin = '/etc/init.d/mw'
if not os.path.exists(initd_bin):
import shutil
+1 -1
View File
@@ -15,7 +15,7 @@ from flask import request
class config_api:
__version = '0.9.14'
__version = '0.10.2'
def __init__(self):
pass
+82 -5
View File
@@ -68,8 +68,8 @@ class crontab_api:
_list[i]['where_hour']), str(_list[i]['where_minute'])))
data.append(tmp)
_ret = {}
_ret['data'] = data
rdata = {}
rdata['data'] = data
count = mw.M('crontab').where('', ()).count()
_page = {}
@@ -78,9 +78,17 @@ class crontab_api:
_page['row'] = psize
_page['tojs'] = "getCronData"
_ret['list'] = mw.getPage(_page)
_ret['p'] = p
return mw.getJson(_ret)
rdata['list'] = mw.getPage(_page)
rdata['p'] = p
# backup hock
bh_file = mw.getPanelDataDir() + "/hook_backup.json"
if os.path.exists(bh_file):
hb_data = mw.readFile(bh_file)
hb_data = json.loads(hb_data)
rdata['backup_hook'] = hb_data
return mw.getJson(rdata)
# 设置计划任务状态
def setCronStatusApi(self):
@@ -107,6 +115,56 @@ class crontab_api:
'id=?', (sid,)).field(self.field).find()
return mw.getJson(data)
# 参数校验
def cronCheck(self, params):
if params['stype'] == 'site' or params['stype'] == 'database' or params['stype'] == 'logs':
if params['save'] == '':
return False, '保留份数不能为空!'
if params['type'] == 'day':
if params['hour'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'day-n':
if params['where1'] == '':
return False, '天不能为空!'
if params['hour'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'hour':
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'hour-n':
if params['hour'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'minute-n':
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'week':
if params['hour'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'month':
if params['where1'] == '':
return False, '日不能为空!'
if params['hour'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
return True, 'OK'
def modifyCrondApi(self):
sid = request.form.get('id', '')
iname = request.form.get('name', '')
@@ -139,6 +197,11 @@ class crontab_api:
'sbody': sbody,
'urladdress': urladdress,
}
is_check_pass, msg = self.cronCheck(params)
if not is_check_pass:
return mw.returnJson(is_check_pass, msg)
cuonConfig, get, name = self.getCrondCycle(params)
cronInfo = mw.M('crontab').where(
'id=?', (sid,)).field(self.field).find()
@@ -202,6 +265,10 @@ class crontab_api:
'urladdress': urladdress,
}
is_check_pass, msg = self.cronCheck(params)
if not is_check_pass:
return mw.returnJson(is_check_pass, msg)
addData = self.add(params)
if addData > 0:
return mw.returnJson(True, '添加成功')
@@ -409,6 +476,16 @@ class crontab_api:
shell = param.sFile
else:
head = "#!/bin/bash\nPATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin\nexport PATH\n"
source_bin_activate = '''
export LANG=en_US.UTF-8
MW_PATH=%s/bin/activate
if [ -f $MW_PATH ];then
source $MW_PATH
fi
''' % (mw.getRunDir(),)
head = head + source_bin_activate + "\n"
log = '.log'
script_dir = mw.getServerDir() + "/mdserver-web/scripts"
+1 -1
View File
@@ -758,7 +758,7 @@ class files_api:
try:
tmps = mw.getRunDir() + '/tmp/panelExec.log'
if stype == 'zip':
os.system("cd " + path + " && unzip -d '" + dfile +
os.system("cd " + path + " && unzip -o -d '" + dfile +
"' '" + sfile + "' > " + tmps + " 2>&1 &")
else:
sfiles = ''
+1 -1
View File
@@ -249,7 +249,7 @@ class firewall_api:
self.firewallReload()
# mw.M('firewall').where(
# "ps=?", ('SSH远程管理服务',)).setField('port', port)
msg = mw.getInfo('改SSH端口为[{1}]成功!', port)
msg = "改SSH端口为[{}]成功!".format(port)
mw.writeLog("防火墙管理", msg)
return mw.returnJson(True, '修改成功!')
+8 -6
View File
@@ -411,7 +411,7 @@ def writeFile(filename, str):
return False
def backFile(self, file, act=None):
def backFile(file, act=None):
"""
@name 备份配置文件
@param file 需要备份的文件
@@ -420,10 +420,12 @@ def backFile(self, file, act=None):
file_type = "_bak"
if act:
file_type = "_def"
execShell("/usr/bin/cp -p {0} {1}".format(file, file + file_type))
# print("cp -p {0} {1}".format(file, file + file_type))
execShell("cp -p {0} {1}".format(file, file + file_type))
def restoreFile(self, file, act=None):
def restoreFile(file, act=None):
"""
@name 还原配置文件
@param file 需要还原的文件
@@ -432,7 +434,7 @@ def restoreFile(self, file, act=None):
file_type = "_bak"
if act:
file_type = "_def"
execShell("/usr/bin/cp -p {1} {0}".format(file, file + file_type))
execShell("cp -p {1} {0}".format(file, file + file_type))
def HttpGet(url, timeout=10):
@@ -724,7 +726,7 @@ def checkIp(ip):
def checkPort(port):
# 检查端口是否合法
ports = ['21', '25', '7200', '888']
ports = ['21', '25', '443', '888']
if port in ports:
return False
intport = int(port)
@@ -807,7 +809,7 @@ def makeConf():
file = getRunDir() + '/data/json/config.json'
if not os.path.exists(file):
c = {}
c['title'] = 'mdserver-web | linux面板'
c['title'] = '祖龙面板'
c['home'] = 'http://github/midoks/mdserver-web'
c['recycle_bin'] = True
c['template'] = 'default'
+1 -1
View File
@@ -9,7 +9,7 @@ if [ -f bin/activate ];then
source bin/activate
fi
# export LC_ALL="en_US.UTF-8"
export LC_ALL="en_US.UTF-8"
mw_start_task()
+1
View File
@@ -1,6 +1,7 @@
server
{
listen {$PORT};
listen [::]:{$PORT};
server_name {$SERVER_NAME};
index index.php index.html index.htm default.php default.htm default.html;
root {$ROOT_DIR};
+418
View File
@@ -0,0 +1,418 @@
# coding:utf-8
'''
doc: https://docs.python.org/zh-cn/3/library/ftplib.html
'''
import sys
import io
import os
import time
import re
import json
import paramiko
import ftplib
sys.path.append(os.getcwd() + "/class/core")
import mw
DEBUG = True
BLOCK_SIZE = 1024 * 1024 * 2
# BLOCK_SIZE = 50
PROGRESS_FILE_NAME = "PROGRESS_FILE_NAME"
"""
=============自定义异常===================
"""
class OsError(Exception):
"""OS端异常"""
class ObjectNotFound(OsError):
"""对象不存在时抛出的异常"""
def __init__(self, *args, **kwargs):
message = "文件对象不存在。"
super(ObjectNotFound, self).__init__(message, *args, **kwargs)
class APIError(Exception):
"""API参数错误异常"""
def __init__(self, *args, **kwargs):
_api_error_msg = 'API资料校验失败,请核实!'
super(APIError, self).__init__(_api_error_msg, *args, **kwargs)
class FtpPSClient:
_title = "FTP"
_name = "ftp"
__host = None
__port = None
__user = None
__password = None
default_port = 21
default_backup_path = "/backup/"
config_file = "cfg.json"
def __init__(self, load_config=True, timeout=10):
self.timeout = timeout
if load_config:
data = self.get_config()
self.injection_config(data)
def get_config(self):
default_config = {
"ftp_host": '',
"ftp_user": '',
"ftp_pass": '',
"backup_path": self.default_backup_path
}
cfg = mw.getServerDir() + "/backup_ftp/" + self.config_file
if os.path.exists(cfg):
data = mw.readFile(cfg)
return json.loads(data)
else:
return default_config
def injection_config(self, data):
host = data["ftp_host"].strip()
if host.find(':') == -1:
self.__port = self.default_port
self.__host = data['ftp_host'].strip()
self.__user = data['ftp_user'].strip()
self.__password = data['ftp_pass'].strip()
bp = data['backup_path'].strip()
if bp:
self.backup_path = self.getPath(bp)
else:
self.backup_path = self.getPath(self.default_backup_path)
def authorize(self):
try:
if self.timeout is not None:
ftp = ftplib.FTP(timeout=self.timeout)
else:
ftp = ftplib.FTP()
debuglevel = 0
# if DEBUG:
# debuglevel = 3
ftp.set_debuglevel(debuglevel)
# ftp.set_pasv(True)
ftp.connect(self.__host, int(self.__port))
ftp.login(self.__user, self.__password)
return ftp
except Exception as e:
raise OsError("无法连接FTP客户端,请检查配置参数是否正确!")
# 取目录路径
def getPath(self, path):
if path[-1:] != '/':
path += '/'
if path[:1] != '/':
path = '/' + path
return path.replace('//', '/')
def generateDownloadUrl(self, object_name):
return 'ftp://' + \
self.__user + ':' + \
self.__password + '@' + \
self.__host + ':' + \
"/" + object_name
def buildDirName(self, data_type, file_name):
import re
prefix_dict = {
"site": "web",
"database": "db",
"path": "path",
}
file_regx = prefix_dict.get(data_type) + "_(.+)_20\d+_\d+\."
sub_search = re.search(file_regx, file_name)
sub_path_name = ""
if sub_search:
sub_path_name = sub_search.groups()[0]
sub_path_name += '/'
# 构建OS存储路径
object_name = self.backup_path + \
data_type + '/' + \
sub_path_name + \
file_name
return object_name
def uploadFile(self, filename, data_type=None, *args, **kwargs):
client = self.authorize()
local_file_name = filename
filename = os.path.abspath(filename)
dirname = os.path.dirname(filename)
temp_name = os.path.split(filename)[1]
object_name = self.buildDirName(data_type, temp_name)
upload_tmp_dir = os.path.join(dirname, ".upload_tmp")
if not os.path.exists(upload_tmp_dir):
os.mkdir(upload_tmp_dir)
print("|-正在上传文件到 {}".format(object_name))
total_bytes = os.path.getsize(filename)
object_md5_name = mw.md5(object_name)
pg_file = os.path.join(upload_tmp_dir, object_md5_name + ".pl")
block_size = BLOCK_SIZE
if kwargs.get("block_size"):
try:
block_size = float(kwargs.get("block_size"))
except:
pass
remote_file_size = None
if not os.path.exists(pg_file):
# import uuid
# uid = str(uuid.uuid1())
progress_info = {
"filename": local_file_name,
"total_bytes": total_bytes,
"uploaded_bytes": 0,
}
mw.writeFile(pg_file, json.dumps(progress_info))
else:
progress_info = json.loads(mw.readFile(pg_file))
if total_bytes == progress_info.get("total_bytes"):
# 取远程文件大小
_max_loop = 10
while _max_loop > 0:
try:
time.sleep(1)
remote_file_size = client.size(object_name)
if remote_file_size > total_bytes:
remote_file_size = None
break
except Exception as e:
if DEBUG:
print(type(e))
print(e)
_max_loop -= 1
else:
remote_file_size = None
uploaded_bytes = 0 if remote_file_size is None else remote_file_size
dir_name = os.path.split(object_name)[0]
if dir_name:
self.createDirP(dir_name)
upload_start = time.time()
try:
if total_bytes > 1024 * 1024 * 1024:
with open(local_file_name, 'rb') as file_handler:
if remote_file_size is not None:
file_handler.seek(remote_file_size)
client.voidcmd("TYPE I")
datasock = ''
esize = ''
datasock, esize = client.ntransfercmd(
"STOR " + object_name, remote_file_size)
while True:
buf = file_handler.read(block_size)
if not len(buf):
break
datasock.sendall(buf)
uploaded_bytes += len(buf)
if DEBUG:
print('\ruploading %.2f%%' %
(float(uploaded_bytes) / total_bytes * 100))
print("uploaded_bytes", uploaded_bytes)
if uploaded_bytes == total_bytes:
break
datasock.close()
if DEBUG:
print('close data handle')
try:
client.voidcmd('NOOP')
except Exception as e:
if DEBUG:
print("Send NOOP command error:")
print(e)
else:
if DEBUG:
print('keep alive cmd success')
client.voidresp()
if DEBUG:
print('No loop cmd')
else:
# 小于1G文件直接上传
file_handler = open(local_file_name, "rb")
client.storbinary('STOR %s' % object_name,
file_handler, blocksize=block_size)
file_handler.close()
except Exception as e:
print(str(e))
completed_file_size = None
_max_loop = 10
while _max_loop > 0:
try:
time.sleep(1)
completed_file_size = client.size(object_name)
break
except Exception as e:
_max_loop -= 1
if DEBUG:
print("size error:" + str(e))
# 上传完成
if completed_file_size == total_bytes:
if DEBUG:
upload_completed = time.time()
upload_diff = upload_completed - upload_start
print("文件上传成功, 耗时: {}s。".format(upload_diff))
if os.path.exists(pg_file):
os.remove(pg_file)
return True
else:
if os.path.exists(pg_file):
os.remove(pg_file)
print("文件上传后大小不一致!")
print("completed_file_size:" + str(completed_file_size))
print("total_bytes:", total_bytes)
print("object_md5_name:", object_md5_name)
print("pg_file:", pg_file)
print("filename:", filename)
print("dirname:", dirname)
print("object_name:", object_name)
return False
def createDirP(self, dir_name):
"""创建远程目录
:param dir_name: 目录名称
:return:
"""
try:
dirnames = dir_name.split('/')
ftp = self.authorize()
# ftp.cwd(get.path);
for dirname in dirnames:
if not dirname or not dirname.strip():
continue
try:
flist = ftp.nlst()
if not dirname in flist:
ftp.mkd(dirname)
except:
# print("mlsd mode.")
try:
flist = list(ftp.mlsd())[1:]
for f in flist:
if dirname == f[0]:
break
else:
ftp.mkd(dirname)
except:
return False
ftp.cwd(dirname)
return True
except:
return False
def createDir(self, path, name):
ftp = self.authorize()
path = self.getPath(path)
ftp.cwd(path)
try:
ftp.mkd(name)
ftp.close()
return True
except Exception as e:
print(str(e))
ftp.close()
return False
def deleteDir(self, path, dir_name):
try:
ftp = self.authorize()
ftp.rmd(dir_name)
return True
except ftplib.error_perm as e:
print("deleteDir:" + str(e) + ":" + dir_name)
except Exception as e:
print(e)
return False
def deleteFile(self, filename):
try:
ftp = self.authorize()
ftp.delete(filename)
return True
except Exception as e:
print(str(e))
return False
def getList(self, path="/"):
ftp = self.authorize()
path = self.getPath(path)
ftp.cwd(path)
mlsd = False
try:
files = list(ftp.mlsd())
mlsd = True
except:
try:
files = ftp.nlst(path)
mlsd = False
except:
raise RuntimeError("FTP服务器数据返回异常!")
ftp.close()
# print(files)
f_list = []
dirs = []
data = []
default_time = '1971/01/01 01:01:01'
for dt in files:
# print(dt)
if mlsd:
dt_name = dt[0]
dt_info = dt[1]
else:
if dt.find("/") >= 0:
dt = dt.split("/")[-1]
tmp = {}
tmp['name'] = dt_name
if dt_name == '.' or dt_name == '..':
continue
tmp['time'] = dt_info['modify']
try:
tmp['size'] = dt_info['size']
tmp['type'] = "File"
tmp['download'] = self.generateDownloadUrl(path + dt_name)
f_list.append(tmp)
except:
tmp['size'] = dt_info['sizd']
tmp['type'] = None
tmp['download'] = ''
dirs.append(tmp)
data = dirs + f_list
mlist = {}
mlist['path'] = path
mlist['list'] = data
return mlist
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

+101
View File
@@ -0,0 +1,101 @@
<style>
.upyunCon {
height: 428px;
}
.up-place {
height: 62px;
border-bottom: 1px solid #ddd;
}
.up-place .btn {
border-radius: 0;
}
.up-place .place-input {
background-color: #f3f3f3;
border: 1px solid #ccc;
height: 30px;
line-height: 28px;
overflow: hidden;
margin: 1px 0 0 -1px;
width: 340px;
}
.place-input ul {
display: inline-block;
position: relative;
width: auto;
}
.place-input ul li {
background: url("/static/img/ico/ico-ltr.png") no-repeat right center;
float: left;
padding-left: 10px;
padding-right: 18px;
}
.place-input ul li a {
height: 28px;
cursor: pointer;
display: inline-block;
}
.upyunlist {
height: 516px;
overflow: auto;
}
.up-bottom {
background-color: #fafafa;
border-top: 1px solid #eee;
bottom: 0;
position: absolute;
width: 100%;
}
.up-use {
line-height: 50px
}
.list-list .cursor span {
line-height: 30px;
}
.btn-title {
margin-top: 1px
}
.tip {
font-size: 10px;
font-style: oblique;
color: green;
}
</style>
<div class="upyunCon">
<div class="up-place pd15">
<button id="backBtn" class="btn btn-default btn-sm glyphicon glyphicon-arrow-left pull-left" title="后退"></button>
<input id="myPath" style="display:none;" type="text" value="">
<div class="place-input pull-left">
<div style="width:1400px;height:28px"><ul></ul></div>
</div>
<button class="refreshBtn btn btn-default btn-sm glyphicon glyphicon-refresh pull-left mr20" title="刷新" style="margin-left:-1px;"></button>
<button class="btn btn-default btn-sm pull-right btn-title" onclick="upyunApi()">帐户设置</button>
<button class="btn btn-default btn-sm pull-right mr20 btn-title" onclick="createDir()">新建文件夹</button>
</div>
<div class="upyunlist pd15">
<div class="divtable" style="margin-bottom:15px">
<table class="table table-hover">
<thead><tr><th>名称</th><th>大小</th><th>更新时间</th><th class="text-right">操作</th></tr></thead>
<tbody class="list-list"></tbody>
</table>
</div>
</div>
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=backup_ftp&f=js/backup_ftp.js", function(){
osList('/');
});
</script>
+257
View File
@@ -0,0 +1,257 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import json
# print(sys.platform)
if sys.platform != "darwin":
os.chdir("/www/server/mdserver-web")
sys.path.append(os.getcwd() + "/class/core")
import mw
_ver = sys.version_info
is_py2 = (_ver[0] == 2)
is_py3 = (_ver[0] == 3)
DEBUG = False
if is_py2:
reload(sys)
sys.setdefaultencoding('utf-8')
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'backup_ftp'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
sys.path.append(getPluginDir() + "/class")
from ftp_client import FtpPSClient
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
t = t.split(':')
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():
return 'start'
def getConf():
cfg = getServerDir() + "/cfg.json"
if not os.path.exists(cfg):
return mw.returnJson(False, "未配置", [])
data = mw.readFile(cfg)
data = json.loads(data)
return mw.returnJson(True, "OK", data)
def setConf():
args = getArgs()
data = checkArgs(args, ['use_sftp', 'ftp_user',
'ftp_pass', 'ftp_host', 'backup_path'])
if not data[0]:
return data[1]
cfg = getServerDir() + "/cfg.json"
values = ['ftp_user',
'ftp_pass',
'ftp_host']
for v in values:
if args[v] == '':
return mw.returnJson(False, '必填资料不能为空,请核实!', [])
if args['backup_path'] == '':
args['backup_path'] = "/backup"
try:
ftp = FtpPSClient(load_config=False)
ftp.injection_config(args)
data = ftp.getList("/")
if data:
mw.writeFile(cfg, mw.getJson(args))
return mw.returnJson(True, '设置成功', [])
except Exception as e:
return mw.returnJson(False, "FTP校验失败,请核实!\n" + str(e), [])
def getList():
cfg = getServerDir() + "/cfg.json"
if not os.path.exists(cfg):
return mw.returnJson(False, "未配置FTP,请点击`账户设置`", [])
args = getArgs()
data = checkArgs(args, ['path'])
if not data[0]:
return data[1]
try:
ftp = FtpPSClient()
flist = ftp.getList(args['path'])
return mw.returnJson(True, "ok", flist)
except Exception as e:
return mw.returnJson(False, str(e), [])
def createDir():
cfg = getServerDir() + "/cfg.json"
if not os.path.exists(cfg):
return mw.returnJson(False, "未配置FTP,请点击`账户设置`", [])
args = getArgs()
data = checkArgs(args, ['path', 'name'])
if not data[0]:
return data[1]
ftp = FtpPSClient()
isok = ftp.createDir(args['path'], args['name'])
if isok:
return mw.returnJson(True, "创建成功")
return mw.returnJson(False, "创建失败")
def deleteDir():
args = getArgs()
data = checkArgs(args, ['dir_name', 'path'])
if not data[0]:
return data[1]
ftp = FtpPSClient()
isok = ftp.deleteDir(args['path'], args['dir_name'])
if isok:
return mw.returnJson(True, "删除成功")
return mw.returnJson(False, "删除失败")
def deleteFile():
args = getArgs()
data = checkArgs(args, ['path', 'filename'])
if not data[0]:
return data[1]
ftp = FtpPSClient()
isok = ftp.deleteFile(args['path'] + "/" + args['filename'])
if isok:
return mw.returnJson(True, "删除成功")
return mw.returnJson(False, "删除失败")
def backupAllFunc(stype):
os.chdir(mw.getRunDir())
name = sys.argv[2]
num = sys.argv[3]
args = stype + " " + name + " " + num
cmd = 'python3 ' + mw.getRunDir() + '/scripts/backup.py ' + args
os.system(cmd)
# 开始执行上传信息
prefix_dict = {
"site": "web",
"database": "db",
"path": "path",
}
find_path = mw.getBackupDir() + '/' + stype + '/' + \
prefix_dict[stype] + '_' + name
find_new_file = "ls " + find_path + \
"_* | grep '.gz' | cut -d \ -f 1 | awk 'END {print}'"
filename = mw.execShell(find_new_file)[0].strip()
if filename == "":
print("not find upload file!")
return False
ftp = FtpPSClient()
ftp.uploadFile(filename, stype)
return True
def backupSite():
# 备份站点
pass
def in_array(name, arr=[]):
for x in arr:
if name == x:
return True
return False
def installPreInspection():
return 'ok'
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 == 'install_pre_inspection':
print(installPreInspection())
elif func == 'conf':
print(getConf())
elif func == 'set_config':
print(setConf())
elif func == "get_list":
print(getList())
elif func == "create_dir":
print(createDir())
elif func == "delete_dir":
print(deleteDir())
elif func == 'delete_file':
print(deleteFile())
elif in_array(func, ['site', 'database', 'path']):
print(backupAllFunc(func))
else:
print('error')
+17
View File
@@ -0,0 +1,17 @@
{
"title":"FTP存储空间",
"hook":["backup"],
"tip":"soft",
"name":"backup_ftp",
"type":"运行环境",
"ps":"将网站或数据库打包备份到FTP存储空间",
"versions":["1.0"],
"install_pre_inspection":false,
"shell":"install.sh",
"checks":"server/backup_ftp",
"path": "server/backup_ftp",
"author":"midoks",
"home":"",
"date":"2022-10-23",
"pid": "4"
}
+32
View File
@@ -0,0 +1,32 @@
#!/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
sys_os=`uname`
Install_App()
{
mkdir -p ${serverPath}/backup_ftp
echo "${1}" > ${serverPath}/backup_ftp/version.pl
echo '安装完成' > $install_tmp
}
Uninstall_App()
{
rm -rf ${serverPath}/backup_ftp
}
action=$1
if [ "${1}" == 'install' ];then
Install_App $2
else
Uninstall_App $2
fi
+258
View File
@@ -0,0 +1,258 @@
function bkfPost(method,args,callback){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(toArrayObject(args));
} else {
_args = JSON.stringify(args);
}
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
$.post('/plugins/run', {name:'backup_ftp', func:method, args:_args}, 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 getFtpLocalTime(data){
var str = data.slice(0,4)+"/"+data.slice(4,6)+"/"+data.slice(6,8)
+ " " + data.slice(8,10)+":"+data.slice(10,12)+":"+data.slice(12,14);
return str;
}
// 自定义部分
var i = null;
//设置API
function upyunApi(){
bkfPost('conf', {}, function(rdata){
var rdata = $.parseJSON(rdata.data);
var token = rdata.data;
var check_status = token.use_sftp;
var sftp_checked = check_status === "true" ? " checked=\"checked\"" : "";
if (typeof(token.ftp_host) == 'undefined'){
token.ftp_host = '';
}
if (typeof(token.ftp_user) == 'undefined'){
token.ftp_user = '';
}
if (typeof(token.ftp_pass) == 'undefined'){
token.ftp_pass = '';
}
if (typeof(token.backup_path) == 'undefined'){
token.backup_path = '';
}
var apicon = '<div class="bingfa mtb15" style="padding-bottom:0px;">\
<p>\
<span class="span_tit">使用SFTP:</span>\ <input style="width: 20px; vertical-align:middle;" type="checkbox" name="use_sftp"'+sftp_checked+'> 是否使用SFTP进行数据传输 \
</p>\
<p>\
<span class="span_tit">Host:</span>\
<input placeholder="请输入主机地址" style="width: 200px;" type="text" name="upyun_service" value="'+token.ftp_host+'"> *服务器地址,FTP默认端口21, SFTP默认端口22\
</p>\
<p>\
<span class="span_tit">用户名:</span>\
<input style="width: 200px;" type="text" name="ftp_username" value="'+token.ftp_user+'"> *指定用户名\
</p>\
<p>\
<span class="span_tit">密码:</span>\
<input style="width: 200px;" type="password" name="ftp_password" value="'+token.ftp_pass+'"> *登录密码\
</p>\
<p>\
<span class="span_tit">存储位置:</span>\
<input placeholder="请输入存储位置" style="width: 200px;" type="text" name="backup_path" value="'+token.backup_path+'"> *相对于根目录的路径,默认是/backup\
</p>\
</div>';
layer.open({
type: 1,
area: "600px",
title: "FTP/SFTP帐户设置",
closeBtn: 1,
shift: 5,
shadeClose: false,
btn: ['确定','取消'],
content:apicon,
yes:function(index,layero){
var data = {
use_sftp:$("input[name='use_sftp']").prop('checked'),
ftp_user:$("input[name='ftp_username']").val(),
ftp_pass:$("input[name='ftp_password']").val(),
ftp_host:$("input[name='upyun_service']").val(),
backup_path:$("input[name='backup_path']").val()
}
bkfPost('set_config', data, function(rdata){
var rdata = $.parseJSON(rdata.data);
if (rdata.status){
showMsg(rdata.msg,function(){
layer.close(index);
osList("/");
},{icon:1},2000);
} else{
layer.msg(rdata.msg,{icon:2});
}
})
},
});
});
}
function createDir(){
layer.open({
type: 1,
area: "400px",
title: "创建目录",
closeBtn: 1,
shift: 5,
shadeClose: false,
btn: ['确定','取消'],
content:'<div class="bingfa bt-form c6" style="padding-bottom: 10px;">\
<p>\
<span class="span_tit">目录名称:</span>\
<input style="width: 200px;" type="text" name="newPath" value="">\
</p>\
</div>',
success:function(){
$("input[name='newPath']").focus().keyup(function(e){
if(e.keyCode == 13) $(".layui-layer-btn0").click();
});
},
yes:function(index,layero){
var name = $("input[name='newPath']").val();
if(name == ''){
layer.msg('目录名称不能为空!',{icon:2});
return;
}
var path = $("#myPath").val();
var dirname = name;
// var loadT = layer.msg('正在创建目录['+dirname+']...',{icon:16,time:0,shade: [0.3, '#000']});
bkfPost('create_dir', {path:path,name:dirname}, function(data){
var rdata = $.parseJSON(data.data);
if(rdata.status) {
showMsg(rdata.msg, function(){
layer.close(index);
osList(path);
} ,{icon:1}, 2000);
} else{
layer.msg(rdata.msg,{icon:2});
}
});
}
});
}
//删除文件
function deleteFile(name, is_dir){
if (is_dir === false){
safeMessage('删除文件','删除后将无法恢复,真的要删除['+name+']吗?',function(){
var path = $("#myPath").val();
var filename = name;
bkfPost('delete_file', {filename:filename,path:path}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
osList(path);
},{icon:rdata.status?1:2},2000);
});
});
} else {
safeMessage('删除文件夹','删除后将无法恢复,真的要删除['+name+']吗?',function(){
var path = $("#myPath").val();
bkfPost('delete_dir', {dir_name:name,path:path}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
osList(path);
},{icon:rdata.status?1:2},2000);
});
});
}
}
function osList(path){
bkfPost('get_list', {path:path}, function(rdata){
var rdata = $.parseJSON(rdata.data);
if(rdata.status === false){
showMsg(rdata.msg,function(){
upyunApi();
},{icon:2},2000);
return;
}
var mlist = rdata.data;
// console.log(mlist);
var listBody = ''
var listFiles = ''
for(var i=0;i<mlist.list.length;i++){
if(mlist.list[i].type == null){
listBody += '<tr><td class="cursor" onclick="osList(\''+(path+'/'+mlist.list[i].name).replace('//','/')+'\')"><span class="ico ico-folder"></span>\<span>'+mlist.list[i].name+'</span></td>\
<td>-</td>\
<td>-</td>\
<td class="text-right"><a class="btlink" onclick="deleteFile(\''+mlist.list[i].name+'\', true)">删除</a></td></tr>'
}else{
listFiles += '<tr><td class="cursor"><span class="ico ico-file"></span>\<span>'+mlist.list[i].name+'</span></td>\
<td>'+toSize(mlist.list[i].size)+'</td>\
<td>'+getFtpLocalTime(mlist.list[i].time)+'</td>\
<td class="text-right"><a target="_blank" href="'+mlist.list[i].download+'" class="btlink">下载</a> | <a class="btlink" onclick="deleteFile(\''+mlist.list[i].name+'\', false)">删除</a></td></tr>'
}
}
listBody += listFiles;
var pathLi='';
var tmp = path.split('/')
var pathname = '';
var n = 0;
for(var i=0;i<tmp.length;i++){
if(n > 0 && tmp[i] == '') continue;
var dirname = tmp[i];
if(dirname == '') {
dirname = '根目录';
n++;
}
pathname += '/' + tmp[i];
pathname = pathname.replace('//','/');
pathLi += '<li><a title="'+pathname+'" onclick="osList(\''+pathname+'\')">'+dirname+'</a></li>';
}
var um = 1;
if(tmp[tmp.length-1] == '') um = 2;
var backPath = tmp.slice(0,tmp.length-um).join('/') || '/';
$('#myPath').val(path);
$(".upyunCon .place-input ul").html(pathLi);
$(".upyunlist .list-list").html(listBody);
upPathLeft();
$('#backBtn').unbind().click(function() {
osList(backPath);
});
$('.upyunCon .refreshBtn').unbind().click(function(){
osList(path);
});
});
}
//计算当前目录偏移
function upPathLeft(){
var UlWidth = $(".place-input ul").width();
var SpanPathWidth = $(".place-input").width() - 20;
var Ml = UlWidth - SpanPathWidth;
if(UlWidth > SpanPathWidth ){
$(".place-input ul").css("left",-Ml)
}
else{
$(".place-input ul").css("left",0)
}
}
// $('.layui-layer-page').css('height','670px');
+1 -1
View File
@@ -17,7 +17,7 @@ pluginService('clean');
function pRead(){
var readme = '<ul class="help-info-text c7">';
readme += '<li>启动后,假如到计划中[2:30]执行。</li>';
readme += '<li>启动后,加入到计划中[2:30]执行。</li>';
readme += '<li>【*】代表要删除。</li>';
readme += '<li>添加/删除清除日志,可在`配置修改`中添加/删除。</li>';
readme += '<li>如果是目录,会寻找【.log】后缀的文件清空。</li>';
+107 -3
View File
@@ -827,6 +827,69 @@ def setDbBackup():
return mw.returnJson(True, 'ok')
def importDbExternal():
args = getArgs()
data = checkArgs(args, ['file', 'name'])
if not data[0]:
return data[1]
file = args['file']
name = args['name']
import_dir = mw.getRootDir() + '/backup/import/'
file_path = import_dir + file
if not os.path.exists(file_path):
return mw.returnJson(False, '文件突然消失?')
exts = ['sql', 'gz', 'zip']
tmp = file.split('.')
ext = tmp[len(tmp) - 1]
if ext not in exts:
return mw.returnJson(False, '导入数据库格式不对!')
tmp = file.split('/')
tmpFile = tmp[len(tmp) - 1]
tmpFile = tmpFile.replace('.sql.' + ext, '.sql')
tmpFile = tmpFile.replace('.' + ext, '.sql')
tmpFile = tmpFile.replace('tar.', '')
# print(tmpFile)
import_sql = ""
if file.find("sql.gz") > -1:
cmd = 'cd ' + import_dir + ' && gzip -dc ' + \
file + " > " + import_dir + tmpFile
info = mw.execShell(cmd)
if info[1] == "":
import_sql = import_dir + tmpFile
if file.find(".zip") > -1:
cmd = 'cd ' + import_dir + ' && unzip -o ' + file
mw.execShell(cmd)
import_sql = import_dir + tmpFile
if file.find("tar.gz") > -1:
cmd = 'cd ' + import_dir + ' && tar -zxvf ' + file
mw.execShell(cmd)
import_sql = import_dir + tmpFile
if import_sql == "":
return mw.returnJson(False, '未找SQL文件')
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile()
os.environ["MYSQL_PWD"] = pwd
mysql_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + \
pwd + ' ' + name + ' < ' + import_sql
# print(mysql_cmd)
os.system(mysql_cmd)
os.remove(import_sql)
return mw.returnJson(True, 'ok')
def importDbBackup():
args = getArgs()
data = checkArgs(args, ['file', 'name'])
@@ -855,13 +918,17 @@ def importDbBackup():
def deleteDbBackup():
args = getArgs()
data = checkArgs(args, ['filename'])
data = checkArgs(args, ['filename', 'path'])
if not data[0]:
return data[1]
path = args['path']
full_file = ""
bkDir = mw.getRootDir() + '/backup/database'
os.remove(bkDir + '/' + args['filename'])
full_file = bkDir + '/' + args['filename']
if path != "":
full_file = path + "/" + args['filename']
os.remove(full_file)
return mw.returnJson(True, 'ok')
@@ -893,6 +960,39 @@ def getDbBackupList():
return mw.returnJson(True, 'ok', rr)
def getDbBackupImportList():
bkImportDir = mw.getRootDir() + '/backup/import'
if not os.path.exists(bkImportDir):
os.mkdir(bkImportDir)
blist = os.listdir(bkImportDir)
rr = []
for x in range(0, len(blist)):
name = blist[x]
p = bkImportDir + '/' + name
data = {}
data['name'] = name
rsize = os.path.getsize(p)
data['size'] = mw.toSize(rsize)
t = os.path.getctime(p)
t = time.localtime(t)
data['time'] = time.strftime('%Y-%m-%d %H:%M:%S', t)
rr.append(data)
data['file'] = p
rdata = {
"list": rr,
"upload_dir": bkImportDir,
}
return mw.returnJson(True, 'ok', rdata)
def getDbList():
args = getArgs()
page = 1
@@ -2427,10 +2527,14 @@ if __name__ == "__main__":
print(setDbBackup())
elif func == 'import_db_backup':
print(importDbBackup())
elif func == 'import_db_external':
print(importDbExternal())
elif func == 'delete_db_backup':
print(deleteDbBackup())
elif func == 'get_db_backup_list':
print(getDbBackupList())
elif func == 'get_db_backup_import_list':
print(getDbBackupImportList())
elif func == 'add_db':
print(addDb())
elif func == 'del_db':
+190 -75
View File
@@ -1,18 +1,9 @@
function str2Obj(str){
var data = {};
kv = str.split('&');
for(i in kv){
v = kv[i].split('=');
data[v[0]] = v[1];
}
return data;
}
function myPost(method,args,callback, title){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(str2Obj(args));
_args = JSON.stringify(toArrayObject(args));
} else {
_args = JSON.stringify(args);
}
@@ -40,7 +31,7 @@ function myPostN(method,args,callback, title){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(str2Obj(args));
_args = JSON.stringify(toArrayObject(args));
} else {
_args = JSON.stringify(args);
}
@@ -59,7 +50,7 @@ function myPostN(method,args,callback, title){
function myAsyncPost(method,args){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(str2Obj(args));
_args = JSON.stringify(toArrayObject(args));
} else {
_args = JSON.stringify(args);
}
@@ -606,7 +597,7 @@ function setDbAccess(username){
yes:function(index){
var data = $("#set_db_access").serialize();
data = decodeURIComponent(data);
var dataObj = str2Obj(data);
var dataObj = toArrayObject(data);
if(!dataObj['access']){
dataObj['access'] = dataObj['dataAccess'];
if ( dataObj['dataAccess'] == 'ip'){
@@ -672,33 +663,15 @@ function setDbPass(id, username, password){
}
function addDatabase(type){
if (type==1){
var data = $("#add_db").serialize();
data = decodeURIComponent(data);
var dataObj = str2Obj(data);
if(!dataObj['address']){
dataObj['address'] = dataObj['dataAccess'];
}
myPost('add_db', dataObj, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
if (rdata.status){
dbList();
}
$('.layui-layer-close1').click();
},{icon: rdata.status ? 1 : 2},600);
});
return;
}
var index = layer.open({
layer.open({
type: 1,
skin: 'demo-class',
area: '500px',
title: '添加数据库',
closeBtn: 1,
shift: 5,
shadeClose: true,
content: "<form class='bt-form pd20 pb70' id='add_db'>\
btn:["提交","关闭"],
content: "<form class='bt-form pd20' id='add_db'>\
<div class='line'>\
<span class='tname'>数据库名</span>\
<div class='info-r'><input name='name' class='bt-input-text mr5' placeholder='新的数据库名称' type='text' style='width:65%' value=''>\
@@ -726,28 +699,39 @@ function addDatabase(type){
</div>\
</div>\
<input type='hidden' name='ps' value='' />\
<div class='bt-form-submit-btn'>\
<button id='my_mod_close' type='button' class='btn btn-danger btn-sm btn-title'>关闭</button>\
<button type='button' class='btn btn-success btn-sm btn-title' onclick=\"addDatabase(1)\" >提交</button>\
</div>\
</form>",
});
success:function(){
$("input[name='name']").keyup(function(){
var v = $(this).val();
$("input[name='db_user']").val(v);
$("input[name='ps']").val(v);
});
$("input[name='name']").keyup(function(){
var v = $(this).val();
$("input[name='db_user']").val(v);
$("input[name='ps']").val(v);
});
$('#my_mod_close').click(function(){
$('.layui-layer-close1').click();
});
$('select[name="dataAccess"]').change(function(){
var v = $(this).val();
if (v == 'ip'){
$(this).after("<input id='dataAccess_subid' class='bt-input-text mr5' type='text' name='address' placeholder='多个IP使用逗号(,)分隔' style='width: 230px; display: inline-block;'>");
} else {
$('#dataAccess_subid').remove();
$('select[name="dataAccess"]').change(function(){
var v = $(this).val();
if (v == 'ip'){
$(this).after("<input id='dataAccess_subid' class='bt-input-text mr5' type='text' name='address' placeholder='多个IP使用逗号(,)分隔' style='width: 230px; display: inline-block;'>");
} else {
$('#dataAccess_subid').remove();
}
});
},
yes:function(index) {
var data = $("#add_db").serialize();
data = decodeURIComponent(data);
var dataObj = toArrayObject(data);
if(!dataObj['address']){
dataObj['address'] = dataObj['dataAccess'];
}
myPost('add_db', dataObj, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
if (rdata.status){
layer.close(index);
dbList();
}
},{icon: rdata.status ? 1 : 2},600);
});
}
});
}
@@ -759,7 +743,6 @@ function delDb(id, name){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
dbList();
$('.layui-layer-close1').click();
},{icon: rdata.status ? 1 : 2}, 600);
});
});
@@ -881,8 +864,11 @@ function openPhpmyadmin(name,username,password){
}
}
function delBackup(filename,name){
myPost('delete_db_backup',{filename:filename},function(){
function delBackup(filename,name,path){
if(typeof(path) == "undefined"){
path = "";
}
myPost('delete_db_backup',{filename:filename,path:path},function(){
layer.msg('执行成功!');
setTimeout(function(){
$('.layui-layer-close2').click();
@@ -902,6 +888,132 @@ function importBackup(file,name){
});
}
function importDbExternal(file,name){
myPost('import_db_external',{file:file,name:name}, function(data){
layer.msg('执行成功!');
});
}
function setLocalImport(db_name){
//上传文件
function uploadDbFiles(upload_dir){
var up_db = layer.open({
type:1,
closeBtn: 1,
title:"上传导入文件["+upload_dir+']',
area: ['500px','300px'],
shadeClose:false,
content:'<div class="fileUploadDiv">\
<input type="hidden" id="input-val" value="'+upload_dir+'" />\
<input type="file" id="file_input" multiple="true" autocomplete="off" />\
<button type="button" id="opt" autocomplete="off">添加文件</button>\
<button type="button" id="up" autocomplete="off" >开始上传</button>\
<span id="totalProgress" style="position: absolute;top: 7px;right: 147px;"></span>\
<span style="float:right;margin-top: 9px;">\
<font>文件编码:</font>\
<select id="fileCodeing" >\
<option value="byte">二进制</option>\
<option value="utf-8">UTF-8</option>\
<option value="gb18030">GB2312</option>\
</select>\
</span>\
<button type="button" id="filesClose" autocomplete="off">关闭</button>\
<ul id="up_box"></ul>\
</div>',
success:function(){
$('#filesClose').click(function(){
layer.close(up_db);
});
}
});
uploadStart(function(){
getList();
});
}
function getList(){
myPost('get_db_backup_import_list',{}, function(data){
var rdata = $.parseJSON(data.data);
var file_list = rdata.data.list;
var upload_dir = rdata.data.upload_dir;
var tbody = '';
for (var i = 0; i < file_list.length; i++) {
tbody += '<tr>\
<td><span> ' + file_list[i]['name'] + '</span></td>\
<td><span> ' + file_list[i]['size'] + '</span></td>\
<td><span> ' + file_list[i]['time'] + '</span></td>\
<td style="text-align: right;">\
<a class="btlink" onclick="importDbExternal(\'' + file_list[i]['name'] + '\',\'' +db_name+ '\')">导入</a> | \
<a class="btlink del" index="'+i+'">删除</a>\
</td>\
</tr>';
}
$('#import_db_file_list').html(tbody);
$('input[name="upload_dir"]').val(upload_dir);
$("#import_db_file_list .del").on('click',function(){
var index = $(this).attr('index');
var filename = file_list[index]["name"];
myPost('delete_db_backup',{filename:filename,path:upload_dir},function(){
showMsg('执行成功!', function(){
getList();
},{icon:1},2000);
});
});
});
}
var layerIndex = layer.open({
type: 1,
title: "从文件导入数据",
area: ['600px', '380px'],
closeBtn: 1,
shadeClose: false,
content: '<div class="pd15">\
<div class="db_list">\
<button id="btn_file_upload" class="btn btn-success btn-sm" type="button">从本地上传</button>\
</div >\
<div class="divtable">\
<input type="hidden" name="upload_dir" value=""> \
<div id="database_fix" style="height:150px;overflow:auto;border:#ddd 1px solid">\
<table class="table table-hover "style="border:none">\
<thead>\
<tr>\
<th>文件名称</th>\
<th>文件大小</th>\
<th>备份时间</th>\
<th style="text-align: right;">操作</th>\
</tr>\
</thead>\
<tbody id="import_db_file_list" class="gztr"></tbody>\
</table>\
</div>\
<ul class="help-info-text c7">\
<li>仅支持sql、zip、sql.gz、(tar.gz|gz|tgz)</li>\
<li>zip、tar.gz压缩包结构:test.zip或test.tar.gz压缩包内,必需包含test.sql</li>\
<li>若文件过大,您还可以使用SFTP工具,将数据库文件上传到/www/backup/import</li>\
</ul>\
</div>\
</div>',
success:function(index){
$('#btn_file_upload').click(function(){
var upload_dir = $('input[name="upload_dir"]').val();
uploadDbFiles(upload_dir);
});
getList();
},
});
}
function setBackup(db_name,obj){
myPost('get_db_backup_list', {name:db_name}, function(data){
@@ -914,21 +1026,21 @@ function setBackup(db_name,obj){
<td><span> ' + rdata.data[i]['time'] + '</span></td>\
<td style="text-align: right;">\
<a class="btlink" onclick="importBackup(\'' + rdata.data[i]['name'] + '\',\'' +db_name+ '\')">导入</a> | \
<a class="btlink" onclick="downloadBackup(\'' + rdata.data[i]['file'] + '\')">下载</a> | \
<a class="btlink" onclick="delBackup(\'' + rdata.data[i]['name'] + '\',\'' +db_name+ '\')">删除</a>\
</td>\
</tr> ';
}
var s = layer.open({
var layerIndex = layer.open({
type: 1,
title: "数据库备份详情",
area: ['600px', '280px'],
closeBtn: 2,
closeBtn: 1,
shadeClose: false,
content: '<div class="pd15">\
<div class="db_list">\
<button id="btn_backup" class="btn btn-success btn-sm" type="button">备份</button>\
<button id="btn_local_import" class="btn btn-success btn-sm" type="button">外部导入</button>\
</div >\
<div class="divtable">\
<div id="database_fix" style="height:150px;overflow:auto;border:#ddd 1px solid">\
@@ -945,18 +1057,21 @@ function setBackup(db_name,obj){
</table>\
</div>\
</div>\
</div>'
});
</div>',
success:function(index){
$('#btn_backup').click(function(){
myPost('set_db_backup',{name:db_name}, function(data){
showMsg('执行成功!', function(){
layer.close(layerIndex);
setBackup(db_name,obj);
}, {icon:1}, 2000);
});
});
$('#btn_backup').click(function(){
myPost('set_db_backup',{name:db_name}, function(data){
layer.msg('执行成功!');
setTimeout(function(){
layer.close(s);
setBackup(db_name,obj);
},2000);
});
$('#btn_local_import').click(function(){
setLocalImport(db_name);
});
},
});
});
}
@@ -1237,7 +1352,7 @@ function repTools(db_name, res){
type: 1,
title: "MySQL工具箱【" + db_name + "】",
area: ['780px', '580px'],
closeBtn: 2,
closeBtn: 1,
shadeClose: false,
content: '<div class="pd15">\
<div class="db_list">\
@@ -1332,7 +1447,7 @@ function addMasterRepSlaveUser(){
yes:function(index){
var data = $("#add_master").serialize();
data = decodeURIComponent(data);
var dataObj = str2Obj(data);
var dataObj = toArrayObject(data);
if(!dataObj['address']){
dataObj['address'] = dataObj['dataAccess'];
}
@@ -1377,7 +1492,7 @@ function updateMasterRepSlaveUser(username){
$('#submit_update_master').click(function(){
var data = $("#update_master").serialize();
data = decodeURIComponent(data);
var dataObj = str2Obj(data);
var dataObj = toArrayObject(data);
myPost('update_master_rep_slave_user', data, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
@@ -1488,7 +1603,7 @@ function setDbMasterAccess(username){
yes:function(index){
var data = $("#set_db_access").serialize();
data = decodeURIComponent(data);
var dataObj = str2Obj(data);
var dataObj = toArrayObject(data);
if(!dataObj['access']){
dataObj['access'] = dataObj['dataAccess'];
if ( dataObj['dataAccess'] == 'ip'){
+12 -2
View File
@@ -146,7 +146,7 @@ def initSiteInfo():
site_contents_new[name] = site_contents[name]
else:
tmp = {}
tmp['cdn'] = False
tmp['cdn'] = True
tmp['log'] = True
tmp['get'] = True
tmp['post'] = True
@@ -253,6 +253,7 @@ def contentReplace(content):
def autoMakeLuaConfSingle(file):
# path = getPluginDir() + "/waf/rule/" + file + ".json"
path = getServerDir() + "/waf/rule/" + file + ".json"
to_path = getServerDir() + "/waf/conf/rule_" + file + ".lua"
content = mw.readFile(path)
@@ -280,7 +281,7 @@ def autoMakeLuaHtmlSingle(file):
def autoMakeLuaConf():
conf_list = ['args', 'cookie', 'ip_black', 'ip_white',
'ipv6_black', 'post', 'scan_black', 'url',
'user_agent']
'url_white', 'user_agent']
for x in conf_list:
autoMakeLuaConfSingle(x)
@@ -424,6 +425,15 @@ def restart():
def reload():
stop()
path = getServerDir()
path_tpl = getPluginDir()
config = path + "/waf/lua/init.lua"
config_tpl = path_tpl + "/waf/lua/init.lua"
content = mw.readFile(config_tpl)
content = contentReplace(content)
mw.writeFile(config, content)
errlog = mw.getServerDir() + "/openresty/nginx/logs/error.log"
mw.execShell('rm -rf ' + errlog)
+2 -1
View File
@@ -4,6 +4,7 @@
"name":"op_waf",
"type":"其他插件",
"ps":"有效防止sql注入/xss/一句话木马等常见渗透攻击",
"install_pre_inspection":true,
"shell":"install.sh",
"checks":"server/op_waf",
"path":"server/op_waf",
@@ -11,5 +12,5 @@
"home":"https://github.com/loveshell/ngx_lua_waf",
"date":"2019-04-21",
"pid": "1",
"versions": ["0.2.2"]
"versions": ["0.2.3"]
}
+71 -1
View File
@@ -691,6 +691,71 @@ function ipWhite(type) {
});
}
//IP白名单
function urlWhite(type) {
var ruleName = "url_white";
if (type == undefined) {
create_l = layer.open({
type: 1,
title: "管理URL白名单",
area: ['700px', '530px'],
closeBtn: 1,
shadeClose: false,
content: '<div class="pd15">\
<div style="border-bottom:#ccc 1px solid;margin-bottom:10px;padding-bottom:10px">\
<input class="bt-input-text" name="ruleValue" type="text" value="" style="width:470px;margin-right:12px;" placeholder="规则内容,请使用正则表达式">\
<input class="bt-input-text mr5" name="rulePs" type="text" style="width:120px;" placeholder="描述">\
<button class="btn btn-success btn-sm va0 pull-right" onclick="addRule(\''+ ruleName + '\');">添加</button>\</div>\
<div class="divtable">\
<div id="jc-file-table" class="table_head_fix" style="max-height:300px;overflow:auto;border:#ddd 1px solid">\
<table class="table table-hover" style="border:none">\
<thead>\
<tr>\
<th width="360">规则</th>\
<th>说明</th>\
<th>操作</th>\
<th style="text-align: right;">状态</th>\
</tr>\
</thead>\
<tbody id="set_obj_conf_con" class="gztr"></tbody>\
</table>\
</div>\
</div>\
<ul class="help-info-text c7 ptb10">\
<li style="color:red;">注意:如果您不了解正则表达式,请不要随意修改规则内容</li>\
<li>您可以添加或修改规则内容,但请使用正则表达式</li>\
<li>内置规则允许修改,但不可以直接删除,您可以设置规则状态来定义防火墙是否使用此规则</li>\
</ul></div>'
});
tableFixed("jc-file-table");
}
getRuleByName(ruleName, function(data){
var tmp = $.parseJSON(data.data);
var rdata = $.parseJSON(tmp.data);
console.log(rdata);
var tbody = ''
for (var i = 0; i < rdata.length; i++) {
var removeRule = ''
if (rdata[i][3] != 0) removeRule = ' | <a class="btlink" onclick="removeRule(\'' + ruleName + '\',' + i + ')">删除</a>';
tbody += '<tr>\
<td class="rule_body_'+ i + '">' + rdata[i][1] + '</td>\
<td class="rule_ps_'+ i + '">' + rdata[i][2] + '</td>\
<td class="rule_modify_'+ i + '"><a class="btlink" onclick="modifyRule(' + i + ',\'' + ruleName + '\')">编辑</a>' + removeRule + '</td>\
<td class="text-right">\
<div class="pull-right">\
<input class="btswitch btswitch-ios" id="closeua_'+ i + '" type="checkbox" ' + (rdata[i][0] ? 'checked' : '') + '>\
<label class="btswitch-btn" style="width:2.0em;height:1.2em;margin-bottom: 0" for="closeua_'+ i + '" onclick="setRuleState(\'' + ruleName + '\',' + i + ')"></label>\
</div>\
</td>\
</tr>'
}
$("#set_obj_conf_con").html(tbody);
});
}
// 获取IPV4黑名单
function getIpv4Address(callback){
@@ -1003,6 +1068,11 @@ function wafGloabl(){
<label class="btswitch-btn" for="closescan" onclick="setObjOpen(\'scan\')"></label>\
</div></td><td class="text-right"><a class="btlink" onclick="scanRule()">设置</a></td>\
</tr>\
<tr>\
<td>URL白名单</td><td>所有规则对URL白名单无效</td><td style="text-align: center;">--</td>\
<td style="text-align: center;">--</td>\
<td class="text-right"><a class="btlink" onclick="urlWhite()">设置</a></td>\
</tr>\
<tr>\
<td>IP白名单</td><td>所有规则对IP白名单无效</td><td style="text-align: center;">--</td>\
<td style="text-align: center;">--</td>\
@@ -1025,7 +1095,7 @@ function wafGloabl(){
con += '<div style="width:645px;margin-top:10px;"><ul class="help-info-text c7">\
<li>继承: 全局设置将在站点配置中自动继承为默认值</li>\
<li>优先级: IP白名单>IP黑名单>URL白名单>URL黑名单>CC防御>禁止国外IP访问>User-Agent>URI过滤>URL参数>Cookie>POST</li>\
<li>优先级: IP白名单>IP黑名单>URL白名单>URL黑名单>CC防御>User-Agent>URI过滤>URL参数>Cookie>POST</li>\
</ul></div>';
$(".soft-man-con").html(con);
});
+9 -3
View File
@@ -76,10 +76,16 @@ function _M.cron(self)
local db = self:initDB()
db:exec([[BEGIN TRANSACTION]])
local stmt2 = db:prepare[[INSERT INTO logs(time, ip, domain, server_name, method, status_code, uri, user_agent, rule_name, reason)
VALUES(:time, :ip, :domain, :server_name, :method, :status_code, :uri, :user_agent, :rule_name, :reason)]]
db:exec([[BEGIN TRANSACTION]])
if not stmt2 then
self:D("waf timer db:prepare fail!:"..tostring(stmt2))
return false
end
for i=1,llen do
local data, _ = ngx.shared.waf_limit:lpop('waf_limit_logs')
@@ -105,7 +111,7 @@ function _M.cron(self)
local res, err = stmt2:step()
if tostring(res) == "5" then
self.D("waf the step database connection is busy, so it will be stored later.")
self:D("waf the step database connection is busy, so it will be stored later.")
return false
end
stmt2:reset()
@@ -522,7 +528,7 @@ function _M.get_sn(self, config_domains)
end
end
end
return request_name
return "unset"
end
function _M.get_random(self,n)
+22 -11
View File
@@ -22,6 +22,7 @@ local post_html = require "html_post"
local other_html = require "html_other"
local user_agent_html = require "html_user_agent"
local cc_safe_js_html = require "html_safe_js"
local cookie_html = require "html_cookie"
local args_rules = require "rule_args"
local ip_white_rules = require "rule_ip_white"
@@ -32,6 +33,7 @@ local user_agent_rules = require "rule_user_agent"
local post_rules = require "rule_post"
local cookie_rules = require "rule_cookie"
local url_rules = require "rule_url"
local url_white_rules = require "rule_url_white"
local server_name = string.gsub(C:get_sn(config_domains),'_','.')
@@ -42,10 +44,10 @@ local function initParams()
data['ip'] = C:get_real_ip(server_name)
data['ipn'] = C:arrip(data['ip'])
data['request_header'] = ngx.req.get_headers()
data['uri'] = ngx.unescape_uri(ngx.var.uri)
data['uri'] = tostring(ngx.unescape_uri(ngx.var.uri))
data['uri_request_args'] = ngx.req.get_uri_args()
data['method'] = ngx.req.get_method()
data['request_uri'] = ngx.var.request_uri
data['request_uri'] = tostring(ngx.var.request_uri)
data['status_code'] = ngx.status
data['user_agent'] = data['request_header']['user-agent']
data['cookie'] = ngx.var.http_cookie
@@ -180,6 +182,13 @@ local function waf_ip_white()
return false
end
local function waf_url_white()
if C:ngx_match_list(url_white_rules, params['uri']) then
return true
end
return false
end
local function waf_ip_black()
-- ipv4 ip black
for _,rule in ipairs(ip_black_rules)
@@ -207,7 +216,7 @@ local function waf_user_agent()
-- if not config['user-agent']['open'] or not C:is_site_config('user-agent') then return false end
-- C:D("waf_user_agent;user_agent_rules:"..json.encode(user_agent_rules)..",ua:"..tostring(params['request_header']['user-agent']))
if C:is_ngx_match_ua(user_agent_rules, params['request_header']['user-agent']) then
if C:ngx_match_list(user_agent_rules, params['request_header']['user-agent']) then
-- C:D("waf_user_agent........... true")
C:write_log('user_agent','regular')
C:return_html(config['user-agent']['status'], user_agent_html)
@@ -295,16 +304,15 @@ local function is_open_waf_cc_increase()
return true
end
if site_config[server_name]['safe_verify']['open'] then
return true
end
-- C:D("waf config:"..json.encode(config))
if cpu_percent >= config['safe_verify']['cpu'] then
return true
end
if cpu_percent >= site_config[server_name]['safe_verify']['cpu'] then
return true
if site_config[server_name] and site_config[server_name]['safe_verify']['open'] then
if cpu_percent >= site_config[server_name]['safe_verify']['cpu'] then
return true
end
end
return false
@@ -440,7 +448,7 @@ local function post_X_Forwarded()
if not config['post']['open'] or not C:is_site_config('post') then return false end
if params['method'] ~= "POST" then return false end
if not params["request_header"]['X-forwarded-For'] then return false end
if C:is_ngx_match_list(post_rules, params["request_header"]['X-forwarded-For']) then
if C:ngx_match_list(post_rules, params["request_header"]['X-forwarded-For']) then
C:write_log('post','regular')
C:return_html(config['post']['status'], post_html)
return true
@@ -467,7 +475,7 @@ end
local function disable_upload_ext(ext)
if not ext then return false end
ext = string.lower(ext)
local ext = string.lower(ext)
if C:is_key(site_config[server_name]['disable_upload_ext'], ext) then
C:write_log('upload_ext', '上传扩展名黑名单')
C:return_html(config['other']['status'],other_html)
@@ -517,6 +525,9 @@ function waf()
-- white ip
if waf_ip_white() then return true end
-- url white
if waf_url_white() then return true end
-- black ip
if waf_ip_black() then return true end
+1 -1
View File
@@ -1 +1 @@
[[[127, 0, 0, 2], [127, 0, 0, 255]]]
[[[127, 0, 0, 1], [127, 0, 0, 255]]]
+1
View File
@@ -0,0 +1 @@
[[1,"^/(phpmyadmin)","MySQL[phpMyAdmin]", 0]]
-1
View File
@@ -1 +0,0 @@
^/phpmyadmin_
+1
View File
@@ -64,6 +64,7 @@ http
server {
listen 80;
listen [::]:80;
server_name 127.0.0.1;
allow 127.0.0.1;
location /nginx_status {
+2 -3
View File
@@ -4,7 +4,8 @@
<p class="bgw" onclick="orPluginService('openresty');">服务</p>
<p onclick="pluginInitD('openresty');">自启动</p>
<p onclick="pluginConfig('openresty');">配置修改</p>
<p onclick="getOpenrestyStatus();">负载状态</p>
<p onclick="getOpStatus();">负载状态</p>
<p onclick="setOpCfg();">性能调整</p>
<p onclick="pluginLogs('openresty');">错误日志</p>
</div>
<div class="bt-w-con pd15">
@@ -14,9 +15,7 @@
</div>
<!-- <script type="text/javascript" src="/plugins/file?name=openresty&f=js/openresty.js"></script> -->
<script type="text/javascript">
$.getScript( "/plugins/file?name=openresty&f=js/openresty.js", function(){
orPluginService('openresty');
});
+121 -3
View File
@@ -6,6 +6,7 @@ import os
import time
import threading
import subprocess
import re
sys.path.append(os.getcwd() + "/class/core")
import mw
@@ -52,6 +53,13 @@ def getArgs():
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 clearTemp():
path_bin = getServerDir() + "/nginx"
mw.execShell('rm -rf ' + path_bin + '/client_body_temp')
@@ -232,7 +240,7 @@ def restyOp_restart():
check = getServerDir() + "/bin/openresty -t"
check_data = mw.execShell(check)
if not check_data[1].find('test is successful') > -1:
return check_data[1]
return 'ERROR: 配置出错<br><a style="color:red;">' + check_data[1].replace("\n", '<br>') + '</a>'
if not mw.isAppleSystem():
threading.Timer(2, op_submit_systemctl_restart, args=()).start()
@@ -291,7 +299,7 @@ def initdUinstall():
def runInfo():
# 取Openresty负载状态
try:
url = 'http://' + mw.getHostAddr() + '/nginx_status'
url = 'http://127.0.0.1/nginx_status'
result = mw.httpGet(url)
tmp = result.split()
data = {}
@@ -304,7 +312,8 @@ def runInfo():
data['Waiting'] = tmp[15]
return mw.getJson(data)
except Exception as e:
url = 'http://127.0.0.1/nginx_status'
url = 'http://' + mw.getHostAddr() + '/nginx_status'
result = mw.httpGet(url)
tmp = result.split()
data = {}
@@ -324,6 +333,111 @@ def errorLogPath():
return getServerDir() + '/nginx/logs/error.log'
def getCfg():
cfg = getConf()
content = mw.readFile(cfg)
unitrep = "[kmgKMG]"
cfg_args = [
{"name": "worker_processes", "ps": "处理进程,auto表示自动,数字表示进程数", 'type': 2},
{"name": "worker_connections", "ps": "最大并发链接数", 'type': 2},
{"name": "keepalive_timeout", "ps": "连接超时时间", 'type': 2},
{"name": "gzip", "ps": "是否开启压缩传输", 'type': 1},
{"name": "gzip_min_length", "ps": "最小压缩文件", 'type': 2},
{"name": "gzip_comp_level", "ps": "压缩率", 'type': 2},
{"name": "client_max_body_size", "ps": "最大上传文件", 'type': 2},
{"name": "server_names_hash_bucket_size",
"ps": "服务器名字的hash表大小", 'type': 2},
{"name": "client_header_buffer_size", "ps": "客户端请求头buffer大小", 'type': 2},
]
# {"name": "client_body_buffer_size", "ps": "请求主体缓冲区"}
rdata = []
for i in cfg_args:
rep = "(%s)\s+(\w+)" % i["name"]
k = re.search(rep, content)
if not k:
return mw.returnJson(False, "获取 key {} 失败".format(k))
k = k.group(1)
v = re.search(rep, content)
if not v:
return mw.returnJson(False, "获取 value {} 失败".format(v))
v = v.group(2)
if re.search(unitrep, v):
u = str.upper(v[-1])
v = v[:-1]
if len(u) == 1:
psstr = u + "B," + i["ps"]
else:
psstr = u + "," + i["ps"]
else:
u = ""
kv = {"name": k, "value": v, "unit": u,
"ps": i["ps"], "type": i["type"]}
rdata.append(kv)
return mw.returnJson(True, "ok", rdata)
def setCfg():
args = getArgs()
data = checkArgs(args, [
'worker_processes', 'worker_connections', 'keepalive_timeout',
'gzip', 'gzip_min_length', 'gzip_comp_level', 'client_max_body_size',
'server_names_hash_bucket_size', 'client_header_buffer_size'
])
if not data[0]:
return data[1]
cfg = getConf()
mw.backFile(cfg)
content = mw.readFile(cfg)
unitrep = "[kmgKMG]"
cfg_args = [
{"name": "worker_processes", "ps": "处理进程,auto表示自动,数字表示进程数", 'type': 2},
{"name": "worker_connections", "ps": "最大并发链接数", 'type': 2},
{"name": "keepalive_timeout", "ps": "连接超时时间", 'type': 2},
{"name": "gzip", "ps": "是否开启压缩传输", 'type': 1},
{"name": "gzip_min_length", "ps": "最小压缩文件", 'type': 2},
{"name": "gzip_comp_level", "ps": "压缩率", 'type': 2},
{"name": "client_max_body_size", "ps": "最大上传文件", 'type': 2},
{"name": "server_names_hash_bucket_size",
"ps": "服务器名字的hash表大小", 'type': 2},
{"name": "client_header_buffer_size", "ps": "客户端请求头buffer大小", 'type': 2},
]
# print(args)
for k, v in args.items():
# print(k, v)
rep = "%s\s+[^kKmMgG\;\n]+" % k
if k == "worker_processes" or k == "gzip":
if not re.search("auto|on|off|\d+", v):
return mw.returnJson(False, '参数值错误')
else:
if not re.search("\d+", v):
return mw.returnJson(False, '参数值错误,请输入数字整数')
if re.search(rep, content):
newconf = "%s %s" % (k, v)
content = re.sub(rep, newconf, content)
elif re.search(rep, content):
newconf = "%s %s" % (k, v)
content = re.sub(rep, newconf, content)
mw.writeFile(cfg, content)
isError = mw.checkWebConfig()
if (isError != True):
mw.restoreFile(cfg)
return mw.returnJson(False, 'ERROR: 配置出错<br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
mw.restartWeb()
return mw.returnJson(True, '设置成功')
def installPreInspection():
return 'ok'
@@ -356,5 +470,9 @@ if __name__ == "__main__":
print(runInfo())
elif func == 'error_log':
print(errorLogPath())
elif func == 'get_cfg':
print(getCfg())
elif func == 'set_cfg':
print(setCfg())
else:
print('error')
+1
View File
@@ -59,6 +59,7 @@ Install_openresty()
cd ${openrestyDir}/openresty-${VERSION} && ./configure \
--prefix=$serverPath/openresty \
--with-ipv6 \
--with-stream \
--with-http_v2_module \
--with-http_ssl_module \
--with-http_slice_module \
+78 -1
View File
@@ -111,8 +111,10 @@ function orPluginOpServiceOp(a,b,c,d,a,v,request_callback){
//查看Nginx负载状态
function getOpenrestyStatus() {
function getOpStatus() {
var loadT = layer.msg('正在处理,请稍后...', { icon: 16, time: 0, shade: 0.3 });
$.post('/plugins/run', {name:'openresty', func:'run_info'}, function(data) {
layer.close(loadT);
if (!data.status){
showMsg(data.msg, function(){}, null,3000);
return;
@@ -134,3 +136,78 @@ function getOpenrestyStatus() {
}
},'json');
}
function setOpCfg(){
orPost('get_cfg', {}, function(data){
var rdata = $.parseJSON(data.data);
var rdata = rdata.data;
console.log(rdata);
var mlist = '';
for (var i = 0; i < rdata.length; i++) {
var w = '70'
var ibody = '<input style="width: ' + w + 'px;" class="bt-input-text mr5" name="' + rdata[i].name + '" value="' + rdata[i].value + '" type="text" >';
switch (rdata[i].type) {
case 0:
var selected_1 = (rdata[i].value == 1) ? 'selected' : '';
var selected_0 = (rdata[i].value == 0) ? 'selected' : '';
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
<option value="1" ' + selected_1 + '>开启</option>\
<option value="0" ' + selected_0 + '>关闭</option>\
</select>';
break;
case 1:
var selected_1 = (rdata[i].value == 'on') ? 'selected' : '';
var selected_0 = (rdata[i].value == 'off') ? 'selected' : '';
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
<option value="on" ' + selected_1 + '>开启</option>\
<option value="off" ' + selected_0 + '>关闭</option>\
</select>';
break;
}
mlist += '<p style="margin-top:15px;"><span>' + rdata[i].name + '</span>' + ibody + "<b class='unit c9'>"+rdata[i].unit+"</b>" +', <font class="c9">' + rdata[i].ps + '</font></p>';
}
var con = '<style>.conf_p p{margin-bottom: 2px}</style><div class="conf_p" style="margin-bottom:0">\
' + mlist + '\
<div style="margin-top:10px; padding-right:15px" class="text-right">\
<button class="btn btn-success btn-sm mr5" onclick="setOpCfg()">刷新</button>\
<button class="btn btn-success btn-sm" onclick="submitConf()">保存</button>\
</div>\
</div>'
$(".soft-man-con").html(con);
});
}
function submitConf() {
var data = {
worker_processes: $("input[name='worker_processes']").val(),
worker_connections: $("input[name='worker_connections']").val(),
keepalive_timeout: $("input[name='keepalive_timeout']").val(),
gzip: $("select[name='gzip']").val() || 'on',
gzip_min_length: $("input[name='gzip_min_length']").val(),
gzip_comp_level: $("input[name='gzip_comp_level']").val(),
client_max_body_size: $("input[name='client_max_body_size']").val(),
server_names_hash_bucket_size: $("input[name='server_names_hash_bucket_size']").val(),
client_header_buffer_size: $("input[name='client_header_buffer_size']").val(),
};
// console.log(data);
orPost('set_cfg', data, function(rdata){
var rdata = $.parseJSON(rdata.data);
console.log(rdata);
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
});
}
+2 -2
View File
@@ -537,7 +537,7 @@ def setSessionConf(version):
passwd = args['passwd']
save_handler = args['save_handler']
if save_handler != "file":
if save_handler != "files":
iprep = r"(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})"
if not re.search(iprep, ip):
return mw.returnJson(False, '请输入正确的IP地址')
@@ -605,7 +605,7 @@ def setSessionConf(version):
phpini = re.sub('\n;session.save_path = "' + "/var/lib/php/sessions" + '"',
'\n;session.save_path = "' + "/var/lib/php/sessions" + '"' + val, phpini)
if save_handler == "file":
if save_handler == "files":
rep = r'\nsession.save_path\s*=\s*(.+)\r?\n'
val = r'\nsession.save_path = "' + session_tmp + '"\n'
if re.search(rep, phpini):
+3 -3
View File
@@ -380,7 +380,7 @@ function getSessionConfig(version){
}
var rdata = rdata.data;
var cacheList = "<option value='file' " + (rdata.save_handler == "file" ? 'selected' : '') + ">file</option>" +
var cacheList = "<option value='files' " + (rdata.save_handler == "files" ? 'selected' : '') + ">files</option>" +
"<option value='redis' " + (rdata.save_handler == "redis" ? 'selected' : '') + ">redis</option>" +
"<option value='memcache' " + (rdata.save_handler == "memcache" ? 'selected' : '') + ">memcache</option>" +
"<option value='memcached' " + (rdata.save_handler == "memcached" ? 'selected' : '') + ">memcached</option>";
@@ -405,7 +405,7 @@ function getSessionConfig(version){
$(".soft-man-con").html(con);
if (rdata.save_handler == 'file'){
if (rdata.save_handler == 'files'){
$('input[name="ip"]').attr('disabled','disabled');
$('input[name="port"]').attr('disabled','disabled');
$('input[name="passwd"]').attr('placeholder','如果没有密码留空');
@@ -436,7 +436,7 @@ function getSessionConfig(version){
$('input[name="port"]').removeAttr('disabled');
$('input[name="passwd"]').removeAttr('disabled');
break;
case 'file':
case 'files':
$('input[name="ip"]').val("").attr('disabled','disabled');
$('input[name="port"]').val("").attr('disabled','disabled');
$('input[name="passwd"]').val("").attr('disabled','disabled');
+2 -2
View File
@@ -539,7 +539,7 @@ def setSessionConf(version):
passwd = args['passwd']
save_handler = args['save_handler']
if save_handler != "file":
if save_handler != "files":
iprep = r"(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})"
if not re.search(iprep, ip):
return mw.returnJson(False, '请输入正确的IP地址')
@@ -607,7 +607,7 @@ def setSessionConf(version):
phpini = re.sub('\n;session.save_path = "/tmp"',
'\n;session.save_path = "/tmp"' + val, phpini)
if save_handler == "file":
if save_handler == "files":
rep = r'\nsession.save_path\s*=\s*(.+)\r?\n'
val = r'\nsession.save_path = "' + session_tmp + '"\n'
if re.search(rep, phpini):
+3 -3
View File
@@ -349,7 +349,7 @@ function getSessionConfig(version){
}
var rdata = rdata.data;
var cacheList = "<option value='file' " + (rdata.save_handler == "file" ? 'selected' : '') + ">file</option>" +
var cacheList = "<option value='files' " + (rdata.save_handler == "files" ? 'selected' : '') + ">files</option>" +
"<option value='redis' " + (rdata.save_handler == "redis" ? 'selected' : '') + ">redis</option>" +
"<option value='memcache' " + (rdata.save_handler == "memcache" ? 'selected' : '') + ">memcache</option>" +
"<option value='memcached' " + (rdata.save_handler == "memcached" ? 'selected' : '') + ">memcached</option>";
@@ -374,7 +374,7 @@ function getSessionConfig(version){
$(".soft-man-con").html(con);
if (rdata.save_handler == 'file'){
if (rdata.save_handler == 'files'){
$('input[name="ip"]').attr('disabled','disabled');
$('input[name="port"]').attr('disabled','disabled');
$('input[name="passwd"]').attr('placeholder','如果没有密码留空');
@@ -405,7 +405,7 @@ function getSessionConfig(version){
$('input[name="port"]').removeAttr('disabled');
$('input[name="passwd"]').removeAttr('disabled');
break;
case 'file':
case 'files':
$('input[name="ip"]').val("").attr('disabled','disabled');
$('input[name="port"]').val("").attr('disabled','disabled');
$('input[name="passwd"]').val("").attr('disabled','disabled');
+2 -2
View File
@@ -679,7 +679,7 @@ def setSessionConf(version):
passwd = args['passwd']
save_handler = args['save_handler']
if save_handler != "file":
if save_handler != "files":
iprep = r"(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})\.(2(5[0-5]{1}|[0-4]\d{1})|[0-1]?\d{1,2})"
if not re.search(iprep, ip):
return mw.returnJson(False, '请输入正确的IP地址')
@@ -743,7 +743,7 @@ def setSessionConf(version):
phpini = re.sub('\n;session.save_path = "' + session_tmp + '"',
'\n;session.save_path = "' + session_tmp + '"' + val, phpini)
if save_handler == "file":
if save_handler == "files":
rep = r'\nsession.save_path\s*=\s*(.+)\r?\n'
val = r'\nsession.save_path = "' + session_tmp + '"\n'
if re.search(rep, phpini):
+7 -4
View File
@@ -81,7 +81,10 @@ function phpSetConfig(version) {
}
var phpCon = '<style>.conf_p p{margin-bottom: 2px}</style><div class="conf_p" style="margin-bottom:0">\
' + mlist + '\
<div style="margin-top:10px; padding-right:15px" class="text-right"><button class="btn btn-success btn-sm mr5" onclick="phpSetConfig(' + version + ')">刷新</button><button class="btn btn-success btn-sm" onclick="submitConf(' + version + ')">保存</button></div>\
<div style="margin-top:10px; padding-right:15px" class="text-right">\
<button class="btn btn-success btn-sm mr5" onclick="phpSetConfig(' + version + ')">刷新</button>\
<button class="btn btn-success btn-sm" onclick="submitConf(' + version + ')">保存</button>\
</div>\
</div>'
$(".soft-man-con").html(phpCon);
});
@@ -339,7 +342,7 @@ function getSessionConfig(version){
}
var rdata = rdata.data;
var cacheList = "<option value='file' " + (rdata.save_handler == "file" ? 'selected' : '') + ">file</option>" +
var cacheList = "<option value='files' " + (rdata.save_handler == "files" ? 'selected' : '') + ">files</option>" +
"<option value='redis' " + (rdata.save_handler == "redis" ? 'selected' : '') + ">redis</option>" +
"<option value='memcache' " + (rdata.save_handler == "memcache" ? 'selected' : '') + ">memcache</option>" +
"<option value='memcached' " + (rdata.save_handler == "memcached" ? 'selected' : '') + ">memcached</option>";
@@ -364,7 +367,7 @@ function getSessionConfig(version){
$(".soft-man-con").html(con);
if (rdata.save_handler == 'file'){
if (rdata.save_handler == 'files'){
$('input[name="ip"]').attr('disabled','disabled');
$('input[name="port"]').attr('disabled','disabled');
$('input[name="passwd"]').attr('placeholder','如果没有密码留空');
@@ -395,7 +398,7 @@ function getSessionConfig(version){
$('input[name="port"]').removeAttr('disabled');
$('input[name="passwd"]').removeAttr('disabled');
break;
case 'file':
case 'files':
$('input[name="ip"]').val("").attr('disabled','disabled');
$('input[name="port"]').val("").attr('disabled','disabled');
$('input[name="passwd"]').val("").attr('disabled','disabled');
+3 -2
View File
@@ -17,6 +17,7 @@
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=phpmyadmin&f=js/phpmyadmin.js");
pluginService('phpmyadmin');
$.getScript( "/plugins/file?name=phpmyadmin&f=js/phpmyadmin.js", function(){
pluginService('phpmyadmin');
});
</script>
+51 -6
View File
@@ -57,7 +57,7 @@ def getConf():
def getConfInc():
return getServerDir() + '/phpmyadmin/config.inc.php'
return getServerDir() + "/" + getCfg()['path'] + '/config.inc.php'
def getPort():
@@ -74,7 +74,8 @@ def getHomePage():
ip = '127.0.0.1'
if not mw.isAppleSystem():
ip = mw.getLocalIp()
url = 'http://' + ip + ':' + port + '/phpmyadmin/index.php'
url = 'http://' + ip + ':' + port + \
'/' + getCfg()['path'] + '/index.php'
return mw.returnJson(True, 'OK', url)
except Exception as e:
return mw.returnJson(False, '插件未启动!')
@@ -124,6 +125,12 @@ def contentReplace(content):
content = content.replace('{$CHOOSE_DB}', 'MariaDB')
content = content.replace('{$CHOOSE_DB_DIR}', 'mariadb')
content = content.replace('{$PMA_PATH}', cfg['path'])
port = cfg["port"]
rep = 'listen\s*(.*);'
content = re.sub(rep, "listen " + port + ';', content)
return content
@@ -133,6 +140,7 @@ def initCfg():
data = {}
data['port'] = '888'
data['choose'] = ''
data['path'] = ''
data['username'] = 'admin'
data['password'] = 'admin'
mw.writeFile(cfg, json.dumps(data))
@@ -161,7 +169,7 @@ def returnCfg():
def status():
conf = getConf()
conf_inc = getServerDir() + '/phpmyadmin/config.inc.php'
conf_inc = getServerDir() + "/" + getCfg()["path"] + '/config.inc.php'
# 两个文件都在,才算启动成功
if os.path.exists(conf) and os.path.exists(conf_inc):
return 'start'
@@ -171,9 +179,16 @@ def status():
def start():
initCfg()
pma_dir = getServerDir() + "/phpmyadmin"
if os.path.exists(pma_dir):
rand_str = mw.getRandomString(6)
rand_str = rand_str.lower()
pma_dir_dst = pma_dir + "_" + rand_str
mw.execShell("mv " + pma_dir + " " + pma_dir_dst)
setCfg('path', 'phpmyadmin_' + rand_str)
file_tpl = getPluginDir() + '/conf/phpmyadmin.conf'
file_run = getConf()
if not os.path.exists(file_run):
centent = mw.readFile(file_tpl)
centent = contentReplace(centent)
@@ -187,11 +202,12 @@ def start():
setCfg('password', username)
mw.writeFile(pma_path, pass_cmd)
tmp = getServerDir() + '/phpmyadmin/tmp'
tmp = getServerDir() + "/" + getCfg()["path"] + '/tmp'
if not os.path.exists(tmp):
os.mkdir(tmp)
mw.execShell("chown -R www:www " + tmp)
conf_run = getServerDir() + '/phpmyadmin/config.inc.php'
conf_run = getServerDir() + "/" + getCfg()["path"] + '/config.inc.php'
if not os.path.exists(conf_run):
conf_tpl = getPluginDir() + '/conf/config.inc.php'
centent = mw.readFile(conf_tpl)
@@ -223,6 +239,12 @@ def restart():
def reload():
file_tpl = getPluginDir() + '/conf/phpmyadmin.conf'
file_run = getConf()
if os.path.exists(file_run):
centent = mw.readFile(file_tpl)
centent = contentReplace(centent)
mw.writeFile(file_run, centent)
return start()
@@ -284,6 +306,8 @@ def setPmaPort():
rep = 'listen\s*(.*);'
content = re.sub(rep, "listen " + port + ';', content)
mw.writeFile(file, content)
setCfg("port", port)
mw.restartWeb()
return mw.returnJson(True, '修改成功!')
@@ -345,6 +369,25 @@ def setPmaPassword():
return mw.returnJson(True, '修改成功!')
def setPmaPath():
args = getArgs()
data = checkArgs(args, ['path'])
if not data[0]:
return data[1]
path = args['path']
if len(path) < 5:
return mw.returnJson(False, '不能小于5位!')
old_path = getServerDir() + "/" + getCfg()['path']
new_path = getServerDir() + "/" + path
mw.execShell("mv " + old_path + " " + new_path)
setCfg('path', path)
return mw.returnJson(True, '修改成功!')
def accessLog():
return getServerDir() + '/access.log'
@@ -395,6 +438,8 @@ if __name__ == "__main__":
print(setPmaUsername())
elif func == 'set_pma_password':
print(setPmaPassword())
elif func == 'set_pma_path':
print(setPmaPath())
elif func == 'access_log':
print(accessLog())
elif func == 'error_log':
+11 -7
View File
@@ -10,10 +10,20 @@ serverPath=$(dirname "$rootPath")
install_tmp=${rootPath}/tmp/mw_install.pl
if [ -f ${rootPath}/bin/activate ];then
source ${rootPath}/bin/activate
fi
if [ "$sys_os" == "Darwin" ];then
BAK='_bak'
else
BAK=''
fi
sysName=`uname`
echo "use system: ${sysName}"
if [ ${sysName} == "Darwin" ]; then
if [ "${sysName}" == "Darwin" ]; then
OSNAME='macos'
elif grep -Eqi "CentOS" /etc/issue || grep -Eq "CentOS" /etc/*-release; then
OSNAME='centos'
@@ -51,13 +61,7 @@ Install_phpmyadmin()
cp -r $serverPath/source/phpmyadmin/$FDIR $serverPath/phpmyadmin/
cd $serverPath/phpmyadmin/ && mv $FDIR phpmyadmin
mkdir -p $serverPath/phpmyadmin/tmp
chown -R www:www $serverPath/phpmyadmin/tmp
if [ "$OSNAME" != 'macos' ];then
chown -R www:www $serverPath/phpmyadmin/tmp
fi
echo "${1}" > ${serverPath}/phpmyadmin/version.pl
echo '安装完成' > $install_tmp
+14
View File
@@ -120,6 +120,12 @@ function safeConf() {
<span class="tname">密码</span>\
<input style="width:110px" class="bt-input-text mr20" name="password" id="pmport" value="' + cfg['password'] + '" placeholder="密码" type="text">\
<button class="btn btn-success btn-sm" onclick="setPmaPassword()">保存</button>\
</div>\
<hr/>\
<div class="ver line">\
<span class="tname">路径名</span>\
<input style="width:180px" class="bt-input-text mr20" name="path" id="pmport" value="' + cfg['path'] + '" placeholder="" type="text">\
<button class="btn btn-success btn-sm" onclick="setPmaPath()">保存</button>\
</div>';
$(".soft-man-con").html(con);
});
@@ -150,6 +156,14 @@ function setPmaPassword(){
});
}
function setPmaPath(){
var path = $("input[name=path]").val();
pmaPost('set_pma_path',{'path':path}, function(data){
var rdata = $.parseJSON(data.data);
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
});
}
//修改phpmyadmin端口
function setPamPort() {
var pmport = $("#pmport").val();
+4 -1
View File
@@ -25,5 +25,8 @@ AutoRename no
AnonymousCantUpload no
MaxDiskUsage 99
CustomerProof yes
PIDFile /var/run/pure-ftpd.pid
PassivePortRange 48000 50000
PIDFile {$SERVER_PATH}/pureftp/etc/pure-ftpd.pid
PureDB {$SERVER_PATH}/pureftp/etc/pureftpd.pdb
VerboseLog yes
+3 -2
View File
@@ -110,8 +110,9 @@ def initDreplace():
pureFtpdConfigBak = getServerDir() + "/etc/pure-ftpd.bak.conf"
pureFtpdConfigTpl = getPluginDir() + "/conf/pure-ftpd.conf"
if not os.path.exists(pureFtpdConfigBak):
shutil.copyfile(pureFtpdConfig, pureFtpdConfigBak)
if not os.path.exists(pureFtpdConfigBak) or not os.path.exists(pureFtpdConfig):
if os.path.exists(pureFtpdConfig):
shutil.copyfile(pureFtpdConfig, pureFtpdConfigBak)
content = mw.readFile(pureFtpdConfigTpl)
content = contentReplace(content)
mw.writeFile(pureFtpdConfig, content)
+1 -1
View File
@@ -10,7 +10,7 @@ my ($conffile, @flg) = @ARGV;
my $PUREFTPD;
-x && ($PUREFTPD=$_, last) for qw(
{$SERVER_PATH}/pureftp/sbin/pure-ftpd
/www/server/pure-ftpd/sbin/pure-ftpd
/www/server/pureftp/sbin/pure-ftpd
/www/server/pureftpd/sbin/pure-ftpd
/www/server/sbin/pure-ftpd
/usr/sbin/pure-ftpd
+1 -1
View File
@@ -17,7 +17,7 @@
# Pure-FTPd Settings
PURE_PERL="{$SERVER_PATH}/pureftp/sbin/pure-config.pl"
PURE_CONF="{$SERVER_PATH}/pureftp/etc/pure-ftpd.conf"
PURE_PID="/var/run/pure-ftpd.pid"
PURE_PID="{$SERVER_PATH}/pureftp/etc/pure-ftpd.pid"
RETVAL=0
prog="Pure-FTPd"
+22 -4
View File
@@ -53,8 +53,8 @@ Install_pureftp()
# curl -sSLo pure-ftpd-1.0.49.tar.gz https://download.pureftpd.org/pub/pure-ftpd/releases/pure-ftpd-1.0.49.tar.gz
if [ ! -f $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz ];then
# wget --no-check-certificate -O $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz $DOWNLOAD
curl -sSLo $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz $DOWNLOAD
wget --no-check-certificate -O $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz $DOWNLOAD
# curl -sSLo $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz $DOWNLOAD
fi
#检测文件是否损坏.
@@ -66,7 +66,8 @@ Install_pureftp()
else
# 重新下载
rm -rf $serverPath/source/pureftp/pure-ftpd-${VER}
curl -sSLo $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz $DOWNLOAD
wget --no-check-certificate -O $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz $DOWNLOAD
# curl -sSLo $serverPath/source/pureftp/pure-ftpd-${VER}.tar.gz $DOWNLOAD
fi
fi
@@ -75,7 +76,24 @@ Install_pureftp()
fi
cd $serverPath/source/pureftp/pure-ftpd-${VER} && ./configure --prefix=${serverPath}/pureftp \
   --with-everything && make && make install && make clean
   CFLAGS=-O2 \
--with-puredb \
--with-quotas \
--with-cookie \
--with-virtualhosts \
--with-diraliases \
--with-sysquotas \
--with-ratios \
--with-altlog \
--with-paranoidmsg \
--with-shadow \
--with-welcomemsg \
--with-throttling \
--with-uploadscript \
--with-language=english \
--with-rfc2640 \
--with-ftpwho \
--with-tls && make && make install && make clean
if [ -d ${serverPath}/pureftp ];then
echo "${1}" > ${serverPath}/pureftp/version.pl
+1 -11
View File
@@ -1,18 +1,8 @@
function str2Obj(str){
var data = {};
kv = str.split('&');
for(i in kv){
v = kv[i].split('=');
data[v[0]] = v[1];
}
return data;
}
function ftpPost(method,args,callback){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(str2Obj(args));
_args = JSON.stringify(toArrayObject(args));
} else {
_args = JSON.stringify(args);
}
+1 -1
View File
@@ -132,7 +132,7 @@ def loadLuaSiteFile():
tmp = {
"name": "unset",
"domain": [],
"domains": [],
}
content.append(tmp)
listToLuaFile(lua_site, content)
+1 -1
View File
@@ -1,6 +1,6 @@
{
"sort": 7,
"ps": "[DEV]网站统计报表[此插件-需要小白鼠反馈问题,慎用!]",
"ps": "网站统计报表",
"name": "webstats",
"title": "网站统计",
"shell": "install.sh",
+2 -2
View File
@@ -149,7 +149,7 @@ function _M.get_sn(self, input_sn)
cache:set(input_sn, v['name'], 86400)
return v["name"]
end
-- self:D("get_sn:"..json.encode(v))
for _,dst_domain in ipairs(v['domains'])
do
if input_sn == dst_domain then
@@ -423,7 +423,7 @@ function _M.cron(self)
-- url统计
if local_url_stats then
for url_md5,url_val in pairs(local_url_stats) do
self:update_statistics_uri( db, url_val["uri"],url_md5, url_val["url_num"], url_val["body_length"])
self:update_statistics_uri( db, tostring(url_val["uri"]),url_md5, url_val["url_num"], url_val["body_length"])
end
end
+1 -1
View File
@@ -332,7 +332,7 @@ log_by_lua_block {
local request_time = C:get_request_time()
local client_port = ngx.var.remote_port
local real_server_name = input_sn
local uri = ngx.var.uri
local uri = tostring(ngx.var.uri)
local status_code = ngx.status
local protocol = ngx.var.server_protocol
local request_uri = ngx.var.request_uri
+1 -1
View File
@@ -14,7 +14,7 @@ chardet==3.0.4
flask-sqlalchemy==2.3.2
cryptography==3.3.2
requests==2.20.0
ConfigParser==5.2.0
configparser==5.2.0
python-engineio==4.3.2
python-socketio>=4.2.0
flask-socketio==5.2.0
+92 -113
View File
@@ -26,12 +26,12 @@ function getLogs(id){
shadeClose:false,
closeBtn:1,
content:'<div class="setchmod bt-form pd20 pb70">'
+'<pre id="crontab-log" style="overflow: auto; border: 0px none; line-height:23px;padding: 15px; margin: 0px; white-space: pre-wrap; height: 405px; background-color: rgb(51,51,51);color:#f1f1f1;border-radius:0px;font-family:"></pre>'
+'<div class="bt-form-submit-btn" style="margin-top: 0px;">'
+'<button type="button" class="btn btn-success btn-sm" onclick="closeLogs('+id+')">清空</button>'
+'<button type="button" class="btn btn-danger btn-sm" onclick="layer.closeAll()">关闭</button>'
+'</div>'
+'</div>'
+'<pre id="crontab-log" style="overflow: auto; border: 0px none; line-height:23px;padding: 15px; margin: 0px; white-space: pre-wrap; height: 405px; background-color: rgb(51,51,51);color:#f1f1f1;border-radius:0px;font-family:"></pre>'
+'<div class="bt-form-submit-btn" style="margin-top: 0px;">'
+'<button type="button" class="btn btn-success btn-sm" onclick="closeLogs('+id+')">清空</button>'
+'<button type="button" class="btn btn-danger btn-sm" onclick="layer.closeAll()">关闭</button>'
+'</div>'
+'</div>'
});
setTimeout(function(){
@@ -68,26 +68,25 @@ function getCronData(page){
}
}
cbody += "<tr>\
<td><input type='checkbox' onclick='checkSelect();' title='"+rdata.data[i].name+"' name='id' value='"+rdata.data[i].id+"'></td>\
<td>"+rdata.data[i].name+"</td>\
<td>"+status+"</td>\
<td>"+rdata.data[i].type+"</td>\
<td>"+rdata.data[i].cycle+"</td>\
<td>"+cron_save +"</td>\
<td>"+cron_backupto+"</td>\
<td>"+rdata.data[i].addtime+"</td>\
<td>\
<a href=\"javascript:startTask("+rdata.data[i].id+");\" class='btlink'>执行</a> | \
<a href=\"javascript:editTaskInfo('"+rdata.data[i].id+"');\" class='btlink'>编辑</a> | \
<a href=\"javascript:getLogs("+rdata.data[i].id+");\" class='btlink'>日志</a> | \
<a href=\"javascript:planDel("+rdata.data[i].id+" ,'"+rdata.data[i].name.replace('\\','\\\\').replace("'","\\'").replace('"','')+"');\" class='btlink'>删除</a>\
</td>\
</tr>";
cbody += "<tr><td><input type='checkbox' onclick='checkSelect();' title='"+rdata.data[i].name+"' name='id' value='"+rdata.data[i].id+"'></td>\
<td>"+rdata.data[i].name+"</td>\
<td>"+status+"</td>\
<td>"+rdata.data[i].type+"</td>\
<td>"+rdata.data[i].cycle+"</td>\
<td>"+cron_save +"</td>\
<td>"+cron_backupto+"</td>\
<td>"+rdata.data[i].addtime+"</td>\
<td>\
<a href=\"javascript:startTask("+rdata.data[i].id+");\" class='btlink'>执行</a> | \
<a href=\"javascript:editTaskInfo('"+rdata.data[i].id+"');\" class='btlink'>编辑</a> | \
<a href=\"javascript:getLogs("+rdata.data[i].id+");\" class='btlink'>日志</a> | \
<a href=\"javascript:planDel("+rdata.data[i].id+" ,'"+rdata.data[i].name.replace('\\','\\\\').replace("'","\\'").replace('"','')+"');\" class='btlink'>删除</a>\
</td>\
</tr>";
}
}
$('#cronbody').html(cbody);
$('#softPage').html(rdata.list)
$('#softPage').html(rdata.list);
},'json');
}
@@ -97,12 +96,18 @@ function setTaskStatus(id,status){
if (index > 0) {
var loadT = layer.msg('正在设置状态,请稍后...',{icon:16,time:0,shade: [0.3, '#000']});
$.post('/crontab/set_cron_status',{id:id},function(rdata){
layer.closeAll();
layer.close(confirm);
layer.msg(rdata.data,{icon:rdata.status?1:2});
if(rdata.status) {
getCronData(1);
if (!rdata.status){
layer.msg(rdata.msg,{icon:rdata.status?1:2});
return;
}
showMsg(rdata.msg,function(){
layer.close(loadT);
layer.close(confirm);
getCronData(1);
},{icon:rdata.status?1:2},2000);
},'json');
}
});
@@ -110,22 +115,24 @@ function setTaskStatus(id,status){
//执行任务脚本
function startTask(id){
layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']});
var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']});
var data='id='+id;
$.post('/crontab/start_task',data,function(rdata){
layer.closeAll();
layer.msg(rdata.msg,{icon:rdata.status?1:2});
showMsg(rdata.msg, function(){
layer.closeAll();
},{icon:rdata.status?1:2,time:2000});
},'json');
}
//清空日志
function closeLogs(id){
layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']});
var loadT = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']});
var data='id='+id;
$.post('/crontab/del_logs',data,function(rdata){
layer.closeAll();
layer.msg(rdata.msg,{icon:rdata.status?1:2});
showMsg(rdata.msg, function(){
layer.closeAll();
},{icon:rdata.status?1:2,time:2000});
},'json');
}
@@ -136,61 +143,13 @@ function planDel(id,name){
var load = layer.msg('正在处理,请稍候...',{icon:16,time:0,shade: [0.3, '#000']});
var data='id='+id;
$.post('/crontab/del',data,function(rdata){
layer.close(load);
showMsg(rdata.msg, function(){
layer.closeAll();
getCronData(1);
},{icon:rdata.status?1:2,time:2000});
},'json');
});
}
//批量删除
function allDeleteCron(){
var checkList = $("input[name=id]");
var dataList = new Array();
for(var i=0;i<checkList.length;i++){
if(!checkList[i].checked) continue;
var tmp = new Object();
tmp.name = checkList[i].title;
tmp.id = checkList[i].value;
dataList.push(tmp);
}
safeMessage('批量删除任务!',"<a style='color:red;'>"+lan.get('del_all_task',[dataList.length])+"</a>",function(){
layer.closeAll();
syncDeleteCron(dataList,0,'');
});
}
//模拟同步开始批量删除数据库
function syncDeleteCron(dataList,successCount,errorMsg){
if(dataList.length < 1) {
layer.msg(lan.get('del_all_task_ok',[successCount]),{icon:1});
return;
}
var loadT = layer.msg(lan.get('del_all_task_the',[dataList[0].name]),{icon:16,time:0,shade: [0.3, '#000']});
$.ajax({
type:'POST',
url:'/crontab?action=DelCrontab',
data:'id='+dataList[0].id+'&name='+dataList[0].name,
async: true,
success:function(frdata){
layer.close(loadT);
if(frdata.status){
successCount++;
$("input[title='"+dataList[0].name+"']").parents("tr").remove();
}else{
if(!errorMsg){
errorMsg = '<br><p>'+lan.crontab.del_task_err+'</p>';
}
errorMsg += '<li>'+dataList[0].name+' -> '+frdata.msg+'</li>'
}
dataList.splice(0,1);
syncDeleteCron(dataList,successCount,errorMsg);
}
});
}
function isURL(str_url){
var strRegex = '^(https|http|ftp|rtsp|mms)?://.+';
@@ -234,15 +193,15 @@ function planAdd(){
break;
}
var where1 = $('#excode_week b').attr('val');
$("#set-Config input[name='where1']").val(where1);
if(where1 > is1 || where1 < is2){
$("#ptime input[name='where1']").focus();
layer.msg('表单不合法,请重新输入!',{icon:2});
return;
}
where1 = $('#excode_week b').attr('val');
$("#set-Config input[name='where1']").val(where1);
var hour = $("#ptime input[name='hour']").val();
if(hour > 23 || hour < 0){
$("#ptime input[name='hour']").focus();
@@ -322,20 +281,32 @@ function planAdd(){
var where1 = $("#ptime input[name='where1']").val();
$("#set-Config input[name='where1']").val(where1);
}
if (type == 'day-n'){
var where1 = $("#ptime input[name='where1']").val();
$("#set-Config input[name='where1']").val(where1);
}
if (type == 'hour-n'){
var where1 = $("#ptime input[name='where1']").val();
$("#set-Config input[name='hour']").val(where1);
}
$("#set-Config input[name='sName']").val(sName);
layer.msg('正在添加,请稍候...!',{icon:16,time:0,shade: [0.3, '#000']});
var data = $("#set-Config").serialize() + '&sBody='+sBody + '&urladdress=' + urladdress;
console.log(data);
// console.log(data);
$.post('/crontab/add',data,function(rdata){
if(!rdata.status) {
layer.msg(rdata.msg,{icon:2, time:2000});
return;
}
layer.closeAll();
layer.msg(rdata.msg,{icon:rdata.status?1:2});
getCronData(1);
showMsg(rdata.msg, function(){
layer.closeAll();
getCronData(1);
},{icon:rdata.status?1:2}, 2000);
},'json');
}
@@ -362,25 +333,25 @@ function allAddCrontab(dataList,successCount,errorMsg){
$("#set-Config input[name='sName']").val(dataList[0]);
var pdata = $("#set-Config").serialize() + '&sBody=&urladdress=';
$.ajax({
type:'POST',
url:'/crontab/add',
data:pdata,
async: true,
success:function(frdata){
layer.close(loadT);
if(frdata.status){
successCount++;
getCronData(1);
}else{
if(!errorMsg){
errorMsg = '<br><p>'+lan.crontab.backup_all_err+'</p>';
}
errorMsg += '<li>'+dataList[0]+' -> '+frdata.msg+'</li>'
type:'POST',
url:'/crontab/add',
data:pdata,
async: true,
success:function(frdata){
layer.close(loadT);
if(frdata.status){
successCount++;
getCronData(1);
}else{
if(!errorMsg){
errorMsg = '<br><p>'+lan.crontab.backup_all_err+'</p>';
}
dataList.splice(0,1);
allAddCrontab(dataList,successCount,errorMsg);
errorMsg += '<li>'+dataList[0]+' -> '+frdata.msg+'</li>'
}
dataList.splice(0,1);
allAddCrontab(dataList,successCount,errorMsg);
}
});
}
@@ -829,12 +800,20 @@ function editTaskInfo(id){
obj.from.where1 = obj.from.minute;
obj.from.minute = '';
}
layer.msg('正在保存编辑内容,请稍后...',{icon:16,time:0,shade: [0.3, '#000']});
var loadT = layer.msg('正在保存编辑内容,请稍后...',{icon:16,time:0,shade: [0.3, '#000']});
$.post('/crontab/modify_crond',obj.from,function(rdata){
layer.closeAll();
getCronData(1);
layer.msg(rdata.msg,{icon:rdata.status?1:2});
initDropdownMenu();
if (!rdata.status){
layer.msg(rdata.msg,{icon:rdata.status?1:2});
return;
}
showMsg(rdata.msg, function(){
layer.closeAll();
getCronData(1);
initDropdownMenu();
},{icon:rdata.status?1:2}, 2000);
},'json');
});
},100);
+83 -88
View File
@@ -329,11 +329,11 @@ function getFiles(Path) {
var cnametext =fmp[0] + fmp[5];
fmp[0] = fmp[0].replace(/'/,"\\'");
if(cnametext.length>20){
cnametext = cnametext.substring(0,20)+'...'
cnametext = cnametext.substring(0,20) + '...';
}
if(isChineseChar(cnametext)){
if(cnametext.length>10){
cnametext = cnametext.substring(0,10)+'...'
cnametext = cnametext.substring(0,10) + '...';
}
}
var timetext ='--';
@@ -376,11 +376,11 @@ function getFiles(Path) {
var cnametext =fmp[0] + fmp[5];
fmp[0] = fmp[0].replace(/'/,"\\'");
if(cnametext.length>48){
cnametext = cnametext.substring(0,48)+'...'
cnametext = cnametext.substring(0,48) + '...';
}
if(isChineseChar(cnametext)){
if(cnametext.length>16){
cnametext = cnametext.substring(0,16)+'...'
cnametext = cnametext.substring(0,16) + '...';
}
}
if(displayZip != -1){
@@ -400,52 +400,52 @@ function getFiles(Path) {
totalSize += parseInt(fmp[1]);
if(getCookie("rank")=="a"){
body += "<tr class='folderBoxTr' data-path='" + rdata.PATH +"/"+ fmp[0] + "' filetype='" + fmp[0] + "'><td><input type='checkbox' name='id' value='"+fmp[0]+"'></td>\
<td class='column-name'><span class='ico ico-"+(getExtName(fmp[0]))+"'></span><a class='text' title='" + fmp[0] + fmp[5] + "'>" + cnametext + "</a></td>\
<td>" + (toSize(fmp[1])) + "</td>\
<td>" + ((fmp[2].length > 11)?fmp[2]:getLocalTime(fmp[2])) + "</td>\
<td>"+fmp[3]+"</td>\
<td>"+fmp[4]+"</td>\
<td class='editmenu'>\
<span><a class='btlink' href='javascript:;' onclick=\"copyFile('" + rdata.PATH +"/"+ fmp[0] + "')\">复制</a> | \
<a class='btlink' href='javascript:;' onclick=\"cutFile('" + rdata.PATH +"/"+ fmp[0] + "')\">剪切</a> | \
<a class='btlink' href='javascript:;' onclick=\"reName(0,'" + fmp[0] + "')\">重命名</a> | \
<a class='btlink' href=\"javascript:setChmod(0,'" + rdata.PATH +"/"+ fmp[0] + "');\">权限</a> | \
<a class='btlink' href=\"javascript:zip('" + rdata.PATH +"/" +fmp[0] + "');\">压缩</a> | \
"+bodyZip+download+"\
<a class='btlink' href='javascript:;' onclick=\"deleteFile('" + rdata.PATH +"/"+ fmp[0] + "')\">删除</a>\
</span></td></tr>";
<td class='column-name'><span class='ico ico-"+(getExtName(fmp[0]))+"'></span><a class='text' title='" + fmp[0] + fmp[5] + "'>" + cnametext + "</a></td>\
<td>" + (toSize(fmp[1])) + "</td>\
<td>" + ((fmp[2].length > 11)?fmp[2]:getLocalTime(fmp[2])) + "</td>\
<td>"+fmp[3]+"</td>\
<td>"+fmp[4]+"</td>\
<td class='editmenu'>\
<span><a class='btlink' href='javascript:;' onclick=\"copyFile('" + rdata.PATH +"/"+ fmp[0] + "')\">复制</a> | \
<a class='btlink' href='javascript:;' onclick=\"cutFile('" + rdata.PATH +"/"+ fmp[0] + "')\">剪切</a> | \
<a class='btlink' href='javascript:;' onclick=\"reName(0,'" + fmp[0] + "')\">重命名</a> | \
<a class='btlink' href=\"javascript:setChmod(0,'" + rdata.PATH +"/"+ fmp[0] + "');\">权限</a> | \
<a class='btlink' href=\"javascript:zip('" + rdata.PATH +"/" +fmp[0] + "');\">压缩</a> | \
"+bodyZip+download+"\
<a class='btlink' href='javascript:;' onclick=\"deleteFile('" + rdata.PATH +"/"+ fmp[0] + "')\">删除</a>\
</span></td>\
</tr>";
}
else{
body += "<div class='file folderBox menufile' data-path='" + rdata.PATH +"/"+ fmp[0] + "' filetype='"+fmp[0]+"' title='文件名:" + fmp[0]+"&#13;大小:"
+ toSize(fmp[1])+"&#13;修改时间:"+getLocalTime(fmp[2])+"&#13;权限:"+fmp[3]+"&#13;所有者:"+fmp[4]+"'>\
<input type='checkbox' name='id' value='"+fmp[0]+"'>\
<div class='ico ico-"+(getExtName(fmp[0]))+"'></div>\
<div class='titleBox'><span class='tname'>" + fmp[0] + "</span></div>\
</div>";
body += "<div class='file folderBox menufile' data-path='" + rdata.PATH +"/"+ fmp[0] + "' filetype='"+fmp[0]+"' title='文件名:" + fmp[0]+"&#13;大小:"
+ toSize(fmp[1])+"&#13;修改时间:"+getLocalTime(fmp[2])+"&#13;权限:"+fmp[3]+"&#13;所有者:"+fmp[4]+"'>\
<input type='checkbox' name='id' value='"+fmp[0]+"'>\
<div class='ico ico-"+(getExtName(fmp[0]))+"'></div>\
<div class='titleBox'><span class='tname'>" + fmp[0] + "</span></div>\
</div>";
}
}
var dirInfo = '(共{1}个目录与{2}个文件,大小:'.replace('{1}',rdata.DIR.length+'').replace('{2}',rdata.DIR.length+'')+'<font id="pathSize">'
+ (toSize(totalSize))+'<a class="btlink ml5" onClick="getPathSize()">获取</a></font>)';
$("#DirInfo").html(dirInfo);
if(getCookie('rank')=='a'){
if( getCookie('rank') == 'a' ){
var tablehtml = '<table width="100%" border="0" cellpadding="0" cellspacing="0" class="table table-hover">\
<thead>\
<tr>\
<th width="30"><input type="checkbox" id="setBox" placeholder=""></th>\
<th>文件名</th>\
<th>大小</th>\
<th>修改时间</th>\
<th>权限</th>\
<th>所有者</th>\
<th style="text-align: right;" width="330">操作</th>\
</tr>\
</thead>\
<tbody id="filesBody" class="list-list">'+body+'</tbody>\
</table>';
<thead>\
<tr>\
<th width="30"><input type="checkbox" id="setBox" placeholder=""></th>\
<th>文件名</th>\
<th>大小</th>\
<th>修改时间</th>\
<th>权限</th>\
<th>所有者</th>\
<th style="text-align: right;" width="330">操作</th>\
</tr>\
</thead>\
<tbody id="filesBody" class="list-list">'+body+'</tbody>\
</table>';
$("#fileCon").removeClass("fileList").html(tablehtml);
$("#tipTools").width($("#fileCon").width());
}
else{
} else {
$("#fileCon").addClass("fileList").html(body);
$("#tipTools").width($("#fileCon").width());
}
@@ -518,25 +518,19 @@ function getFiles(Path) {
});
// //禁用右键
// $("#fileCon").bind("contextmenu",function(e){
// return false;
// });
// bindselect();
//禁用右键
$("#fileCon").bind("contextmenu",function(e){
return false;
});
bindselect();
// //绑定右键
// $("#fileCon").mousedown(function(e){
// var count = totalFile();
// if(e.which == 3) {
// if(count>1){
// RClickAll(e);
// }
// else{
// return
// }
// }
// });
$("#fileCon").mousedown(function(e){
var count = totalFile();
if(e.which == 3) {
if(count>1){rightMenuClickAll(e);} else {return;}
}
});
$(".folderBox,.folderBoxTr").mousedown(function(e){
// console.log(e);
@@ -1217,7 +1211,7 @@ function zip(dirName,submits) {
}
//解压目录
function unZip(fileName,type) {
function unZip(fileName, type) {
var path = $("#DirPathPlace input").val();
if(type.length ==3){
var sfile = encodeURIComponent($("#sfile").val());
@@ -1233,7 +1227,7 @@ function unZip(fileName,type) {
return
}
type = (type == 1) ? 'tar':'zip'
type = (type == 1) ? 'tar':'zip';
var umpass = '';
if(type == 'zip'){
umpass = '<div class="line"><span class="tname">'+lan.files.zip_pass_title+'</span><input type="text" class="bt-input-text" id="unpass" value="" placeholder="'+lan.files.zip_pass_msg+'" style="width:330px" /></div>'
@@ -1245,19 +1239,19 @@ function unZip(fileName,type) {
area: '490px',
title: '解压文件',
content: '<div class="bt-form pd20 pb70">'
+'<div class="line unzipdiv">'
+'<span class="tname">'+lan.files.unzip_name+'</span><input type="text" class="bt-input-text" id="sfile" value="' +fileName + '" placeholder="'+lan.files.unzip_name_title+'" style="width:330px" /></div>'
+'<div class="line"><span class="tname">'+lan.files.unzip_to+'</span><input type="text" class="bt-input-text" id="dfile" value="'+path + '" placeholder="'+lan.files.unzip_to+'" style="width:330px" /></div>'
+ umpass +'<div class="line"><span class="tname">'+lan.files.unzip_coding+'</span><select class="bt-input-text" name="coding">'
+'<option value="UTF-8">UTF-8</option>'
+'<option value="gb18030">GBK</option>'
+'</select>'
+'</div>'
+'<div class="bt-form-submit-btn">'
+'<button type="button" class="btn btn-danger btn-sm btn-title" onclick="layer.closeAll()">'+lan.public.close+'</button>'
+'<button type="button" id="ReNameBtn" class="btn btn-success btn-sm btn-title" onclick="unZip(\'' + fileName + '\',\''+type+'\')">'+lan.files.file_menu_unzip+'</button>'
+'</div>'
+'</div>'
+'<div class="line unzipdiv">'
+'<span class="tname">'+lan.files.unzip_name+'</span><input type="text" class="bt-input-text" id="sfile" value="' +fileName + '" placeholder="'+lan.files.unzip_name_title+'" style="width:330px" /></div>'
+'<div class="line"><span class="tname">'+lan.files.unzip_to+'</span><input type="text" class="bt-input-text" id="dfile" value="'+path + '" placeholder="'+lan.files.unzip_to+'" style="width:330px" /></div>'
+ umpass +'<div class="line"><span class="tname">'+lan.files.unzip_coding+'</span><select class="bt-input-text" name="coding">'
+'<option value="UTF-8">UTF-8</option>'
+'<option value="gb18030">GBK</option>'
+'</select>'
+'</div>'
+'<div class="bt-form-submit-btn">'
+'<button type="button" class="btn btn-danger btn-sm btn-title" onclick="layer.closeAll()">'+lan.public.close+'</button>'
+'<button type="button" id="ReNameBtn" class="btn btn-success btn-sm btn-title" onclick="unZip(\'' + fileName + '\',\''+type+'\')">'+lan.files.file_menu_unzip+'</button>'
+'</div>'
+'</div>'
});
}
@@ -1323,7 +1317,8 @@ function uploadFiles(){
title:lan.files.up_title,
area: ['500px','300px'],
shadeClose:false,
content:'<div class="fileUploadDiv"><input type="hidden" id="input-val" value="'+path+'" />\
content:'<div class="fileUploadDiv">\
<input type="hidden" id="input-val" value="'+path+'" />\
<input type="file" id="file_input" multiple="true" autocomplete="off" />\
<button type="button" id="opt" autocomplete="off">添加文件</button>\
<button type="button" id="up" autocomplete="off" >开始上传</button>\
@@ -1337,7 +1332,8 @@ function uploadFiles(){
</select>\
</span>\
<button type="button" id="filesClose" autocomplete="off" onClick="layer.closeAll()" >关闭</button>\
<ul id="up_box"></ul></div>'
<ul id="up_box"></ul>\
</div>'
});
uploadStart();
}
@@ -1558,28 +1554,27 @@ $("#DirPathPlace input").keyup(function(e){
function pathPlaceBtn(path){
var html = '';
var title = '';
var Dpath = path;
if(path == '/'){
html ='<li><a title="/">'+lan.files.path_root+'</a></li>';
html = '<li><a title="/">'+lan.files.path_root+'</a></li>';
}
else{
Dpath = path.split("/");
for(var i = 0; i<Dpath.length; i++ ){
title += Dpath[i]+'/';
Dpath[0] = lan.files.path_root;
html +='<li><a title="'+title+'">'+Dpath[i]+'</a></li>';
var dst_path = path.split("/");
for(var i = 0; i<dst_path.length; i++ ){
title += dst_path[i]+'/';
dst_path[0] = lan.files.path_root;
html += '<li><a title="'+title+'">'+dst_path[i]+'</a></li>';
}
}
html = '<div style="width:1200px;height:26px"><ul>'+html+'</ul></div>';
$("#PathPlaceBtn").html(html);
$("#PathPlaceBtn ul li a").click(function(e){
var Gopath = $(this).attr("title");
if(Gopath.length>1){
if(Gopath.substr(Gopath.length-1,Gopath.length) =='/'){
Gopath = Gopath.substr(0,Gopath.length-1);
var go_path = $(this).attr("title");
if(go_path.length>1){
if(go_path.substr(go_path.length-1,go_path.length) =='/'){
Gopath = go_path.substr(0,go_path.length-1);
}
}
getFiles(Gopath);
getFiles(go_path);
e.stopPropagation();
});
pathLeft();
@@ -1590,10 +1585,10 @@ function pathLeft(){
var SpanPathWidth = $("#PathPlaceBtn").width() - 50;
var Ml = UlWidth - SpanPathWidth;
if(UlWidth > SpanPathWidth ){
$("#PathPlaceBtn ul").css("left",-Ml)
$("#PathPlaceBtn ul").css("left",-Ml);
}
else{
$("#PathPlaceBtn ul").css("left",0)
$("#PathPlaceBtn ul").css("left",0);
}
}
//路径快捷点击
+2
View File
@@ -200,6 +200,8 @@ function uploadStart(d) {
}
if(!d) {
getFiles(getCookie("open_dir_path"));
} else{
d();
}
},
error: function(j) {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 22 KiB

+1 -2
View File
@@ -55,10 +55,9 @@
<li onclick="javascript:batch(4);"><a style="cursor: pointer;">删除</a></li>
</ul>
<script src="/static/app/files.js?v={{config.version}}"></script>
<script src="/static/js/jquery-ui.min.js?v={{config.version}}"></script>
<script src="/static/js/jquery.contextify.min.js?v={{config.version}}"></script>
<script src="/static/app/files.js?v={{config.version}}"></script>
<script src="/static/app/upload.js?v={{config.version}}"></script>
<script type="text/javascript">
setTimeout(function() {
+10
View File
@@ -14,6 +14,15 @@
<!--[if lte IE 9]>
<script src="/static/js/requestAnimationFrame.js"></script>
<![endif]-->
<!-- 统计一下用户量,让我的开发更有激情 -->
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-FC74BB2RGD"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-FC74BB2RGD');
</script>
</head>
<body>
@@ -52,6 +61,7 @@
<script src="/static/build/addons/fullscreen/fullscreen.js?v={{config.version}}"></script>
<script src="/static/js/socket.io.min.js?v={{config.version}}"></script>
<script src="/static/build/addons/winptyCompat/winptyCompat.js?v={{config.version}}"></script>
<script src="/static/app/upload.js?v={{config.version}}"></script>
<script src="/static/app/public.js?v={{config.version}}"></script>
<script src="/static/js/echarts.min.js?v={{config.version}}"></script>
<script src="/static/js/jquery.dragsort-0.5.2.min.js?v={{config.version}}"></script>
+2 -2
View File
@@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="icon" href="/static/favicon.ico" type="image/x-icon" />
<link rel="shortcut icon" href="/static/favicon.ico" type="image/x-icon" />
<title>mdserver-web</title>
<title>{{data['title']}}</title>
<link rel="stylesheet" type="text/css" href="/static/css/site.css?v={{config.version}}">
<link rel="stylesheet" type="text/css" href="/static/css/login.css?v={{config.version}}">
<style type="text/css">
@@ -122,7 +122,7 @@
<div class="login">
<div class="account">
<form class="loginform" method="post" action="/login" onsubmit="return false;">
<div class="rlogo">mdserver-web</div>
<div class="rlogo">{{data['title']}}</div>
<div class="line"><input class="inputtxt" value="" name="username" datatype="*" nullmsg="请填写账户" errormsg="请填写账户" placeholder="账户" type="text"><div class="Validform_checktip"></div></div>
<div class="line"><input class="inputtxt" name="password" value="" datatype="*" nullmsg="请填写密码" errormsg="请填写密码" placeholder="密码" type="password">
<div class="Validform_checktip"></div>
+1 -1
View File
@@ -17,7 +17,7 @@
</div>
<div class="safe bgw mtb15 pd15">
<div class="info-title-tips">
<p><span class="glyphicon glyphicon-alert" style="color:#f39c12; margin-right:10px;"></span>使用mw|linux面板创建站点时会自动创建权限配置,统一使用www用户。</p>
<p><span class="glyphicon glyphicon-alert" style="color:#f39c12; margin-right:10px;"></span>创建站点时会自动创建权限配置,统一使用www用户。</p>
</div>
<button onclick="webAdd(0)" class="btn btn-success btn-sm btn-title" type="button">添加站点</button>&nbsp;
<button onclick="siteDefaultPage();" class="btn btn-default btn-sm btn-title" type="button">修改默认页</button>
+8 -4
View File
@@ -42,14 +42,18 @@ class backupTools:
filename = backup_path + "/web_" + name + "_" + \
time.strftime('%Y%m%d_%H%M%S', time.localtime()) + '.tar.gz'
mw.execShell("cd " + os.path.dirname(path) + " && tar zcvf '" +
filename + "' '" + os.path.basename(path) + "' > /dev/null")
cmd = "cd " + os.path.dirname(path) + " && tar zcvf '" + \
filename + "' '" + os.path.basename(path) + "' > /dev/null"
# print(cmd)
mw.execShell(cmd)
endDate = time.strftime('%Y/%m/%d %X', time.localtime())
print(filename)
if not os.path.exists(filename):
log = "网站[" + name + u"]备份失败!"
log = "网站[" + name + "]备份失败!"
print("★[" + endDate + "] " + log)
print(
"----------------------------------------------------------------------------")
@@ -60,7 +64,7 @@ class backupTools:
sql.table('backup').add('type,name,pid,filename,addtime,size', ('0', os.path.basename(
filename), pid, filename, endDate, os.path.getsize(filename)))
log = "网站[" + name + "]备份成功,用时[" + str(round(outTime, 2)) + "]秒"
mw.writeLog(u'计划任务', log)
mw.writeLog('计划任务', log)
print("★[" + endDate + "] " + log)
print("|---保留最新的[" + count + "]份备份")
print("|---文件名:" + filename)
+10 -2
View File
@@ -14,7 +14,7 @@
PATH=/usr/local/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export LC_ALL="en_US.UTF-8"
# export LC_ALL="en_US.UTF-8"
mw_path={$SERVER_PATH}
PATH=$PATH:$mw_path/bin
@@ -243,8 +243,16 @@ case "$1" in
elif [ "$v4" != "" ]; then
address="MW-Panel-Url: http://$v4:$port$auth_path"
elif [ "$v6" != "" ]; then
echo 'True' > $mw_path/data/ipv6.pl
if [ ! -f $mw_path/data/ipv6.pl ];then
# Need to restart ipv6 to take effect
mw_stop
mw_start
echo 'True' > $mw_path/data/ipv6.pl
fi
address="MW-Panel-Url: http://[$v6]:$port$auth_path"
else
address="MW-Panel-Url: http://you-network-ip:$port$auth_path"
fi
+2 -1
View File
@@ -101,7 +101,8 @@ yum install -y pcre pcre-devel
yum install -y icu libicu-devel
yum install -y freetype freetype-devel
yum install -y openssl openssl-devel
yum install -y graphviz libxml2 libxml2-devel
yum install -y libxml2 libxml2-devel
yum install -y graphviz
yum install -y sqlite-devel
yum install -y oniguruma oniguruma-devel
yum install -y ImageMagick ImageMagick-devel
+15 -3
View File
@@ -1,7 +1,7 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
LANG=en_US.UTF-8
export LANG=en_US.UTF-8
# RED='\e[1;31m' # 红色
# GREEN='\e[1;32m' # 绿色
@@ -16,6 +16,10 @@ if grep -Eq "Debian" /etc/*-release; then
ln -sf /bin/bash /bin/sh
fi
# synchronize time first
apt-get install ntpdate -y
ntpdate time.nist.gov | logger -t NTP
apt update -y
apt-get update -y
@@ -112,13 +116,21 @@ apt install -y libxpm-dev
apt install -y libwebp-dev
apt install -y libfreetype6-dev
sudo localedef -i en_US -f UTF-8 en_US.UTF-8
VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
if [ "$VERSION_ID" == "9" ];then
sed "s/flask==2.0.3/flask==1.1.1/g" -i /www/server/mdserver-web/requirements.txt
sed "s/cryptography==3.3.2/cryptography==2.5/g" -i /www/server/mdserver-web/requirements.txt
sed "s/configparser==5.2.0/configparser==4.0.2/g" -i /www/server/mdserver-web/requirements.txt
sed "s/flask-socketio==5.2.0/flask-socketio==4.2.0/g" -i /www/server/mdserver-web/requirements.txt
sed "s/python-engineio==4.3.2/python-engineio==3.9.0/g" -i /www/server/mdserver-web/requirements.txt
# pip3 install -r /www/server/mdserver-web/requirements.txt
fi
cd /www/server/mdserver-web/scripts && bash lib.sh
chmod 755 /www/server/mdserver-web/data
cd /www/server/mdserver-web && ./cli.sh start
isStart=`ps -ef|grep 'gunicorn -c setting.py app:app' |grep -v grep|awk '{print $2}'`
n=0
+6 -2
View File
@@ -1,14 +1,14 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
LANG=en_US.UTF-8
export LANG=en_US.UTF-8
export DEBIAN_FRONTEND=noninteractive
if grep -Eq "Ubuntu" /etc/*-release; then
sudo ln -sf /bin/bash /bin/sh
#sudo dpkg-reconfigure dash
fi
apt update -y
apt-get update -y
@@ -24,6 +24,10 @@ if [ ! -d /root/.acme.sh ];then
curl https://get.acme.sh | sh
fi
apt install -y locate
locale-gen en_US.UTF-8
localedef -v -c -i en_US -f UTF-8 en_US.UTF-8
if [ -f /usr/sbin/ufw ];then
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# LANG=en_US.UTF-8
is64bit=`getconf LONG_BIT`
if [ -f /etc/motd ];then
echo "" > /etc/motd
fi
startTime=`date +%s`
_os=`uname`
echo "use system: ${_os}"
if [ "$EUID" -ne 0 ]
then echo "Please run as root!"
exit
fi
rm -rf /usr/bin/mw
rm -rf /www
endTime=`date +%s`
((outTime=(${endTime}-${startTime})/60))
echo -e "Time consumed:\033[32m $outTime \033[0mMinute!"
systemctl daemon-reload
+1 -1
View File
@@ -1,7 +1,7 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
LANG=en_US.UTF-8
export LANG=en_US.UTF-8
if [ -f /etc/motd ];then
+10 -1
View File
@@ -1,13 +1,22 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
LANG=en_US.UTF-8
export LANG=en_US.UTF-8
if grep -Eq "Ubuntu" /etc/*-release; then
sudo ln -sf /bin/bash /bin/sh
#sudo dpkg-reconfigure dash
fi
VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
if [ "$VERSION_ID" == "9" ];then
sed "s/flask==2.0.3/flask==1.1.1/g" -i /www/server/mdserver-web/requirements.txt
sed "s/cryptography==3.3.2/cryptography==2.5/g" -i /www/server/mdserver-web/requirements.txt
sed "s/configparser==5.2.0/configparser==4.0.2/g" -i /www/server/mdserver-web/requirements.txt
sed "s/flask-socketio==5.2.0/flask-socketio==4.2.0/g" -i /www/server/mdserver-web/requirements.txt
sed "s/python-engineio==4.3.2/python-engineio==3.9.0/g" -i /www/server/mdserver-web/requirements.txt
# pip3 install -r /www/server/mdserver-web/requirements.txt
fi
cd /www/server/mdserver-web/scripts && bash lib.sh
chmod 755 /www/server/mdserver-web/data