mirror of
https://github.com/midoks/mdserver-web.git
synced 2026-10-10 04:59:25 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
696797f125 | ||
|
|
fcfad78506 | ||
|
|
4787d6e7bb | ||
|
|
80ef6dbe4c | ||
|
|
78cdad5fb8 | ||
|
|
274f0638ac | ||
|
|
1e97b146db | ||
|
|
fbbc506de9 | ||
|
|
58ec4c2c30 | ||
|
|
a2298ade04 | ||
|
|
d5fc464603 | ||
|
|
c952be4e45 | ||
|
|
17a92518b4 | ||
|
|
c7154eea7b | ||
|
|
7cab1492c0 | ||
|
|
77b6391d9e | ||
|
|
08a91ed0f2 | ||
|
|
b4d090021c | ||
|
|
bf064d89f4 | ||
|
|
7314071634 | ||
|
|
49a606baa2 | ||
|
|
aaabe5628e | ||
|
|
ea03373363 | ||
|
|
515d25648f | ||
|
|
a30792178a | ||
|
|
1c74ce3c58 | ||
|
|
832f3b45eb | ||
|
|
2c3ad7ea99 | ||
|
|
6b3dae6169 | ||
|
|
d387546986 | ||
|
|
6929c5a173 | ||
|
|
d66e9d7fc6 | ||
|
|
17470600e9 | ||
|
|
d1017ddb5c | ||
|
|
67161e62ce | ||
|
|
87ad51f6ad | ||
|
|
2239d178f4 | ||
|
|
4ff95f057c | ||
|
|
f32ba65596 | ||
|
|
d3db79a4ed | ||
|
|
ddc8cde44c | ||
|
|
719ad042c7 | ||
|
|
9134dfb1a9 | ||
|
|
9611834a9d | ||
|
|
8d5cc2e926 | ||
|
|
01b9c51247 | ||
|
|
21ca9d8874 | ||
|
|
d2d5a0e12f | ||
|
|
ad2e9958f8 | ||
|
|
7cf64673f9 | ||
|
|
c49bc6f823 | ||
|
|
24e6c57eec | ||
|
|
a6842f2679 | ||
|
|
42ba0ea285 | ||
|
|
19e6055b7c | ||
|
|
1388c697e0 | ||
|
|
2a97c0f8b3 | ||
|
|
8aa4084373 | ||
|
|
4e16773ed8 | ||
|
|
b4b9b6eb1f | ||
|
|
b35ae736a3 | ||
|
|
ec78197317 | ||
|
|
1558e93f33 | ||
|
|
99a15ffc82 | ||
|
|
2b0b697a6a | ||
|
|
a93953a61d | ||
|
|
1ce68581af | ||
|
|
df5979a4cf | ||
|
|
9197dcbe03 | ||
|
|
4100824c43 | ||
|
|
c22b2b3774 | ||
|
|
0fbd3163e2 | ||
|
|
500171ed66 | ||
|
|
1c6b6f1090 | ||
|
|
fc8a54c4b3 | ||
|
|
4ade42380d | ||
|
|
6b2c21ab47 | ||
|
|
6f5ea82a58 | ||
|
|
73c849931c | ||
|
|
39d76f5621 | ||
|
|
bc435ea848 | ||
|
|
ed82dd3c12 | ||
|
|
0a5a86625f | ||
|
|
53f56c949b | ||
|
|
7e61e37d80 | ||
|
|
c05d0244db | ||
|
|
226d0addc5 | ||
|
|
35cce35ff3 | ||
|
|
9450103c4b | ||
|
|
4478f005e7 | ||
|
|
71be0e3196 | ||
|
|
e6a79216c4 | ||
|
|
3f12d4b8d6 | ||
|
|
683f041b1d | ||
|
|
e08cebd48e | ||
|
|
d02dea3150 | ||
|
|
18bb92c9e2 | ||
|
|
f9b45ee4d9 | ||
|
|
dcbc86ea5c | ||
|
|
300728cb17 | ||
|
|
51aa028f97 | ||
|
|
e30ac33cea | ||
|
|
1c2f3c4378 | ||
|
|
17eadfccff | ||
|
|
b55a6d3bd9 | ||
|
|
48936e7918 | ||
|
|
1a99c53cb6 | ||
|
|
a8b5df25d0 | ||
|
|
73b4be90f5 | ||
|
|
1ecf12ab5c | ||
|
|
758da44141 | ||
|
|
8aa44408cc | ||
|
|
ccf4ab7d81 | ||
|
|
61766a8942 | ||
|
|
ce9ac280c7 | ||
|
|
94bd8cbe8e | ||
|
|
705f2445ee | ||
|
|
bc8d16f2c2 | ||
|
|
fe9eb7dc38 | ||
|
|
94195b5246 | ||
|
|
24009267be | ||
|
|
c28412aea5 | ||
|
|
847b9d569b | ||
|
|
b3c3a4457d | ||
|
|
89a84f5ca0 | ||
|
|
4432fa15cc | ||
|
|
c6a600299d | ||
|
|
082b0ebcc3 | ||
|
|
828436b772 | ||
|
|
658a727fe3 | ||
|
|
bedd6e5409 | ||
|
|
ef8bc8bf57 | ||
|
|
a22f89797a | ||
|
|
eaf341cda6 | ||
|
|
58d69d734d | ||
|
|
e19f7702db | ||
|
|
b546a39f29 | ||
|
|
81b570214a | ||
|
|
44401f5944 | ||
|
|
4ad190586a | ||
|
|
25bca124b4 | ||
|
|
98165bb449 | ||
|
|
6693edb924 | ||
|
|
bae1beec3e | ||
|
|
0682ca4d4d | ||
|
|
5bc10bebd0 | ||
|
|
25df8b430b | ||
|
|
4f8fc31ca3 | ||
|
|
a44f54e719 | ||
|
|
12068046ec | ||
|
|
d6f57e5ea7 | ||
|
|
869d7795a6 | ||
|
|
0a3d976c6a | ||
|
|
c4d39ef9fb | ||
|
|
8e5b499601 | ||
|
|
e6dff6094c | ||
|
|
47dec45a27 | ||
|
|
4706af7423 | ||
|
|
1bc458eb8f | ||
|
|
dc3f872592 | ||
|
|
3d48289329 | ||
|
|
6126539929 | ||
|
|
cff5df6d13 | ||
|
|
23a0b74f9d | ||
|
|
5927a3abab | ||
|
|
4b176ad702 | ||
|
|
577103506d | ||
|
|
18f8d2b905 | ||
|
|
3a6b5339d2 | ||
|
|
c25d4305f7 | ||
|
|
b6ef87e97b | ||
|
|
4335668cd4 | ||
|
|
0fc2ab011d | ||
|
|
683035e6b7 | ||
|
|
f2dfa7ad48 | ||
|
|
2ae2c188e7 | ||
|
|
b5cd0564cb | ||
|
|
2f274ce202 | ||
|
|
0a67932473 | ||
|
|
7b7cb215c4 | ||
|
|
f6e98ae286 | ||
|
|
5958f6e067 | ||
|
|
6dadfb1019 | ||
|
|
f5aeb5664b | ||
|
|
ca1080937d | ||
|
|
1dd934ea3f | ||
|
|
a34512d057 | ||
|
|
575c1e48ae | ||
|
|
9f3ee4997b | ||
|
|
cfaa2d9f25 | ||
|
|
aded1fc731 | ||
|
|
4a6e847743 | ||
|
|
24fac51538 | ||
|
|
148b32fed3 | ||
|
|
fff19d2c4a | ||
|
|
a5e0332179 | ||
|
|
1349655570 | ||
|
|
b9e617bf0f | ||
|
|
f65d10e1e4 | ||
|
|
6d9ec087ce | ||
|
|
bfb9da0471 | ||
|
|
8a95a68c81 | ||
|
|
d9d81ded3d | ||
|
|
403660c980 | ||
|
|
ac06cfc83c | ||
|
|
c50012b9b5 | ||
|
|
baeb0ddc8f | ||
|
|
83f8c99275 | ||
|
|
2d2b454a31 | ||
|
|
aad41ce14b | ||
|
|
ebf8c82d33 | ||
|
|
d25100fec3 | ||
|
|
fe658d9009 | ||
|
|
f37c2c9bc6 | ||
|
|
a1550765aa | ||
|
|
b020f23161 | ||
|
|
4afe2061e2 | ||
|
|
ab64c01981 | ||
|
|
8a97734700 | ||
|
|
16b4c64aa5 | ||
|
|
df2a5919af | ||
|
|
40a81b794c | ||
|
|
8fc7ab3abf | ||
|
|
ca4205c89a | ||
|
|
c89b15a645 | ||
|
|
7a863c4544 | ||
|
|
5d81406fdf | ||
|
|
cce59c0521 | ||
|
|
0a5c5aae12 | ||
|
|
7478426206 | ||
|
|
c0ed736edb | ||
|
|
313eb25ccd | ||
|
|
f58595755b | ||
|
|
18a892a576 | ||
|
|
03d558092f | ||
|
|
3cffa9223a | ||
|
|
c0d5aff2f5 | ||
|
|
3fa4c7804b | ||
|
|
22acce8bc0 | ||
|
|
e53e5cd672 | ||
|
|
a7d861de22 | ||
|
|
ddeef3c17e | ||
|
|
af733ac28a | ||
|
|
148e2021dc | ||
|
|
50c3fe58f4 | ||
|
|
5ed3728d4f | ||
|
|
9bf06c9d21 | ||
|
|
08b3738cfd | ||
|
|
a04f59a3aa | ||
|
|
155b8d2b4c | ||
|
|
039d2794ab | ||
|
|
0d2721523c | ||
|
|
f042665c05 | ||
|
|
0118548c24 | ||
|
|
1ba6625525 | ||
|
|
293f8ab0af | ||
|
|
dc68868883 | ||
|
|
30f835b6b8 | ||
|
|
4d90fdfe4f | ||
|
|
bb4067aa6b | ||
|
|
5dbf89b872 | ||
|
|
cc244a8ea5 |
@@ -156,6 +156,9 @@ data/bind_domain.pl
|
||||
|
||||
plugins/l2tp
|
||||
plugins/openlitespeed
|
||||
plugins/migration_api
|
||||
plugins/system_safe
|
||||
plugins/tamper_proof
|
||||
plugins/gdrive
|
||||
plugins/mtproxy
|
||||
plugins/zimg
|
||||
@@ -164,6 +167,7 @@ plugins/mail
|
||||
plugins/fastdfs
|
||||
plugins/v2ray
|
||||
plugins/frp
|
||||
plugins/file_search
|
||||
debug.out
|
||||
|
||||
|
||||
|
||||
@@ -72,7 +72,6 @@ PHP[72-81]支持phpMyAdmin[5.2.0]
|
||||
# 特别赞助
|
||||
|
||||
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/)
|
||||
- [奈飞丝 - 奈飞中国 Netflix MOD首发](https://naifeis.com/index.php#/register?code=k7P7V6Ur)
|
||||
|
||||
# AD - VPS推荐 - 🙏
|
||||
|
||||
@@ -92,15 +91,18 @@ docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:
|
||||
```
|
||||
|
||||
|
||||
### 版本更新 0.12.0
|
||||
### 版本更新 0.12.2
|
||||
|
||||
此次版本:默认面板名为【大圣面板】。
|
||||
由于#309问题。先注释。提前发版。
|
||||
|
||||
* 安装脚本优化 @hiCasper。
|
||||
* 数据库[pg/apt/yum/mariadb]计划任务备份ok。
|
||||
* php源码加入sodium扩展。
|
||||
* 站点分类问题 #300。
|
||||
* 开放菜单权限配置。
|
||||
* 升级SSH终端2.0。
|
||||
* 增加已安装类型。
|
||||
* 加入切换linux软件源的命令。
|
||||
* iptables安装优化。
|
||||
* 网站统计POST获取数据优化。
|
||||
* mysql[apt/yum]迁移优化。
|
||||
* 优化防火墙导入。
|
||||
* 图标可设置。
|
||||
* 各种细节优化。
|
||||
|
||||
|
||||
### JSDelivr安装地址
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
from route import app, socketio
|
||||
|
||||
|
||||
@@ -38,12 +40,12 @@ try:
|
||||
PORT = int(f.read())
|
||||
f.close()
|
||||
|
||||
HOST = '0.0.0.0'
|
||||
# HOST = '0.0.0.0'
|
||||
# app.run(host=HOST, port=PORT)
|
||||
|
||||
http_server = WSGIServer(
|
||||
(HOST, PORT), app, handler_class=WebSocketHandler)
|
||||
|
||||
http_server.serve_forever()
|
||||
|
||||
socketio.run(app, host=HOST, port=PORT)
|
||||
except Exception as ex:
|
||||
print(ex)
|
||||
|
||||
@@ -371,7 +371,9 @@ fi
|
||||
response = urllib.request.urlopen(req, timeout=timeout)
|
||||
return response
|
||||
except Exception as ex:
|
||||
raise Exception("requestsPost: {}".format(str(ex)))
|
||||
|
||||
self.getError()
|
||||
raise Exception("requestsPost: {}".format(self.getError(str(ex))))
|
||||
|
||||
def getRequestJson(self, response):
|
||||
try:
|
||||
@@ -825,7 +827,7 @@ fi
|
||||
ret_title, json.dumps(a_auth)))
|
||||
break
|
||||
|
||||
if number_of_checks == self._max_check_num:
|
||||
if number_of_checks == self.__max_check_num:
|
||||
raise StopIteration(
|
||||
"错误:已尝试验证{0}次. 最大验证次数为{1}. 验证时间间隔为{2}秒.".format(
|
||||
number_of_checks,
|
||||
|
||||
@@ -736,7 +736,7 @@ class cert_request:
|
||||
ret_title, json.dumps(a_auth)))
|
||||
break
|
||||
|
||||
if number_of_checks == self._max_check_num:
|
||||
if number_of_checks == self.__max_check_num:
|
||||
raise StopIteration(
|
||||
"错误:已尝试验证{0}次. 最大验证次数为{1}. 验证时间间隔为{2}秒.".format(
|
||||
number_of_checks,
|
||||
|
||||
@@ -27,7 +27,7 @@ from flask import request
|
||||
|
||||
class config_api:
|
||||
|
||||
__version = '0.12.0'
|
||||
__version = '0.12.2'
|
||||
__api_addr = 'data/api.json'
|
||||
|
||||
def __init__(self):
|
||||
@@ -340,11 +340,13 @@ class config_api:
|
||||
|
||||
# 获取面板证书
|
||||
def getPanelSslApi(self):
|
||||
cert = {}
|
||||
cert = self.getPanelSslData()
|
||||
return mw.getJson(cert)
|
||||
|
||||
def getPanelSslData(self):
|
||||
cert = {}
|
||||
keyPath = 'ssl/private.pem'
|
||||
certPath = 'ssl/cert.pem'
|
||||
|
||||
if not os.path.exists(certPath):
|
||||
mw.createSSL()
|
||||
|
||||
@@ -352,7 +354,7 @@ class config_api:
|
||||
cert['certPem'] = mw.readFile(certPath)
|
||||
cert['rep'] = os.path.exists('ssl/input.pl')
|
||||
cert['info'] = mw.getCertName(certPath)
|
||||
return mw.getJson(cert)
|
||||
return cert
|
||||
|
||||
# 保存面板证书
|
||||
def savePanelSslApi(self):
|
||||
@@ -378,6 +380,54 @@ class config_api:
|
||||
mw.writeFile('ssl/input.pl', 'True')
|
||||
return mw.returnJson(True, '证书已保存!')
|
||||
|
||||
# 申请面板let证书
|
||||
def applyPanelLetSslApi(self):
|
||||
|
||||
# check domain is bind?
|
||||
bind_domain = 'data/bind_domain.pl'
|
||||
if not os.path.exists(bind_domain):
|
||||
return mw.returnJson(False, '未绑定域名!')
|
||||
|
||||
siteName = mw.readFile(bind_domain).strip()
|
||||
auth_to = mw.getRunDir() + "/tmp"
|
||||
to_args = {
|
||||
'domains': [siteName],
|
||||
'auth_type': 'http',
|
||||
'auth_to': auth_to,
|
||||
}
|
||||
|
||||
src_letpath = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName
|
||||
src_csrpath = src_letpath + "/fullchain.pem" # 生成证书路径
|
||||
src_keypath = src_letpath + "/privkey.pem" # 密钥文件路径
|
||||
|
||||
dst_letpath = mw.getRunDir() + '/ssl'
|
||||
dst_csrpath = dst_letpath + '/cert.pem'
|
||||
dst_keypath = dst_letpath + '/private.pem'
|
||||
|
||||
if not os.path.exists(src_letpath):
|
||||
import cert_api
|
||||
data = cert_api.cert_api().applyCertApi(to_args)
|
||||
if not data['status']:
|
||||
msg = data['msg']
|
||||
if type(data['msg']) != str:
|
||||
msg = data['msg'][0]
|
||||
emsg = data['msg'][1]['challenges'][0]['error']
|
||||
msg = msg + '<p><span>响应状态:</span>' + str(emsg['status']) + '</p><p><span>错误类型:</span>' + emsg[
|
||||
'type'] + '</p><p><span>错误代码:</span>' + emsg['detail'] + '</p>'
|
||||
return mw.returnJson(data['status'], msg, data['msg'])
|
||||
|
||||
mw.buildSoftLink(src_csrpath, dst_csrpath, True)
|
||||
mw.buildSoftLink(src_keypath, dst_keypath, True)
|
||||
mw.execShell('echo "lets" > "' + dst_letpath + '/README"')
|
||||
|
||||
data = self.getPanelSslData()
|
||||
|
||||
tmp_well_know = auth_to + '/.well-known'
|
||||
if os.path.exists(tmp_well_know):
|
||||
mw.execShell('rm -rf ' + tmp_well_know)
|
||||
|
||||
return mw.returnJson(True, '申请成功!', data)
|
||||
|
||||
def setPanelDomainApi(self):
|
||||
domain = request.form.get('domain', '')
|
||||
|
||||
@@ -731,12 +781,21 @@ class config_api:
|
||||
"id=?", (1,)).getField('username')
|
||||
|
||||
# databases hook 获取
|
||||
database_file = 'data/hook_database.json'
|
||||
if os.path.exists(database_file):
|
||||
df = mw.readFile(database_file)
|
||||
database_hook_file = 'data/hook_database.json'
|
||||
if os.path.exists(database_hook_file):
|
||||
df = mw.readFile(database_hook_file)
|
||||
df = json.loads(df)
|
||||
data['hook_database'] = df
|
||||
else:
|
||||
data['hook_database'] = []
|
||||
|
||||
# menu hook 获取
|
||||
menu_hook_file = 'data/hook_menu.json'
|
||||
if os.path.exists(menu_hook_file):
|
||||
df = mw.readFile(menu_hook_file)
|
||||
df = json.loads(df)
|
||||
data['hook_menu'] = df
|
||||
else:
|
||||
data['hook_menu'] = []
|
||||
|
||||
return data
|
||||
|
||||
@@ -117,6 +117,7 @@ class Sql():
|
||||
sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \
|
||||
self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT
|
||||
# print(sql)
|
||||
# print(self.__OPT_PARAM)
|
||||
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
|
||||
data = result.fetchall()
|
||||
# 构造字曲系列
|
||||
|
||||
+83
-62
@@ -28,10 +28,14 @@ from flask import request
|
||||
class firewall_api:
|
||||
|
||||
__isFirewalld = False
|
||||
__isIptables = False
|
||||
__isUfw = False
|
||||
__isMac = False
|
||||
|
||||
def __init__(self):
|
||||
iptables_file = mw.systemdCfgDir() + '/iptables.service'
|
||||
if os.path.exists(iptables_file):
|
||||
self.__isIptables = True
|
||||
if os.path.exists('/usr/sbin/firewalld'):
|
||||
self.__isFirewalld = True
|
||||
if os.path.exists('/usr/sbin/ufw'):
|
||||
@@ -55,13 +59,15 @@ class firewall_api:
|
||||
if self.__isUfw:
|
||||
mw.execShell('ufw deny from ' + address + ' to any')
|
||||
else:
|
||||
if self.__isFirewalld:
|
||||
if self.__isIptables:
|
||||
cmd = 'iptables -I INPUT -s ' + address + ' -j DROP'
|
||||
mw.execShell(cmd)
|
||||
elif self.__isFirewalld:
|
||||
cmd = 'firewall-cmd --permanent --add-rich-rule=\'rule family=ipv4 source address="' + \
|
||||
address + '" drop\''
|
||||
mw.execShell(cmd)
|
||||
else:
|
||||
cmd = 'iptables -I INPUT -s ' + address + ' -j DROP'
|
||||
mw.execShell(cmd)
|
||||
pass
|
||||
|
||||
msg = mw.getInfo('屏蔽IP[{1}]成功!', (address,))
|
||||
mw.writeLog("防火墙管理", msg)
|
||||
@@ -117,15 +123,14 @@ class firewall_api:
|
||||
address = port
|
||||
if self.__isUfw:
|
||||
mw.execShell('ufw delete deny from ' + address + ' to any')
|
||||
elif self.__isIptables:
|
||||
cmd = 'iptables -D INPUT -s ' + address + ' -j DROP'
|
||||
mw.execShell(cmd)
|
||||
elif self.__isFirewalld:
|
||||
mw.execShell(
|
||||
'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv4 source address="' + address + '" drop\'')
|
||||
else:
|
||||
if self.__isFirewalld:
|
||||
mw.execShell(
|
||||
'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv4 source address="' + address + '" drop\'')
|
||||
elif self.__isMac:
|
||||
pass
|
||||
else:
|
||||
cmd = 'iptables -D INPUT -s ' + address + ' -j DROP'
|
||||
mw.execShell(cmd)
|
||||
pass
|
||||
|
||||
msg = mw.getInfo('解除IP[{1}]的屏蔽!', (address,))
|
||||
mw.writeLog("防火墙管理", msg)
|
||||
@@ -144,15 +149,16 @@ class firewall_api:
|
||||
return mw.returnJson(False, '失败,不能删除当前面板端口!')
|
||||
if self.__isUfw:
|
||||
mw.execShell('ufw delete allow ' + port + '/tcp')
|
||||
elif self.__isIptables:
|
||||
mw.execShell(
|
||||
'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
|
||||
elif self.__isFirewalld:
|
||||
mw.execShell(
|
||||
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp')
|
||||
mw.execShell(
|
||||
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp')
|
||||
else:
|
||||
if self.__isFirewalld:
|
||||
mw.execShell(
|
||||
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp')
|
||||
mw.execShell(
|
||||
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp')
|
||||
else:
|
||||
mw.execShell(
|
||||
'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
|
||||
pass
|
||||
msg = mw.getInfo('删除防火墙放行端口[{1}]成功!', (port,))
|
||||
mw.writeLog("防火墙管理", msg)
|
||||
mw.M('firewall').where("id=?", (sid,)).delete()
|
||||
@@ -249,24 +255,15 @@ class firewall_api:
|
||||
conf = re.sub(rep, "Port " + port + "\n", conf)
|
||||
mw.writeFile(file, conf)
|
||||
|
||||
if self.__isFirewalld:
|
||||
mw.execShell('setenforce 0')
|
||||
mw.execShell(
|
||||
'sed -i "s#SELINUX=enforcing#SELINUX=disabled#" /etc/selinux/config')
|
||||
mw.execShell("systemctl restart sshd.service")
|
||||
elif self.__isUfw:
|
||||
mw.execShell('ufw allow ' + port + '/tcp')
|
||||
self.addAcceptPortArgs(port, 'SSH端口修改', 'port')
|
||||
if self.__isUfw:
|
||||
mw.execShell("service ssh restart")
|
||||
else:
|
||||
mw.execShell(
|
||||
'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
|
||||
elif self.__isIptables:
|
||||
mw.execShell("/etc/init.d/sshd restart")
|
||||
|
||||
self.firewallReload()
|
||||
# mw.M('firewall').where(
|
||||
# "ps=?", ('SSH远程管理服务',)).setField('port', port)
|
||||
msg = "改SSH端口为[{}]成功!".format(port)
|
||||
mw.writeLog("防火墙管理", msg)
|
||||
elif self.__isFirewalld:
|
||||
mw.execShell("systemctl restart sshd.service")
|
||||
else:
|
||||
return mw.returnJson(False, '修改失败!')
|
||||
return mw.returnJson(True, '修改成功!')
|
||||
|
||||
def setSshStatusApi(self):
|
||||
@@ -342,29 +339,52 @@ class firewall_api:
|
||||
status = request.form.get('status', '1')
|
||||
return mw.getJson(self.setFw(status))
|
||||
|
||||
def setFwIptables(self, status):
|
||||
# iptables特殊处理
|
||||
if status == '1':
|
||||
mw.execShell('service iptables save')
|
||||
mw.execShell('service iptables stop')
|
||||
else:
|
||||
# 重新导入数据
|
||||
_list = mw.M('firewall').field('id,port,ps,addtime').limit(
|
||||
'0,1000').order('id desc').select()
|
||||
|
||||
mw.execShell('iptables -P INPUT DROP')
|
||||
mw.execShell('iptables -P OUTPUT ACCEPT')
|
||||
for x in _list:
|
||||
port = x['port']
|
||||
if mw.isIpAddr(port):
|
||||
cmd = 'iptables -I INPUT -s ' + port + ' -j DROP'
|
||||
mw.execShell(cmd)
|
||||
else:
|
||||
self.addAcceptPort(port)
|
||||
|
||||
mw.execShell('service iptables save')
|
||||
mw.execShell('service iptables start')
|
||||
|
||||
def setFw(self, status):
|
||||
|
||||
if self.__isIptables:
|
||||
self.setFwIptables(status)
|
||||
return mw.returnData(True, '设置成功!')
|
||||
|
||||
if status == '1':
|
||||
if self.__isUfw:
|
||||
mw.execShell('/usr/sbin/ufw disable')
|
||||
if self.__isFirewalld:
|
||||
|
||||
elif self.__isFirewalld:
|
||||
mw.execShell('systemctl stop firewalld.service')
|
||||
mw.execShell('systemctl disable firewalld.service')
|
||||
elif self.__isMac:
|
||||
pass
|
||||
else:
|
||||
mw.execShell('service iptables save')
|
||||
mw.execShell('service iptables stop')
|
||||
pass
|
||||
else:
|
||||
if self.__isUfw:
|
||||
mw.execShell("echo 'y'| ufw enable")
|
||||
if self.__isFirewalld:
|
||||
elif self.__isFirewalld:
|
||||
mw.execShell('systemctl start firewalld.service')
|
||||
mw.execShell('systemctl enable firewalld.service')
|
||||
elif self.__isMac:
|
||||
pass
|
||||
else:
|
||||
mw.execShell('service iptables save')
|
||||
mw.execShell('service iptables restart')
|
||||
pass
|
||||
|
||||
return mw.returnData(True, '设置成功!')
|
||||
|
||||
@@ -418,27 +438,28 @@ class firewall_api:
|
||||
def addAcceptPort(self, port):
|
||||
if self.__isUfw:
|
||||
mw.execShell('ufw allow ' + port + '/tcp')
|
||||
elif self.__isIptables:
|
||||
cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT'
|
||||
mw.execShell(cmd)
|
||||
elif self.__isFirewalld:
|
||||
port = port.replace(':', '-')
|
||||
cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp'
|
||||
mw.execShell(cmd)
|
||||
elif self.__isMac:
|
||||
pass
|
||||
else:
|
||||
cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT'
|
||||
mw.execShell(cmd)
|
||||
pass
|
||||
return True
|
||||
|
||||
def firewallReload(self):
|
||||
if self.__isUfw:
|
||||
mw.execShell('/usr/sbin/ufw reload')
|
||||
return
|
||||
if self.__isFirewalld:
|
||||
mw.execShell('firewall-cmd --reload')
|
||||
elif self.__isMac:
|
||||
pass
|
||||
else:
|
||||
elif self.__isIptables:
|
||||
mw.execShell('service iptables save')
|
||||
mw.execShell('service iptables restart')
|
||||
elif self.__isFirewalld:
|
||||
mw.execShell('firewall-cmd --reload')
|
||||
else:
|
||||
pass
|
||||
|
||||
def getFwStatus(self):
|
||||
if self.__isUfw:
|
||||
@@ -447,17 +468,17 @@ class firewall_api:
|
||||
if data[0].strip() == 'inactive':
|
||||
return False
|
||||
return True
|
||||
if self.__isFirewalld:
|
||||
elif self.__isIptables:
|
||||
cmd = "systemctl status iptables | grep 'inactive'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] != '':
|
||||
return False
|
||||
return True
|
||||
elif self.__isFirewalld:
|
||||
cmd = "ps -ef|grep firewalld |grep -v grep | awk '{print $2}'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return False
|
||||
return True
|
||||
elif self.__isMac:
|
||||
return False
|
||||
else:
|
||||
cmd = "ps -ef|grep iptables |grep -v grep | awk '{print $2}'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return False
|
||||
return True
|
||||
return False
|
||||
|
||||
+148
-9
@@ -219,6 +219,15 @@ def isInstalledWeb():
|
||||
return False
|
||||
|
||||
|
||||
def isIpAddr(ip):
|
||||
check_ip = re.compile(
|
||||
'^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$')
|
||||
if check_ip.match(ip):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def restartWeb():
|
||||
return opWeb("reload")
|
||||
|
||||
@@ -445,16 +454,30 @@ def getDate():
|
||||
return time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
|
||||
|
||||
def getDataFromInt(val):
|
||||
time_format = '%Y-%m-%d %H:%M:%S'
|
||||
time_str = time.localtime(val)
|
||||
return time.strftime(time_format, time_str)
|
||||
|
||||
|
||||
def writeLog(stype, msg, args=()):
|
||||
# 写日志
|
||||
try:
|
||||
import time
|
||||
import db
|
||||
import json
|
||||
from flask import session
|
||||
uid = 1
|
||||
if 'uid' in session:
|
||||
uid = session['uid']
|
||||
return writeDbLog(stype, msg, args, uid)
|
||||
except Exception as e:
|
||||
print(getTracebackInfo())
|
||||
return False
|
||||
|
||||
|
||||
def writeDbLog(stype, msg, args=(), uid=1):
|
||||
try:
|
||||
import time
|
||||
import db
|
||||
import json
|
||||
sql = db.Sql()
|
||||
mdate = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
wmsg = getInfo(msg, args)
|
||||
@@ -490,6 +513,18 @@ def backFile(file, act=None):
|
||||
execShell("cp -p {0} {1}".format(file, file + file_type))
|
||||
|
||||
|
||||
def removeBackFile(file, act=None):
|
||||
"""
|
||||
@name 删除备份配置文件
|
||||
@param file 需要删除备份文件
|
||||
@param act 如果存在,则还原默认配置
|
||||
"""
|
||||
file_type = "_bak"
|
||||
if act:
|
||||
file_type = "_def"
|
||||
execShell("rm -rf {0}".format(file + file_type))
|
||||
|
||||
|
||||
def restoreFile(file, act=None):
|
||||
"""
|
||||
@name 还原配置文件
|
||||
@@ -544,28 +579,37 @@ def dePunycode(domain):
|
||||
def enCrypt(key, strings):
|
||||
# 加密字符串
|
||||
try:
|
||||
import base64
|
||||
_key = md5(key).encode('utf-8')
|
||||
_key = base64.urlsafe_b64encode(_key)
|
||||
|
||||
if type(strings) != bytes:
|
||||
strings = strings.encode('utf-8')
|
||||
import cryptography
|
||||
from cryptography.fernet import Fernet
|
||||
f = Fernet(key)
|
||||
f = Fernet(_key)
|
||||
result = f.encrypt(strings)
|
||||
return result.decode('utf-8')
|
||||
except:
|
||||
# print(get_error_info())
|
||||
print(getTracebackInfo())
|
||||
return strings
|
||||
|
||||
|
||||
def deCrypt(key, strings):
|
||||
# 解密字符串
|
||||
try:
|
||||
import base64
|
||||
_key = md5(key).encode('utf-8')
|
||||
_key = base64.urlsafe_b64encode(_key)
|
||||
|
||||
if type(strings) != bytes:
|
||||
strings = strings.decode('utf-8')
|
||||
strings = strings.encode('utf-8')
|
||||
from cryptography.fernet import Fernet
|
||||
f = Fernet(key)
|
||||
f = Fernet(_key)
|
||||
result = f.decrypt(strings).decode('utf-8')
|
||||
return result
|
||||
except:
|
||||
# print(get_error_info())
|
||||
print(getTracebackInfo())
|
||||
return strings
|
||||
|
||||
|
||||
@@ -1246,6 +1290,10 @@ def toSize(size):
|
||||
return str(round(size, 2)) + ' ' + b
|
||||
|
||||
|
||||
def getPathSuffix(path):
|
||||
return os.path.splitext(path)[-1]
|
||||
|
||||
|
||||
def getMacAddress():
|
||||
# 获取mac
|
||||
import uuid
|
||||
@@ -1454,8 +1502,95 @@ def getMyORMDb():
|
||||
o = ormDb.ORM()
|
||||
return o
|
||||
|
||||
|
||||
##################### ssh start #########################################
|
||||
def getSshDir():
|
||||
if isAppleSystem():
|
||||
user = execShell("who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
|
||||
return '/Users/' + user + '/.ssh'
|
||||
return '/root/.ssh'
|
||||
|
||||
|
||||
def createRsa():
|
||||
# ssh-keygen -t rsa -P "" -C "midoks@163.com"
|
||||
ssh_dir = getSshDir()
|
||||
# mw.execShell("rm -f /root/.ssh/*")
|
||||
if not os.path.exists(ssh_dir + '/authorized_keys'):
|
||||
execShell('touch ' + ssh_dir + '/authorized_keys')
|
||||
|
||||
if not os.path.exists(ssh_dir + '/id_rsa.pub') and os.path.exists(ssh_dir + '/id_rsa'):
|
||||
execShell('echo y | ssh-keygen -q -t rsa -P "" -f ' +
|
||||
ssh_dir + '/id_rsa')
|
||||
else:
|
||||
execShell('ssh-keygen -q -t rsa -P "" -f ' + ssh_dir + '/id_rsa')
|
||||
|
||||
execShell('cat ' + ssh_dir + '/id_rsa.pub >> ' +
|
||||
ssh_dir + '/authorized_keys')
|
||||
execShell('chmod 600 ' + ssh_dir + '/authorized_keys')
|
||||
|
||||
|
||||
def createSshInfo():
|
||||
ssh_dir = getSshDir()
|
||||
if not os.path.exists(ssh_dir + '/id_rsa') or not os.path.exists(ssh_dir + '/id_rsa.pub'):
|
||||
createRsa()
|
||||
|
||||
# 检查是否写入authorized_keys
|
||||
data = execShell("cat " + ssh_dir + "/id_rsa.pub | awk '{print $3}'")
|
||||
if data[0] != "":
|
||||
cmd = "cat " + ssh_dir + "/authorized_keys | grep " + data[0]
|
||||
ak_data = execShell(cmd)
|
||||
if ak_data[0] == "":
|
||||
cmd = 'cat ' + ssh_dir + '/id_rsa.pub >> ' + ssh_dir + '/authorized_keys'
|
||||
execShell(cmd)
|
||||
execShell('chmod 600 ' + ssh_dir + '/authorized_keys')
|
||||
|
||||
|
||||
def connectSsh():
|
||||
import paramiko
|
||||
ssh = paramiko.SSHClient()
|
||||
createSshInfo()
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
|
||||
port = getSSHPort()
|
||||
try:
|
||||
ssh.connect('127.0.0.1', port, timeout=5)
|
||||
except Exception as e:
|
||||
ssh.connect('localhost', port, timeout=5)
|
||||
except Exception as e:
|
||||
ssh.connect(getHostAddr(), port, timeout=30)
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
shell = ssh.invoke_shell(term='xterm', width=83, height=21)
|
||||
shell.setblocking(0)
|
||||
return shell
|
||||
|
||||
|
||||
def clearSsh():
|
||||
# 服务器IP
|
||||
ip = getHostAddr()
|
||||
sh = '''
|
||||
#!/bin/bash
|
||||
PLIST=`who | grep localhost | awk '{print $2}'`
|
||||
for i in $PLIST
|
||||
do
|
||||
ps -t /dev/$i |grep -v TTY | awk '{print $1}' | xargs kill -9
|
||||
done
|
||||
|
||||
# getHostAddr
|
||||
PLIST=`who | grep "${ip}" | awk '{print $2}'`
|
||||
for i in $PLIST
|
||||
do
|
||||
ps -t /dev/$i |grep -v TTY | awk '{print $1}' | xargs kill -9
|
||||
done
|
||||
'''
|
||||
if not isAppleSystem():
|
||||
info = execShell(sh)
|
||||
print(info[0], info[1])
|
||||
##################### ssh end #########################################
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 打印相关
|
||||
# 打印相关 START
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -1474,3 +1609,7 @@ def echoEnd(tag):
|
||||
|
||||
def echoInfo(msg):
|
||||
print("|-{}".format(msg))
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 打印相关 END
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
+79
-15
@@ -27,7 +27,7 @@ import sys
|
||||
import threading
|
||||
import multiprocessing
|
||||
|
||||
|
||||
from flask import render_template
|
||||
from flask import request
|
||||
|
||||
|
||||
@@ -75,12 +75,35 @@ class plugins_api:
|
||||
data = self.getPluginList(sType, int(sPage))
|
||||
return mw.getJson(data)
|
||||
|
||||
def menuGetAbsPath(self, tag, path):
|
||||
if path[0:1] == '/':
|
||||
return path
|
||||
else:
|
||||
return mw.getPluginDir() + '/' + tag + '/' + path
|
||||
|
||||
def menuApi(self):
|
||||
import config_api
|
||||
data = config_api.config_api().get()
|
||||
tag = request.args.get('tag', '')
|
||||
menu_file = 'data/hook_menu.json'
|
||||
content = ''
|
||||
if os.path.exists(menu_file):
|
||||
t = mw.readFile(menu_file)
|
||||
tlist = json.loads(t)
|
||||
for menu_data in tlist:
|
||||
if 'path' in menu_data:
|
||||
tpath = self.menuGetAbsPath(tag, menu_data['path'])
|
||||
content = mw.readFile(tpath)
|
||||
data['plugin_content'] = content
|
||||
return render_template('plugin_menu.html', data=data)
|
||||
|
||||
def fileApi(self):
|
||||
name = request.args.get('name', '')
|
||||
if name.strip() == '':
|
||||
return ''
|
||||
|
||||
f = request.args.get('f', '')
|
||||
|
||||
if f.strip() == '':
|
||||
return ''
|
||||
|
||||
@@ -88,8 +111,16 @@ class plugins_api:
|
||||
if not os.path.exists(file):
|
||||
return ''
|
||||
|
||||
c = open(file, 'rb').read()
|
||||
return c
|
||||
suffix = mw.getPathSuffix(file)
|
||||
if suffix == '.css':
|
||||
content = mw.readFile(file)
|
||||
from flask import Response
|
||||
from flask import make_response
|
||||
v = Response(content, headers={
|
||||
'Content-Type': 'text/css; charset="utf-8"'})
|
||||
return make_response(v)
|
||||
content = open(file, 'rb').read()
|
||||
return content
|
||||
|
||||
def indexListApi(self):
|
||||
data = self.getIndexList()
|
||||
@@ -214,10 +245,7 @@ class plugins_api:
|
||||
isNeedAdd = False
|
||||
|
||||
if isNeedAdd:
|
||||
tmp = {}
|
||||
tmp['title'] = info['title']
|
||||
tmp['name'] = info['name']
|
||||
data.append(tmp)
|
||||
data.append(info)
|
||||
mw.writeFile(hookPath, json.dumps(data))
|
||||
|
||||
def hookUninstallFile(self, hook_name, info):
|
||||
@@ -233,21 +261,39 @@ class plugins_api:
|
||||
mw.writeFile(hookPath, json.dumps(data))
|
||||
|
||||
def hookInstall(self, info):
|
||||
valid_hook = ['backup', 'database']
|
||||
valid_list_hook = ['menu']
|
||||
if 'hook' in info:
|
||||
hooks = info['hook']
|
||||
for x in hooks:
|
||||
if x in ['backup', 'database']:
|
||||
self.hookInstallFile(x, info)
|
||||
return True
|
||||
for h in hooks:
|
||||
hooks_type = type(h)
|
||||
if hooks_type == dict:
|
||||
tag = h['tag']
|
||||
if tag in valid_list_hook:
|
||||
self.hookInstallFile(tag, h[tag])
|
||||
elif hooks_type == str:
|
||||
for x in hooks:
|
||||
if x in valid_hook:
|
||||
self.hookInstallFile(x, info)
|
||||
return True
|
||||
return False
|
||||
|
||||
def hookUninstall(self, info):
|
||||
valid_hook = ['backup', 'database']
|
||||
valid_list_hook = ['menu']
|
||||
if 'hook' in info:
|
||||
hooks = info['hook']
|
||||
for x in hooks:
|
||||
if x in ['backup', 'database']:
|
||||
self.hookUninstallFile(x, info)
|
||||
return True
|
||||
for h in hooks:
|
||||
hooks_type = type(h)
|
||||
if hooks_type == dict:
|
||||
tag = h['tag']
|
||||
if tag in valid_list_hook:
|
||||
self.hookUninstallFile(tag, h[tag])
|
||||
elif hooks_type == str:
|
||||
for x in hooks:
|
||||
if x in valid_hook:
|
||||
self.hookUninstallFile(x, info)
|
||||
return True
|
||||
return False
|
||||
|
||||
def uninstallOldApi(self):
|
||||
@@ -618,6 +664,7 @@ class plugins_api:
|
||||
"ps": info['ps'],
|
||||
"dependnet": "",
|
||||
"mutex": "",
|
||||
"icon": "",
|
||||
"path": path,
|
||||
"install_checks": checks,
|
||||
"uninsatll_checks": checks,
|
||||
@@ -632,6 +679,9 @@ class plugins_api:
|
||||
"uninstall_pre_inspection": False,
|
||||
}
|
||||
|
||||
if 'icon' in info:
|
||||
pInfo['icon'] = info['icon']
|
||||
|
||||
if checks.find('VERSION') > -1:
|
||||
pInfo['install_checks'] = checks.replace(
|
||||
'VERSION', info['versions'])
|
||||
@@ -677,6 +727,7 @@ class plugins_api:
|
||||
def makeList(self, data, sType='0'):
|
||||
plugins_info = []
|
||||
|
||||
# 相应类型
|
||||
if (data['pid'] == sType):
|
||||
if type(data['versions']) == list and 'coexist' in data and data['coexist']:
|
||||
tmp_data = self.makeCoexist(data)
|
||||
@@ -687,6 +738,7 @@ class plugins_api:
|
||||
plugins_info.append(pg)
|
||||
return plugins_info
|
||||
|
||||
# 全部
|
||||
if sType == '0':
|
||||
if type(data['versions']) == list and 'coexist' in data and data['coexist']:
|
||||
tmp_data = self.makeCoexist(data)
|
||||
@@ -696,6 +748,18 @@ class plugins_api:
|
||||
pg = self.getPluginInfo(data)
|
||||
plugins_info.append(pg)
|
||||
|
||||
# 已经安装
|
||||
if sType == '-1':
|
||||
if type(data['versions']) == list and 'coexist' in data and data['coexist']:
|
||||
tmp_data = self.makeCoexist(data)
|
||||
for index in range(len(tmp_data)):
|
||||
if tmp_data[index]['setup']:
|
||||
plugins_info.append(tmp_data[index])
|
||||
else:
|
||||
pg = self.getPluginInfo(data)
|
||||
if pg['setup']:
|
||||
plugins_info.append(pg)
|
||||
|
||||
# print plugins_info, data
|
||||
return plugins_info
|
||||
|
||||
|
||||
+73
-12
@@ -817,9 +817,9 @@ class site_api:
|
||||
if not len(domains):
|
||||
return mw.returnJson(False, '请选择域名')
|
||||
|
||||
file = self.getHostConf(siteName)
|
||||
if os.path.exists(file):
|
||||
siteConf = mw.readFile(file)
|
||||
host_conf_file = self.getHostConf(siteName)
|
||||
if os.path.exists(host_conf_file):
|
||||
siteConf = mw.readFile(host_conf_file)
|
||||
if siteConf.find('301-END') != -1:
|
||||
return mw.returnJson(False, '检测到您的站点做了301重定向设置,请先关闭重定向!')
|
||||
|
||||
@@ -837,7 +837,15 @@ class site_api:
|
||||
if os.path.exists(proxy_dir_file):
|
||||
return mw.returnJson(False, '检测到您的站点做了反向代理设置,请先关闭反向代理!')
|
||||
|
||||
auth_to = self.getSitePath(siteName)
|
||||
# fix binddir domain ssl apply question
|
||||
mw.backFile(host_conf_file)
|
||||
auth_to = self.getSitePath(siteName)
|
||||
rep = "\s*root\s*(.+);"
|
||||
replace_root = "\n\troot " + auth_to + ";"
|
||||
siteConf = re.sub(rep, replace_root, siteConf)
|
||||
mw.writeFile(host_conf_file, siteConf)
|
||||
mw.restartWeb()
|
||||
|
||||
to_args = {
|
||||
'domains': domains,
|
||||
'auth_type': 'http',
|
||||
@@ -855,6 +863,7 @@ class site_api:
|
||||
if not os.path.exists(src_letpath):
|
||||
import cert_api
|
||||
data = cert_api.cert_api().applyCertApi(to_args)
|
||||
mw.restoreFile(host_conf_file)
|
||||
if not data['status']:
|
||||
msg = data['msg']
|
||||
if type(data['msg']) != str:
|
||||
@@ -876,6 +885,8 @@ class site_api:
|
||||
|
||||
result['csr'] = mw.readFile(src_csrpath)
|
||||
result['key'] = mw.readFile(src_keypath)
|
||||
|
||||
mw.restartWeb()
|
||||
return mw.returnJson(data['status'], data['msg'], result)
|
||||
|
||||
def getAcmeLogsApi(self):
|
||||
@@ -1118,6 +1129,26 @@ class site_api:
|
||||
host = self.getHostConf(siteName)
|
||||
return mw.getJson({'host': host})
|
||||
|
||||
def saveHostConfApi(self):
|
||||
path = request.form.get('path', '')
|
||||
data = request.form.get('data', '')
|
||||
encoding = request.form.get('encoding', '')
|
||||
|
||||
import files_api
|
||||
|
||||
mw.backFile(path)
|
||||
save_ret_data = files_api.files_api().saveBody(path, data, encoding)
|
||||
rdata = json.loads(save_ret_data)
|
||||
|
||||
if rdata['status']:
|
||||
isError = mw.checkWebConfig()
|
||||
if isError != True:
|
||||
mw.restoreFile(path)
|
||||
return mw.returnJson(False, 'ERROR: 检测到配置文件有错误,请先排除后再操作<br><br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
|
||||
mw.restartWeb()
|
||||
mw.removeBackFile(path)
|
||||
return save_ret_data
|
||||
|
||||
def getRewriteConfApi(self):
|
||||
siteName = request.form.get('siteName', '')
|
||||
rewrite = self.getRewriteConf(siteName)
|
||||
@@ -1211,14 +1242,26 @@ class site_api:
|
||||
return mw.returnJson(False, '端口范围不合法!')
|
||||
|
||||
opid = mw.M('domain').where(
|
||||
"name=? AND (port=? OR pid=?)", (domain, domain_port, pid)).getField('pid')
|
||||
if opid:
|
||||
if mw.M('sites').where('id=?', (opid,)).count():
|
||||
return mw.returnJson(False, '指定域名已绑定过!')
|
||||
mw.M('domain').where('pid=?', (opid,)).delete()
|
||||
"name=? AND (port=? OR pid=?)", (domain_name, domain_port, pid,)).getField('pid')
|
||||
# print(opid)
|
||||
is_bind = False
|
||||
if type(opid) == list and len(opid) > 0:
|
||||
is_bind = True
|
||||
|
||||
if type(opid) == int and opid > 0:
|
||||
is_bind = True
|
||||
|
||||
if type(opid) == str and int(opid) > 0:
|
||||
is_bind = True
|
||||
|
||||
if is_bind:
|
||||
return mw.returnJson(False, '您添加的域名[{}],已绑定!'.format(domain_name))
|
||||
# if mw.M('sites').where('id=?', (opid,)).count():
|
||||
# return mw.returnJson(False, '指定域名已绑定过!')
|
||||
# mw.M('domain').where('pid=?', (opid,)).delete()
|
||||
|
||||
if mw.M('binding').where('domain=?', (domain,)).count():
|
||||
return mw.returnJson(False, '您添加的域名已存在!')
|
||||
return mw.returnJson(False, '您添加的域名,子目录已绑定!')
|
||||
|
||||
self.nginxAddDomain(webname, domain_name, domain_port)
|
||||
|
||||
@@ -1270,17 +1313,18 @@ class site_api:
|
||||
content = content.replace('{$ROOT_DIR}', webdir)
|
||||
content = content.replace('{$SERVER_MAIN}', siteInfo['name'])
|
||||
content = content.replace('{$OR_REWRITE}', self.rewritePath)
|
||||
content = content.replace('{$PHP_DIR}', self.setupPath + '/php')
|
||||
content = content.replace('{$LOGPATH}', mw.getLogsDir())
|
||||
|
||||
conf += "\r\n" + content
|
||||
shutil.copyfile(filename, '/tmp/backup.conf')
|
||||
mw.backFile(filename)
|
||||
mw.writeFile(filename, conf)
|
||||
conf = mw.readFile(filename)
|
||||
|
||||
# 检查配置是否有误
|
||||
isError = mw.checkWebConfig()
|
||||
if isError != True:
|
||||
shutil.copyfile('/tmp/backup.conf', filename)
|
||||
mw.restoreFile(filename)
|
||||
return mw.returnJson(False, 'ERROR: <br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
|
||||
|
||||
mw.M('binding').add('pid,domain,port,path,addtime',
|
||||
@@ -1290,6 +1334,7 @@ class site_api:
|
||||
(siteInfo['name'], dirName, domain))
|
||||
mw.writeLog('网站管理', msg)
|
||||
mw.restartWeb()
|
||||
mw.removeBackFile(filename)
|
||||
return mw.returnJson(True, '添加成功!')
|
||||
|
||||
def delDirBindApi(self):
|
||||
@@ -2543,6 +2588,22 @@ location ^~ {from} {\n\
|
||||
mw.execShell('rm -rf ' + ssl_acme_dir)
|
||||
|
||||
mw.M('sites').where("id=?", (sid,)).delete()
|
||||
|
||||
# binding domain delete
|
||||
binding_list = mw.M('binding').field(
|
||||
'id,domain').where("pid=?", (sid,)).select()
|
||||
|
||||
for x in binding_list:
|
||||
wlog = mw.getLogsDir() + "/" + webname + "_" + x['domain'] + ".log"
|
||||
wlog_error = mw.getLogsDir() + "/" + webname + "_" + \
|
||||
x['domain'] + ".error.log"
|
||||
|
||||
if os.path.exists(wlog):
|
||||
mw.execShell('rm -rf ' + wlog)
|
||||
if os.path.exists(wlog_error):
|
||||
mw.execShell('rm -rf ' + wlog_error)
|
||||
|
||||
mw.M('binding').where("pid=?", (sid,)).delete()
|
||||
mw.restartWeb()
|
||||
return mw.returnJson(True, '站点删除成功!')
|
||||
|
||||
|
||||
@@ -0,0 +1,456 @@
|
||||
# coding: utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# SSH终端操作
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
import json
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import socket
|
||||
import threading
|
||||
import re
|
||||
|
||||
from io import BytesIO, StringIO
|
||||
|
||||
import mw
|
||||
import paramiko
|
||||
|
||||
from flask_socketio import SocketIO, emit, send
|
||||
|
||||
|
||||
class ssh_terminal:
|
||||
|
||||
__debug_file = 'logs/terminal.log'
|
||||
__log_type = 'SSH终端'
|
||||
|
||||
# websocketio 唯一标识
|
||||
__sid = ''
|
||||
|
||||
__host = None
|
||||
__type = '0'
|
||||
__port = 22
|
||||
__user = None
|
||||
__pass = None
|
||||
__pkey = None
|
||||
__key_passwd = None
|
||||
|
||||
__rep_ssh_config = False
|
||||
__rep_ssh_service = False
|
||||
__sshd_config_backup = None
|
||||
|
||||
__ssh = None
|
||||
__tp = None
|
||||
__ps = None
|
||||
|
||||
__ssh_list = {}
|
||||
__ssh_last_request_time = {}
|
||||
|
||||
def __init__(self):
|
||||
ht = threading.Thread(target=self.heartbeat)
|
||||
ht.start()
|
||||
|
||||
def debug(self, msg):
|
||||
msg = "{} - {}:{} => {} \n".format(mw.formatDate(),
|
||||
self.__host, self.__port, msg)
|
||||
if not mw.isDebugMode():
|
||||
return
|
||||
mw.writeFile(self.__debug_file, msg, 'a+')
|
||||
|
||||
def returnMsg(self, status, msg):
|
||||
return {'status': status, 'msg': msg}
|
||||
|
||||
def restartSsh(self, act='reload'):
|
||||
'''
|
||||
重启ssh 无参数传递
|
||||
'''
|
||||
version = mw.readFile('/etc/redhat-release')
|
||||
if not os.path.exists('/etc/redhat-release'):
|
||||
mw.execShell('service ssh ' + act)
|
||||
elif version.find(' 7.') != -1 or version.find(' 8.') != -1:
|
||||
mw.execShell("systemctl " + act + " sshd.service")
|
||||
else:
|
||||
mw.execShell("/etc/init.d/sshd " + act)
|
||||
|
||||
def isRunning(self, rep=False):
|
||||
try:
|
||||
if rep and self.__rep_ssh_service:
|
||||
self.restartSsh('stop')
|
||||
return True
|
||||
|
||||
status = self.getSshStatus()
|
||||
if not status:
|
||||
self.restartSsh('start')
|
||||
self.__rep_ssh_service = True
|
||||
return True
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def setSshdConfig(self, rep=False):
|
||||
self.isRunning(rep)
|
||||
if rep and not self.__rep_ssh_config:
|
||||
return False
|
||||
|
||||
try:
|
||||
sshd_config_file = '/etc/ssh/sshd_config'
|
||||
if not os.path.exists(sshd_config_file):
|
||||
return False
|
||||
|
||||
sshd_config = mw.readFile(sshd_config_file)
|
||||
if not sshd_config:
|
||||
return False
|
||||
|
||||
if rep:
|
||||
if self.__sshd_config_backup:
|
||||
mw.writeFile(sshd_config_file, self.__sshd_config_backup)
|
||||
self.restartSsh()
|
||||
return True
|
||||
|
||||
pin = r'^\s*PubkeyAuthentication\s+(yes|no)'
|
||||
pubkey_status = re.findall(pin, sshd_config, re.I)
|
||||
if pubkey_status:
|
||||
if pubkey_status[0] == 'yes':
|
||||
pubkey_status = True
|
||||
else:
|
||||
pubkey_status = False
|
||||
|
||||
pin = r'^\s*RSAAuthentication\s+(yes|no)'
|
||||
rsa_status = re.findall(pin, sshd_config, re.I)
|
||||
if rsa_status:
|
||||
if rsa_status[0] == 'yes':
|
||||
rsa_status = True
|
||||
else:
|
||||
rsa_status = False
|
||||
|
||||
self._sshd_config_backup = sshd_config
|
||||
is_write = False
|
||||
if not pubkey_status:
|
||||
sshd_config = re.sub(
|
||||
r'\n#?PubkeyAuthentication\s\w+', '\nPubkeyAuthentication yes', sshd_config)
|
||||
is_write = True
|
||||
if not rsa_status:
|
||||
sshd_config = re.sub(
|
||||
r'\n#?RSAAuthentication\s\w+', '\nRSAAuthentication yes', sshd_config)
|
||||
is_write = True
|
||||
|
||||
if is_write:
|
||||
mw.writeFile(sshd_config_file, sshd_config)
|
||||
self.__rep_ssh_config = True
|
||||
self.restartSsh()
|
||||
else:
|
||||
self.__sshd_config_backup = None
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def setSid(self, sid):
|
||||
self.__sid = sid
|
||||
|
||||
def connect(self, sid):
|
||||
# self.connectBySocket()
|
||||
if self.__host in ['127.0.0.1', 'localhost']:
|
||||
return self.connectLocalSsh(sid)
|
||||
else:
|
||||
return self.connectBySocket(sid)
|
||||
|
||||
def connectLocalSsh(self, sid):
|
||||
mw.createSshInfo()
|
||||
self.__ps = paramiko.SSHClient()
|
||||
self.__ps.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
|
||||
self.__port = mw.getSSHPort()
|
||||
try:
|
||||
self.__ps.connect(self.__host, self.__port, timeout=60)
|
||||
except Exception as e:
|
||||
self.__ps.connect('127.0.0.1', self.__port)
|
||||
except Exception as e:
|
||||
self.__ps.connect('localhost', self.__port)
|
||||
except Exception as e:
|
||||
self.setSshdConfig(True)
|
||||
self.__ps.close()
|
||||
e = str(e)
|
||||
if e.find('websocket error!') != -1:
|
||||
return self.returnMsg(True, '连接成功')
|
||||
if e.find('Authentication timeout') != -1:
|
||||
self.debug("认证超时{}".format(e))
|
||||
return self.returnMsg(False, '认证超时,请按回车重试!{}'.format(e))
|
||||
if e.find('Connection reset by peer') != -1:
|
||||
self.debug('目标服务器主动拒绝连接')
|
||||
return self.returnMsg(False, '目标服务器主动拒绝连接')
|
||||
if e.find('Error reading SSH protocol banner') != -1:
|
||||
self.debug('协议头响应超时')
|
||||
return self.returnMsg(False, '协议头响应超时,与目标服务器之间的网络质量太糟糕:' + e)
|
||||
if not e:
|
||||
self.debug('SSH协议握手超时')
|
||||
return self.returnMsg(False, "SSH协议握手超时,与目标服务器之间的网络质量太糟糕")
|
||||
err = mw.getTracebackInfo()
|
||||
self.debug(err)
|
||||
return self.returnMsg(False, "未知错误: {}".format(err))
|
||||
|
||||
self.debug('local-ssh:认证成功,正在构建会话通道')
|
||||
ssh = self.__ps.invoke_shell(
|
||||
term='xterm', width=83, height=21)
|
||||
ssh.setblocking(0)
|
||||
self.__ssh_list[sid] = ssh
|
||||
mw.writeLog(self.__log_type, '成功登录到SSH服务器 [{}:{}]'.format(
|
||||
self.__host, self.__port))
|
||||
self.debug('local-ssh:通道已构建')
|
||||
return self.returnMsg(True, '连接成功!')
|
||||
|
||||
def connectBySocket(self, sid):
|
||||
if not self.__host:
|
||||
return self.returnMsg(False, '错误的连接地址')
|
||||
if not self.__user:
|
||||
self.__user = 'root'
|
||||
if not self.__port:
|
||||
self.__port = 22
|
||||
|
||||
self.setSshdConfig(True)
|
||||
num = 0
|
||||
while num < 5:
|
||||
num += 1
|
||||
try:
|
||||
self.debug('正在尝试第{}次连接'.format(num))
|
||||
if self.__rep_ssh_config:
|
||||
time.sleep(0.1)
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(2 + num)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 8192)
|
||||
sock.connect((self.__host, self.__port))
|
||||
break
|
||||
except Exception as e:
|
||||
if num == 5:
|
||||
self.setSshdConfig(True)
|
||||
self.debug('重试连接失败,{}'.format(e))
|
||||
return self.returnMsg(False, '连接目标服务器失败, {}:{}'.format(self.__host, self.__port))
|
||||
else:
|
||||
time.sleep(0.2)
|
||||
|
||||
# print(self.__host, sock)
|
||||
self.__tp = paramiko.Transport(sock)
|
||||
try:
|
||||
self.__tp.start_client()
|
||||
self.__tp.banner_timeout = 60
|
||||
if not self.__pass and not self.__pkey:
|
||||
return self.returnMsg(False, '密码或私钥不能都为空: {}:{}'.format(self.__host, self.__port))
|
||||
|
||||
if self.__pkey != '' and self.__type != '0':
|
||||
self.debug('正在认证私钥')
|
||||
p_file = StringIO(str(self.__pkey.replace('\\n', '\n')))
|
||||
# p_file = "/tmp/t_ssh_pkey.txt"
|
||||
# mw.writeFile(p_file, self.__pkey.replace('\\n', '\n'))
|
||||
# mw.execShell('chmod 600 ' + p_file)
|
||||
try:
|
||||
p_file.seek(0)
|
||||
pkey = paramiko.RSAKey.from_private_key(p_file)
|
||||
except:
|
||||
try:
|
||||
p_file.seek(0) # 重置游标
|
||||
pkey = paramiko.Ed25519Key.from_private_key(
|
||||
p_file)
|
||||
except:
|
||||
try:
|
||||
p_file.seek(0)
|
||||
pkey = paramiko.ECDSAKey.from_private_key(p_file)
|
||||
except:
|
||||
p_file.seek(0)
|
||||
pkey = paramiko.DSSKey.from_private_key(p_file)
|
||||
|
||||
self.__tp.auth_publickey(username=self.__user, key=pkey)
|
||||
else:
|
||||
try:
|
||||
self.__tp.auth_none(self.__user)
|
||||
except Exception as e:
|
||||
e = str(e)
|
||||
if e.find('keyboard-interactive') >= 0:
|
||||
self._auth_interactive()
|
||||
else:
|
||||
self.debug('正在认证密码')
|
||||
self.__tp.auth_password(
|
||||
username=self.__user, password=self.__pass)
|
||||
except Exception as e:
|
||||
self.setSshdConfig(True)
|
||||
self.__tp.close()
|
||||
e = str(e)
|
||||
# print(e)
|
||||
if e.find('Authentication timeout') != -1:
|
||||
self.debug("认证超时{}".format(e))
|
||||
return self.returnMsg(False, '认证超时,请按回车重试!{}'.format(e))
|
||||
if e.find('Authentication failed') != -1:
|
||||
self.debug('认证失败{}'.format(e))
|
||||
return self.returnMsg(False, '帐号或密码错误: {}'.format(e + "," + self.__user + "@" + self.__host + ":" + str(self.__port)))
|
||||
if e.find('Bad authentication type; allowed types') != -1:
|
||||
self.debug('认证失败{}'.format(e))
|
||||
if self.__host in ['127.0.0.1', 'localhost'] and self.__pass == 'none':
|
||||
return self.returnMsg(False, '帐号或密码错误: {}'.format("Authentication failed ," + self.__user + "@" + self.__host + ":" + str(self.__port)))
|
||||
return self.returnMsg(False, '不支持的身份验证类型: {}'.format(e))
|
||||
if e.find('Connection reset by peer') != -1:
|
||||
self.debug('目标服务器主动拒绝连接')
|
||||
return self.returnMsg(False, '目标服务器主动拒绝连接')
|
||||
if e.find('Error reading SSH protocol banner') != -1:
|
||||
self.debug('协议头响应超时')
|
||||
return self.returnMsg(False, '协议头响应超时,与目标服务器之间的网络质量太糟糕:' + e)
|
||||
if not e:
|
||||
self.debug('SSH协议握手超时')
|
||||
return self.returnMsg(False, "SSH协议握手超时,与目标服务器之间的网络质量太糟糕")
|
||||
err = mw.getTracebackInfo()
|
||||
self.debug(err)
|
||||
return self.returnMsg(False, "未知错误: {}".format(err))
|
||||
|
||||
self.debug('认证成功,正在构建会话通道')
|
||||
|
||||
ssh = self.__tp.open_session()
|
||||
ssh.get_pty(term='xterm', width=100, height=34)
|
||||
ssh.invoke_shell()
|
||||
self.__ssh_list[sid] = ssh
|
||||
mw.writeLog(self.__log_type, '成功登录到SSH服务器 [{}:{}]'.format(
|
||||
self.__host, self.__port))
|
||||
self.debug('通道已构建')
|
||||
return self.returnMsg(True, '连接成功.')
|
||||
|
||||
def getSshInfo(self, file):
|
||||
rdata = mw.readFile(file)
|
||||
destr = mw.deCrypt('mdserver-web', rdata)
|
||||
return json.loads(destr)
|
||||
|
||||
def setAttr(self, sid, info):
|
||||
self.__host = info['host'].strip()
|
||||
|
||||
# 外部连接获取
|
||||
if not self.__host in ['127.0.0.1', 'localhost']:
|
||||
dst_info = mw.getServerDir() + '/webssh/host/' + self.__host + '/info.json'
|
||||
if os.path.exists(dst_info):
|
||||
info = self.getSshInfo(dst_info)
|
||||
|
||||
if 'type' in info:
|
||||
self.__type = info['type']
|
||||
|
||||
if 'port' in info:
|
||||
self.__port = int(info['port'])
|
||||
if 'username' in info:
|
||||
self.__user = info['username']
|
||||
if 'pkey' in info:
|
||||
self.__pkey = info['pkey']
|
||||
if 'password' in info:
|
||||
self.__pass = info['password']
|
||||
if 'pkey_passwd' in info:
|
||||
self.__key_passwd = info['pkey_passwd']
|
||||
|
||||
# print(self.__host, self.__pass, self.__key_passwd)
|
||||
try:
|
||||
result = self.connect(sid)
|
||||
# print(result)
|
||||
except Exception as ex:
|
||||
if str(ex).find("NoneType") == -1:
|
||||
raise ex
|
||||
return result
|
||||
|
||||
def send(self):
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
try:
|
||||
if self.__ssh:
|
||||
self.__ssh.close()
|
||||
if self.__tp: # 关闭宿主服务
|
||||
self.__tp.close()
|
||||
if self.__ps:
|
||||
self.__ps.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
def resize(self, sid, data):
|
||||
try:
|
||||
self.__ssh_list[sid].resize_pty(
|
||||
width=data['cols'], height=data['rows'])
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def wsSend(self, recv):
|
||||
try:
|
||||
t = recv.decode("utf-8")
|
||||
return emit('server_response', {'data': t})
|
||||
except Exception as e:
|
||||
return emit('server_response', {'data': recv})
|
||||
|
||||
def wsSendConnect(self):
|
||||
return emit('connect', {'data': 'ok'})
|
||||
|
||||
def wsSendReConnect(self):
|
||||
return emit('reconnect', {'data': 'ok'})
|
||||
|
||||
def heartbeat(self):
|
||||
# limit_cos = 10
|
||||
while True:
|
||||
time.sleep(3)
|
||||
cur_time = time.time()
|
||||
for x in list(self.__ssh_list.keys()):
|
||||
ssh_last_time = self.__ssh_last_request_time[x]
|
||||
sid_off_cos = cur_time - ssh_last_time
|
||||
|
||||
# print("heartbeat off cos :", x, sid_off_cos)
|
||||
if sid_off_cos > 3:
|
||||
cur_ssh = self.__ssh_list[x]
|
||||
if not cur_ssh:
|
||||
del(self.__ssh_list[x])
|
||||
del(self.__ssh_last_request_time[x])
|
||||
continue
|
||||
|
||||
if cur_ssh.exit_status_ready():
|
||||
del(self.__ssh_list[x])
|
||||
del(self.__ssh_last_request_time[x])
|
||||
continue
|
||||
|
||||
cur_ssh.send("exit\r\n")
|
||||
del(self.__ssh_list[x])
|
||||
del(self.__ssh_last_request_time[x])
|
||||
|
||||
def run(self, sid, info):
|
||||
self.__sid = sid
|
||||
if not self.__sid:
|
||||
return self.wsSend('WebSocketIO无效')
|
||||
|
||||
self.__ssh_last_request_time[sid] = time.time()
|
||||
if not sid in self.__ssh_list:
|
||||
if type(info) == dict and 'host' in info:
|
||||
result = self.setAttr(sid, info)
|
||||
if result['status']:
|
||||
return self.wsSendConnect()
|
||||
else:
|
||||
return self.wsSend(result['msg'])
|
||||
else:
|
||||
return self.wsSendReConnect()
|
||||
|
||||
result = self.returnMsg(False, '')
|
||||
if sid in self.__ssh_list:
|
||||
if 'resize' in info:
|
||||
self.resize(sid, info)
|
||||
result = self.returnMsg(True, '已连接')
|
||||
if result['status']:
|
||||
if type(info) == str:
|
||||
time.sleep(0.1)
|
||||
cur_ssh = self.__ssh_list[sid]
|
||||
if cur_ssh.exit_status_ready():
|
||||
self.wsSend("logout\r\n")
|
||||
del(self.__ssh_list[sid])
|
||||
return
|
||||
cur_ssh.send(info)
|
||||
try:
|
||||
time.sleep(0.005)
|
||||
recv = cur_ssh.recv(8192)
|
||||
return self.wsSend(recv)
|
||||
except Exception as ex:
|
||||
return self.wsSend('')
|
||||
else:
|
||||
return self.wsSend(result['msg'])
|
||||
@@ -43,7 +43,12 @@ mw_start(){
|
||||
|
||||
mw_start_debug(){
|
||||
python3 task.py >> $DIR/logs/task.log 2>&1 &
|
||||
gunicorn -b :7200 -k gevent -w 1 app:app
|
||||
port=7200
|
||||
if [ -f /www/server/mdserver-web/data/port.pl ];then
|
||||
port=$(cat /www/server/mdserver-web/data/port.pl)
|
||||
fi
|
||||
# gunicorn -b :${port} -k gevent -w 1 app:app
|
||||
gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
|
||||
}
|
||||
|
||||
mw_start_debug2(){
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
"type":0,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"已安装",
|
||||
"type":-1,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"运行环境",
|
||||
"type":1,
|
||||
|
||||
@@ -17,7 +17,7 @@ server
|
||||
#ERROR-PAGE-END
|
||||
|
||||
#PHP-INFO-START
|
||||
include enable-php-{$PHPVER}.conf;
|
||||
include {$PHP_DIR}/conf/enable-php-{$PHPVER}.conf;
|
||||
#PHP-INFO-END
|
||||
|
||||
#REWRITE-START
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"checks":"server/backup_ftp",
|
||||
"path": "server/backup_ftp",
|
||||
"author":"midoks",
|
||||
"home":"",
|
||||
"home":"https://github.com/midoks/mdserver-web",
|
||||
"date":"2022-10-23",
|
||||
"pid": "4"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"sort": 7,
|
||||
"ps": "简单邮件服务[DEV]",
|
||||
"ps": "[<b style='color:red;'>DEV</b>]简单邮件服务",
|
||||
"name": "imail",
|
||||
"title": "邮件服务",
|
||||
"shell": "install.sh",
|
||||
|
||||
@@ -10,8 +10,8 @@ serverPath=$(dirname "$rootPath")
|
||||
install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
VERSION=0.0.19
|
||||
|
||||
# bash install.sh install 0.0.16
|
||||
## cd /www/server/mdserver-web/plugins/imail && bash install.sh install 0.0.16
|
||||
# bash install.sh install 0.0.19
|
||||
## cd /www/server/mdserver-web/plugins/imail && bash install.sh install 0.0.19
|
||||
|
||||
bash ${rootPath}/scripts/getos.sh
|
||||
OSNAME=`cat ${rootPath}/data/osname.pl`
|
||||
@@ -24,7 +24,7 @@ get_arch() {
|
||||
TMP_ARCH=`arch`
|
||||
if [ "$TMP_ARCH" == "x86_64" ];then
|
||||
ARCH="amd64"
|
||||
else if [ "$TMP_ARCH" == "aarch64" ];then
|
||||
elif [ "$TMP_ARCH" == "aarch64" ];then
|
||||
ARCH="arm64"
|
||||
else
|
||||
echo $ARCH
|
||||
|
||||
@@ -40,4 +40,18 @@ CREATE TABLE IF NOT EXISTS `slave_id_rsa` (
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
-- 从库配置主库的[user]
|
||||
-- drop table `slave_user`;
|
||||
CREATE TABLE IF NOT EXISTS `slave_sync_user` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`ip` TEXT,
|
||||
`port` TEXT,
|
||||
`user` TEXT,
|
||||
`pass` TEXT,
|
||||
`mode` TEXT,
|
||||
`cmd` TEXT,
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ replicate-ignore-db = information_schema
|
||||
replicate-ignore-db = performance_schema
|
||||
replicate-ignore-db = mysql
|
||||
replicate-ignore-db = test
|
||||
replicate-wild-ignore-table=mysql.%
|
||||
|
||||
|
||||
innodb_data_home_dir = {$SERVER_APP_PATH}/data
|
||||
|
||||
+316
-16
@@ -140,20 +140,27 @@ def contentReplace(content):
|
||||
def pSqliteDb(dbname='databases'):
|
||||
file = getServerDir() + '/mariadb.db'
|
||||
name = 'mysql'
|
||||
if not os.path.exists(file):
|
||||
|
||||
import_sql = mw.readFile(getPluginDir() + '/conf/mariadb.sql')
|
||||
md5_sql = mw.md5(import_sql)
|
||||
|
||||
import_sign = False
|
||||
save_md5_file = getServerDir() + '/import_sql.md5'
|
||||
if os.path.exists(save_md5_file):
|
||||
save_md5_sql = mw.readFile(save_md5_file)
|
||||
if save_md5_sql != md5_sql:
|
||||
import_sign = True
|
||||
mw.writeFile(save_md5_file, md5_sql)
|
||||
else:
|
||||
mw.writeFile(save_md5_file, md5_sql)
|
||||
|
||||
if not os.path.exists(file) or import_sql:
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
csql = mw.readFile(getPluginDir() + '/conf/mariadb.sql')
|
||||
csql_list = csql.split(';')
|
||||
csql_list = import_sql.split(';')
|
||||
for index in range(len(csql_list)):
|
||||
conn.execute(csql_list[index], ())
|
||||
else:
|
||||
# 现有run
|
||||
# conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
# csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
|
||||
# csql_list = csql.split(';')
|
||||
# for index in range(len(csql_list)):
|
||||
# conn.execute(csql_list[index], ())
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
return conn
|
||||
|
||||
|
||||
@@ -280,6 +287,13 @@ def binLog():
|
||||
return mw.returnJson(True, '设置成功!')
|
||||
|
||||
|
||||
def cleanBinLog():
|
||||
db = pMysqlDb()
|
||||
cleanTime = time.strftime('%Y-%m-%d %H:%i:%s', time.localtime())
|
||||
db.execute("PURGE MASTER LOGS BEFORE '" + cleanTime + "';")
|
||||
return mw.returnJson(True, '清理BINLOG成功!')
|
||||
|
||||
|
||||
def setSkipGrantTables(v):
|
||||
'''
|
||||
设置是否密码验证
|
||||
@@ -1785,7 +1799,7 @@ def addMasterRepSlaveUser(version=''):
|
||||
# address = args['address'].strip()
|
||||
# dataAccess = args['dataAccess'].strip()
|
||||
|
||||
reg = "^[\w\.-]+$"
|
||||
reg = "^[\w-]+$"
|
||||
if not re.match(reg, username):
|
||||
return mw.returnJson(False, '用户名不能带有特殊符号!')
|
||||
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
|
||||
@@ -1806,11 +1820,15 @@ def addMasterRepSlaveUser(version=''):
|
||||
sql = "GRANT REPLICATION SLAVE ON *.* TO '" + username + \
|
||||
"'@'%' identified by '" + password + "';"
|
||||
result = pdb.execute(sql)
|
||||
result = pdb.execute('FLUSH PRIVILEGES;')
|
||||
|
||||
isError = isSqlError(result)
|
||||
if isError != None:
|
||||
return isError
|
||||
|
||||
sql_select = "grant select,lock tables,PROCESS on *.* to " + username + "@'%';"
|
||||
pdb.execute(sql_select)
|
||||
pdb.execute('FLUSH PRIVILEGES;')
|
||||
|
||||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
psdb.add('username,password,accept,ps,addtime',
|
||||
(username, password, '%', '', addTime))
|
||||
@@ -1847,10 +1865,16 @@ def getMasterRepSlaveUserCmd(version):
|
||||
|
||||
mode = recognizeDbMode()
|
||||
|
||||
# 查找同步点
|
||||
# SELECT BINLOG_GTID_POS('master1-bin.000002', 561866201);
|
||||
|
||||
if mode == "gtid":
|
||||
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
|
||||
clist[0]['username'] + "', MASTER_PASSWORD='" + \
|
||||
clist[0]['password'] + "', MASTER_AUTO_POSITION=1"
|
||||
clist[0]['password'] + "', " + "MASTER_LOG_FILE='" + mstatus[0]["File"] + \
|
||||
"',MASTER_LOG_POS=" + \
|
||||
str(mstatus[0]["Position"]) + \
|
||||
",MASTER_USE_GTID=slave_pos,MASTER_CONNECT_RETRY=10;"
|
||||
else:
|
||||
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
|
||||
clist[0]['username'] + "', MASTER_PASSWORD='" + \
|
||||
@@ -1936,6 +1960,123 @@ def getSlaveSSHList(version=''):
|
||||
return mw.getJson(data)
|
||||
|
||||
|
||||
def getSlaveSyncUserByIp(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['ip'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ip = args['ip']
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').where(
|
||||
"ip=?", (ip,)).select()
|
||||
return mw.returnJson(True, 'ok', data)
|
||||
|
||||
|
||||
def addSlaveSyncUser(version=''):
|
||||
import base64
|
||||
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['ip'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ip = args['ip']
|
||||
if ip == "":
|
||||
return mw.returnJson(True, 'ok')
|
||||
|
||||
data = checkArgs(args, ['port', 'user', 'pass', 'mode'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
cmd = args['cmd']
|
||||
port = args['port']
|
||||
user = args['user']
|
||||
apass = args['pass']
|
||||
mode = args['mode']
|
||||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip').where("ip=?", (ip,)).select()
|
||||
if len(data) > 0:
|
||||
res = conn.where("ip=?", (ip,)).save(
|
||||
'port,user,pass,mode,cmd', (port, user, apass, mode, cmd))
|
||||
else:
|
||||
conn.add('ip,port,user,cmd,user,pass,mode,addtime',
|
||||
(ip, port, user, cmd, user, apass, mode, addTime))
|
||||
|
||||
return mw.returnJson(True, '设置成功!')
|
||||
|
||||
|
||||
def delSlaveSyncUser(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['ip'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ip = args['ip']
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
conn.where("ip=?", (ip,)).delete()
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
|
||||
|
||||
def getSlaveSyncUserList(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['page', 'page_size'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
page = int(args['page'])
|
||||
page_size = int(args['page_size'])
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
limit = str((page - 1) * page_size) + ',' + str(page_size)
|
||||
|
||||
field = 'id,ip,port,user,pass,cmd,addtime'
|
||||
clist = conn.field(field).limit(limit).order('id desc').select()
|
||||
count = conn.count()
|
||||
|
||||
data = {}
|
||||
_page = {}
|
||||
_page['count'] = count
|
||||
_page['p'] = page
|
||||
_page['row'] = page_size
|
||||
_page['tojs'] = args['tojs']
|
||||
data['page'] = mw.getPage(_page)
|
||||
data['data'] = clist
|
||||
|
||||
return mw.getJson(data)
|
||||
|
||||
|
||||
def getSyncModeFile():
|
||||
return getServerDir() + "/sync.mode"
|
||||
|
||||
|
||||
def getSlaveSyncMode(version):
|
||||
sync_mode = getSyncModeFile()
|
||||
if os.path.exists(sync_mode):
|
||||
mode = mw.readFile(sync_mode).strip()
|
||||
return mw.returnJson(True, 'ok', mode)
|
||||
return mw.returnJson(False, 'fail')
|
||||
|
||||
|
||||
def setSlaveSyncMode(version):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['mode'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
mode = args['mode']
|
||||
sync_mode = getSyncModeFile()
|
||||
|
||||
if mode == 'none':
|
||||
os.remove(sync_mode)
|
||||
else:
|
||||
mw.writeFile(sync_mode, mode)
|
||||
return mw.returnJson(True, '设置成功', mode)
|
||||
|
||||
|
||||
def getSlaveSSHByIp(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['ip'])
|
||||
@@ -2013,8 +2154,6 @@ def getSlaveList(version=''):
|
||||
|
||||
db = pMysqlDb()
|
||||
dlist = db.query('show slave status')
|
||||
|
||||
# print(dlist)
|
||||
ret = []
|
||||
for x in range(0, len(dlist)):
|
||||
tmp = {}
|
||||
@@ -2024,6 +2163,24 @@ def getSlaveList(version=''):
|
||||
tmp['Master_Log_File'] = dlist[x]["Master_Log_File"]
|
||||
tmp['Slave_IO_Running'] = dlist[x]["Slave_IO_Running"]
|
||||
tmp['Slave_SQL_Running'] = dlist[x]["Slave_SQL_Running"]
|
||||
tmp['Last_Error'] = dlist[x]["Last_Error"]
|
||||
tmp['Last_IO_Error'] = dlist[x]["Last_IO_Error"]
|
||||
tmp['Last_SQL_Error'] = dlist[x]["Last_SQL_Error"]
|
||||
tmp['Slave_SQL_Running_State'] = dlist[x]["Slave_SQL_Running_State"]
|
||||
|
||||
tmp['Error'] = ''
|
||||
if tmp['Last_Error'] != '':
|
||||
tmp['Error'] = tmp['Last_Error']
|
||||
|
||||
if tmp['Last_IO_Error'] != '':
|
||||
tmp['Error'] = tmp['Last_IO_Error']
|
||||
|
||||
if tmp['Last_SQL_Error'] != '':
|
||||
tmp['Error'] = tmp['Last_SQL_Error']
|
||||
|
||||
if tmp['Error'] == '':
|
||||
tmp['Error'] = tmp['Slave_SQL_Running_State']
|
||||
|
||||
ret.append(tmp)
|
||||
data = {}
|
||||
data['data'] = ret
|
||||
@@ -2040,6 +2197,47 @@ def getSlaveSyncCmd(version=''):
|
||||
|
||||
|
||||
def initSlaveStatus(version=''):
|
||||
mode_file = getSyncModeFile()
|
||||
if not os.path.exists(mode_file):
|
||||
return mw.returnJson(False, '需要先设置同步配置')
|
||||
|
||||
mode = mw.readFile(mode_file)
|
||||
if mode == 'ssh':
|
||||
return initSlaveStatusSSH(version)
|
||||
if mode == 'sync-user':
|
||||
return initSlaveStatusSyncUser(version)
|
||||
|
||||
|
||||
def initSlaveStatusSyncUser(version=''):
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').find()
|
||||
if len(data) < 1:
|
||||
return mw.returnJson(False, '需要先添加同步用户配置!')
|
||||
|
||||
# print(data)
|
||||
db = pMysqlDb()
|
||||
dlist = db.query('show slave status')
|
||||
if len(dlist) > 0:
|
||||
return mw.returnJson(False, '已经初始化好了zz...')
|
||||
|
||||
u = data
|
||||
|
||||
mode_name = 'classic'
|
||||
if u['mode'] == '1':
|
||||
mode_name = 'gtid'
|
||||
|
||||
local_mode = recognizeDbMode()
|
||||
if local_mode != mode_name:
|
||||
return mw.returnJson(False, '同步模式不一致!')
|
||||
|
||||
# print(u['cmd'])
|
||||
t = db.query(u['cmd'])
|
||||
# print(t)
|
||||
db.query("start slave")
|
||||
return mw.returnJson(True, '初始化成功!')
|
||||
|
||||
|
||||
def initSlaveStatusSSH(version=''):
|
||||
db = pMysqlDb()
|
||||
dlist = db.query('show slave status')
|
||||
if len(dlist) > 0:
|
||||
@@ -2110,6 +2308,41 @@ def initSlaveStatus(version=''):
|
||||
|
||||
|
||||
def setSlaveStatus(version=''):
|
||||
mode_file = getSyncModeFile()
|
||||
if not os.path.exists(mode_file):
|
||||
return mw.returnJson(False, '需要先设置同步配置')
|
||||
|
||||
mode = mw.readFile(mode_file)
|
||||
if mode == 'ssh':
|
||||
return setSlaveStatusSSH(version)
|
||||
if mode == 'sync-user':
|
||||
return setSlaveStatusSyncUser(version)
|
||||
|
||||
|
||||
def setSlaveStatusSyncUser(version=''):
|
||||
db = pMysqlDb()
|
||||
dlist = db.query('show slave status')
|
||||
if len(dlist) == 0:
|
||||
return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!')
|
||||
|
||||
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
|
||||
db.query('stop slave')
|
||||
else:
|
||||
ip = dlist[0]['Master_Host']
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').find()
|
||||
if len(data) == 0:
|
||||
return mw.returnJson(False, '没有数据无法重启!')
|
||||
user = data['user']
|
||||
apass = data['pass']
|
||||
|
||||
db.query("start slave")
|
||||
# db.query("start slave user='{}' password='{}';".format(user, apass))
|
||||
|
||||
return mw.returnJson(True, '设置成功!')
|
||||
|
||||
|
||||
def setSlaveStatusSSH(version=''):
|
||||
|
||||
db = pMysqlDb()
|
||||
dlist = db.query('show slave status')
|
||||
@@ -2178,6 +2411,61 @@ def writeDbSyncStatus(data):
|
||||
|
||||
|
||||
def doFullSync(version=''):
|
||||
mode_file = getSyncModeFile()
|
||||
if not os.path.exists(mode_file):
|
||||
return mw.returnJson(False, '需要先设置同步配置')
|
||||
|
||||
mode = mw.readFile(mode_file)
|
||||
if mode == 'ssh':
|
||||
return doFullSyncSSH(version)
|
||||
if mode == 'sync-user':
|
||||
return doFullSyncUser(version)
|
||||
|
||||
|
||||
def doFullSyncUser(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['db'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
sync_db = args['db']
|
||||
|
||||
db = pMysqlDb()
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').find()
|
||||
user = data['user']
|
||||
apass = data['pass']
|
||||
port = data['port']
|
||||
ip = data['ip']
|
||||
|
||||
bak_file = '/tmp/tmp.sql'
|
||||
|
||||
writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0})
|
||||
|
||||
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20})
|
||||
|
||||
if not os.path.exists(bak_file):
|
||||
dump_sql_data = getServerDir() + "/bin/mysqldump --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
|
||||
port + " -u" + user + " -p" + apass + " " + sync_db + " > " + bak_file
|
||||
mw.execShell(dump_sql_data)
|
||||
|
||||
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
|
||||
if os.path.exists(bak_file):
|
||||
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
|
||||
sock = getSocketFile()
|
||||
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
|
||||
' ' + sync_db + ' < ' + bak_file
|
||||
mw.execShell(my_import_cmd)
|
||||
|
||||
db.query("start slave")
|
||||
|
||||
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
|
||||
os.system("rm -rf " + bak_file)
|
||||
return True
|
||||
|
||||
|
||||
def doFullSyncSSH(version=''):
|
||||
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['db'])
|
||||
@@ -2483,6 +2771,18 @@ if __name__ == "__main__":
|
||||
print(delSlaveSSH(version))
|
||||
elif func == 'update_slave_ssh':
|
||||
print(updateSlaveSSH(version))
|
||||
elif func == 'get_slave_sync_user_list':
|
||||
print(getSlaveSyncUserList(version))
|
||||
elif func == 'get_slave_sync_user_by_ip':
|
||||
print(getSlaveSyncUserByIp(version))
|
||||
elif func == 'add_slave_sync_user':
|
||||
print(addSlaveSyncUser(version))
|
||||
elif func == 'del_slave_sync_user':
|
||||
print(delSlaveSyncUser(version))
|
||||
elif func == 'get_slave_sync_mode':
|
||||
print(getSlaveSyncMode(version))
|
||||
elif func == 'set_slave_sync_mode':
|
||||
print(setSlaveSyncMode(version))
|
||||
elif func == 'init_slave_status':
|
||||
print(initSlaveStatus(version))
|
||||
elif func == 'set_slave_status':
|
||||
|
||||
+274
-10
@@ -39,7 +39,7 @@ function myPostN(method,args,callback, title){
|
||||
if (typeof(title) != 'undefined'){
|
||||
_title = title;
|
||||
}
|
||||
$.post('/plugins/run', {name:'mysql', func:method, args:_args}, function(data) {
|
||||
$.post('/plugins/run', {name:'mariadb', func:method, args:_args}, function(data) {
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
@@ -655,8 +655,8 @@ function addDatabase(type){
|
||||
<span class='tname'>数据库名</span>\
|
||||
<div class='info-r'><input name='name' class='bt-input-text mr5' placeholder='新的数据库名称' type='text' style='width:65%' value=''>\
|
||||
<select class='bt-input-text mr5 codeing_a5nGsm' name='codeing' style='width:27%'>\
|
||||
<option value='utf8'>utf-8</option>\
|
||||
<option value='utf8mb4'>utf8mb4</option>\
|
||||
<option value='utf8'>utf-8</option>\
|
||||
<option value='gbk'>gbk</option>\
|
||||
<option value='big5'>big5</option>\
|
||||
</select>\
|
||||
@@ -797,7 +797,7 @@ function openPhpmyadmin(name,username,password){
|
||||
data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_cfg'});
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if (rdata.choose != 'mariadb'){
|
||||
layer.msg('当前为['+rdata.choose+']模式,若要使用请切换模式.',{icon:2,shade: [0.3, '#000']});
|
||||
layer.msg('当前为['+rdata.choose+']模式,若要使用请修改phpMyAdmin访问切换.',{icon:2,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
var phpmyadmin_cfg = rdata;
|
||||
@@ -1504,7 +1504,7 @@ function getMasterRepSlaveUserCmd(username, db=''){
|
||||
title: '同步命令',
|
||||
area: '500px',
|
||||
content:"<form class='bt-form pd20 pb70' id='add_master'>\
|
||||
<div class='line'>"+cmd+"</div>\
|
||||
<div class='line' style='word-wrap: break-word;word-break: normal;'>"+cmd+"</div>\
|
||||
<div class='bt-form-submit-btn'>\
|
||||
<button type='button' class='btn btn-success btn-sm btn-title class-copy-cmd'>复制</button>\
|
||||
</div>\
|
||||
@@ -1795,11 +1795,27 @@ function addSlaveSSH(ip=''){
|
||||
}
|
||||
|
||||
|
||||
|
||||
function delSlaveSSH(ip){
|
||||
myPost('del_slave_ssh', {ip:ip}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2});
|
||||
getSlaveSSHPage();
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
getSlaveSSHPage();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function delSlaveSyncUser(ip){
|
||||
myPost('del_slave_sync_user', {ip:ip}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
getSlaveSyncUserPage();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1848,6 +1864,234 @@ function getSlaveSSHPage(page=1){
|
||||
});
|
||||
}
|
||||
|
||||
function addSlaveSyncUser(ip=''){
|
||||
|
||||
myPost('get_slave_sync_user_by_ip', {ip:ip}, function(rdata){
|
||||
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
|
||||
var ip = '127.0.0.1';
|
||||
var port = "22";
|
||||
var cmd = '';
|
||||
var user = 'input_sync_user';
|
||||
var pass = 'input_sync_pwd';
|
||||
var mode = '0';
|
||||
|
||||
if (rdata.data.length>0){
|
||||
ip = rdata.data[0]['ip'];
|
||||
port = rdata.data[0]['port'];
|
||||
cmd = rdata.data[0]['cmd'];
|
||||
user = rdata.data[0]['user'];
|
||||
pass = rdata.data[0]['pass'];
|
||||
mode = rdata.data[0]['mode'];
|
||||
}
|
||||
|
||||
var index = layer.open({
|
||||
type: 1,
|
||||
area: ['500px','470px'],
|
||||
title: '同步账户',
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: true,
|
||||
btn:["确认","取消"],
|
||||
content: "<form class='bt-form pd20'>\
|
||||
<div class='line'><span class='tname'>IP</span><div class='info-r'><input name='ip' class='bt-input-text mr5' type='text' style='width:330px;' value='"+ip+"'></div></div>\
|
||||
<div class='line'><span class='tname'>端口</span><div class='info-r'><input name='port' class='bt-input-text mr5' type='number' style='width:330px;' value='"+port+"'></div></div>\
|
||||
<div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\
|
||||
<div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>CMD[必填]</span>\
|
||||
<div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\
|
||||
</div>\
|
||||
<input type='hidden' name='mode' value='"+mode+"' />\
|
||||
</form>",
|
||||
success:function(){
|
||||
$('textarea[name="cmd"]').html(cmd);
|
||||
|
||||
$('textarea[name="cmd"]').change(function(){
|
||||
var val = $(this).val();
|
||||
var vlist = val.split(',');
|
||||
var a = {};
|
||||
for (var i in vlist) {
|
||||
var tmp = toTrim(vlist[i]);
|
||||
var tmp_a = tmp.split(" ");
|
||||
var real_tmp = tmp_a[tmp_a.length-1];
|
||||
var kv = real_tmp.split("=");
|
||||
a[kv[0]] = kv[1].replace("'",'').replace("'",'');
|
||||
}
|
||||
|
||||
$('input[name="ip"]').val(a['MASTER_HOST']);
|
||||
$('input[name="port"]').val(a['MASTER_PORT']);
|
||||
$('input[name="user"]').val(a['MASTER_USER']);
|
||||
$('input[name="pass"]').val(a['MASTER_PASSWORD']);
|
||||
|
||||
console.log(a['MASTER_USE_GTID'],typeof(a['MASTER_USE_GTID']));
|
||||
if (typeof(a['MASTER_USE_GTID']) != 'undefined' ){
|
||||
$('input[name="mode"]').val('1');
|
||||
}
|
||||
});
|
||||
},
|
||||
yes:function(index){
|
||||
var ip = $('input[name="ip"]').val();
|
||||
var port = $('input[name="port"]').val();
|
||||
var user = $('input[name="user"]').val();
|
||||
var pass = $('input[name="pass"]').val();
|
||||
var cmd = $('textarea[name="cmd"]').val();
|
||||
var mode = $('input[name="mode"]').val();
|
||||
|
||||
var data = {ip:ip,port:port,cmd:cmd,user:user,pass:pass,mode:mode};
|
||||
myPost('add_slave_sync_user', data, function(ret_data){
|
||||
layer.close(index);
|
||||
var rdata = $.parseJSON(ret_data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
getSlaveSyncUserPage();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2},600);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getSlaveSyncUserPage(page=1){
|
||||
var _data = {};
|
||||
_data['page'] = page;
|
||||
_data['page_size'] = 5;
|
||||
_data['tojs'] ='getSlaveSyncUserPage';
|
||||
myPost('get_slave_sync_user_list', _data, function(data){
|
||||
var layerId = null;
|
||||
var rdata = [];
|
||||
try {
|
||||
rdata = $.parseJSON(data.data);
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
var list = '';
|
||||
var user_list = rdata['data'];
|
||||
for (i in user_list) {
|
||||
var ip = user_list[i]['ip'];
|
||||
var port = user_list[i]['port'];
|
||||
var user = user_list[i]['user'];
|
||||
var apass = user_list[i]['pass'];
|
||||
|
||||
var cmd = '未设置';
|
||||
if (user_list[i]['cmd']!=''){
|
||||
cmd = '已设置';
|
||||
}
|
||||
|
||||
list += '<tr><td>'+ip+'</td>\
|
||||
<td>'+port+'</td>\
|
||||
<td>'+user+'</td>\
|
||||
<td>'+apass+'</td>\
|
||||
<td>'+cmd+'</td>\
|
||||
<td>\
|
||||
<a class="btlink" onclick="addSlaveSyncUser(\''+ip+'\');">修改</a> | \
|
||||
<a class="btlink" onclick="delSlaveSyncUser(\''+ip+'\');">删除</a>\
|
||||
</td>\
|
||||
</tr>';
|
||||
}
|
||||
|
||||
$('.get-slave-ssh-list tbody').html(list);
|
||||
$('.dataTables_paginate_4').html(rdata['page']);
|
||||
});
|
||||
}
|
||||
|
||||
function getSlaveCfg(){
|
||||
|
||||
myPost('get_slave_sync_mode', '', function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var mode_none = 'success';
|
||||
var mode_ssh = 'danger';
|
||||
var mode_sync_user = 'danger';
|
||||
if(rdata.status){
|
||||
var mode_none = 'danger';
|
||||
if (rdata.data == 'ssh'){
|
||||
var mode_ssh = 'success';
|
||||
var mode_sync_user = 'danger';
|
||||
} else {
|
||||
var mode_ssh = 'danger';
|
||||
var mode_sync_user = 'success';
|
||||
}
|
||||
}
|
||||
|
||||
layerId = layer.open({
|
||||
type: 1,
|
||||
title: '同步配置',
|
||||
area: ['400px','180px'],
|
||||
content:"<div class='bt-form pd20 c6'>\
|
||||
<p class='conf_p'>\
|
||||
<span class='f14 c6 mr20'>当前从库同步模式</span>\
|
||||
<b class='f14 c6 mr20'></b>\
|
||||
<button class='btn btn-"+mode_none+" btn-xs slave-db-mode btn-none'>无</button>\
|
||||
<button class='btn btn-"+mode_ssh+" btn-xs slave-db-mode btn-ssh'>SSH</button>\
|
||||
<button class='btn btn-"+mode_sync_user+" btn-xs slave-db-mode btn-sync-user'>同步账户</button>\
|
||||
</p>\
|
||||
<hr />\
|
||||
<p class='conf_p'>\
|
||||
<span class='f14 c6 mr20'>配置设置</span>\
|
||||
<b class='f14 c6 mr20'></b>\
|
||||
<button class='btn btn-success btn-xs btn-slave-ssh'>SSH</button>\
|
||||
<button class='btn btn-success btn-xs btn-slave-user'>同步账户</button>\
|
||||
</p>\
|
||||
</div>",
|
||||
success:function(){
|
||||
$('.btn-slave-ssh').click(function(){
|
||||
getSlaveSSHList();
|
||||
});
|
||||
|
||||
$('.btn-slave-user').click(function(){
|
||||
getSlaveUserList();
|
||||
});
|
||||
|
||||
$('.slave-db-mode').click(function(){
|
||||
var _this = this;
|
||||
var mode = 'none';
|
||||
if ($(this).hasClass('btn-ssh')){
|
||||
mode = 'ssh';
|
||||
}
|
||||
if ($(this).hasClass('btn-sync-user')){
|
||||
mode = 'sync-user';
|
||||
}
|
||||
|
||||
myPost('set_slave_sync_mode', {mode:mode}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
$('.slave-db-mode').remove('btn-success').addClass('btn-danger');
|
||||
$(_this).removeClass('btn-danger').addClass('btn-success');
|
||||
},{icon:rdata.status?1:2},2000);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getSlaveUserList(){
|
||||
|
||||
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
|
||||
page += '<div class="table_toolbar" style="left:0px;"><span class="sync btn btn-default btn-sm" onclick="addSlaveSyncUser()" title="">添加同步账户</span></div>';
|
||||
|
||||
layerId = layer.open({
|
||||
type: 1,
|
||||
title: '同步账户列表',
|
||||
area: '500px',
|
||||
content:"<div class='bt-form pd20 c6'>\
|
||||
<div class='divtable mtb10'>\
|
||||
<div><table class='table table-hover get-slave-ssh-list'>\
|
||||
<thead><tr><th>IP</th><th>PORT</th><th>同步账户</th><th>同步密码</th><th>CMD</th><th>操作</th></tr></thead>\
|
||||
<tbody></tbody>\
|
||||
</table></div>\
|
||||
"+page +"\
|
||||
</div>\
|
||||
</div>",
|
||||
success:function(){
|
||||
getSlaveSyncUserPage(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getSlaveSSHList(page=1){
|
||||
|
||||
@@ -1968,7 +2212,7 @@ function masterOrSlaveConf(version=''){
|
||||
for(i in rdata.data){
|
||||
|
||||
var v = rdata.data[i];
|
||||
var status = "异常";
|
||||
var status = "<a class='btlink db_error'>异常</>";
|
||||
if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){
|
||||
status = "正常";
|
||||
}
|
||||
@@ -2010,6 +2254,26 @@ function masterOrSlaveConf(version=''){
|
||||
// <span class="sync btn btn-default btn-sm" onclick="getMasterRepSlaveList()" title="">添加</span>\
|
||||
// </div>
|
||||
$(".table_slave_status_list").html(con);
|
||||
|
||||
$('.db_error').click(function(){
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '同步异常信息',
|
||||
area: '500px',
|
||||
content:"<form class='bt-form pd20 pb70'>\
|
||||
<div class='line'>"+v['Error']+"</div>\
|
||||
<div class='bt-form-submit-btn'>\
|
||||
<button type='button' class='btn btn-success btn-sm btn-title class-copy-db-err'>复制</button>\
|
||||
</div>\
|
||||
</form>",
|
||||
success:function(){
|
||||
copyText(v['Error']);
|
||||
$('.class-copy-db-err').click(function(){
|
||||
copyText(v['Error']);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2087,7 +2351,7 @@ function masterOrSlaveConf(version=''){
|
||||
<p class="conf_p">\
|
||||
<span class="f14 c6 mr20">Slave[从]配置</span><span class="f14 c6 mr20"></span>\
|
||||
<button class="btn '+(!rdata.slave_status ? 'btn-danger' : 'btn-success')+' btn-xs btn-slave">'+(!rdata.slave_status ? '未启动' : '已启动') +'</button>\
|
||||
<button class="btn btn-success btn-xs" onclick="getSlaveSSHList()" >[主]SSH配置</button>\
|
||||
<button class="btn btn-success btn-xs" onclick="getSlaveCfg()" >同步配置</button>\
|
||||
<button class="btn btn-success btn-xs" onclick="initSlaveStatus()" >初始化</button>\
|
||||
</p>\
|
||||
<hr/>\
|
||||
@@ -2161,10 +2425,10 @@ function masterOrSlaveConf(version=''){
|
||||
getMasterDbList();
|
||||
}
|
||||
|
||||
if (rdata.slave_status){
|
||||
// if (rdata.slave_status){
|
||||
getAsyncMasterDbList();
|
||||
getAsyncDataList()
|
||||
}
|
||||
// }
|
||||
});
|
||||
}
|
||||
getMasterStatus();
|
||||
|
||||
@@ -104,14 +104,14 @@ rm -r /var/lib/mongodb
|
||||
Install_Linux_Debian()
|
||||
{
|
||||
##################### debian start #####################
|
||||
if [ "$SYS_VERSION_ID" -ge "11" ]; then
|
||||
if [ "$SYS_VERSION_ID" -eq "11" ] && [ "$VERSION" -eq "4.4" ]; then
|
||||
echo "Not yet supported"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
if [ -f /usr/lib/systemd/system/mongod.service ];then
|
||||
echo 'alreay exist!'
|
||||
echo 'alreay install exist!'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
@@ -36,11 +36,12 @@ max_connect_errors = 100
|
||||
open_files_limit = 2560
|
||||
max_allowed_packet = 128M
|
||||
|
||||
#skip-external-locking
|
||||
#skip-grant-tables
|
||||
#loose-skip-innodb
|
||||
skip_name_resolve = 1
|
||||
skip-ssl
|
||||
#skip-networking
|
||||
#skip-name-resolve
|
||||
#skip-external-locking
|
||||
#loose-skip-innodb
|
||||
#skip-grant-tables
|
||||
|
||||
log-bin=mysql-bin
|
||||
binlog_format=mixed
|
||||
|
||||
@@ -40,4 +40,17 @@ CREATE TABLE IF NOT EXISTS `slave_id_rsa` (
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
-- 从库配置主库的[user]
|
||||
-- drop table `slave_user`;
|
||||
CREATE TABLE IF NOT EXISTS `slave_sync_user` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`ip` TEXT,
|
||||
`port` TEXT,
|
||||
`user` TEXT,
|
||||
`pass` TEXT,
|
||||
`mode` TEXT,
|
||||
`cmd` TEXT,
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<p onclick="myLogs();">日志</p>
|
||||
<p onclick="pluginLogs('mysql-apt',$('.plugin_version').attr('version'),'show_log');">慢日志</p>
|
||||
<p onclick="dbList()">管理列表</p>
|
||||
<p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
|
||||
+353
-30
@@ -122,20 +122,27 @@ def contentReplace(content):
|
||||
def pSqliteDb(dbname='databases'):
|
||||
file = getServerDir() + '/mysql.db'
|
||||
name = 'mysql'
|
||||
if not os.path.exists(file):
|
||||
|
||||
import_sql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
|
||||
md5_sql = mw.md5(import_sql)
|
||||
|
||||
import_sign = False
|
||||
save_md5_file = getServerDir() + '/import_sql.md5'
|
||||
if os.path.exists(save_md5_file):
|
||||
save_md5_sql = mw.readFile(save_md5_file)
|
||||
if save_md5_sql != md5_sql:
|
||||
import_sign = True
|
||||
mw.writeFile(save_md5_file, md5_sql)
|
||||
else:
|
||||
mw.writeFile(save_md5_file, md5_sql)
|
||||
|
||||
if not os.path.exists(file) or import_sql:
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
|
||||
csql_list = csql.split(';')
|
||||
csql_list = import_sql.split(';')
|
||||
for index in range(len(csql_list)):
|
||||
conn.execute(csql_list[index], ())
|
||||
else:
|
||||
# 现有run
|
||||
# conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
# csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
|
||||
# csql_list = csql.split(';')
|
||||
# for index in range(len(csql_list)):
|
||||
# conn.execute(csql_list[index], ())
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
return conn
|
||||
|
||||
|
||||
@@ -154,9 +161,16 @@ def pMysqlDb():
|
||||
|
||||
def initDreplace(version=''):
|
||||
|
||||
my_dir = getServerDir() + '/etc'
|
||||
if not os.path.exists(my_dir):
|
||||
os.mkdir(my_dir)
|
||||
conf_dir = getServerDir() + '/etc'
|
||||
mode_dir = conf_dir + '/mode'
|
||||
|
||||
conf_list = [
|
||||
conf_dir,
|
||||
mode_dir,
|
||||
]
|
||||
for conf in conf_list:
|
||||
if not os.path.exists(conf):
|
||||
os.mkdir(conf)
|
||||
|
||||
tmp_dir = getServerDir() + '/tmp'
|
||||
if not os.path.exists(tmp_dir):
|
||||
@@ -164,12 +178,26 @@ def initDreplace(version=''):
|
||||
mw.execShell("chown -R mysql:mysql " + tmp_dir)
|
||||
mw.execShell("chmod 750 " + tmp_dir)
|
||||
|
||||
mysql_conf = my_dir + '/my.cnf'
|
||||
if not os.path.exists(mysql_conf):
|
||||
mysql_conf_tpl = getPluginDir() + '/conf/my' + version + '.cnf'
|
||||
content = mw.readFile(mysql_conf_tpl)
|
||||
my_conf = conf_dir + '/my.cnf'
|
||||
if not os.path.exists(my_conf):
|
||||
tpl = getPluginDir() + '/conf/my' + version + '.cnf'
|
||||
content = mw.readFile(tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(mysql_conf, content)
|
||||
mw.writeFile(my_conf, content)
|
||||
|
||||
classic_conf = mode_dir + '/classic.cnf'
|
||||
if not os.path.exists(classic_conf):
|
||||
tpl = getPluginDir() + '/conf/classic.cnf'
|
||||
content = mw.readFile(tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(classic_conf, content)
|
||||
|
||||
gtid_conf = mode_dir + '/gtid.cnf'
|
||||
if not os.path.exists(gtid_conf):
|
||||
tpl = getPluginDir() + '/conf/gtid.cnf'
|
||||
content = mw.readFile(tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(gtid_conf, content)
|
||||
|
||||
# systemd
|
||||
systemDir = mw.systemdCfgDir()
|
||||
@@ -383,6 +411,7 @@ def my8cmd(version, method):
|
||||
initDreplace(version)
|
||||
# mysql 8.0 and 5.7
|
||||
try:
|
||||
isInited = True
|
||||
if version == '5.7':
|
||||
isInited = initMysql57Data()
|
||||
elif version == '8.0':
|
||||
@@ -455,7 +484,7 @@ def getMyDbPos():
|
||||
return tmp.groups()[0].strip()
|
||||
|
||||
|
||||
def setMyDbPos():
|
||||
def setMyDbPos(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['datadir'])
|
||||
if not data[0]:
|
||||
@@ -470,7 +499,7 @@ def setMyDbPos():
|
||||
mw.execShell('mkdir -p ' + t_datadir)
|
||||
|
||||
# mw.execShell('/etc/init.d/mysqld stop')
|
||||
stop()
|
||||
stop(version)
|
||||
mw.execShell('cp -rf ' + s_datadir + '/* ' + t_datadir + '/')
|
||||
mw.execShell('chown -R mysql mysql ' + t_datadir)
|
||||
mw.execShell('chmod -R 755 ' + t_datadir)
|
||||
@@ -484,7 +513,7 @@ def setMyDbPos():
|
||||
|
||||
mycnf = mycnf.replace(s_datadir, t_datadir)
|
||||
mw.writeFile(myfile, mycnf)
|
||||
start()
|
||||
restart(version)
|
||||
|
||||
result = mw.execShell(
|
||||
'ps aux|grep mysqld| grep -v grep|grep -v python')
|
||||
@@ -745,10 +774,13 @@ def importDbExternal():
|
||||
pwd + ' ' + name + ' < ' + import_sql
|
||||
|
||||
# print(mysql_cmd)
|
||||
os.system(mysql_cmd)
|
||||
rdata = mw.execShell(mysql_cmd)
|
||||
if ext != 'sql':
|
||||
os.remove(import_sql)
|
||||
|
||||
if rdata[1].lower().find('error') > -1:
|
||||
return mw.returnJson(False, rdata[1])
|
||||
|
||||
return mw.returnJson(True, 'ok')
|
||||
|
||||
|
||||
@@ -774,7 +806,12 @@ def importDbBackup():
|
||||
' ' + name + ' < ' + file_path_sql
|
||||
|
||||
# print(mysql_cmd)
|
||||
os.system(mysql_cmd)
|
||||
# os.system(mysql_cmd)
|
||||
|
||||
rdata = mw.execShell(mysql_cmd)
|
||||
if rdata[1].lower().find('error') > -1:
|
||||
return mw.returnJson(False, rdata[1])
|
||||
|
||||
return mw.returnJson(True, 'ok')
|
||||
|
||||
|
||||
@@ -1104,7 +1141,7 @@ def addDb():
|
||||
dataAccess = args['dataAccess'].strip()
|
||||
ps = args['ps'].strip()
|
||||
|
||||
reg = "^[\w\.-]+$"
|
||||
reg = "^[\w-]+$"
|
||||
if not re.match(reg, args['name']):
|
||||
return mw.returnJson(False, '数据库名称不能带有特殊符号!')
|
||||
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
|
||||
@@ -1772,7 +1809,7 @@ def addMasterRepSlaveUser(version=''):
|
||||
# address = args['address'].strip()
|
||||
# dataAccess = args['dataAccess'].strip()
|
||||
|
||||
reg = "^[\w\.-]+$"
|
||||
reg = "^[\w-]+$"
|
||||
if not re.match(reg, username):
|
||||
return mw.returnJson(False, '用户名不能带有特殊符号!')
|
||||
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
|
||||
@@ -1800,17 +1837,18 @@ def addMasterRepSlaveUser(version=''):
|
||||
if isError != None:
|
||||
return isError
|
||||
|
||||
sql = "FLUSH PRIVILEGES;"
|
||||
pdb.execute(sql)
|
||||
else:
|
||||
sql = "GRANT REPLICATION SLAVE ON *.* TO '" + username + \
|
||||
"'@'%' identified by '" + password + "';"
|
||||
result = pdb.execute(sql)
|
||||
result = pdb.execute('FLUSH PRIVILEGES;')
|
||||
isError = isSqlError(result)
|
||||
if isError != None:
|
||||
return isError
|
||||
|
||||
sql_select = "grant select,lock tables,PROCESS on *.* to " + username + "@'%';"
|
||||
pdb.execute(sql_select)
|
||||
pdb.execute('FLUSH PRIVILEGES;')
|
||||
|
||||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
psdb.add('username,password,accept,ps,addtime',
|
||||
(username, password, '%', '', addTime))
|
||||
@@ -1947,6 +1985,123 @@ def getSlaveSSHList(version=''):
|
||||
return mw.getJson(data)
|
||||
|
||||
|
||||
def getSlaveSyncUserByIp(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['ip'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ip = args['ip']
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').where(
|
||||
"ip=?", (ip,)).select()
|
||||
return mw.returnJson(True, 'ok', data)
|
||||
|
||||
|
||||
def addSlaveSyncUser(version=''):
|
||||
import base64
|
||||
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['ip'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ip = args['ip']
|
||||
if ip == "":
|
||||
return mw.returnJson(True, 'ok')
|
||||
|
||||
data = checkArgs(args, ['port', 'user', 'pass', 'mode'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
cmd = args['cmd']
|
||||
port = args['port']
|
||||
user = args['user']
|
||||
apass = args['pass']
|
||||
mode = args['mode']
|
||||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip').where("ip=?", (ip,)).select()
|
||||
if len(data) > 0:
|
||||
res = conn.where("ip=?", (ip,)).save(
|
||||
'port,user,pass,mode,cmd', (port, user, apass, mode, cmd))
|
||||
else:
|
||||
conn.add('ip,port,user,cmd,user,pass,mode,addtime',
|
||||
(ip, port, user, cmd, user, apass, mode, addTime))
|
||||
|
||||
return mw.returnJson(True, '设置成功!')
|
||||
|
||||
|
||||
def delSlaveSyncUser(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['ip'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ip = args['ip']
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
conn.where("ip=?", (ip,)).delete()
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
|
||||
|
||||
def getSlaveSyncUserList(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['page', 'page_size'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
page = int(args['page'])
|
||||
page_size = int(args['page_size'])
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
limit = str((page - 1) * page_size) + ',' + str(page_size)
|
||||
|
||||
field = 'id,ip,port,user,pass,cmd,addtime'
|
||||
clist = conn.field(field).limit(limit).order('id desc').select()
|
||||
count = conn.count()
|
||||
|
||||
data = {}
|
||||
_page = {}
|
||||
_page['count'] = count
|
||||
_page['p'] = page
|
||||
_page['row'] = page_size
|
||||
_page['tojs'] = args['tojs']
|
||||
data['page'] = mw.getPage(_page)
|
||||
data['data'] = clist
|
||||
|
||||
return mw.getJson(data)
|
||||
|
||||
|
||||
def getSyncModeFile():
|
||||
return getServerDir() + "/sync.mode"
|
||||
|
||||
|
||||
def getSlaveSyncMode(version):
|
||||
sync_mode = getSyncModeFile()
|
||||
if os.path.exists(sync_mode):
|
||||
mode = mw.readFile(sync_mode).strip()
|
||||
return mw.returnJson(True, 'ok', mode)
|
||||
return mw.returnJson(False, 'fail')
|
||||
|
||||
|
||||
def setSlaveSyncMode(version):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['mode'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
mode = args['mode']
|
||||
sync_mode = getSyncModeFile()
|
||||
|
||||
if mode == 'none':
|
||||
os.remove(sync_mode)
|
||||
else:
|
||||
mw.writeFile(sync_mode, mode)
|
||||
return mw.returnJson(True, '设置成功', mode)
|
||||
|
||||
|
||||
def getSlaveSSHByIp(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['ip'])
|
||||
@@ -2033,6 +2188,24 @@ def getSlaveList(version=''):
|
||||
tmp['Master_Log_File'] = dlist[x]["Master_Log_File"]
|
||||
tmp['Slave_IO_Running'] = dlist[x]["Slave_IO_Running"]
|
||||
tmp['Slave_SQL_Running'] = dlist[x]["Slave_SQL_Running"]
|
||||
tmp['Last_Error'] = dlist[x]["Last_Error"]
|
||||
tmp['Last_IO_Error'] = dlist[x]["Last_IO_Error"]
|
||||
tmp['Last_SQL_Error'] = dlist[x]["Last_SQL_Error"]
|
||||
tmp['Slave_SQL_Running_State'] = dlist[x]["Slave_SQL_Running_State"]
|
||||
|
||||
tmp['Error'] = ''
|
||||
if tmp['Last_Error'] != '':
|
||||
tmp['Error'] = tmp['Last_Error']
|
||||
|
||||
if tmp['Last_IO_Error'] != '':
|
||||
tmp['Error'] = tmp['Last_IO_Error']
|
||||
|
||||
if tmp['Last_SQL_Error'] != '':
|
||||
tmp['Error'] = tmp['Last_SQL_Error']
|
||||
|
||||
if tmp['Error'] == '':
|
||||
tmp['Error'] = tmp['Slave_SQL_Running_State']
|
||||
|
||||
ret.append(tmp)
|
||||
data = {}
|
||||
data['data'] = ret
|
||||
@@ -2049,6 +2222,47 @@ def getSlaveSyncCmd(version=''):
|
||||
|
||||
|
||||
def initSlaveStatus(version=''):
|
||||
mode_file = getSyncModeFile()
|
||||
if not os.path.exists(mode_file):
|
||||
return mw.returnJson(False, '需要先设置同步配置')
|
||||
|
||||
mode = mw.readFile(mode_file)
|
||||
if mode == 'ssh':
|
||||
return initSlaveStatusSSH(version)
|
||||
if mode == 'sync-user':
|
||||
return initSlaveStatusSyncUser(version)
|
||||
|
||||
|
||||
def initSlaveStatusSyncUser(version=''):
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').find()
|
||||
if len(data) < 1:
|
||||
return mw.returnJson(False, '需要先添加同步用户配置!')
|
||||
|
||||
# print(data)
|
||||
db = pMysqlDb()
|
||||
dlist = db.query('show slave status')
|
||||
if len(dlist) > 0:
|
||||
return mw.returnJson(False, '已经初始化好了zz...')
|
||||
|
||||
u = data
|
||||
|
||||
mode_name = 'classic'
|
||||
if u['mode'] == '1':
|
||||
mode_name = 'gtid'
|
||||
|
||||
local_mode = recognizeDbMode()
|
||||
if local_mode != mode_name:
|
||||
return mw.returnJson(False, '同步模式不一致!')
|
||||
|
||||
t = db.query(u['cmd'])
|
||||
# print(t)
|
||||
db.query("start slave user='{}' password='{}';".format(
|
||||
u['user'], u['pass']))
|
||||
return mw.returnJson(True, '初始化成功!')
|
||||
|
||||
|
||||
def initSlaveStatusSSH(version=''):
|
||||
db = pMysqlDb()
|
||||
dlist = db.query('show slave status')
|
||||
if len(dlist) > 0:
|
||||
@@ -2117,6 +2331,41 @@ def initSlaveStatus(version=''):
|
||||
|
||||
|
||||
def setSlaveStatus(version=''):
|
||||
mode_file = getSyncModeFile()
|
||||
if not os.path.exists(mode_file):
|
||||
return mw.returnJson(False, '需要先设置同步配置')
|
||||
|
||||
mode = mw.readFile(mode_file)
|
||||
if mode == 'ssh':
|
||||
return setSlaveStatusSSH(version)
|
||||
if mode == 'sync-user':
|
||||
return setSlaveStatusSyncUser(version)
|
||||
|
||||
|
||||
def setSlaveStatusSyncUser(version=''):
|
||||
db = pMysqlDb()
|
||||
dlist = db.query('show slave status')
|
||||
if len(dlist) == 0:
|
||||
return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!')
|
||||
|
||||
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
|
||||
db.query('stop slave')
|
||||
else:
|
||||
ip = dlist[0]['Master_Host']
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').find()
|
||||
if len(data) == 0:
|
||||
return mw.returnJson(False, '没有数据无法重启!')
|
||||
user = data['user']
|
||||
apass = data['pass']
|
||||
|
||||
db.query("start slave")
|
||||
# db.query("start slave user='{}' password='{}';".format(user, apass))
|
||||
|
||||
return mw.returnJson(True, '设置成功!')
|
||||
|
||||
|
||||
def setSlaveStatusSSH(version=''):
|
||||
|
||||
db = pMysqlDb()
|
||||
dlist = db.query('show slave status')
|
||||
@@ -2185,6 +2434,68 @@ def writeDbSyncStatus(data):
|
||||
|
||||
|
||||
def doFullSync(version=''):
|
||||
mode_file = getSyncModeFile()
|
||||
if not os.path.exists(mode_file):
|
||||
return mw.returnJson(False, '需要先设置同步配置')
|
||||
|
||||
mode = mw.readFile(mode_file)
|
||||
if mode == 'ssh':
|
||||
return doFullSyncSSH(version)
|
||||
if mode == 'sync-user':
|
||||
return doFullSyncUser(version)
|
||||
|
||||
|
||||
def doFullSyncUser(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['db'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
sync_db = args['db']
|
||||
|
||||
db = pMysqlDb()
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').find()
|
||||
user = data['user']
|
||||
apass = data['pass']
|
||||
port = data['port']
|
||||
ip = data['ip']
|
||||
|
||||
bak_file = '/tmp/tmp.sql'
|
||||
|
||||
writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0})
|
||||
dmp_option = ''
|
||||
mode = recognizeDbMode()
|
||||
if mode == 'gtid':
|
||||
dmp_option = ' --set-gtid-purged=off '
|
||||
|
||||
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20})
|
||||
|
||||
if not os.path.exists(bak_file):
|
||||
dump_sql_data = getServerDir() + "/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
|
||||
port + " -u" + user + " -p" + apass + " " + sync_db + " > " + bak_file
|
||||
mw.execShell(dump_sql_data)
|
||||
|
||||
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
|
||||
if os.path.exists(bak_file):
|
||||
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
|
||||
sock = getSocketFile()
|
||||
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
|
||||
' ' + sync_db + ' < ' + bak_file
|
||||
mw.execShell(my_import_cmd)
|
||||
|
||||
if version == '8.0':
|
||||
db.query("start slave user='{}' password='{}';".format(user, apass))
|
||||
else:
|
||||
db.query("start slave")
|
||||
|
||||
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
|
||||
os.system("rm -rf " + bak_file)
|
||||
return True
|
||||
|
||||
|
||||
def doFullSyncSSH(version=''):
|
||||
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['db'])
|
||||
@@ -2423,7 +2734,7 @@ if __name__ == "__main__":
|
||||
elif func == 'my_db_pos':
|
||||
print(getMyDbPos())
|
||||
elif func == 'set_db_pos':
|
||||
print(setMyDbPos())
|
||||
print(setMyDbPos(version))
|
||||
elif func == 'my_port':
|
||||
print(getMyPort())
|
||||
elif func == 'set_my_port':
|
||||
@@ -2516,6 +2827,18 @@ if __name__ == "__main__":
|
||||
print(delSlaveSSH(version))
|
||||
elif func == 'update_slave_ssh':
|
||||
print(updateSlaveSSH(version))
|
||||
elif func == 'get_slave_sync_user_list':
|
||||
print(getSlaveSyncUserList(version))
|
||||
elif func == 'get_slave_sync_user_by_ip':
|
||||
print(getSlaveSyncUserByIp(version))
|
||||
elif func == 'add_slave_sync_user':
|
||||
print(addSlaveSyncUser(version))
|
||||
elif func == 'del_slave_sync_user':
|
||||
print(delSlaveSyncUser(version))
|
||||
elif func == 'get_slave_sync_mode':
|
||||
print(getSlaveSyncMode(version))
|
||||
elif func == 'set_slave_sync_mode':
|
||||
print(setSlaveSyncMode(version))
|
||||
elif func == 'init_slave_status':
|
||||
print(initSlaveStatus(version))
|
||||
elif func == 'set_slave_status':
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"tip":"soft",
|
||||
"name":"mysql-apt",
|
||||
"type":"运行环境",
|
||||
"ps":"[<b style='color:red;'>潜龙勿用</b>]一种关系数据库管理系统[debian,ubuntu](极速安装)",
|
||||
"ps":"一种关系数据库管理系统[debian,ubuntu](极速安装)",
|
||||
"todo_versions":["5.7","8.0"],
|
||||
"versions":["5.7","8.0"],
|
||||
"shell":"install.sh",
|
||||
|
||||
@@ -731,7 +731,7 @@ function addDatabase(type){
|
||||
layer.close(index);
|
||||
dbList();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2},600);
|
||||
},{icon: rdata.status ? 1 : 2}, 2000);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -819,7 +819,7 @@ function openPhpmyadmin(name,username,password){
|
||||
data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_cfg'});
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if (rdata.choose != 'mysql-apt'){
|
||||
layer.msg('当前为['+rdata.choose+']模式,若要使用请切换模式.',{icon:2,shade: [0.3, '#000']});
|
||||
layer.msg('当前为['+rdata.choose+']模式,若要使用请修改phpMyAdmin访问切换.',{icon:2,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1545,13 +1545,9 @@ function getMasterRepSlaveUserCmd(username, db=''){
|
||||
function delMasterRepSlaveUser(username){
|
||||
myPost('del_master_rep_slave_user', {username:username}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.msg(rdata.msg);
|
||||
|
||||
$('.layui-layer-close1').click();
|
||||
|
||||
setTimeout(function(){
|
||||
showMsg(rdata.msg, function(){
|
||||
getMasterRepSlaveList();
|
||||
},1000);
|
||||
},{icon: rdata.status ? 1 : 2},1000)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1821,8 +1817,23 @@ function addSlaveSSH(ip=''){
|
||||
function delSlaveSSH(ip){
|
||||
myPost('del_slave_ssh', {ip:ip}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2});
|
||||
getSlaveSSHPage();
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
getSlaveSSHPage();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function delSlaveSyncUser(ip){
|
||||
myPost('del_slave_sync_user', {ip:ip}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
getSlaveSyncUserPage();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1872,6 +1883,237 @@ function getSlaveSSHPage(page=1){
|
||||
}
|
||||
|
||||
|
||||
|
||||
function addSlaveSyncUser(ip=''){
|
||||
|
||||
myPost('get_slave_sync_user_by_ip', {ip:ip}, function(rdata){
|
||||
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
|
||||
var ip = '127.0.0.1';
|
||||
var port = "22";
|
||||
var cmd = '';
|
||||
var user = 'input_sync_user';
|
||||
var pass = 'input_sync_pwd';
|
||||
var mode = '0';
|
||||
|
||||
if (rdata.data.length>0){
|
||||
ip = rdata.data[0]['ip'];
|
||||
port = rdata.data[0]['port'];
|
||||
cmd = rdata.data[0]['cmd'];
|
||||
user = rdata.data[0]['user'];
|
||||
pass = rdata.data[0]['pass'];
|
||||
mode = rdata.data[0]['mode'];
|
||||
}
|
||||
|
||||
var index = layer.open({
|
||||
type: 1,
|
||||
area: ['500px','470px'],
|
||||
title: '同步账户',
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: true,
|
||||
btn:["确认","取消"],
|
||||
content: "<form class='bt-form pd20'>\
|
||||
<div class='line'><span class='tname'>IP</span><div class='info-r'><input name='ip' class='bt-input-text mr5' type='text' style='width:330px;' value='"+ip+"'></div></div>\
|
||||
<div class='line'><span class='tname'>端口</span><div class='info-r'><input name='port' class='bt-input-text mr5' type='number' style='width:330px;' value='"+port+"'></div></div>\
|
||||
<div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\
|
||||
<div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>CMD[最好填好]</span>\
|
||||
<div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\
|
||||
</div>\
|
||||
<input type='hidden' name='mode' value='"+mode+"' />\
|
||||
</form>",
|
||||
success:function(){
|
||||
$('textarea[name="cmd"]').html(cmd);
|
||||
|
||||
$('textarea[name="cmd"]').change(function(){
|
||||
var val = $(this).val();
|
||||
var vlist = val.split(',');
|
||||
var a = {};
|
||||
for (var i in vlist) {
|
||||
var tmp = toTrim(vlist[i]);
|
||||
var tmp_a = tmp.split(" ");
|
||||
var real_tmp = tmp_a[tmp_a.length-1];
|
||||
var kv = real_tmp.split("=");
|
||||
a[kv[0]] = kv[1].replace("'",'').replace("'",'');
|
||||
}
|
||||
|
||||
$('input[name="ip"]').val(a['MASTER_HOST']);
|
||||
$('input[name="port"]').val(a['MASTER_PORT']);
|
||||
$('input[name="user"]').val(a['MASTER_USER']);
|
||||
$('input[name="pass"]').val(a['MASTER_PASSWORD']);
|
||||
|
||||
console.log(a['MASTER_AUTO_POSITION'],typeof(a['MASTER_AUTO_POSITION']));
|
||||
if (typeof(a['MASTER_AUTO_POSITION']) != 'undefined' ){
|
||||
$('input[name="mode"]').val('1');
|
||||
}
|
||||
});
|
||||
},
|
||||
yes:function(index){
|
||||
var ip = $('input[name="ip"]').val();
|
||||
var port = $('input[name="port"]').val();
|
||||
var user = $('input[name="user"]').val();
|
||||
var pass = $('input[name="pass"]').val();
|
||||
var cmd = $('textarea[name="cmd"]').val();
|
||||
var mode = $('input[name="mode"]').val();
|
||||
|
||||
var data = {ip:ip,port:port,cmd:cmd,user:user,pass:pass,mode:mode};
|
||||
myPost('add_slave_sync_user', data, function(ret_data){
|
||||
layer.close(index);
|
||||
var rdata = $.parseJSON(ret_data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
getSlaveSyncUserPage();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2},600);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getSlaveSyncUserPage(page=1){
|
||||
var _data = {};
|
||||
_data['page'] = page;
|
||||
_data['page_size'] = 5;
|
||||
_data['tojs'] ='getSlaveSyncUserPage';
|
||||
myPost('get_slave_sync_user_list', _data, function(data){
|
||||
var layerId = null;
|
||||
var rdata = [];
|
||||
try {
|
||||
rdata = $.parseJSON(data.data);
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
var list = '';
|
||||
var user_list = rdata['data'];
|
||||
for (i in user_list) {
|
||||
var ip = user_list[i]['ip'];
|
||||
var port = user_list[i]['port'];
|
||||
var user = user_list[i]['user'];
|
||||
var apass = user_list[i]['pass'];
|
||||
|
||||
var cmd = '未设置';
|
||||
if (user_list[i]['cmd']!=''){
|
||||
cmd = '已设置';
|
||||
}
|
||||
|
||||
list += '<tr><td>'+ip+'</td>\
|
||||
<td>'+port+'</td>\
|
||||
<td>'+user+'</td>\
|
||||
<td>'+apass+'</td>\
|
||||
<td>'+cmd+'</td>\
|
||||
<td>\
|
||||
<a class="btlink" onclick="addSlaveSyncUser(\''+ip+'\');">修改</a> | \
|
||||
<a class="btlink" onclick="delSlaveSyncUser(\''+ip+'\');">删除</a>\
|
||||
</td>\
|
||||
</tr>';
|
||||
}
|
||||
|
||||
$('.get-slave-ssh-list tbody').html(list);
|
||||
$('.dataTables_paginate_4').html(rdata['page']);
|
||||
});
|
||||
}
|
||||
|
||||
function getSlaveCfg(){
|
||||
|
||||
myPost('get_slave_sync_mode', '', function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var mode_none = 'success';
|
||||
var mode_ssh = 'danger';
|
||||
var mode_sync_user = 'danger';
|
||||
if(rdata.status){
|
||||
var mode_none = 'danger';
|
||||
if (rdata.data == 'ssh'){
|
||||
var mode_ssh = 'success';
|
||||
var mode_sync_user = 'danger';
|
||||
} else {
|
||||
var mode_ssh = 'danger';
|
||||
var mode_sync_user = 'success';
|
||||
}
|
||||
}
|
||||
|
||||
layerId = layer.open({
|
||||
type: 1,
|
||||
title: '同步配置',
|
||||
area: ['400px','180px'],
|
||||
content:"<div class='bt-form pd20 c6'>\
|
||||
<p class='conf_p'>\
|
||||
<span class='f14 c6 mr20'>当前从库同步模式</span>\
|
||||
<b class='f14 c6 mr20'></b>\
|
||||
<button class='btn btn-"+mode_none+" btn-xs slave-db-mode btn-none'>无</button>\
|
||||
<button class='btn btn-"+mode_ssh+" btn-xs slave-db-mode btn-ssh'>SSH</button>\
|
||||
<button class='btn btn-"+mode_sync_user+" btn-xs slave-db-mode btn-sync-user'>同步账户</button>\
|
||||
</p>\
|
||||
<hr />\
|
||||
<p class='conf_p'>\
|
||||
<span class='f14 c6 mr20'>配置设置</span>\
|
||||
<b class='f14 c6 mr20'></b>\
|
||||
<button class='btn btn-success btn-xs btn-slave-ssh'>SSH</button>\
|
||||
<button class='btn btn-success btn-xs btn-slave-user'>同步账户</button>\
|
||||
</p>\
|
||||
</div>",
|
||||
success:function(){
|
||||
$('.btn-slave-ssh').click(function(){
|
||||
getSlaveSSHList();
|
||||
});
|
||||
|
||||
$('.btn-slave-user').click(function(){
|
||||
getSlaveUserList();
|
||||
});
|
||||
|
||||
$('.slave-db-mode').click(function(){
|
||||
var _this = this;
|
||||
var mode = 'none';
|
||||
if ($(this).hasClass('btn-ssh')){
|
||||
mode = 'ssh';
|
||||
}
|
||||
if ($(this).hasClass('btn-sync-user')){
|
||||
mode = 'sync-user';
|
||||
}
|
||||
|
||||
myPost('set_slave_sync_mode', {mode:mode}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
$('.slave-db-mode').remove('btn-success').addClass('btn-danger');
|
||||
$(_this).removeClass('btn-danger').addClass('btn-success');
|
||||
},{icon:rdata.status?1:2},2000);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function getSlaveUserList(){
|
||||
|
||||
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
|
||||
page += '<div class="table_toolbar" style="left:0px;"><span class="sync btn btn-default btn-sm" onclick="addSlaveSyncUser()" title="">添加同步账户</span></div>';
|
||||
|
||||
layerId = layer.open({
|
||||
type: 1,
|
||||
title: '同步账户列表',
|
||||
area: '500px',
|
||||
content:"<div class='bt-form pd20 c6'>\
|
||||
<div class='divtable mtb10'>\
|
||||
<div><table class='table table-hover get-slave-ssh-list'>\
|
||||
<thead><tr><th>IP</th><th>PORT</th><th>同步账户</th><th>同步密码</th><th>CMD</th><th>操作</th></tr></thead>\
|
||||
<tbody></tbody>\
|
||||
</table></div>\
|
||||
"+page +"\
|
||||
</div>\
|
||||
</div>",
|
||||
success:function(){
|
||||
getSlaveSyncUserPage(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getSlaveSSHList(page=1){
|
||||
|
||||
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
|
||||
@@ -1991,7 +2233,7 @@ function masterOrSlaveConf(version=''){
|
||||
for(i in rdata.data){
|
||||
|
||||
var v = rdata.data[i];
|
||||
var status = "异常";
|
||||
var status = "<a class='btlink db_error'>异常</>";
|
||||
if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){
|
||||
status = "正常";
|
||||
}
|
||||
@@ -2033,6 +2275,26 @@ function masterOrSlaveConf(version=''){
|
||||
// <span class="sync btn btn-default btn-sm" onclick="getMasterRepSlaveList()" title="">添加</span>\
|
||||
// </div>
|
||||
$(".table_slave_status_list").html(con);
|
||||
|
||||
$('.db_error').click(function(){
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '同步异常信息',
|
||||
area: '500px',
|
||||
content:"<form class='bt-form pd20 pb70'>\
|
||||
<div class='line'>"+v['Error']+"</div>\
|
||||
<div class='bt-form-submit-btn'>\
|
||||
<button type='button' class='btn btn-success btn-sm btn-title class-copy-db-err'>复制</button>\
|
||||
</div>\
|
||||
</form>",
|
||||
success:function(){
|
||||
copyText(v['Error']);
|
||||
$('.class-copy-db-err').click(function(){
|
||||
copyText(v['Error']);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2079,7 +2341,6 @@ function masterOrSlaveConf(version=''){
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getMasterStatus(){
|
||||
myPost('get_master_status', '', function(rdata){
|
||||
@@ -2110,7 +2371,7 @@ function masterOrSlaveConf(version=''){
|
||||
<p class="conf_p">\
|
||||
<span class="f14 c6 mr20">Slave[从]配置</span><span class="f14 c6 mr20"></span>\
|
||||
<button class="btn '+(!rdata.slave_status ? 'btn-danger' : 'btn-success')+' btn-xs btn-slave">'+(!rdata.slave_status ? '未启动' : '已启动') +'</button>\
|
||||
<button class="btn btn-success btn-xs" onclick="getSlaveSSHList()" >[主]SSH配置</button>\
|
||||
<button class="btn btn-success btn-xs" onclick="getSlaveCfg()" >同步配置</button>\
|
||||
<button class="btn btn-success btn-xs" onclick="initSlaveStatus()" >初始化</button>\
|
||||
</p>\
|
||||
<hr/>\
|
||||
@@ -2184,10 +2445,10 @@ function masterOrSlaveConf(version=''){
|
||||
getMasterDbList();
|
||||
}
|
||||
|
||||
if (rdata.slave_status){
|
||||
// if (rdata.slave_status){
|
||||
getAsyncMasterDbList();
|
||||
getAsyncDataList()
|
||||
}
|
||||
// }
|
||||
});
|
||||
}
|
||||
getMasterStatus();
|
||||
|
||||
@@ -37,11 +37,12 @@ max_connect_errors = 100
|
||||
open_files_limit = 2560
|
||||
max_allowed_packet = 128M
|
||||
|
||||
#skip-external-locking
|
||||
#skip-grant-tables
|
||||
#loose-skip-innodb
|
||||
skip_name_resolve = 1
|
||||
skip-ssl
|
||||
#skip-networking
|
||||
#skip-name-resolve
|
||||
#skip-external-locking
|
||||
#loose-skip-innodb
|
||||
#skip-grant-tables
|
||||
|
||||
log-bin=mysql-bin
|
||||
binlog_format=mixed
|
||||
|
||||
@@ -40,4 +40,17 @@ CREATE TABLE IF NOT EXISTS `slave_id_rsa` (
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
-- 从库配置主库的[user]
|
||||
-- drop table `slave_user`;
|
||||
CREATE TABLE IF NOT EXISTS `slave_sync_user` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`ip` TEXT,
|
||||
`port` TEXT,
|
||||
`user` TEXT,
|
||||
`pass` TEXT,
|
||||
`mode` TEXT,
|
||||
`cmd` TEXT,
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<p onclick="myLogs();">日志</p>
|
||||
<p onclick="pluginLogs('mysql-yum',$('.plugin_version').attr('version'),'show_log');">慢日志</p>
|
||||
<p onclick="dbList()">管理列表</p>
|
||||
<p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
|
||||
+342
-34
@@ -129,20 +129,27 @@ def contentReplace(content):
|
||||
def pSqliteDb(dbname='databases'):
|
||||
file = getServerDir() + '/mysql.db'
|
||||
name = 'mysql'
|
||||
if not os.path.exists(file):
|
||||
|
||||
import_sql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
|
||||
md5_sql = mw.md5(import_sql)
|
||||
|
||||
import_sign = False
|
||||
save_md5_file = getServerDir() + '/import_sql.md5'
|
||||
if os.path.exists(save_md5_file):
|
||||
save_md5_sql = mw.readFile(save_md5_file)
|
||||
if save_md5_sql != md5_sql:
|
||||
import_sign = True
|
||||
mw.writeFile(save_md5_file, md5_sql)
|
||||
else:
|
||||
mw.writeFile(save_md5_file, md5_sql)
|
||||
|
||||
if not os.path.exists(file) or import_sql:
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
|
||||
csql_list = csql.split(';')
|
||||
csql_list = import_sql.split(';')
|
||||
for index in range(len(csql_list)):
|
||||
conn.execute(csql_list[index], ())
|
||||
else:
|
||||
# 现有run
|
||||
# conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
# csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
|
||||
# csql_list = csql.split(';')
|
||||
# for index in range(len(csql_list)):
|
||||
# conn.execute(csql_list[index], ())
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
return conn
|
||||
|
||||
|
||||
@@ -161,9 +168,16 @@ def pMysqlDb():
|
||||
|
||||
def initDreplace(version=''):
|
||||
|
||||
my_dir = getServerDir() + '/etc'
|
||||
if not os.path.exists(my_dir):
|
||||
os.mkdir(my_dir)
|
||||
conf_dir = getServerDir() + '/etc'
|
||||
mode_dir = conf_dir + '/mode'
|
||||
|
||||
conf_list = [
|
||||
conf_dir,
|
||||
mode_dir,
|
||||
]
|
||||
for conf in conf_list:
|
||||
if not os.path.exists(conf):
|
||||
os.mkdir(conf)
|
||||
|
||||
tmp_dir = getServerDir() + '/tmp'
|
||||
if not os.path.exists(tmp_dir):
|
||||
@@ -171,12 +185,26 @@ def initDreplace(version=''):
|
||||
mw.execShell("chown -R mysql:mysql " + tmp_dir)
|
||||
mw.execShell("chmod 750 " + tmp_dir)
|
||||
|
||||
mysql_conf = my_dir + '/my.cnf'
|
||||
if not os.path.exists(mysql_conf):
|
||||
mysql_conf_tpl = getPluginDir() + '/conf/my' + version + '.cnf'
|
||||
content = mw.readFile(mysql_conf_tpl)
|
||||
my_conf = conf_dir + '/my.cnf'
|
||||
if not os.path.exists(my_conf):
|
||||
tpl = getPluginDir() + '/conf/my' + version + '.cnf'
|
||||
content = mw.readFile(tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(mysql_conf, content)
|
||||
mw.writeFile(my_conf, content)
|
||||
|
||||
classic_conf = mode_dir + '/classic.cnf'
|
||||
if not os.path.exists(classic_conf):
|
||||
tpl = getPluginDir() + '/conf/classic.cnf'
|
||||
content = mw.readFile(tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(classic_conf, content)
|
||||
|
||||
gtid_conf = mode_dir + '/gtid.cnf'
|
||||
if not os.path.exists(gtid_conf):
|
||||
tpl = getPluginDir() + '/conf/gtid.cnf'
|
||||
content = mw.readFile(tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(gtid_conf, content)
|
||||
|
||||
# systemd
|
||||
systemDir = mw.systemdCfgDir()
|
||||
@@ -233,6 +261,13 @@ def binLog():
|
||||
return mw.returnJson(True, '设置成功!')
|
||||
|
||||
|
||||
def cleanBinLog():
|
||||
db = pMysqlDb()
|
||||
cleanTime = time.strftime('%Y-%m-%d %H:%i:%s', time.localtime())
|
||||
db.execute("PURGE MASTER LOGS BEFORE '" + cleanTime + "';")
|
||||
return mw.returnJson(True, '清理BINLOG成功!')
|
||||
|
||||
|
||||
def setSkipGrantTables(v):
|
||||
'''
|
||||
设置是否密码验证
|
||||
@@ -362,6 +397,7 @@ def my8cmd(version, method):
|
||||
initDreplace(version)
|
||||
# mysql 8.0 and 5.7
|
||||
try:
|
||||
isInited = True
|
||||
if version == '5.7':
|
||||
isInited = initMysql57Data()
|
||||
elif version == '8.0':
|
||||
@@ -434,7 +470,7 @@ def getMyDbPos():
|
||||
return tmp.groups()[0].strip()
|
||||
|
||||
|
||||
def setMyDbPos():
|
||||
def setMyDbPos(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['datadir'])
|
||||
if not data[0]:
|
||||
@@ -449,7 +485,7 @@ def setMyDbPos():
|
||||
mw.execShell('mkdir -p ' + t_datadir)
|
||||
|
||||
# mw.execShell('/etc/init.d/mysqld stop')
|
||||
stop()
|
||||
stop(version)
|
||||
mw.execShell('cp -rf ' + s_datadir + '/* ' + t_datadir + '/')
|
||||
mw.execShell('chown -R mysql mysql ' + t_datadir)
|
||||
mw.execShell('chmod -R 755 ' + t_datadir)
|
||||
@@ -463,17 +499,17 @@ def setMyDbPos():
|
||||
|
||||
mycnf = mycnf.replace(s_datadir, t_datadir)
|
||||
mw.writeFile(myfile, mycnf)
|
||||
start()
|
||||
restart(version)
|
||||
|
||||
result = mw.execShell(
|
||||
'ps aux|grep mysqld| grep -v grep|grep -v python')
|
||||
'ps aux|grep "mysql-apt/bin/usr/sbin/mysqld"| grep -v grep|grep -v python')
|
||||
if len(result[0]) > 10:
|
||||
mw.writeFile('data/datadir.pl', t_datadir)
|
||||
return mw.returnJson(True, '存储目录迁移成功!')
|
||||
else:
|
||||
mw.execShell('pkill -9 mysqld')
|
||||
mw.writeFile(myfile, mw.readFile(path + '/etc/my_backup.cnf'))
|
||||
start()
|
||||
start(version)
|
||||
return mw.returnJson(False, '文件迁移失败!')
|
||||
|
||||
|
||||
@@ -719,11 +755,13 @@ def importDbExternal():
|
||||
mysql_cmd = getServerDir() + '/bin/usr/bin/mysql -S ' + sock + ' -uroot -p' + \
|
||||
pwd + ' ' + name + ' < ' + import_sql
|
||||
|
||||
# print(mysql_cmd)
|
||||
os.system(mysql_cmd)
|
||||
rdata = mw.execShell(mysql_cmd)
|
||||
if ext != 'sql':
|
||||
os.remove(import_sql)
|
||||
|
||||
if rdata[1].lower().find('error') > -1:
|
||||
return mw.returnJson(False, rdata[1])
|
||||
|
||||
return mw.returnJson(True, 'ok')
|
||||
|
||||
|
||||
@@ -748,8 +786,10 @@ def importDbBackup():
|
||||
mysql_cmd = getServerDir() + '/bin/usr/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
|
||||
' ' + name + ' < ' + file_path_sql
|
||||
|
||||
# print(mysql_cmd)
|
||||
os.system(mysql_cmd)
|
||||
rdata = mw.execShell(mysql_cmd)
|
||||
if rdata[1].lower().find('error') > -1:
|
||||
return mw.returnJson(False, rdata[1])
|
||||
|
||||
return mw.returnJson(True, 'ok')
|
||||
|
||||
|
||||
@@ -1079,7 +1119,7 @@ def addDb():
|
||||
dataAccess = args['dataAccess'].strip()
|
||||
ps = args['ps'].strip()
|
||||
|
||||
reg = "^[\w\.-]+$"
|
||||
reg = "^[\w-]+$"
|
||||
if not re.match(reg, args['name']):
|
||||
return mw.returnJson(False, '数据库名称不能带有特殊符号!')
|
||||
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
|
||||
@@ -1747,7 +1787,7 @@ def addMasterRepSlaveUser(version=''):
|
||||
# address = args['address'].strip()
|
||||
# dataAccess = args['dataAccess'].strip()
|
||||
|
||||
reg = "^[\w\.-]+$"
|
||||
reg = "^[\w-]+$"
|
||||
if not re.match(reg, username):
|
||||
return mw.returnJson(False, '用户名不能带有特殊符号!')
|
||||
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
|
||||
@@ -1775,17 +1815,18 @@ def addMasterRepSlaveUser(version=''):
|
||||
if isError != None:
|
||||
return isError
|
||||
|
||||
sql = "FLUSH PRIVILEGES;"
|
||||
pdb.execute(sql)
|
||||
else:
|
||||
sql = "GRANT REPLICATION SLAVE ON *.* TO '" + username + \
|
||||
"'@'%' identified by '" + password + "';"
|
||||
result = pdb.execute(sql)
|
||||
result = pdb.execute('FLUSH PRIVILEGES;')
|
||||
isError = isSqlError(result)
|
||||
if isError != None:
|
||||
return isError
|
||||
|
||||
sql_select = "grant select,lock tables,PROCESS on *.* to " + username + "@'%';"
|
||||
pdb.execute(sql_select)
|
||||
pdb.execute('FLUSH PRIVILEGES;')
|
||||
|
||||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
psdb.add('username,password,accept,ps,addtime',
|
||||
(username, password, '%', '', addTime))
|
||||
@@ -1922,6 +1963,123 @@ def getSlaveSSHList(version=''):
|
||||
return mw.getJson(data)
|
||||
|
||||
|
||||
def getSlaveSyncUserByIp(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['ip'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ip = args['ip']
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').where(
|
||||
"ip=?", (ip,)).select()
|
||||
return mw.returnJson(True, 'ok', data)
|
||||
|
||||
|
||||
def addSlaveSyncUser(version=''):
|
||||
import base64
|
||||
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['ip'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ip = args['ip']
|
||||
if ip == "":
|
||||
return mw.returnJson(True, 'ok')
|
||||
|
||||
data = checkArgs(args, ['port', 'user', 'pass', 'mode'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
cmd = args['cmd']
|
||||
port = args['port']
|
||||
user = args['user']
|
||||
apass = args['pass']
|
||||
mode = args['mode']
|
||||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip').where("ip=?", (ip,)).select()
|
||||
if len(data) > 0:
|
||||
res = conn.where("ip=?", (ip,)).save(
|
||||
'port,user,pass,mode,cmd', (port, user, apass, mode, cmd))
|
||||
else:
|
||||
conn.add('ip,port,user,cmd,user,pass,mode,addtime',
|
||||
(ip, port, user, cmd, user, apass, mode, addTime))
|
||||
|
||||
return mw.returnJson(True, '设置成功!')
|
||||
|
||||
|
||||
def delSlaveSyncUser(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['ip'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ip = args['ip']
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
conn.where("ip=?", (ip,)).delete()
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
|
||||
|
||||
def getSlaveSyncUserList(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['page', 'page_size'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
page = int(args['page'])
|
||||
page_size = int(args['page_size'])
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
limit = str((page - 1) * page_size) + ',' + str(page_size)
|
||||
|
||||
field = 'id,ip,port,user,pass,cmd,addtime'
|
||||
clist = conn.field(field).limit(limit).order('id desc').select()
|
||||
count = conn.count()
|
||||
|
||||
data = {}
|
||||
_page = {}
|
||||
_page['count'] = count
|
||||
_page['p'] = page
|
||||
_page['row'] = page_size
|
||||
_page['tojs'] = args['tojs']
|
||||
data['page'] = mw.getPage(_page)
|
||||
data['data'] = clist
|
||||
|
||||
return mw.getJson(data)
|
||||
|
||||
|
||||
def getSyncModeFile():
|
||||
return getServerDir() + "/sync.mode"
|
||||
|
||||
|
||||
def getSlaveSyncMode(version):
|
||||
sync_mode = getSyncModeFile()
|
||||
if os.path.exists(sync_mode):
|
||||
mode = mw.readFile(sync_mode).strip()
|
||||
return mw.returnJson(True, 'ok', mode)
|
||||
return mw.returnJson(False, 'fail')
|
||||
|
||||
|
||||
def setSlaveSyncMode(version):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['mode'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
mode = args['mode']
|
||||
sync_mode = getSyncModeFile()
|
||||
|
||||
if mode == 'none':
|
||||
os.remove(sync_mode)
|
||||
else:
|
||||
mw.writeFile(sync_mode, mode)
|
||||
return mw.returnJson(True, '设置成功', mode)
|
||||
|
||||
|
||||
def getSlaveSSHByIp(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['ip'])
|
||||
@@ -2024,6 +2182,47 @@ def getSlaveSyncCmd(version=''):
|
||||
|
||||
|
||||
def initSlaveStatus(version=''):
|
||||
mode_file = getSyncModeFile()
|
||||
if not os.path.exists(mode_file):
|
||||
return mw.returnJson(False, '需要先设置同步配置')
|
||||
|
||||
mode = mw.readFile(mode_file)
|
||||
if mode == 'ssh':
|
||||
return initSlaveStatusSSH(version)
|
||||
if mode == 'sync-user':
|
||||
return initSlaveStatusSyncUser(version)
|
||||
|
||||
|
||||
def initSlaveStatusSyncUser(version=''):
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').find()
|
||||
if len(data) < 1:
|
||||
return mw.returnJson(False, '需要先添加同步用户配置!')
|
||||
|
||||
# print(data)
|
||||
db = pMysqlDb()
|
||||
dlist = db.query('show slave status')
|
||||
if len(dlist) > 0:
|
||||
return mw.returnJson(False, '已经初始化好了zz...')
|
||||
|
||||
u = data
|
||||
|
||||
mode_name = 'classic'
|
||||
if u['mode'] == '1':
|
||||
mode_name = 'gtid'
|
||||
|
||||
local_mode = recognizeDbMode()
|
||||
if local_mode != mode_name:
|
||||
return mw.returnJson(False, '同步模式不一致!')
|
||||
|
||||
t = db.query(u['cmd'])
|
||||
# print(t)
|
||||
db.query("start slave user='{}' password='{}';".format(
|
||||
u['user'], u['pass']))
|
||||
return mw.returnJson(True, '初始化成功!')
|
||||
|
||||
|
||||
def initSlaveStatusSSH(version=''):
|
||||
db = pMysqlDb()
|
||||
dlist = db.query('show slave status')
|
||||
if len(dlist) > 0:
|
||||
@@ -2092,6 +2291,41 @@ def initSlaveStatus(version=''):
|
||||
|
||||
|
||||
def setSlaveStatus(version=''):
|
||||
mode_file = getSyncModeFile()
|
||||
if not os.path.exists(mode_file):
|
||||
return mw.returnJson(False, '需要先设置同步配置')
|
||||
|
||||
mode = mw.readFile(mode_file)
|
||||
if mode == 'ssh':
|
||||
return setSlaveStatusSSH(version)
|
||||
if mode == 'sync-user':
|
||||
return setSlaveStatusSyncUser(version)
|
||||
|
||||
|
||||
def setSlaveStatusSyncUser(version=''):
|
||||
db = pMysqlDb()
|
||||
dlist = db.query('show slave status')
|
||||
if len(dlist) == 0:
|
||||
return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!')
|
||||
|
||||
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
|
||||
db.query('stop slave')
|
||||
else:
|
||||
ip = dlist[0]['Master_Host']
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').find()
|
||||
if len(data) == 0:
|
||||
return mw.returnJson(False, '没有数据无法重启!')
|
||||
user = data['user']
|
||||
apass = data['pass']
|
||||
|
||||
db.query("start slave")
|
||||
# db.query("start slave user='{}' password='{}';".format(user, apass))
|
||||
|
||||
return mw.returnJson(True, '设置成功!')
|
||||
|
||||
|
||||
def setSlaveStatusSSH(version=''):
|
||||
|
||||
db = pMysqlDb()
|
||||
dlist = db.query('show slave status')
|
||||
@@ -2160,6 +2394,68 @@ def writeDbSyncStatus(data):
|
||||
|
||||
|
||||
def doFullSync(version=''):
|
||||
mode_file = getSyncModeFile()
|
||||
if not os.path.exists(mode_file):
|
||||
return mw.returnJson(False, '需要先设置同步配置')
|
||||
|
||||
mode = mw.readFile(mode_file)
|
||||
if mode == 'ssh':
|
||||
return doFullSyncSSH(version)
|
||||
if mode == 'sync-user':
|
||||
return doFullSyncUser(version)
|
||||
|
||||
|
||||
def doFullSyncUser(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['db'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
sync_db = args['db']
|
||||
|
||||
db = pMysqlDb()
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').find()
|
||||
user = data['user']
|
||||
apass = data['pass']
|
||||
port = data['port']
|
||||
ip = data['ip']
|
||||
|
||||
bak_file = '/tmp/tmp.sql'
|
||||
|
||||
writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0})
|
||||
dmp_option = ''
|
||||
mode = recognizeDbMode()
|
||||
if mode == 'gtid':
|
||||
dmp_option = ' --set-gtid-purged=off '
|
||||
|
||||
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20})
|
||||
|
||||
if not os.path.exists(bak_file):
|
||||
dump_sql_data = getServerDir() + "/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
|
||||
port + " -u" + user + " -p" + apass + " " + sync_db + " > " + bak_file
|
||||
mw.execShell(dump_sql_data)
|
||||
|
||||
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
|
||||
if os.path.exists(bak_file):
|
||||
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
|
||||
sock = getSocketFile()
|
||||
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
|
||||
' ' + sync_db + ' < ' + bak_file
|
||||
mw.execShell(my_import_cmd)
|
||||
|
||||
if version == '8.0':
|
||||
db.query("start slave user='{}' password='{}';".format(user, apass))
|
||||
else:
|
||||
db.query("start slave")
|
||||
|
||||
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
|
||||
os.system("rm -rf " + bak_file)
|
||||
return True
|
||||
|
||||
|
||||
def doFullSyncSSH(version=''):
|
||||
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['db'])
|
||||
@@ -2392,7 +2688,7 @@ if __name__ == "__main__":
|
||||
elif func == 'my_db_pos':
|
||||
print(getMyDbPos())
|
||||
elif func == 'set_db_pos':
|
||||
print(setMyDbPos())
|
||||
print(setMyDbPos(version))
|
||||
elif func == 'my_port':
|
||||
print(getMyPort())
|
||||
elif func == 'set_my_port':
|
||||
@@ -2485,6 +2781,18 @@ if __name__ == "__main__":
|
||||
print(delSlaveSSH(version))
|
||||
elif func == 'update_slave_ssh':
|
||||
print(updateSlaveSSH(version))
|
||||
elif func == 'get_slave_sync_user_list':
|
||||
print(getSlaveSyncUserList(version))
|
||||
elif func == 'get_slave_sync_user_by_ip':
|
||||
print(getSlaveSyncUserByIp(version))
|
||||
elif func == 'add_slave_sync_user':
|
||||
print(addSlaveSyncUser(version))
|
||||
elif func == 'del_slave_sync_user':
|
||||
print(delSlaveSyncUser(version))
|
||||
elif func == 'get_slave_sync_mode':
|
||||
print(getSlaveSyncMode(version))
|
||||
elif func == 'set_slave_sync_mode':
|
||||
print(setSlaveSyncMode(version))
|
||||
elif func == 'init_slave_status':
|
||||
print(initSlaveStatus(version))
|
||||
elif func == 'set_slave_status':
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"tip":"soft",
|
||||
"name":"mysql-yum",
|
||||
"type":"运行环境",
|
||||
"ps":"[<b style='color:red;'>潜龙勿用</b>]一种关系数据库管理系统[centos](极速安装)",
|
||||
"ps":"一种关系数据库管理系统[centos](极速安装)",
|
||||
"todo_versions":["5.7","8.0"],
|
||||
"versions":["5.7","8.0"],
|
||||
"shell":"install.sh",
|
||||
|
||||
@@ -265,7 +265,7 @@ function myPerfOpt() {
|
||||
}
|
||||
|
||||
function reBootMySqld(){
|
||||
pluginOpService('mysql-apt','restart','');
|
||||
pluginOpService('mysql-yum','restart','');
|
||||
}
|
||||
|
||||
|
||||
@@ -731,7 +731,7 @@ function addDatabase(type){
|
||||
layer.close(index);
|
||||
dbList();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2},600);
|
||||
},{icon: rdata.status ? 1 : 2}, 2000);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -818,8 +818,8 @@ function openPhpmyadmin(name,username,password){
|
||||
|
||||
data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_cfg'});
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if (rdata.choose != 'mysql-apt'){
|
||||
layer.msg('当前为['+rdata.choose+']模式,若要使用请切换模式.',{icon:2,shade: [0.3, '#000']});
|
||||
if (rdata.choose != 'mysql-yum'){
|
||||
layer.msg('当前为['+rdata.choose+']模式,若要使用请修改phpMyAdmin访问切换.',{icon:2,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1207,7 +1207,7 @@ function myLogs(){
|
||||
var limitCon = '<p class="conf_p">\
|
||||
<span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\
|
||||
'+line_status+'\
|
||||
<p class="f14 c6 mtb10" style="border-top:#ddd 1px solid; padding:10px 0">错误日志<button class="btn btn-default btn-clear btn-xs" style="float:right;">清理日志</button></p>\
|
||||
<p class="f14 c6 mtb10" style="border-top:#ddd 1px solid; padding:10px 0">错误日志<button class="btn btn-default btn-clear btn-xs" style="float:right;" >清理日志</button></p>\
|
||||
<textarea readonly style="margin: 0px;width: 100%;height: 440px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\
|
||||
</p>';
|
||||
$(".soft-man-con").html(limitCon);
|
||||
@@ -1545,13 +1545,9 @@ function getMasterRepSlaveUserCmd(username, db=''){
|
||||
function delMasterRepSlaveUser(username){
|
||||
myPost('del_master_rep_slave_user', {username:username}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.msg(rdata.msg);
|
||||
|
||||
$('.layui-layer-close1').click();
|
||||
|
||||
setTimeout(function(){
|
||||
showMsg(rdata.msg, function(){
|
||||
getMasterRepSlaveList();
|
||||
},1000);
|
||||
},{icon: rdata.status ? 1 : 2},1000)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1821,8 +1817,23 @@ function addSlaveSSH(ip=''){
|
||||
function delSlaveSSH(ip){
|
||||
myPost('del_slave_ssh', {ip:ip}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2});
|
||||
getSlaveSSHPage();
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
getSlaveSSHPage();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function delSlaveSyncUser(ip){
|
||||
myPost('del_slave_sync_user', {ip:ip}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
getSlaveSyncUserPage();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1872,6 +1883,237 @@ function getSlaveSSHPage(page=1){
|
||||
}
|
||||
|
||||
|
||||
|
||||
function addSlaveSyncUser(ip=''){
|
||||
|
||||
myPost('get_slave_sync_user_by_ip', {ip:ip}, function(rdata){
|
||||
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
|
||||
var ip = '127.0.0.1';
|
||||
var port = "22";
|
||||
var cmd = '';
|
||||
var user = 'input_sync_user';
|
||||
var pass = 'input_sync_pwd';
|
||||
var mode = '0';
|
||||
|
||||
if (rdata.data.length>0){
|
||||
ip = rdata.data[0]['ip'];
|
||||
port = rdata.data[0]['port'];
|
||||
cmd = rdata.data[0]['cmd'];
|
||||
user = rdata.data[0]['user'];
|
||||
pass = rdata.data[0]['pass'];
|
||||
mode = rdata.data[0]['mode'];
|
||||
}
|
||||
|
||||
var index = layer.open({
|
||||
type: 1,
|
||||
area: ['500px','470px'],
|
||||
title: '同步账户',
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: true,
|
||||
btn:["确认","取消"],
|
||||
content: "<form class='bt-form pd20'>\
|
||||
<div class='line'><span class='tname'>IP</span><div class='info-r'><input name='ip' class='bt-input-text mr5' type='text' style='width:330px;' value='"+ip+"'></div></div>\
|
||||
<div class='line'><span class='tname'>端口</span><div class='info-r'><input name='port' class='bt-input-text mr5' type='number' style='width:330px;' value='"+port+"'></div></div>\
|
||||
<div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\
|
||||
<div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>CMD[最好填好]</span>\
|
||||
<div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\
|
||||
</div>\
|
||||
<input type='hidden' name='mode' value='"+mode+"' />\
|
||||
</form>",
|
||||
success:function(){
|
||||
$('textarea[name="cmd"]').html(cmd);
|
||||
|
||||
$('textarea[name="cmd"]').change(function(){
|
||||
var val = $(this).val();
|
||||
var vlist = val.split(',');
|
||||
var a = {};
|
||||
for (var i in vlist) {
|
||||
var tmp = toTrim(vlist[i]);
|
||||
var tmp_a = tmp.split(" ");
|
||||
var real_tmp = tmp_a[tmp_a.length-1];
|
||||
var kv = real_tmp.split("=");
|
||||
a[kv[0]] = kv[1].replace("'",'').replace("'",'');
|
||||
}
|
||||
|
||||
$('input[name="ip"]').val(a['MASTER_HOST']);
|
||||
$('input[name="port"]').val(a['MASTER_PORT']);
|
||||
$('input[name="user"]').val(a['MASTER_USER']);
|
||||
$('input[name="pass"]').val(a['MASTER_PASSWORD']);
|
||||
|
||||
console.log(a['MASTER_AUTO_POSITION'],typeof(a['MASTER_AUTO_POSITION']));
|
||||
if (typeof(a['MASTER_AUTO_POSITION']) != 'undefined' ){
|
||||
$('input[name="mode"]').val('1');
|
||||
}
|
||||
});
|
||||
},
|
||||
yes:function(index){
|
||||
var ip = $('input[name="ip"]').val();
|
||||
var port = $('input[name="port"]').val();
|
||||
var user = $('input[name="user"]').val();
|
||||
var pass = $('input[name="pass"]').val();
|
||||
var cmd = $('textarea[name="cmd"]').val();
|
||||
var mode = $('input[name="mode"]').val();
|
||||
|
||||
var data = {ip:ip,port:port,cmd:cmd,user:user,pass:pass,mode:mode};
|
||||
myPost('add_slave_sync_user', data, function(ret_data){
|
||||
layer.close(index);
|
||||
var rdata = $.parseJSON(ret_data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
getSlaveSyncUserPage();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2},600);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getSlaveSyncUserPage(page=1){
|
||||
var _data = {};
|
||||
_data['page'] = page;
|
||||
_data['page_size'] = 5;
|
||||
_data['tojs'] ='getSlaveSyncUserPage';
|
||||
myPost('get_slave_sync_user_list', _data, function(data){
|
||||
var layerId = null;
|
||||
var rdata = [];
|
||||
try {
|
||||
rdata = $.parseJSON(data.data);
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
var list = '';
|
||||
var user_list = rdata['data'];
|
||||
for (i in user_list) {
|
||||
var ip = user_list[i]['ip'];
|
||||
var port = user_list[i]['port'];
|
||||
var user = user_list[i]['user'];
|
||||
var apass = user_list[i]['pass'];
|
||||
|
||||
var cmd = '未设置';
|
||||
if (user_list[i]['cmd']!=''){
|
||||
cmd = '已设置';
|
||||
}
|
||||
|
||||
list += '<tr><td>'+ip+'</td>\
|
||||
<td>'+port+'</td>\
|
||||
<td>'+user+'</td>\
|
||||
<td>'+apass+'</td>\
|
||||
<td>'+cmd+'</td>\
|
||||
<td>\
|
||||
<a class="btlink" onclick="addSlaveSyncUser(\''+ip+'\');">修改</a> | \
|
||||
<a class="btlink" onclick="delSlaveSyncUser(\''+ip+'\');">删除</a>\
|
||||
</td>\
|
||||
</tr>';
|
||||
}
|
||||
|
||||
$('.get-slave-ssh-list tbody').html(list);
|
||||
$('.dataTables_paginate_4').html(rdata['page']);
|
||||
});
|
||||
}
|
||||
|
||||
function getSlaveCfg(){
|
||||
|
||||
myPost('get_slave_sync_mode', '', function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var mode_none = 'success';
|
||||
var mode_ssh = 'danger';
|
||||
var mode_sync_user = 'danger';
|
||||
if(rdata.status){
|
||||
var mode_none = 'danger';
|
||||
if (rdata.data == 'ssh'){
|
||||
var mode_ssh = 'success';
|
||||
var mode_sync_user = 'danger';
|
||||
} else {
|
||||
var mode_ssh = 'danger';
|
||||
var mode_sync_user = 'success';
|
||||
}
|
||||
}
|
||||
|
||||
layerId = layer.open({
|
||||
type: 1,
|
||||
title: '同步配置',
|
||||
area: ['400px','180px'],
|
||||
content:"<div class='bt-form pd20 c6'>\
|
||||
<p class='conf_p'>\
|
||||
<span class='f14 c6 mr20'>当前从库同步模式</span>\
|
||||
<b class='f14 c6 mr20'></b>\
|
||||
<button class='btn btn-"+mode_none+" btn-xs slave-db-mode btn-none'>无</button>\
|
||||
<button class='btn btn-"+mode_ssh+" btn-xs slave-db-mode btn-ssh'>SSH</button>\
|
||||
<button class='btn btn-"+mode_sync_user+" btn-xs slave-db-mode btn-sync-user'>同步账户</button>\
|
||||
</p>\
|
||||
<hr />\
|
||||
<p class='conf_p'>\
|
||||
<span class='f14 c6 mr20'>配置设置</span>\
|
||||
<b class='f14 c6 mr20'></b>\
|
||||
<button class='btn btn-success btn-xs btn-slave-ssh'>SSH</button>\
|
||||
<button class='btn btn-success btn-xs btn-slave-user'>同步账户</button>\
|
||||
</p>\
|
||||
</div>",
|
||||
success:function(){
|
||||
$('.btn-slave-ssh').click(function(){
|
||||
getSlaveSSHList();
|
||||
});
|
||||
|
||||
$('.btn-slave-user').click(function(){
|
||||
getSlaveUserList();
|
||||
});
|
||||
|
||||
$('.slave-db-mode').click(function(){
|
||||
var _this = this;
|
||||
var mode = 'none';
|
||||
if ($(this).hasClass('btn-ssh')){
|
||||
mode = 'ssh';
|
||||
}
|
||||
if ($(this).hasClass('btn-sync-user')){
|
||||
mode = 'sync-user';
|
||||
}
|
||||
|
||||
myPost('set_slave_sync_mode', {mode:mode}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
$('.slave-db-mode').remove('btn-success').addClass('btn-danger');
|
||||
$(_this).removeClass('btn-danger').addClass('btn-success');
|
||||
},{icon:rdata.status?1:2},2000);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function getSlaveUserList(){
|
||||
|
||||
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
|
||||
page += '<div class="table_toolbar" style="left:0px;"><span class="sync btn btn-default btn-sm" onclick="addSlaveSyncUser()" title="">添加同步账户</span></div>';
|
||||
|
||||
layerId = layer.open({
|
||||
type: 1,
|
||||
title: '同步账户列表',
|
||||
area: '500px',
|
||||
content:"<div class='bt-form pd20 c6'>\
|
||||
<div class='divtable mtb10'>\
|
||||
<div><table class='table table-hover get-slave-ssh-list'>\
|
||||
<thead><tr><th>IP</th><th>PORT</th><th>同步账户</th><th>同步密码</th><th>CMD</th><th>操作</th></tr></thead>\
|
||||
<tbody></tbody>\
|
||||
</table></div>\
|
||||
"+page +"\
|
||||
</div>\
|
||||
</div>",
|
||||
success:function(){
|
||||
getSlaveSyncUserPage(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getSlaveSSHList(page=1){
|
||||
|
||||
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
|
||||
@@ -1991,7 +2233,7 @@ function masterOrSlaveConf(version=''){
|
||||
for(i in rdata.data){
|
||||
|
||||
var v = rdata.data[i];
|
||||
var status = "异常";
|
||||
var status = "<a class='btlink db_error'>异常</>";
|
||||
if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){
|
||||
status = "正常";
|
||||
}
|
||||
@@ -2033,6 +2275,26 @@ function masterOrSlaveConf(version=''){
|
||||
// <span class="sync btn btn-default btn-sm" onclick="getMasterRepSlaveList()" title="">添加</span>\
|
||||
// </div>
|
||||
$(".table_slave_status_list").html(con);
|
||||
|
||||
$('.db_error').click(function(){
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '同步异常信息',
|
||||
area: '500px',
|
||||
content:"<form class='bt-form pd20 pb70'>\
|
||||
<div class='line'>"+v['Error']+"</div>\
|
||||
<div class='bt-form-submit-btn'>\
|
||||
<button type='button' class='btn btn-success btn-sm btn-title class-copy-db-err'>复制</button>\
|
||||
</div>\
|
||||
</form>",
|
||||
success:function(){
|
||||
copyText(v['Error']);
|
||||
$('.class-copy-db-err').click(function(){
|
||||
copyText(v['Error']);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2079,7 +2341,6 @@ function masterOrSlaveConf(version=''){
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getMasterStatus(){
|
||||
myPost('get_master_status', '', function(rdata){
|
||||
@@ -2110,7 +2371,7 @@ function masterOrSlaveConf(version=''){
|
||||
<p class="conf_p">\
|
||||
<span class="f14 c6 mr20">Slave[从]配置</span><span class="f14 c6 mr20"></span>\
|
||||
<button class="btn '+(!rdata.slave_status ? 'btn-danger' : 'btn-success')+' btn-xs btn-slave">'+(!rdata.slave_status ? '未启动' : '已启动') +'</button>\
|
||||
<button class="btn btn-success btn-xs" onclick="getSlaveSSHList()" >[主]SSH配置</button>\
|
||||
<button class="btn btn-success btn-xs" onclick="getSlaveCfg()" >同步配置</button>\
|
||||
<button class="btn btn-success btn-xs" onclick="initSlaveStatus()" >初始化</button>\
|
||||
</p>\
|
||||
<hr/>\
|
||||
@@ -2184,10 +2445,10 @@ function masterOrSlaveConf(version=''){
|
||||
getMasterDbList();
|
||||
}
|
||||
|
||||
if (rdata.slave_status){
|
||||
// if (rdata.slave_status){
|
||||
getAsyncMasterDbList();
|
||||
getAsyncDataList()
|
||||
}
|
||||
// }
|
||||
});
|
||||
}
|
||||
getMasterStatus();
|
||||
|
||||
@@ -67,7 +67,8 @@ binlog-ignore-db = performance_schema
|
||||
|
||||
#slave
|
||||
log-slave-updates = 1
|
||||
skip-slave-start = 1
|
||||
# Prevent replication from starting automatically with MySQL
|
||||
#skip-slave-start = 1
|
||||
#replicate-do-db
|
||||
replicate-ignore-db = information_schema
|
||||
replicate-ignore-db = performance_schema
|
||||
|
||||
@@ -69,7 +69,8 @@ binlog-ignore-db = performance_schema
|
||||
|
||||
#slave
|
||||
log-slave-updates = 1
|
||||
skip-slave-start = 1
|
||||
# Prevent replication from starting automatically with MySQL
|
||||
#skip-slave-start = 1
|
||||
#replicate-do-db
|
||||
replicate-ignore-db = information_schema
|
||||
replicate-ignore-db = performance_schema
|
||||
|
||||
@@ -8,9 +8,6 @@ default-character-set = UTF8MB4
|
||||
[mysqld]
|
||||
!include {$SERVER_APP_PATH}/etc/mode/classic.cnf
|
||||
|
||||
;sha256_password_private_key_path=mysql.pem
|
||||
;sha256_password_public_key_path=mysql.pub
|
||||
|
||||
pid-file = {$SERVER_APP_PATH}/data/mysql.pid
|
||||
user = mysql
|
||||
port = 3306
|
||||
@@ -44,6 +41,7 @@ open_files_limit = 2560
|
||||
max_allowed_packet = 128M
|
||||
|
||||
skip_name_resolve = 1
|
||||
skip-ssl
|
||||
#skip-networking
|
||||
#skip-external-locking
|
||||
#loose-skip-innodb
|
||||
@@ -70,8 +68,10 @@ binlog-ignore-db = information_schema
|
||||
binlog-ignore-db = performance_schema
|
||||
|
||||
#slave
|
||||
log-slave-updates = 1
|
||||
skip-slave-start = 1
|
||||
# 5.7 It can not be opened later
|
||||
#log-slave-updates = 1
|
||||
# Prevent replication from starting automatically with MySQL
|
||||
#skip-slave-start = 1
|
||||
#replicate-do-db
|
||||
replicate-ignore-db = information_schema
|
||||
replicate-ignore-db = performance_schema
|
||||
|
||||
@@ -68,7 +68,8 @@ binlog-ignore-db = performance_schema
|
||||
|
||||
#slave
|
||||
log_replica_updates = 1
|
||||
skip_replica_start = 1
|
||||
# Prevent replication from starting automatically with MySQL
|
||||
#skip_replica_start = 1
|
||||
#replicate-do-db
|
||||
replicate-ignore-db = information_schema
|
||||
replicate-ignore-db = performance_schema
|
||||
|
||||
@@ -40,4 +40,17 @@ CREATE TABLE IF NOT EXISTS `slave_id_rsa` (
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
-- 从库配置主库的[user]
|
||||
-- drop table `slave_user`;
|
||||
CREATE TABLE IF NOT EXISTS `slave_sync_user` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`ip` TEXT,
|
||||
`port` TEXT,
|
||||
`user` TEXT,
|
||||
`pass` TEXT,
|
||||
`mode` TEXT,
|
||||
`cmd` TEXT,
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
.conf_p span {
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
width: 135px;
|
||||
width: 115px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
+335
-30
@@ -127,20 +127,27 @@ def contentReplace(content):
|
||||
def pSqliteDb(dbname='databases'):
|
||||
file = getServerDir() + '/mysql.db'
|
||||
name = 'mysql'
|
||||
if not os.path.exists(file):
|
||||
|
||||
import_sql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
|
||||
md5_sql = mw.md5(import_sql)
|
||||
|
||||
import_sign = False
|
||||
save_md5_file = getServerDir() + '/import_sql.md5'
|
||||
if os.path.exists(save_md5_file):
|
||||
save_md5_sql = mw.readFile(save_md5_file)
|
||||
if save_md5_sql != md5_sql:
|
||||
import_sign = True
|
||||
mw.writeFile(save_md5_file, md5_sql)
|
||||
else:
|
||||
mw.writeFile(save_md5_file, md5_sql)
|
||||
|
||||
if not os.path.exists(file) or import_sql:
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
|
||||
csql_list = csql.split(';')
|
||||
csql_list = import_sql.split(';')
|
||||
for index in range(len(csql_list)):
|
||||
conn.execute(csql_list[index], ())
|
||||
else:
|
||||
# 现有run
|
||||
# conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
# csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
|
||||
# csql_list = csql.split(';')
|
||||
# for index in range(len(csql_list)):
|
||||
# conn.execute(csql_list[index], ())
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
return conn
|
||||
|
||||
|
||||
@@ -481,14 +488,14 @@ def initMysql8Pwd():
|
||||
|
||||
# 删除冗余账户
|
||||
hostname = mw.execShell('hostname')[0].strip()
|
||||
if hostname != 'localhost':
|
||||
drop_hostname = serverdir + '/bin/mysql --defaults-file=' + \
|
||||
myconf + ' -uroot -p' + pwd + ' -e "drop user \'\'@\'' + hostname + '\'";'
|
||||
mw.execShell(drop_hostname)
|
||||
|
||||
drop_hostname = serverdir + '/bin/mysql --defaults-file=' + \
|
||||
myconf + ' -uroot -p' + pwd + ' -e "drop user \'\'@\'' + hostname + '\'";'
|
||||
mw.execShell(drop_hostname)
|
||||
|
||||
drop_root_hostname = serverdir + '/bin/mysql --defaults-file=' + \
|
||||
myconf + ' -uroot -p' + pwd + ' -e "drop user \'root\'@\'' + hostname + '\'";'
|
||||
mw.execShell(drop_root_hostname)
|
||||
drop_root_hostname = serverdir + '/bin/mysql --defaults-file=' + \
|
||||
myconf + ' -uroot -p' + pwd + ' -e "drop user \'root\'@\'' + hostname + '\'";'
|
||||
mw.execShell(drop_root_hostname)
|
||||
|
||||
pSqliteDb('config').where('id=?', (1,)).save('mysql_root', (pwd,))
|
||||
|
||||
@@ -899,11 +906,13 @@ def importDbExternal():
|
||||
mysql_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + \
|
||||
pwd + ' ' + name + ' < ' + import_sql
|
||||
|
||||
# print(mysql_cmd)
|
||||
os.system(mysql_cmd)
|
||||
rdata = mw.execShell(mysql_cmd)
|
||||
if ext != 'sql':
|
||||
os.remove(import_sql)
|
||||
|
||||
if rdata[1].lower().find('error') > -1:
|
||||
return mw.returnJson(False, rdata[1])
|
||||
|
||||
return mw.returnJson(True, 'ok')
|
||||
|
||||
|
||||
@@ -923,13 +932,23 @@ def importDbBackup():
|
||||
cmd = 'cd ' + mw.getRootDir() + '/backup/database && gzip -d ' + file
|
||||
mw.execShell(cmd)
|
||||
|
||||
local_mode = recognizeDbMode()
|
||||
if local_mode == 'gtid':
|
||||
pdb = pMysqlDb()
|
||||
pdb.execute('reset master')
|
||||
|
||||
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
|
||||
sock = getSocketFile()
|
||||
mysql_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
|
||||
' ' + name + ' < ' + file_path_sql
|
||||
|
||||
# print(mysql_cmd)
|
||||
os.system(mysql_cmd)
|
||||
# os.system(mysql_cmd)
|
||||
|
||||
rdata = mw.execShell(mysql_cmd)
|
||||
if rdata[1].lower().find('error') > -1:
|
||||
return mw.returnJson(False, rdata[1])
|
||||
|
||||
return mw.returnJson(True, 'ok')
|
||||
|
||||
|
||||
@@ -1260,7 +1279,7 @@ def addDb():
|
||||
dataAccess = args['dataAccess'].strip()
|
||||
ps = args['ps'].strip()
|
||||
|
||||
reg = "^[\w\.-]+$"
|
||||
reg = "^[\w-]+$"
|
||||
if not re.match(reg, args['name']):
|
||||
return mw.returnJson(False, '数据库名称不能带有特殊符号!')
|
||||
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
|
||||
@@ -1928,7 +1947,7 @@ def addMasterRepSlaveUser(version=''):
|
||||
# address = args['address'].strip()
|
||||
# dataAccess = args['dataAccess'].strip()
|
||||
|
||||
reg = "^[\w\.-]+$"
|
||||
reg = "^[\w-]+$"
|
||||
if not re.match(reg, username):
|
||||
return mw.returnJson(False, '用户名不能带有特殊符号!')
|
||||
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
|
||||
@@ -1956,17 +1975,18 @@ def addMasterRepSlaveUser(version=''):
|
||||
if isError != None:
|
||||
return isError
|
||||
|
||||
sql = "FLUSH PRIVILEGES;"
|
||||
pdb.execute(sql)
|
||||
else:
|
||||
sql = "GRANT REPLICATION SLAVE ON *.* TO '" + username + \
|
||||
sql = "grant replication SLAVE ON *.* TO '" + username + \
|
||||
"'@'%' identified by '" + password + "';"
|
||||
result = pdb.execute(sql)
|
||||
result = pdb.execute('FLUSH PRIVILEGES;')
|
||||
isError = isSqlError(result)
|
||||
if isError != None:
|
||||
return isError
|
||||
|
||||
sql_select = "grant select,lock tables,PROCESS on *.* to " + username + "@'%';"
|
||||
pdb.execute(sql_select)
|
||||
pdb.execute('FLUSH PRIVILEGES;')
|
||||
|
||||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
psdb.add('username,password,accept,ps,addtime',
|
||||
(username, password, '%', '', addTime))
|
||||
@@ -2103,6 +2123,123 @@ def getSlaveSSHList(version=''):
|
||||
return mw.getJson(data)
|
||||
|
||||
|
||||
def getSlaveSyncUserByIp(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['ip'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ip = args['ip']
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').where(
|
||||
"ip=?", (ip,)).select()
|
||||
return mw.returnJson(True, 'ok', data)
|
||||
|
||||
|
||||
def addSlaveSyncUser(version=''):
|
||||
import base64
|
||||
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['ip'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ip = args['ip']
|
||||
if ip == "":
|
||||
return mw.returnJson(True, 'ok')
|
||||
|
||||
data = checkArgs(args, ['port', 'user', 'pass', 'mode'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
cmd = args['cmd']
|
||||
port = args['port']
|
||||
user = args['user']
|
||||
apass = args['pass']
|
||||
mode = args['mode']
|
||||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip').where("ip=?", (ip,)).select()
|
||||
if len(data) > 0:
|
||||
res = conn.where("ip=?", (ip,)).save(
|
||||
'port,user,pass,mode,cmd', (port, user, apass, mode, cmd))
|
||||
else:
|
||||
conn.add('ip,port,user,cmd,user,pass,mode,addtime',
|
||||
(ip, port, user, cmd, user, apass, mode, addTime))
|
||||
|
||||
return mw.returnJson(True, '设置成功!')
|
||||
|
||||
|
||||
def delSlaveSyncUser(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['ip'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ip = args['ip']
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
conn.where("ip=?", (ip,)).delete()
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
|
||||
|
||||
def getSlaveSyncUserList(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['page', 'page_size'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
page = int(args['page'])
|
||||
page_size = int(args['page_size'])
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
limit = str((page - 1) * page_size) + ',' + str(page_size)
|
||||
|
||||
field = 'id,ip,port,user,pass,cmd,addtime'
|
||||
clist = conn.field(field).limit(limit).order('id desc').select()
|
||||
count = conn.count()
|
||||
|
||||
data = {}
|
||||
_page = {}
|
||||
_page['count'] = count
|
||||
_page['p'] = page
|
||||
_page['row'] = page_size
|
||||
_page['tojs'] = args['tojs']
|
||||
data['page'] = mw.getPage(_page)
|
||||
data['data'] = clist
|
||||
|
||||
return mw.getJson(data)
|
||||
|
||||
|
||||
def getSyncModeFile():
|
||||
return getServerDir() + "/sync.mode"
|
||||
|
||||
|
||||
def getSlaveSyncMode(version):
|
||||
sync_mode = getSyncModeFile()
|
||||
if os.path.exists(sync_mode):
|
||||
mode = mw.readFile(sync_mode).strip()
|
||||
return mw.returnJson(True, 'ok', mode)
|
||||
return mw.returnJson(False, 'fail')
|
||||
|
||||
|
||||
def setSlaveSyncMode(version):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['mode'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
mode = args['mode']
|
||||
sync_mode = getSyncModeFile()
|
||||
|
||||
if mode == 'none':
|
||||
os.remove(sync_mode)
|
||||
else:
|
||||
mw.writeFile(sync_mode, mode)
|
||||
return mw.returnJson(True, '设置成功', mode)
|
||||
|
||||
|
||||
def getSlaveSSHByIp(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['ip'])
|
||||
@@ -2160,7 +2297,7 @@ def delSlaveSSH(version=''):
|
||||
|
||||
conn = pSqliteDb('slave_id_rsa')
|
||||
conn.where("ip=?", (ip,)).delete()
|
||||
return mw.returnJson(True, 'ok')
|
||||
return mw.returnJson(True, '删除SSH成功!')
|
||||
|
||||
|
||||
def updateSlaveSSH(version=''):
|
||||
@@ -2189,6 +2326,24 @@ def getSlaveList(version=''):
|
||||
tmp['Master_Log_File'] = dlist[x]["Master_Log_File"]
|
||||
tmp['Slave_IO_Running'] = dlist[x]["Slave_IO_Running"]
|
||||
tmp['Slave_SQL_Running'] = dlist[x]["Slave_SQL_Running"]
|
||||
tmp['Last_Error'] = dlist[x]["Last_Error"]
|
||||
tmp['Last_IO_Error'] = dlist[x]["Last_IO_Error"]
|
||||
tmp['Last_SQL_Error'] = dlist[x]["Last_SQL_Error"]
|
||||
tmp['Slave_SQL_Running_State'] = dlist[x]["Slave_SQL_Running_State"]
|
||||
|
||||
tmp['Error'] = ''
|
||||
if tmp['Last_Error'] != '':
|
||||
tmp['Error'] = tmp['Last_Error']
|
||||
|
||||
if tmp['Last_IO_Error'] != '':
|
||||
tmp['Error'] = tmp['Last_IO_Error']
|
||||
|
||||
if tmp['Last_SQL_Error'] != '':
|
||||
tmp['Error'] = tmp['Last_SQL_Error']
|
||||
|
||||
if tmp['Error'] == '':
|
||||
tmp['Error'] = tmp['Slave_SQL_Running_State']
|
||||
|
||||
ret.append(tmp)
|
||||
data = {}
|
||||
data['data'] = ret
|
||||
@@ -2205,6 +2360,47 @@ def getSlaveSyncCmd(version=''):
|
||||
|
||||
|
||||
def initSlaveStatus(version=''):
|
||||
mode_file = getSyncModeFile()
|
||||
if not os.path.exists(mode_file):
|
||||
return mw.returnJson(False, '需要先设置同步配置')
|
||||
|
||||
mode = mw.readFile(mode_file)
|
||||
if mode == 'ssh':
|
||||
return initSlaveStatusSSH(version)
|
||||
if mode == 'sync-user':
|
||||
return initSlaveStatusSyncUser(version)
|
||||
|
||||
|
||||
def initSlaveStatusSyncUser(version=''):
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').find()
|
||||
if len(data) < 1:
|
||||
return mw.returnJson(False, '需要先添加同步用户配置!')
|
||||
|
||||
# print(data)
|
||||
db = pMysqlDb()
|
||||
dlist = db.query('show slave status')
|
||||
if len(dlist) > 0:
|
||||
return mw.returnJson(False, '已经初始化好了zz...')
|
||||
|
||||
u = data
|
||||
|
||||
mode_name = 'classic'
|
||||
if u['mode'] == '1':
|
||||
mode_name = 'gtid'
|
||||
|
||||
local_mode = recognizeDbMode()
|
||||
if local_mode != mode_name:
|
||||
return mw.returnJson(False, '同步模式不一致!')
|
||||
|
||||
t = db.query(u['cmd'])
|
||||
# print(t)
|
||||
db.query("start slave user='{}' password='{}';".format(
|
||||
u['user'], u['pass']))
|
||||
return mw.returnJson(True, '初始化成功!')
|
||||
|
||||
|
||||
def initSlaveStatusSSH(version=''):
|
||||
db = pMysqlDb()
|
||||
dlist = db.query('show slave status')
|
||||
if len(dlist) > 0:
|
||||
@@ -2214,7 +2410,7 @@ def initSlaveStatus(version=''):
|
||||
data = conn.field('ip,port,id_rsa').find()
|
||||
|
||||
if len(data) < 1:
|
||||
return mw.returnJson(False, '需要先配置【[主]SSH配置】!')
|
||||
return mw.returnJson(False, '需要先添加SSH配置!')
|
||||
|
||||
SSH_PRIVATE_KEY = "/tmp/t_ssh.txt"
|
||||
ip = data['ip']
|
||||
@@ -2273,11 +2469,46 @@ def initSlaveStatus(version=''):
|
||||
|
||||
|
||||
def setSlaveStatus(version=''):
|
||||
mode_file = getSyncModeFile()
|
||||
if not os.path.exists(mode_file):
|
||||
return mw.returnJson(False, '需要先设置同步配置')
|
||||
|
||||
mode = mw.readFile(mode_file)
|
||||
if mode == 'ssh':
|
||||
return setSlaveStatusSSH(version)
|
||||
if mode == 'sync-user':
|
||||
return setSlaveStatusSyncUser(version)
|
||||
|
||||
|
||||
def setSlaveStatusSyncUser(version=''):
|
||||
db = pMysqlDb()
|
||||
dlist = db.query('show slave status')
|
||||
if len(dlist) == 0:
|
||||
return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!')
|
||||
|
||||
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
|
||||
db.query('stop slave')
|
||||
else:
|
||||
ip = dlist[0]['Master_Host']
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').find()
|
||||
if len(data) == 0:
|
||||
return mw.returnJson(False, '没有数据无法重启!')
|
||||
user = data['user']
|
||||
apass = data['pass']
|
||||
|
||||
db.query("start slave")
|
||||
# db.query("start slave user='{}' password='{}';".format(user, apass))
|
||||
|
||||
return mw.returnJson(True, '设置成功!')
|
||||
|
||||
|
||||
def setSlaveStatusSSH(version=''):
|
||||
|
||||
db = pMysqlDb()
|
||||
dlist = db.query('show slave status')
|
||||
if len(dlist) == 0:
|
||||
return mw.returnJson(False, '需要手动添加主服务命令或者执行[初始化]!')
|
||||
return mw.returnJson(False, '需要手动添加主服务命令或者执行初始化!')
|
||||
|
||||
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
|
||||
db.query('stop slave')
|
||||
@@ -2342,6 +2573,68 @@ def writeDbSyncStatus(data):
|
||||
|
||||
|
||||
def doFullSync(version=''):
|
||||
mode_file = getSyncModeFile()
|
||||
if not os.path.exists(mode_file):
|
||||
return mw.returnJson(False, '需要先设置同步配置')
|
||||
|
||||
mode = mw.readFile(mode_file)
|
||||
if mode == 'ssh':
|
||||
return doFullSyncSSH(version)
|
||||
if mode == 'sync-user':
|
||||
return doFullSyncUser(version)
|
||||
|
||||
|
||||
def doFullSyncUser(version=''):
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['db'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
sync_db = args['db']
|
||||
|
||||
db = pMysqlDb()
|
||||
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').find()
|
||||
user = data['user']
|
||||
apass = data['pass']
|
||||
port = data['port']
|
||||
ip = data['ip']
|
||||
|
||||
bak_file = '/tmp/tmp.sql'
|
||||
|
||||
writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0})
|
||||
dmp_option = ''
|
||||
mode = recognizeDbMode()
|
||||
if mode == 'gtid':
|
||||
dmp_option = ' --set-gtid-purged=off '
|
||||
|
||||
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20})
|
||||
|
||||
if not os.path.exists(bak_file):
|
||||
dump_sql_data = getServerDir() + "/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
|
||||
port + " -u" + user + " -p" + apass + " " + sync_db + " > " + bak_file
|
||||
mw.execShell(dump_sql_data)
|
||||
|
||||
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
|
||||
if os.path.exists(bak_file):
|
||||
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
|
||||
sock = getSocketFile()
|
||||
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
|
||||
' ' + sync_db + ' < ' + bak_file
|
||||
mw.execShell(my_import_cmd)
|
||||
|
||||
if version == '8.0':
|
||||
db.query("start slave user='{}' password='{}';".format(user, apass))
|
||||
else:
|
||||
db.query("start slave")
|
||||
|
||||
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
|
||||
os.system("rm -rf " + bak_file)
|
||||
return True
|
||||
|
||||
|
||||
def doFullSyncSSH(version=''):
|
||||
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['db'])
|
||||
@@ -2653,6 +2946,18 @@ if __name__ == "__main__":
|
||||
print(delSlaveSSH(version))
|
||||
elif func == 'update_slave_ssh':
|
||||
print(updateSlaveSSH(version))
|
||||
elif func == 'get_slave_sync_user_list':
|
||||
print(getSlaveSyncUserList(version))
|
||||
elif func == 'get_slave_sync_user_by_ip':
|
||||
print(getSlaveSyncUserByIp(version))
|
||||
elif func == 'add_slave_sync_user':
|
||||
print(addSlaveSyncUser(version))
|
||||
elif func == 'del_slave_sync_user':
|
||||
print(delSlaveSyncUser(version))
|
||||
elif func == 'get_slave_sync_mode':
|
||||
print(getSlaveSyncMode(version))
|
||||
elif func == 'set_slave_sync_mode':
|
||||
print(setSlaveSyncMode(version))
|
||||
elif func == 'init_slave_status':
|
||||
print(initSlaveStatus(version))
|
||||
elif func == 'set_slave_status':
|
||||
|
||||
+276
-15
@@ -731,7 +731,7 @@ function addDatabase(type){
|
||||
layer.close(index);
|
||||
dbList();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2},600);
|
||||
},{icon: rdata.status ? 1 : 2}, 2000);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -819,7 +819,7 @@ function openPhpmyadmin(name,username,password){
|
||||
data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_cfg'});
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if (rdata.choose != 'mysql'){
|
||||
layer.msg('当前为['+rdata.choose+']模式,若要使用请切换模式.',{icon:2,shade: [0.3, '#000']});
|
||||
layer.msg('当前为['+rdata.choose+']模式,若要使用请修改phpMyAdmin访问切换.',{icon:2,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1545,13 +1545,9 @@ function getMasterRepSlaveUserCmd(username, db=''){
|
||||
function delMasterRepSlaveUser(username){
|
||||
myPost('del_master_rep_slave_user', {username:username}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.msg(rdata.msg);
|
||||
|
||||
$('.layui-layer-close1').click();
|
||||
|
||||
setTimeout(function(){
|
||||
showMsg(rdata.msg, function(){
|
||||
getMasterRepSlaveList();
|
||||
},1000);
|
||||
},{icon: rdata.status ? 1 : 2},1000)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1821,8 +1817,23 @@ function addSlaveSSH(ip=''){
|
||||
function delSlaveSSH(ip){
|
||||
myPost('del_slave_ssh', {ip:ip}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2});
|
||||
getSlaveSSHPage();
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
getSlaveSSHPage();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function delSlaveSyncUser(ip){
|
||||
myPost('del_slave_sync_user', {ip:ip}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
getSlaveSyncUserPage();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1872,6 +1883,237 @@ function getSlaveSSHPage(page=1){
|
||||
}
|
||||
|
||||
|
||||
|
||||
function addSlaveSyncUser(ip=''){
|
||||
|
||||
myPost('get_slave_sync_user_by_ip', {ip:ip}, function(rdata){
|
||||
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
|
||||
var ip = '127.0.0.1';
|
||||
var port = "22";
|
||||
var cmd = '';
|
||||
var user = 'input_sync_user';
|
||||
var pass = 'input_sync_pwd';
|
||||
var mode = '0';
|
||||
|
||||
if (rdata.data.length>0){
|
||||
ip = rdata.data[0]['ip'];
|
||||
port = rdata.data[0]['port'];
|
||||
cmd = rdata.data[0]['cmd'];
|
||||
user = rdata.data[0]['user'];
|
||||
pass = rdata.data[0]['pass'];
|
||||
mode = rdata.data[0]['mode'];
|
||||
}
|
||||
|
||||
var index = layer.open({
|
||||
type: 1,
|
||||
area: ['500px','470px'],
|
||||
title: '同步账户',
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: true,
|
||||
btn:["确认","取消"],
|
||||
content: "<form class='bt-form pd20'>\
|
||||
<div class='line'><span class='tname'>IP</span><div class='info-r'><input name='ip' class='bt-input-text mr5' type='text' style='width:330px;' value='"+ip+"'></div></div>\
|
||||
<div class='line'><span class='tname'>端口</span><div class='info-r'><input name='port' class='bt-input-text mr5' type='number' style='width:330px;' value='"+port+"'></div></div>\
|
||||
<div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\
|
||||
<div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>CMD[最好填好]</span>\
|
||||
<div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\
|
||||
</div>\
|
||||
<input type='hidden' name='mode' value='"+mode+"' />\
|
||||
</form>",
|
||||
success:function(){
|
||||
$('textarea[name="cmd"]').html(cmd);
|
||||
|
||||
$('textarea[name="cmd"]').change(function(){
|
||||
var val = $(this).val();
|
||||
var vlist = val.split(',');
|
||||
var a = {};
|
||||
for (var i in vlist) {
|
||||
var tmp = toTrim(vlist[i]);
|
||||
var tmp_a = tmp.split(" ");
|
||||
var real_tmp = tmp_a[tmp_a.length-1];
|
||||
var kv = real_tmp.split("=");
|
||||
a[kv[0]] = kv[1].replace("'",'').replace("'",'');
|
||||
}
|
||||
|
||||
$('input[name="ip"]').val(a['MASTER_HOST']);
|
||||
$('input[name="port"]').val(a['MASTER_PORT']);
|
||||
$('input[name="user"]').val(a['MASTER_USER']);
|
||||
$('input[name="pass"]').val(a['MASTER_PASSWORD']);
|
||||
|
||||
console.log(a['MASTER_AUTO_POSITION'],typeof(a['MASTER_AUTO_POSITION']));
|
||||
if (typeof(a['MASTER_AUTO_POSITION']) != 'undefined' ){
|
||||
$('input[name="mode"]').val('1');
|
||||
}
|
||||
});
|
||||
},
|
||||
yes:function(index){
|
||||
var ip = $('input[name="ip"]').val();
|
||||
var port = $('input[name="port"]').val();
|
||||
var user = $('input[name="user"]').val();
|
||||
var pass = $('input[name="pass"]').val();
|
||||
var cmd = $('textarea[name="cmd"]').val();
|
||||
var mode = $('input[name="mode"]').val();
|
||||
|
||||
var data = {ip:ip,port:port,cmd:cmd,user:user,pass:pass,mode:mode};
|
||||
myPost('add_slave_sync_user', data, function(ret_data){
|
||||
layer.close(index);
|
||||
var rdata = $.parseJSON(ret_data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
getSlaveSyncUserPage();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2},600);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getSlaveSyncUserPage(page=1){
|
||||
var _data = {};
|
||||
_data['page'] = page;
|
||||
_data['page_size'] = 5;
|
||||
_data['tojs'] ='getSlaveSyncUserPage';
|
||||
myPost('get_slave_sync_user_list', _data, function(data){
|
||||
var layerId = null;
|
||||
var rdata = [];
|
||||
try {
|
||||
rdata = $.parseJSON(data.data);
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
var list = '';
|
||||
var user_list = rdata['data'];
|
||||
for (i in user_list) {
|
||||
var ip = user_list[i]['ip'];
|
||||
var port = user_list[i]['port'];
|
||||
var user = user_list[i]['user'];
|
||||
var apass = user_list[i]['pass'];
|
||||
|
||||
var cmd = '未设置';
|
||||
if (user_list[i]['cmd']!=''){
|
||||
cmd = '已设置';
|
||||
}
|
||||
|
||||
list += '<tr><td>'+ip+'</td>\
|
||||
<td>'+port+'</td>\
|
||||
<td>'+user+'</td>\
|
||||
<td>'+apass+'</td>\
|
||||
<td>'+cmd+'</td>\
|
||||
<td>\
|
||||
<a class="btlink" onclick="addSlaveSyncUser(\''+ip+'\');">修改</a> | \
|
||||
<a class="btlink" onclick="delSlaveSyncUser(\''+ip+'\');">删除</a>\
|
||||
</td>\
|
||||
</tr>';
|
||||
}
|
||||
|
||||
$('.get-slave-ssh-list tbody').html(list);
|
||||
$('.dataTables_paginate_4').html(rdata['page']);
|
||||
});
|
||||
}
|
||||
|
||||
function getSlaveCfg(){
|
||||
|
||||
myPost('get_slave_sync_mode', '', function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var mode_none = 'success';
|
||||
var mode_ssh = 'danger';
|
||||
var mode_sync_user = 'danger';
|
||||
if(rdata.status){
|
||||
var mode_none = 'danger';
|
||||
if (rdata.data == 'ssh'){
|
||||
var mode_ssh = 'success';
|
||||
var mode_sync_user = 'danger';
|
||||
} else {
|
||||
var mode_ssh = 'danger';
|
||||
var mode_sync_user = 'success';
|
||||
}
|
||||
}
|
||||
|
||||
layerId = layer.open({
|
||||
type: 1,
|
||||
title: '同步配置',
|
||||
area: ['400px','180px'],
|
||||
content:"<div class='bt-form pd20 c6'>\
|
||||
<p class='conf_p'>\
|
||||
<span class='f14 c6 mr20'>当前从库同步模式</span>\
|
||||
<b class='f14 c6 mr20'></b>\
|
||||
<button class='btn btn-"+mode_none+" btn-xs slave-db-mode btn-none'>无</button>\
|
||||
<button class='btn btn-"+mode_ssh+" btn-xs slave-db-mode btn-ssh'>SSH</button>\
|
||||
<button class='btn btn-"+mode_sync_user+" btn-xs slave-db-mode btn-sync-user'>同步账户</button>\
|
||||
</p>\
|
||||
<hr />\
|
||||
<p class='conf_p'>\
|
||||
<span class='f14 c6 mr20'>配置设置</span>\
|
||||
<b class='f14 c6 mr20'></b>\
|
||||
<button class='btn btn-success btn-xs btn-slave-ssh'>SSH</button>\
|
||||
<button class='btn btn-success btn-xs btn-slave-user'>同步账户</button>\
|
||||
</p>\
|
||||
</div>",
|
||||
success:function(){
|
||||
$('.btn-slave-ssh').click(function(){
|
||||
getSlaveSSHList();
|
||||
});
|
||||
|
||||
$('.btn-slave-user').click(function(){
|
||||
getSlaveUserList();
|
||||
});
|
||||
|
||||
$('.slave-db-mode').click(function(){
|
||||
var _this = this;
|
||||
var mode = 'none';
|
||||
if ($(this).hasClass('btn-ssh')){
|
||||
mode = 'ssh';
|
||||
}
|
||||
if ($(this).hasClass('btn-sync-user')){
|
||||
mode = 'sync-user';
|
||||
}
|
||||
|
||||
myPost('set_slave_sync_mode', {mode:mode}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
$('.slave-db-mode').remove('btn-success').addClass('btn-danger');
|
||||
$(_this).removeClass('btn-danger').addClass('btn-success');
|
||||
},{icon:rdata.status?1:2},2000);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function getSlaveUserList(){
|
||||
|
||||
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
|
||||
page += '<div class="table_toolbar" style="left:0px;"><span class="sync btn btn-default btn-sm" onclick="addSlaveSyncUser()" title="">添加同步账户</span></div>';
|
||||
|
||||
layerId = layer.open({
|
||||
type: 1,
|
||||
title: '同步账户列表',
|
||||
area: '500px',
|
||||
content:"<div class='bt-form pd20 c6'>\
|
||||
<div class='divtable mtb10'>\
|
||||
<div><table class='table table-hover get-slave-ssh-list'>\
|
||||
<thead><tr><th>IP</th><th>PORT</th><th>同步账户</th><th>同步密码</th><th>CMD</th><th>操作</th></tr></thead>\
|
||||
<tbody></tbody>\
|
||||
</table></div>\
|
||||
"+page +"\
|
||||
</div>\
|
||||
</div>",
|
||||
success:function(){
|
||||
getSlaveSyncUserPage(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getSlaveSSHList(page=1){
|
||||
|
||||
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
|
||||
@@ -1991,7 +2233,7 @@ function masterOrSlaveConf(version=''){
|
||||
for(i in rdata.data){
|
||||
|
||||
var v = rdata.data[i];
|
||||
var status = "异常";
|
||||
var status = "<a class='btlink db_error'>异常</>";
|
||||
if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){
|
||||
status = "正常";
|
||||
}
|
||||
@@ -2033,6 +2275,26 @@ function masterOrSlaveConf(version=''){
|
||||
// <span class="sync btn btn-default btn-sm" onclick="getMasterRepSlaveList()" title="">添加</span>\
|
||||
// </div>
|
||||
$(".table_slave_status_list").html(con);
|
||||
|
||||
$('.db_error').click(function(){
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '同步异常信息',
|
||||
area: '500px',
|
||||
content:"<form class='bt-form pd20 pb70'>\
|
||||
<div class='line'>"+v['Error']+"</div>\
|
||||
<div class='bt-form-submit-btn'>\
|
||||
<button type='button' class='btn btn-success btn-sm btn-title class-copy-db-err'>复制</button>\
|
||||
</div>\
|
||||
</form>",
|
||||
success:function(){
|
||||
copyText(v['Error']);
|
||||
$('.class-copy-db-err').click(function(){
|
||||
copyText(v['Error']);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2079,7 +2341,6 @@ function masterOrSlaveConf(version=''){
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getMasterStatus(){
|
||||
myPost('get_master_status', '', function(rdata){
|
||||
@@ -2110,7 +2371,7 @@ function masterOrSlaveConf(version=''){
|
||||
<p class="conf_p">\
|
||||
<span class="f14 c6 mr20">Slave[从]配置</span><span class="f14 c6 mr20"></span>\
|
||||
<button class="btn '+(!rdata.slave_status ? 'btn-danger' : 'btn-success')+' btn-xs btn-slave">'+(!rdata.slave_status ? '未启动' : '已启动') +'</button>\
|
||||
<button class="btn btn-success btn-xs" onclick="getSlaveSSHList()" >[主]SSH配置</button>\
|
||||
<button class="btn btn-success btn-xs" onclick="getSlaveCfg()" >同步配置</button>\
|
||||
<button class="btn btn-success btn-xs" onclick="initSlaveStatus()" >初始化</button>\
|
||||
</p>\
|
||||
<hr/>\
|
||||
@@ -2184,10 +2445,10 @@ function masterOrSlaveConf(version=''){
|
||||
getMasterDbList();
|
||||
}
|
||||
|
||||
if (rdata.slave_status){
|
||||
// if (rdata.slave_status){
|
||||
getAsyncMasterDbList();
|
||||
getAsyncDataList()
|
||||
}
|
||||
// }
|
||||
});
|
||||
}
|
||||
getMasterStatus();
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
lua_shared_dict waf_limit 30m;
|
||||
lua_shared_dict waf_drop_ip 10m;
|
||||
lua_shared_dict waf_drop_sum 10m;
|
||||
lua_package_path "{$WAF_PATH}/html/?.lua;{$WAF_PATH}/conf/?.lua;{$WAF_PATH}/lua/?.lua;{$ROOT_PATH}/openresty/lualib/?.lua;;";
|
||||
lua_package_cpath "{$WAF_PATH}/conf/?.so;{$ROOT_PATH}/openresty/lualib/?.so;;";
|
||||
|
||||
init_worker_by_lua_file {$WAF_PATH}/lua/init_worker.lua;
|
||||
access_by_lua_file {$WAF_PATH}/lua/init.lua;
|
||||
|
||||
# init_by_lua_file {$WAF_PATH}/lua/init.lua;
|
||||
lua_shared_dict waf_drop_sum 10m;
|
||||
+104
-37
@@ -6,6 +6,7 @@ import os
|
||||
import time
|
||||
import subprocess
|
||||
import json
|
||||
import re
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
@@ -228,17 +229,8 @@ def initTotalInfo():
|
||||
mw.writeFile(path_total, cjson)
|
||||
|
||||
|
||||
def status():
|
||||
path = getConf()
|
||||
if not os.path.exists(path):
|
||||
return 'stop'
|
||||
|
||||
conf = mw.readFile(path)
|
||||
if conf.find("#include luawaf.conf;") != -1:
|
||||
return 'stop'
|
||||
if conf.find("luawaf.conf;") == -1:
|
||||
return 'stop'
|
||||
return 'start'
|
||||
def dstWafConf():
|
||||
return mw.getServerDir() + "/web_conf/nginx/vhost/opwaf.conf"
|
||||
|
||||
|
||||
def contentReplace(content):
|
||||
@@ -328,7 +320,8 @@ def autoMakeConfig():
|
||||
|
||||
def restartWeb():
|
||||
autoMakeConfig()
|
||||
mw.restartWeb()
|
||||
mw.opWeb('stop')
|
||||
mw.opWeb('start')
|
||||
|
||||
|
||||
def initDreplace():
|
||||
@@ -370,11 +363,12 @@ def initDreplace():
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(init_worker, content)
|
||||
|
||||
waf_conf = mw.getServerDir() + "/openresty/nginx/conf/luawaf.conf"
|
||||
waf_tpl = getPluginDir() + "/conf/luawaf.conf"
|
||||
content = mw.readFile(waf_tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(waf_conf, content)
|
||||
waf_conf = dstWafConf()
|
||||
if not os.path.exists(waf_conf):
|
||||
waf_tpl = getPluginDir() + "/conf/luawaf.conf"
|
||||
content = mw.readFile(waf_tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(waf_conf, content)
|
||||
|
||||
initDomainInfo()
|
||||
initSiteInfo()
|
||||
@@ -388,37 +382,61 @@ def initDreplace():
|
||||
mw.execShell("chown -R www:www " + path)
|
||||
|
||||
|
||||
def status():
|
||||
path = getConf()
|
||||
if not os.path.exists(path):
|
||||
return 'stop'
|
||||
|
||||
waf_conf = dstWafConf()
|
||||
if not os.path.exists(waf_conf):
|
||||
return 'stop'
|
||||
return 'start'
|
||||
|
||||
|
||||
def start():
|
||||
initDreplace()
|
||||
|
||||
path = getConf()
|
||||
path = mw.getServerDir() + '/web_conf/nginx/lua/lua.conf'
|
||||
init_worker_lua = getServerDir() + '/waf/lua/init_worker.lua'
|
||||
init_lua = getServerDir() + '/waf/lua/init.lua'
|
||||
conf = mw.readFile(path)
|
||||
conf = conf.replace('#include luawaf.conf;', "include luawaf.conf;")
|
||||
conf = re.sub('init_worker_by_lua_file (.*);',
|
||||
"init_worker_by_lua_file " + init_worker_lua + ";", conf)
|
||||
conf = re.sub('access_by_lua_file (.*);',
|
||||
"access_by_lua_file " + init_lua + ";", conf)
|
||||
mw.writeFile(path, conf)
|
||||
|
||||
import tool_task
|
||||
tool_task.createBgTask()
|
||||
|
||||
mw.restartWeb()
|
||||
restartWeb()
|
||||
return 'ok'
|
||||
|
||||
|
||||
def stop():
|
||||
path = getConf()
|
||||
conf = mw.readFile(path)
|
||||
conf = conf.replace('include luawaf.conf;', "#include luawaf.conf;")
|
||||
|
||||
path = mw.getServerDir() + '/web_conf/nginx/lua/lua.conf'
|
||||
empty_lua = mw.getServerDir() + '/web_conf/nginx/lua/empty.lua'
|
||||
conf = mw.readFile(path)
|
||||
conf = re.sub('init_worker_by_lua_file (.*);',
|
||||
"init_worker_by_lua_file " + empty_lua + ";", conf)
|
||||
conf = re.sub('access_by_lua_file (.*);',
|
||||
"access_by_lua_file " + empty_lua + ";", conf)
|
||||
mw.writeFile(path, conf)
|
||||
|
||||
wafconf = dstWafConf()
|
||||
if os.path.exists(wafconf):
|
||||
os.remove(wafconf)
|
||||
|
||||
import tool_task
|
||||
tool_task.removeBgTask()
|
||||
|
||||
mw.restartWeb()
|
||||
restartWeb()
|
||||
return 'ok'
|
||||
|
||||
|
||||
def restart():
|
||||
mw.restartWeb()
|
||||
restartWeb()
|
||||
return 'ok'
|
||||
|
||||
|
||||
@@ -428,6 +446,18 @@ def reload():
|
||||
path = getServerDir()
|
||||
path_tpl = getPluginDir()
|
||||
|
||||
config = path + "/waf/lua/common.lua"
|
||||
config_tpl = path_tpl + "/waf/lua/common.lua"
|
||||
content = mw.readFile(config_tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(config, content)
|
||||
|
||||
config = path + "/waf/lua/init_worker.lua"
|
||||
config_tpl = path_tpl + "/waf/lua/init_worker.lua"
|
||||
content = mw.readFile(config_tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(config, content)
|
||||
|
||||
config = path + "/waf/lua/init.lua"
|
||||
config_tpl = path_tpl + "/waf/lua/init.lua"
|
||||
content = mw.readFile(config_tpl)
|
||||
@@ -438,6 +468,8 @@ def reload():
|
||||
mw.execShell('rm -rf ' + errlog)
|
||||
|
||||
start()
|
||||
|
||||
restartWeb()
|
||||
return 'ok'
|
||||
|
||||
|
||||
@@ -1022,24 +1054,41 @@ def removeSiteCdnHeader():
|
||||
|
||||
def outputData():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['s_Name'])
|
||||
data = checkArgs(args, ['sname'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
path = getRuleJsonPath(args['s_Name'])
|
||||
path = getRuleJsonPath(args['sname'])
|
||||
content = mw.readFile(path)
|
||||
return mw.returnJson(True, 'ok', content)
|
||||
|
||||
|
||||
def importData():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['s_Name', 'pdata'])
|
||||
data = checkArgs(args, ['sname', 'pdata'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
path = getRuleJsonPath(args['s_Name'])
|
||||
mw.writeFile(path, args['pdata'])
|
||||
restartWeb()
|
||||
path = getRuleJsonPath(args['sname'])
|
||||
|
||||
source_data = mw.readFile(path)
|
||||
source_data = json.loads(source_data)
|
||||
|
||||
save_data = []
|
||||
save_data.append(source_data[0])
|
||||
pdata = args['pdata'].strip()
|
||||
try:
|
||||
pdata = json.loads(pdata)
|
||||
mw.writeFile(path, json.dumps(pdata))
|
||||
except Exception as e:
|
||||
pdata = pdata.split("\\n")
|
||||
for x in pdata:
|
||||
pval = x.strip()
|
||||
if pval != "":
|
||||
vv = json.loads(pval)
|
||||
save_data.append(vv[0])
|
||||
mw.writeFile(path, json.dumps(save_data))
|
||||
# restartWeb()
|
||||
return mw.returnJson(True, '设置成功!')
|
||||
|
||||
|
||||
@@ -1167,6 +1216,28 @@ def getWafConf():
|
||||
return mw.readFile(conf)
|
||||
|
||||
|
||||
def cleanDropIp():
|
||||
url = "http://127.0.0.1/clean_waf_drop_ip"
|
||||
data = mw.httpGet(url)
|
||||
return mw.returnJson(True, 'ok!', data)
|
||||
|
||||
|
||||
def testRun():
|
||||
# args = getArgs()
|
||||
# data = checkArgs(args, ['siteName'])
|
||||
# if not data[0]:
|
||||
# return data[1]
|
||||
|
||||
conf_json = getServerDir() + "/waf/default.json"
|
||||
data = mw.readFile(conf_json)
|
||||
pdata = json.loads(data)
|
||||
default_site = pdata['default']
|
||||
|
||||
url = "http://" + default_site + '/?t=../etc/passwd'
|
||||
returnData = mw.httpGet(url, 10)
|
||||
return mw.returnJson(True, '测试运行成功!', returnData)
|
||||
|
||||
|
||||
def installPreInspection():
|
||||
check_op = mw.getServerDir() + "/openresty"
|
||||
if not os.path.exists(check_op):
|
||||
@@ -1174,12 +1245,6 @@ def installPreInspection():
|
||||
return 'ok'
|
||||
|
||||
|
||||
def cleanDropIp():
|
||||
url = "http://127.0.0.1/clean_waf_drop_ip"
|
||||
data = mw.httpGet(url)
|
||||
return mw.returnJson(True, 'ok!', data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'status':
|
||||
@@ -1268,5 +1333,7 @@ if __name__ == "__main__":
|
||||
print(getWafSite())
|
||||
elif func == 'clean_drop_ip':
|
||||
print(cleanDropIp())
|
||||
elif func == 'test_run':
|
||||
print(testRun())
|
||||
else:
|
||||
print('error')
|
||||
|
||||
@@ -12,5 +12,5 @@
|
||||
"home":"https://github.com/loveshell/ngx_lua_waf",
|
||||
"date":"2019-04-21",
|
||||
"pid": "1",
|
||||
"versions": ["0.2.3"]
|
||||
"versions": ["0.2.4"]
|
||||
}
|
||||
+39
-21
@@ -568,14 +568,14 @@ function outputLayer(rdata, name, type) {
|
||||
'<div class="ml0" style="position:relative;" id="focus_tips">' +
|
||||
'<textarea class="bt-input-text mr20 config" name="config" style="width: 300px; height: 250px; line-height: 22px; display: none;" id="lead_data">' + (rdata != '' ? JSON.stringify(rdata) : '') + '</textarea>' +
|
||||
'<div class="placeholder c9" style="top: 15px; left: 15px; display:' + (rdata == "" ? "block;" : "none;") + '">导入格式如下:' +
|
||||
(name == 'ip_white' || name == 'ip_black' ? "[[[127, 0, 0, 1],[127, 0, 0, 255]]]" : "[\"^/test\",\"^/web\"]") +
|
||||
(name == 'ip_white' || name == 'ip_black' ? "[[[127, 0, 0, 1],[127, 0, 0, 255]],[[192, 0, 0, 1],[192, 0, 0, 255]]]" : "[\"^/test\",\"^/web\"]") +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="line "><div class="ml0">' +
|
||||
(type ? '<button name="btn_save_to" class="btn btn-success btn-sm mr5 btn_save_to" >导出配置</button>' : '<button name="btn_save" class="btn btn-success btn-sm mr5 btn_save">保存</button>') +
|
||||
'</div></div>' +
|
||||
'</div>'
|
||||
'</div>',
|
||||
});
|
||||
var lead_error = CodeMirror.fromTextArea(document.getElementById("lead_data"), {
|
||||
mode: 'html',
|
||||
@@ -585,7 +585,10 @@ function outputLayer(rdata, name, type) {
|
||||
});
|
||||
setTimeout(function () {
|
||||
$('.btn_save').on('click', function () {
|
||||
importData(name, lead_error.getValue());
|
||||
importData(name, lead_error.getValue(), function(){
|
||||
layer.close(window.Load_layer);
|
||||
ipWhiteLoadList();
|
||||
});
|
||||
})
|
||||
$('.btn_save_to').on('click', function () {
|
||||
funDownload(lead_error.getValue(), name + '.json');
|
||||
@@ -601,7 +604,7 @@ function outputLayer(rdata, name, type) {
|
||||
function outputData(name, callback) {
|
||||
var loadT = layer.msg('正在导出数据..', { icon: 16, time: 0 });
|
||||
|
||||
owPost('output_data', { s_Name: name } , function(data){
|
||||
owPost('output_data', { sname: name } , function(data){
|
||||
var tmp = $.parseJSON(data.data);
|
||||
var rdata = $.parseJSON(tmp.data);
|
||||
if (callback) callback(rdata,res);
|
||||
@@ -611,7 +614,7 @@ function outputData(name, callback) {
|
||||
|
||||
//导入数据
|
||||
function importData(name, pdata, callback) {
|
||||
owPost('import_data', { s_Name: name, pdata: pdata } , function(data){
|
||||
owPost('import_data', { sname: name, pdata: pdata } , function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if (callback) callback();
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
@@ -622,6 +625,22 @@ function fileInput(name) {
|
||||
outputLayer('', name, false);
|
||||
}
|
||||
|
||||
|
||||
function ipWhiteLoadList(){
|
||||
getRuleByName('ip_white', function(data){
|
||||
var tmp = $.parseJSON(data.data);
|
||||
var rdata = $.parseJSON(tmp.data);
|
||||
var tbody = ''
|
||||
for (var i = 0; i < rdata.length; i++) {
|
||||
tbody += '<tr>\
|
||||
<td>'+ rdata[i][0].join('.') + '</td>\
|
||||
<td>'+ rdata[i][1].join('.') + '</td>\
|
||||
<td class="text-right"><a class="btlink" onclick="removeIpWhite('+ i + ')">删除</a></td>\
|
||||
</tr>'
|
||||
}
|
||||
$("#ip_white_con").html(tbody);
|
||||
});
|
||||
}
|
||||
//IP白名单
|
||||
function ipWhite(type) {
|
||||
if (type == undefined) {
|
||||
@@ -675,20 +694,7 @@ function ipWhite(type) {
|
||||
});
|
||||
tableFixed("ipWhite");
|
||||
}
|
||||
|
||||
getRuleByName('ip_white', function(data){
|
||||
var tmp = $.parseJSON(data.data);
|
||||
var rdata = $.parseJSON(tmp.data);
|
||||
var tbody = ''
|
||||
for (var i = 0; i < rdata.length; i++) {
|
||||
tbody += '<tr>\
|
||||
<td>'+ rdata[i][0].join('.') + '</td>\
|
||||
<td>'+ rdata[i][1].join('.') + '</td>\
|
||||
<td class="text-right"><a class="btlink" onclick="removeIpWhite('+ i + ')">删除</a></td>\
|
||||
</tr>'
|
||||
}
|
||||
$("#ip_white_con").html(tbody);
|
||||
});
|
||||
ipWhiteLoadList();
|
||||
}
|
||||
|
||||
//IP白名单
|
||||
@@ -1712,9 +1718,12 @@ function wafLogs(){
|
||||
<button data-name="l7" type="button" class="btn btn-default">近7天</button>\
|
||||
<button data-name="l30" type="button" class="btn btn-default">近30天</button>\
|
||||
</div>\
|
||||
<span class="last-span"><input data-name="" type="text" id="time_choose" lay-key="1000001_'+randstr+'" class="form-control btn-group-sm" autocomplete="off" placeholder="自定义时间" style="display: inline-block;font-size: 12px;padding: 0 10px;height:30px;width: 200px;"></span>\
|
||||
<span class="last-span"><input data-name="" type="text" id="time_choose" lay-key="1000001_'+randstr+'" class="form-control btn-group-sm" autocomplete="off" placeholder="自定义时间" style="display: inline-block;font-size: 12px;padding: 0 10px;height:30px;width: 155px;"></span>\
|
||||
</div>\
|
||||
<div style="float:right;">\
|
||||
<button id="UncoverAll" class="btn btn-success btn-sm" style="padding-left: 5px;padding-right: 5px;">解封所有</button>\
|
||||
<button id="testRun" class="btn btn-default btn-sm" style="padding-left: 5px;padding-right: 5px;">测试</button>\
|
||||
</div>\
|
||||
<div style="float:right;"><button id="UncoverAll" class="btn btn-success btn-sm">解封所有</button></div>\
|
||||
</div>\
|
||||
<div class="divtable mtb10" id="ws_table"></div>\
|
||||
</div>';
|
||||
@@ -1733,6 +1742,15 @@ function wafLogs(){
|
||||
});
|
||||
});
|
||||
|
||||
//测试demo
|
||||
$("#testRun").click(function(){
|
||||
owPost('test_run',{},function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg, function(){
|
||||
wafLogRequest(1);
|
||||
},{icon:1,shade: [0.3, '#000']},2000);
|
||||
});
|
||||
});
|
||||
|
||||
//日期范围
|
||||
laydate.render({
|
||||
|
||||
@@ -309,12 +309,12 @@ def test_Upload():
|
||||
|
||||
def test_start():
|
||||
# test_OK()
|
||||
# test_Dir()
|
||||
test_Dir()
|
||||
# test_UA()
|
||||
test_Header()
|
||||
# test_Header()
|
||||
# test_UA_for(1000)
|
||||
test_POST()
|
||||
test_scan()
|
||||
# test_POST()
|
||||
# test_scan()
|
||||
# test_CC()
|
||||
# test_url_ext()
|
||||
# test_cdn()
|
||||
@@ -322,6 +322,7 @@ def test_start():
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.system('cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/op_waf && sh install.sh uninstall 0.2.2 && sh install.sh install 0.2.2')
|
||||
os.system('cd /Users/midoks/Desktop/mwdev/server/mdserver-web/ && python3 plugins/openresty/index.py stop && python3 plugins/openresty/index.py start')
|
||||
# os.system('cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/op_waf && sh install.sh uninstall 0.2.2 && sh install.sh install 0.2.2')
|
||||
os.system('cd /Users/midoks/Desktop/mwdev/server/mdserver-web/ && python3 /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/op_waf/index.py reload')
|
||||
# os.system('cd /Users/midoks/Desktop/mwdev/server/mdserver-web/ && python3 plugins/openresty/index.py stop && python3 plugins/openresty/index.py start')
|
||||
test_start()
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
local waf_root = "{$WAF_ROOT}"
|
||||
local waf_cpath = waf_root.."/waf/lua/?.lua;"..waf_root.."/waf/conf/?.lua;"..waf_root.."/waf/html/?.lua;"
|
||||
local waf_sopath = waf_root.."/waf/conf/?.so;"
|
||||
|
||||
if not package.path:find(waf_cpath) then
|
||||
package.path = waf_cpath .. package.path
|
||||
end
|
||||
|
||||
if not package.cpath:find(waf_sopath) then
|
||||
package.cpath = waf_sopath .. package.cpath
|
||||
end
|
||||
|
||||
local setmetatable = setmetatable
|
||||
local _M = { _VERSION = '0.02' }
|
||||
@@ -9,7 +20,6 @@ local sqlite3 = require "lsqlite3"
|
||||
local ngx_match = ngx.re.find
|
||||
local debug_mode = false
|
||||
|
||||
local waf_root = "{$WAF_ROOT}"
|
||||
local cpath = waf_root.."/waf/"
|
||||
local log_dir = waf_root.."/logs/"
|
||||
local rpath = cpath.."/rule/"
|
||||
@@ -62,12 +72,12 @@ end
|
||||
|
||||
-- 后台任务
|
||||
function _M.cron(self)
|
||||
local timer_every_get_data = function (premature)
|
||||
self.clean_log()
|
||||
local timer_every_get_data = function(premature)
|
||||
self:clean_log()
|
||||
end
|
||||
ngx.timer.every(10, timer_every_get_data)
|
||||
|
||||
local timer_every_import_data = function (premature)
|
||||
local timer_every_import_data = function(premature)
|
||||
|
||||
local llen, _ = ngx.shared.waf_limit:llen('waf_limit_logs')
|
||||
if llen == 0 then
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
local waf_root = "{$WAF_ROOT}"
|
||||
local waf_cpath = waf_root.."/waf/lua/?.lua;"..waf_root.."/waf/conf/?.lua;"..waf_root.."/waf/html/?.lua;"
|
||||
local waf_sopath = waf_root.."/waf/conf/?.so;"
|
||||
|
||||
if not package.path:find(waf_cpath) then
|
||||
package.path = waf_cpath .. package.path
|
||||
end
|
||||
|
||||
if not package.cpath:find(waf_sopath) then
|
||||
package.cpath = waf_sopath .. package.cpath
|
||||
end
|
||||
|
||||
local json = require "cjson"
|
||||
local ngx_match = ngx.re.find
|
||||
@@ -11,11 +22,11 @@ local config = require "waf_config"
|
||||
local site_config = require "waf_site"
|
||||
local config_domains = require "waf_domains"
|
||||
|
||||
-- C:D("config:"..C:to_json(config))
|
||||
|
||||
C:setConfData(config, site_config)
|
||||
C:setDebug(true)
|
||||
|
||||
-- C:D("config:"..C:to_json(config))
|
||||
|
||||
local get_html = require "html_get"
|
||||
local post_html = require "html_post"
|
||||
@@ -246,7 +257,8 @@ local function waf_drop_ip()
|
||||
end
|
||||
|
||||
local function waf_cc()
|
||||
if not config['cc']['open'] or not C:is_site_config('cc') then return false end
|
||||
if not config['cc']['open'] then return false end
|
||||
if not C:is_site_config('cc') then return false end
|
||||
|
||||
local ip = params['ip']
|
||||
|
||||
@@ -521,7 +533,6 @@ end
|
||||
|
||||
function waf()
|
||||
min_route()
|
||||
|
||||
-- white ip
|
||||
if waf_ip_white() then return true end
|
||||
|
||||
@@ -550,9 +561,8 @@ function waf()
|
||||
|
||||
-- 扫描软件禁止
|
||||
if waf_scan_black() then return true end
|
||||
|
||||
if waf_post() then return true end
|
||||
|
||||
|
||||
if site_config[server_name] and site_config[server_name]['open'] then
|
||||
if X_Forwarded() then return true end
|
||||
if post_X_Forwarded() then return true end
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
local waf_root = "{$WAF_ROOT}"
|
||||
local waf_cpath = waf_root.."/waf/lua/?.lua;"..waf_root.."/waf/conf/?.lua;"..waf_root.."/waf/html/?.lua;"
|
||||
local waf_sopath = waf_root.."/waf/conf/?.so;"
|
||||
|
||||
if not package.path:find(waf_cpath) then
|
||||
package.path = waf_cpath .. package.path
|
||||
end
|
||||
|
||||
if not package.cpath:find(waf_sopath) then
|
||||
package.cpath = waf_sopath .. package.cpath
|
||||
end
|
||||
|
||||
local json = require "cjson"
|
||||
|
||||
local waf_root = "{$WAF_ROOT}"
|
||||
local cpath = waf_root.."/waf/"
|
||||
|
||||
local __C = require "common"
|
||||
local C = __C:getInstance()
|
||||
|
||||
|
||||
local function timer_stats_total_log(premature)
|
||||
C:timer_stats_total()
|
||||
end
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
lua_package_path "{$SERVER_PATH}/web_conf/nginx/lua/?.lua;{$SERVER_PATH}/openresty/lualib/?.lua;;";
|
||||
lua_package_cpath "{$SERVER_PATH}/web_conf/nginx/lua/?.so;{$SERVER_PATH}/openresty/lualib/?.so;;";
|
||||
|
||||
lua_code_cache on;
|
||||
|
||||
#waf
|
||||
init_worker_by_lua_file {$SERVER_PATH}/web_conf/nginx/lua/empty.lua;
|
||||
|
||||
#waf && webstats need;
|
||||
access_by_lua_file {$SERVER_PATH}/web_conf/nginx/lua/empty.lua;
|
||||
@@ -16,7 +16,7 @@ http
|
||||
{
|
||||
include mime.types;
|
||||
|
||||
#include luawaf.conf;
|
||||
include {$SERVER_PATH}/web_conf/nginx/lua/lua.conf;
|
||||
#include proxy.conf;
|
||||
|
||||
default_type application/octet-stream;
|
||||
@@ -28,6 +28,7 @@ http
|
||||
server_names_hash_bucket_size 512;
|
||||
client_header_buffer_size 32k;
|
||||
large_client_header_buffers 4 32k;
|
||||
client_body_buffer_size 50m;
|
||||
client_max_body_size 50m;
|
||||
|
||||
sendfile on;
|
||||
|
||||
@@ -131,10 +131,34 @@ def confReplace():
|
||||
content = content.replace('{$OS_USER}', user)
|
||||
content = content.replace('{$OS_USER_GROUP}', user_group)
|
||||
|
||||
# ng_conf_md5 = ''
|
||||
# ng_conf_md5_file = getServerDir() + '/nginx_conf.md5'
|
||||
# if not os.path.exists(ng_conf_md5_file):
|
||||
# ng_conf_md5 = mw.md5(content)
|
||||
# mw.writeFile(ng_conf_md5_file, ng_conf_md5)
|
||||
# else:
|
||||
# ng_conf_md5 = mw.writeFile(ng_conf_md5_file).strip()
|
||||
|
||||
# 主配置文件
|
||||
nconf = getServerDir() + '/nginx/conf/nginx.conf'
|
||||
mw.writeFile(nconf, content)
|
||||
|
||||
# lua配置
|
||||
lua_conf_dir = mw.getServerDir() + '/web_conf/nginx/lua'
|
||||
if not os.path.exists(lua_conf_dir):
|
||||
mw.execShell('mkdir -p ' + lua_conf_dir)
|
||||
|
||||
lua_conf = lua_conf_dir + '/lua.conf'
|
||||
if not os.path.exists(lua_conf):
|
||||
lua_conf_tpl = getPluginDir() + '/conf/lua.conf'
|
||||
lua_content = mw.readFile(lua_conf_tpl)
|
||||
lua_content = lua_content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(lua_conf, lua_content)
|
||||
|
||||
empty_lua = lua_conf_dir + '/empty.lua'
|
||||
if not os.path.exists(empty_lua):
|
||||
mw.writeFile(empty_lua, '')
|
||||
|
||||
# 静态配置
|
||||
php_conf = mw.getServerDir() + '/web_conf/php/conf'
|
||||
if not os.path.exists(php_conf):
|
||||
|
||||
@@ -49,15 +49,20 @@ if [ "$OSNAME" == "ubuntu" ];then
|
||||
fi
|
||||
# apt install $(grep-aptavail -S PHP-defaults -s Package -n)
|
||||
|
||||
|
||||
if [ ! -f /etc/apt/sources.list.d/php.list ] && [ "$OSNAME" == "debian" ];then
|
||||
# install php source
|
||||
|
||||
apt update -y
|
||||
apt -y install apt-transport-https lsb-release ca-certificates curl
|
||||
curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg
|
||||
sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'
|
||||
apt update -y
|
||||
|
||||
# install php source
|
||||
apt update -y
|
||||
apt -y install apt-transport-https lsb-release ca-certificates curl
|
||||
cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
|
||||
if [ ! -z "$cn" ];then
|
||||
curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://mirror.sjtu.edu.cn/sury/php/apt.gpg
|
||||
sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://mirror.sjtu.edu.cn/sury/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'
|
||||
else
|
||||
curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg
|
||||
sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'
|
||||
fi
|
||||
apt update -y
|
||||
fi
|
||||
|
||||
|
||||
@@ -85,14 +90,16 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php-apt/${type} ];then
|
||||
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install xml
|
||||
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install mcrypt
|
||||
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install mysqlnd
|
||||
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install mysql
|
||||
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install gettext
|
||||
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install redis
|
||||
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install memcached
|
||||
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install mbstring
|
||||
echo "install PHP-APT[${type}] extend end"
|
||||
|
||||
if [ ! -f /usr/local/bin/composer ];then
|
||||
cd /tmp
|
||||
curl -sS https://getcomposer.org/installer | php
|
||||
curl -sS https://getcomposer.org/installer | /usr/bin/php${type:0:1}.${type:1:2}
|
||||
mv composer.phar /usr/local/bin/composer
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -57,5 +57,4 @@ fi
|
||||
echo "apt install -y php${version}-${extName}"
|
||||
echo "apt remove -y php${version}-${extName}"
|
||||
|
||||
|
||||
|
||||
systemctl restart php${version}-fpm
|
||||
|
||||
@@ -35,6 +35,24 @@
|
||||
"shell": "mysqlnd.sh",
|
||||
"check": "mysqlnd"
|
||||
},
|
||||
{
|
||||
"name": "mysql",
|
||||
"versions": [
|
||||
"56",
|
||||
"70",
|
||||
"71",
|
||||
"72",
|
||||
"73",
|
||||
"74",
|
||||
"80",
|
||||
"81",
|
||||
"82"
|
||||
],
|
||||
"type": "数据库",
|
||||
"msg": "用于使用MySQL数据库的模块!",
|
||||
"shell": "mysql.sh",
|
||||
"check": "mysql"
|
||||
},
|
||||
{
|
||||
"name": "sqlite3",
|
||||
"versions": [
|
||||
|
||||
@@ -68,6 +68,8 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php-yum/${type} ];then
|
||||
|
||||
# 安装通用扩展
|
||||
echo "install PHP-YUM[${type}] extend start"
|
||||
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install mysqlnd
|
||||
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install mysql
|
||||
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install gd
|
||||
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install iconv
|
||||
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install exif
|
||||
@@ -77,11 +79,12 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php-yum/${type} ];then
|
||||
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install gettext
|
||||
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install redis
|
||||
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install memcached
|
||||
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install mbstring
|
||||
echo "install PHP-YUM[${type}] extend end"
|
||||
|
||||
if [ ! -f /usr/local/bin/composer ];then
|
||||
cd /tmp
|
||||
curl -sS https://getcomposer.org/installer | php
|
||||
curl -sS https://getcomposer.org/installer | /opt/remi/php${type}/root/usr/bin/php
|
||||
mv composer.phar /usr/local/bin/composer
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -28,7 +28,7 @@ fi
|
||||
|
||||
Uninstall_php()
|
||||
{
|
||||
# $serverPath/php-ya/init.d/php${PHP_VER} stop
|
||||
yum remove -y php74 php74-php-fpm php74-*
|
||||
rm -rf $serverPath/php-yum/${PHP_VER}
|
||||
echo "卸载php-${version}..." > $install_tmp
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ fi
|
||||
|
||||
Uninstall_php()
|
||||
{
|
||||
# $serverPath/php-ya/init.d/php${PHP_VER} stop
|
||||
yum remove -y php81 php81-php-fpm php81-*
|
||||
rm -rf $serverPath/php-yum/${PHP_VER}
|
||||
echo "卸载php-${version}..." > $install_tmp
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ fi
|
||||
|
||||
Uninstall_php()
|
||||
{
|
||||
# $serverPath/php-yum/init.d/php${PHP_VER} stop
|
||||
yum remove -y php82 php82-php-fpm php82-*
|
||||
rm -rf $serverPath/php-yum/${PHP_VER}
|
||||
echo "卸载php-${version}..." > $install_tmp
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ extName=$3
|
||||
FILE=${curPath}/${version}/${extName}.sh
|
||||
|
||||
# yum install -y php81-php-yar
|
||||
# yum install -y php74-php-pecl-mysql
|
||||
|
||||
|
||||
if [ "$action" == 'install' ];then
|
||||
@@ -30,6 +31,10 @@ if [ "$action" == 'install' ];then
|
||||
else
|
||||
yum install -y php${version}-php*-${extName}
|
||||
fi
|
||||
|
||||
# if [ "${extName}" == "mysql" ];then
|
||||
# yum install -y php74-php-pecl-mysql
|
||||
# fi
|
||||
fi
|
||||
|
||||
|
||||
@@ -46,5 +51,7 @@ fi
|
||||
echo "yum install -y php${version}-php-${extName}"
|
||||
echo "yum remove -y php${version}-php-${extName}"
|
||||
|
||||
bash ${rootPath}/plugins/php-yum/versions/lib.sh $version restart
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ serverPath=$(dirname "$rootPath")
|
||||
version=$1
|
||||
action=$2
|
||||
|
||||
if [ -f /lib/systemd/system/php${version}.service ];then
|
||||
if [ -f /lib/systemd/system/php${version}-php-fpm.service ];then
|
||||
systemctl ${action} php${version}
|
||||
elif [[ -f /usr/lib/systemd/system/php${version}.service ]]; then
|
||||
elif [[ -f /usr/lib/systemd/system/php${version}-php-fpm.service ]]; then
|
||||
systemctl ${action} php${version}
|
||||
else
|
||||
$serverPath/php/init.d/php${version} ${action}
|
||||
|
||||
@@ -25,6 +25,19 @@
|
||||
"shell": "mysqlnd.sh",
|
||||
"check": "mysqlnd"
|
||||
},
|
||||
{
|
||||
"name": "mysql",
|
||||
"versions": [
|
||||
"74",
|
||||
"80",
|
||||
"81",
|
||||
"82"
|
||||
],
|
||||
"type": "数据库",
|
||||
"msg": "用于使用MySQL数据库的模块!",
|
||||
"shell": "mysql.sh",
|
||||
"check": "mysql"
|
||||
},
|
||||
{
|
||||
"name": "oci8",
|
||||
"versions": [
|
||||
|
||||
@@ -61,6 +61,7 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php/${type} ];then
|
||||
# cd /www/server/mdserver-web/plugins/php/versions/common && bash iconv.sh install 53
|
||||
# cd /www/server/mdserver-web/plugins/php/versions/common && bash gd.sh install 56
|
||||
# cd /www/server/mdserver-web/plugins/php/versions/common && bash openssl.sh install 56
|
||||
# cd /www/server/mdserver-web/plugins/php/versions/common && bash sodium.sh install 81
|
||||
cd ${rootPath}/plugins/php/versions/common && bash gd.sh install ${type}
|
||||
cd ${rootPath}/plugins/php/versions/common && bash iconv.sh install ${type}
|
||||
cd ${rootPath}/plugins/php/versions/common && bash exif.sh install ${type}
|
||||
@@ -81,7 +82,7 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php/${type} ];then
|
||||
|
||||
if [ ! -f /usr/local/bin/composer ];then
|
||||
cd /tmp
|
||||
curl -sS https://getcomposer.org/installer | php
|
||||
curl -sS https://getcomposer.org/installer | /www/server/php/${type}/bin/php
|
||||
mv composer.phar /usr/local/bin/composer
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -16,7 +16,7 @@ SOURCE_ROOT=$rootPath/source/lib
|
||||
|
||||
VERSION=1.0.18
|
||||
#----------------------------- libsodium start -------------------------#
|
||||
if [ ! -d ${SERVER_ROOT}/libsodium-stable ];then
|
||||
if [ ! -f /usr/local/lib/libsodium.so ];then
|
||||
cd ${SOURCE_ROOT}
|
||||
if [ ! -f ${SOURCE_ROOT}/libsodium-${VERSION}-stable.tar.gz ];then
|
||||
# wget --no-check-certificate -O libsodium-1.0.18-stable.tar.gz https://download.libsodium.org/libsodium/releases/libsodium-1.0.18-stable.tar.gz -T 20
|
||||
@@ -24,6 +24,6 @@ if [ ! -d ${SERVER_ROOT}/libsodium-stable ];then
|
||||
fi
|
||||
tar -zxvf libsodium-${VERSION}-stable.tar.gz
|
||||
cd libsodium-stable
|
||||
./configure --prefix=${SERVER_ROOT}/libsodium && make && make check && sudo make install
|
||||
./configure && make && make check && sudo make install
|
||||
fi
|
||||
#----------------------------- libsodium end -------------------------#
|
||||
@@ -106,7 +106,8 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then
|
||||
$OPTIONS \
|
||||
--enable-fastcgi \
|
||||
--enable-fpm
|
||||
make ZEND_EXTRA_LIBS='-liconv' && make install && make clean
|
||||
# ZEND_EXTRA_LIBS='-liconv'
|
||||
make && make install && make clean
|
||||
fi
|
||||
|
||||
if [ "$?" != "0" ];then
|
||||
|
||||
@@ -47,8 +47,9 @@ Install_lib()
|
||||
cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
|
||||
cd ${LIBNAME}-${LIBV}
|
||||
$serverPath/php/$version/bin/phpize
|
||||
./configure --with-php-config=$serverPath/php/$version/bin/php-config \
|
||||
--with-icu-dir=${serverPath}/lib/icu
|
||||
|
||||
# --with-icu-dir=${serverPath}/lib/icu
|
||||
./configure --with-php-config=$serverPath/php/$version/bin/php-config
|
||||
make clean && make && make install && make clean
|
||||
|
||||
fi
|
||||
|
||||
Executable
+115
@@ -0,0 +1,115 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
|
||||
appPath=$(dirname "$curPath")
|
||||
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
sourcePath=${serverPath}/source/php
|
||||
|
||||
actionType=$1
|
||||
version=$2
|
||||
|
||||
LIBNAME=gmp
|
||||
LIBV=0
|
||||
|
||||
if [ "$version" == "53" ];then
|
||||
echo "i wont support it"
|
||||
exit
|
||||
fi
|
||||
|
||||
|
||||
LIB_PATH_NAME=lib/php
|
||||
if [ -d $serverPath/php/${version}/lib64 ];then
|
||||
LIB_PATH_NAME=lib64
|
||||
fi
|
||||
|
||||
NON_ZTS_FILENAME=`ls $serverPath/php/${version}/${LIB_PATH_NAME}/extensions | grep no-debug-non-zts`
|
||||
extFile=$serverPath/php/${version}/${LIB_PATH_NAME}/extensions/${NON_ZTS_FILENAME}/${LIBNAME}.so
|
||||
|
||||
sysName=`uname`
|
||||
if [ "$sysName" == "Darwin" ];then
|
||||
BAK='_bak'
|
||||
else
|
||||
BAK=''
|
||||
fi
|
||||
|
||||
Install_lib()
|
||||
{
|
||||
|
||||
isInstall=`cat $serverPath/php/$version/etc/php.ini|grep "${LIBNAME}.so"`
|
||||
if [ "${isInstall}" != "" ];then
|
||||
echo "php-$version 已安装${LIBNAME},请选择其它版本!"
|
||||
return
|
||||
fi
|
||||
|
||||
if [ ! -f "$extFile" ];then
|
||||
|
||||
if [ ! -d $sourcePath/php${version}/ext ];then
|
||||
cd ${rootPath}/plugins/php && /bin/bash install.sh install ${version}
|
||||
fi
|
||||
|
||||
cd $sourcePath/php${version}/ext/${LIBNAME}
|
||||
|
||||
$serverPath/php/$version/bin/phpize
|
||||
./configure --with-php-config=$serverPath/php/$version/bin/php-config
|
||||
|
||||
# FIND_C99=`cat Makefile|grep c99`
|
||||
# if [ "$FIND_C99" == "" ];then
|
||||
# sed -i $BAK 's/CFLAGS \=/CFLAGS \= -std=c99/g' Makefile
|
||||
# fi
|
||||
|
||||
make clean && make && make install && make clean
|
||||
|
||||
fi
|
||||
|
||||
if [ ! -f "$extFile" ];then
|
||||
echo "ERROR!"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "" >> $serverPath/php/$version/etc/php.ini
|
||||
echo "[${LIBNAME}]" >> $serverPath/php/$version/etc/php.ini
|
||||
echo "extension=${LIBNAME}.so" >> $serverPath/php/$version/etc/php.ini
|
||||
|
||||
bash ${rootPath}/plugins/php/versions/lib.sh $version restart
|
||||
echo '==========================================================='
|
||||
echo 'successful!'
|
||||
}
|
||||
|
||||
|
||||
Uninstall_lib()
|
||||
{
|
||||
if [ ! -f "$serverPath/php/$version/bin/php-config" ];then
|
||||
echo "php-$version 未安装,请选择其它版本!"
|
||||
return
|
||||
fi
|
||||
|
||||
if [ ! -f "$extFile" ];then
|
||||
echo "php-$version 未安装${LIBNAME},请选择其它版本!"
|
||||
return
|
||||
fi
|
||||
|
||||
echo $serverPath/php/$version/etc/php.ini
|
||||
sed -i $BAK "/${LIBNAME}.so/d" $serverPath/php/$version/etc/php.ini
|
||||
sed -i $BAK "/${LIBNAME}/d" $serverPath/php/$version/etc/php.ini
|
||||
|
||||
rm -f $extFile
|
||||
bash ${rootPath}/plugins/php/versions/lib.sh $version restart
|
||||
echo '==============================================='
|
||||
echo 'successful!'
|
||||
}
|
||||
|
||||
|
||||
|
||||
if [ "$actionType" == 'install' ];then
|
||||
Install_lib
|
||||
elif [ "$actionType" == 'uninstall' ];then
|
||||
Uninstall_lib
|
||||
fi
|
||||
@@ -80,7 +80,12 @@ Install_lib()
|
||||
fi
|
||||
cd $php_lib/sg11
|
||||
# echo "mv $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.lin $extFile"
|
||||
cp -rf $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.${SUFFIX_NAME} $extFile
|
||||
if [ -f $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.${SUFFIX_NAME} ];then
|
||||
cp -rf $php_lib/sg11/${DEFAULT_OSNAME}/ixed.${SG_VER}.${SUFFIX_NAME} $extFile
|
||||
else
|
||||
echo 'Not supported temporarily'
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ "$OSNAME" == 'macos' ];then
|
||||
xattr -c * $extFile
|
||||
|
||||
@@ -53,7 +53,7 @@ Install_lib()
|
||||
wget -O $php_lib/${LIBNAME}-${LIBV}.tgz http://pecl.php.net/get/lib${LIBNAME}-${LIBV}.tgz
|
||||
cd $php_lib && tar xvf ${LIBNAME}-${LIBV}.tgz
|
||||
fi
|
||||
cd $php_lib/${LIBNAME}-${LIBV}
|
||||
cd $php_lib/lib${LIBNAME}-${LIBV}
|
||||
|
||||
$serverPath/php/$version/bin/phpize
|
||||
./configure --with-php-config=$serverPath/php/$version/bin/php-config --with-sodium=$serverPath/lib/libsodium
|
||||
|
||||
@@ -79,6 +79,23 @@
|
||||
"shell": "sodium.sh",
|
||||
"check": "sodium.so"
|
||||
},
|
||||
{
|
||||
"name": "gmp",
|
||||
"versions": [
|
||||
"70",
|
||||
"71",
|
||||
"72",
|
||||
"73",
|
||||
"74",
|
||||
"80",
|
||||
"81",
|
||||
"82"
|
||||
],
|
||||
"type": "运算库",
|
||||
"msg": "一个开源的数学运算库",
|
||||
"shell": "gmp.sh",
|
||||
"check": "gmp.so"
|
||||
},
|
||||
{
|
||||
"name": "opcache",
|
||||
"versions": [
|
||||
|
||||
@@ -85,7 +85,7 @@ def getPhpVer(expect=55):
|
||||
v = site_api.site_api().getPhpVersion()
|
||||
is_find = False
|
||||
for i in range(len(v)):
|
||||
t = int(v[i]['version'])
|
||||
t = str(v[i]['version'])
|
||||
if (t == expect):
|
||||
is_find = True
|
||||
return str(t)
|
||||
|
||||
@@ -17,6 +17,7 @@ AntiWarez yes
|
||||
Bind 0.0.0.0,21
|
||||
Umask 133:022
|
||||
MinUID 100
|
||||
PassivePortRange 39000 40000
|
||||
AllowUserFXP no
|
||||
AllowAnonymousFXP no
|
||||
ProhibitDotFilesWrite no
|
||||
|
||||
@@ -81,6 +81,22 @@ def contentReplace(content):
|
||||
return content
|
||||
|
||||
|
||||
def ftp_release_port(port):
|
||||
from collections import namedtuple
|
||||
try:
|
||||
import firewall_api
|
||||
firewall_api.firewall_api().addAcceptPortArgs(port, 'pure-ftpd', 'port')
|
||||
return port
|
||||
except Exception as e:
|
||||
return "Release failed {}".format(e)
|
||||
|
||||
|
||||
def openFtpPort():
|
||||
for i in ["21", "39000:40000"]:
|
||||
ftp_release_port(i)
|
||||
return True
|
||||
|
||||
|
||||
def initDreplace():
|
||||
|
||||
file_tpl = getInitDTpl()
|
||||
@@ -89,6 +105,7 @@ def initDreplace():
|
||||
initD_path = getServerDir() + '/init.d'
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
openFtpPort()
|
||||
file_bin = initD_path + '/' + getPluginName()
|
||||
|
||||
# initd replace
|
||||
|
||||
@@ -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
|
||||
|
||||
#检测文件是否损坏.
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
resetPluginWinHeight(480);
|
||||
$.getScript( "/plugins/file?name=swap&f=js/swap.js", function(){
|
||||
pluginService('swap');
|
||||
});
|
||||
|
||||
@@ -122,7 +122,7 @@ def getLog():
|
||||
|
||||
logPath = args['path']
|
||||
|
||||
content = mw.getLastLine(logPath, 100)
|
||||
content = mw.getLastLine(logPath, 16)
|
||||
return mw.returnJson(True, 'ok', content)
|
||||
|
||||
|
||||
@@ -132,11 +132,16 @@ def runShellArgs(args):
|
||||
if data[i]['access_key'] == args['access_key']:
|
||||
script_dir = getServerDir() + "/scripts"
|
||||
shellFile = script_dir + '/' + args['access_key']
|
||||
param = ''
|
||||
if hasattr(args, 'param'):
|
||||
param = args['param']
|
||||
os.system("bash {} \"{}\" >> {}.log &".format(
|
||||
shellFile, param.replace('"', r'\"'), shellFile))
|
||||
param = args['params']
|
||||
if param == '':
|
||||
param = 'no-parameters'
|
||||
|
||||
param = re.sub("\"", '', param)
|
||||
|
||||
cmd = "bash {} {} >> {}.log 2>&1 &".format(
|
||||
shellFile, param, shellFile)
|
||||
# print(cmd)
|
||||
os.system(cmd)
|
||||
data[i]['count'] += 1
|
||||
data[i]['uptime'] = int(time.time())
|
||||
mw.writeFile(getCfgFilePath(), json.dumps(data))
|
||||
@@ -150,6 +155,7 @@ def runShell():
|
||||
if not check_arg[0]:
|
||||
return check_arg[1]
|
||||
|
||||
args['params'] = 'panel-test'
|
||||
return runShellArgs(args)
|
||||
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ Install_App()
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
rm -rf $serverPath/redis
|
||||
rm -rf $serverPath/webhook
|
||||
echo "Uninstall_App" > $install_tmp
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,32 @@ function whPost(method, args,callback){
|
||||
},'json');
|
||||
}
|
||||
|
||||
|
||||
function whPostNoMessage(method, args,callback){
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'webhook';
|
||||
req_data['func'] = method;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, function(data) {;
|
||||
if (!data.status){
|
||||
//错误展示10S
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function whPostCallbak(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
@@ -153,42 +179,60 @@ function showWebHookCode(url,code){
|
||||
<div class="line help">\
|
||||
<b>WebHook使用方法:</b><br>\
|
||||
GET/POST:<br>\
|
||||
'+window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '')+'/hook?access_key='+code+'&param=aaa<br>\
|
||||
'+window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '')+'/hook?access_key='+code+'&params=aaa<br>\
|
||||
@param access_key string HOOK密钥<br>\
|
||||
@param param string 自定义参数(在hook脚本中使用$1接收)<br>\
|
||||
@param params string 自定义参数(在hook脚本中使用$1接收)| 多个参数 "1 2" -> $1为1, $2为2<br>\
|
||||
</div>\
|
||||
</div>'
|
||||
})
|
||||
}
|
||||
|
||||
//查看日志
|
||||
function getLogs(path){
|
||||
|
||||
|
||||
whPost('get_log', {"path":path}, function(rdata){
|
||||
function getLogsTimer(path,success,error){
|
||||
whPostNoMessage('get_log', {"path":path}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
if (!rdata.status){
|
||||
layer.msg(rdata.msg,{icon:2});
|
||||
}
|
||||
|
||||
layer.open({
|
||||
type:1,
|
||||
title:'任务执行日志',
|
||||
area: ['600px','400px'],
|
||||
shadeClose:false,
|
||||
closeBtn:2,
|
||||
content:'<div class="bt-logs" style="font-size:0;">\
|
||||
<textarea class="bt-input-text mr20 site_logs pd10" name="site_logs" style="width:100%;line-height:22px;white-space: pre-line;font-size:12px;height:358px;border: none;" readonly="readonly">'+ (rdata.data || '当前没有日志.') +'</textarea>\
|
||||
</div>',
|
||||
success:function(){
|
||||
$('[name="site_logs"]').scrollTop($('[name="site_logs"]')[0].scrollHeight)
|
||||
if (typeof(error) == 'function'){
|
||||
error();
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$('[name="site_logs"]').text(rdata.data);
|
||||
if (typeof(success) == 'function'){
|
||||
success();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//查看日志
|
||||
function getLogs(path){
|
||||
logs_web = layer.open({
|
||||
type:1,
|
||||
title:'任务执行日志',
|
||||
area: ['600px','400px'],
|
||||
shadeClose:false,
|
||||
closeBtn:2,
|
||||
content:'<div class="bt-logs" style="font-size:0;">\
|
||||
<textarea class="bt-input-text mr20 site_logs pd10" name="site_logs" style="width:100%;line-height:22px;white-space: pre-line;font-size:12px;height:358px;border: none;" readonly="readonly">正在获取中...</textarea>\
|
||||
</div>',
|
||||
success:function(){
|
||||
$('[name="site_logs"]').scrollTop($('[name="site_logs"]')[0].scrollHeight);
|
||||
|
||||
logs_timer = setInterval(function(){
|
||||
getLogsTimer(path,function(){
|
||||
|
||||
},function(){
|
||||
layer.msg(rdata.msg,{icon:2});
|
||||
layer.close(logs_web);
|
||||
});
|
||||
},1000);
|
||||
},
|
||||
cancel:function(){
|
||||
clearInterval(logs_timer);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//运行
|
||||
function runHook(key){
|
||||
whPost('run_shell', {"access_key":key}, function(rdata){
|
||||
@@ -220,5 +264,3 @@ function deleteHook(key, title){
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 297 B |
@@ -1,7 +1,5 @@
|
||||
<script type="text/javascript">
|
||||
resetPluginWinWidth(480);
|
||||
// $.getScript( "/plugins/file?name=webssh&f=js/webssh.js", function() {
|
||||
//});
|
||||
if ($(".term-box #term").text() != 'W') {
|
||||
layer.closeAll();
|
||||
webShell();
|
||||
|
||||
+197
-4
@@ -11,10 +11,203 @@ sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
|
||||
def status():
|
||||
return 'start'
|
||||
class App():
|
||||
|
||||
__cmd_file = 'cmd.json'
|
||||
__cmd_path = ''
|
||||
__host_dir = ''
|
||||
|
||||
def __init__(self):
|
||||
self.__cmd_path = self.getServerDir() + '/' + self.__cmd_file
|
||||
|
||||
if not os.path.exists(self.__cmd_path):
|
||||
mw.writeFile(self.__cmd_path, '[]')
|
||||
|
||||
self.__host_dir = self.getServerDir() + '/host'
|
||||
if not os.path.exists(self.__host_dir):
|
||||
os.makedirs(self.__host_dir)
|
||||
|
||||
def getPluginName(self):
|
||||
return 'webssh'
|
||||
|
||||
def getPluginDir(self):
|
||||
return mw.getPluginDir() + '/' + self.getPluginName()
|
||||
|
||||
def getServerDir(self):
|
||||
return mw.getServerDir() + '/' + self.getPluginName()
|
||||
|
||||
def getArgs(self):
|
||||
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(self, 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(self):
|
||||
return 'start'
|
||||
|
||||
def saveCmd(self, t):
|
||||
data_tmp = json.loads(mw.readFile(self.__cmd_path))
|
||||
is_has = False
|
||||
for x in range(0, len(data_tmp) - 1):
|
||||
if data_tmp[x]['title'] == t['title']:
|
||||
is_has = True
|
||||
data_tmp[x]['cmd'] = t['cmd']
|
||||
if not is_has:
|
||||
data_tmp.append(t)
|
||||
mw.writeFile(self.__cmd_path, json.dumps(data_tmp))
|
||||
|
||||
def add_cmd(self):
|
||||
args = self.getArgs()
|
||||
check = self.checkArgs(args, ['title', 'cmd'])
|
||||
if not check[0]:
|
||||
return check[1]
|
||||
|
||||
title = args['title'].strip()
|
||||
cmd = args['cmd']
|
||||
|
||||
t = {
|
||||
'title': title,
|
||||
'cmd': cmd
|
||||
}
|
||||
self.saveCmd(t)
|
||||
|
||||
return mw.returnJson(True, '添加成功!')
|
||||
|
||||
def del_cmd(self):
|
||||
args = self.getArgs()
|
||||
check = self.checkArgs(args, ['title'])
|
||||
if not check[0]:
|
||||
return check[1]
|
||||
|
||||
title = args['title'].strip()
|
||||
data_tmp = json.loads(mw.readFile(self.__cmd_path))
|
||||
for x in range(0, len(data_tmp)):
|
||||
if data_tmp[x]['title'] == title:
|
||||
del(data_tmp[x])
|
||||
mw.writeFile(self.__cmd_path, json.dumps(data_tmp))
|
||||
return mw.returnJson(True, '删除成功')
|
||||
return mw.returnJson(False, '删除无效')
|
||||
|
||||
def get_cmd_list(self):
|
||||
alist = json.loads(mw.readFile(self.__cmd_path))
|
||||
return mw.returnJson(True, 'ok', alist)
|
||||
|
||||
def getSshInfo(self, file):
|
||||
rdata = mw.readFile(file)
|
||||
destr = mw.deCrypt('mdserver-web', rdata)
|
||||
return json.loads(destr)
|
||||
|
||||
def get_server_by_host(self):
|
||||
args = self.getArgs()
|
||||
check = self.checkArgs(args, ['host'])
|
||||
if not check[0]:
|
||||
return check[1]
|
||||
|
||||
info_file = self.__host_dir + '/' + args['host'] + '/info.json'
|
||||
if os.path.exists(info_file):
|
||||
try:
|
||||
info_tmp = self.getSshInfo(info_file)
|
||||
host_info = {}
|
||||
host_info['host'] = args['host']
|
||||
host_info['port'] = info_tmp['port']
|
||||
host_info['ps'] = info_tmp['ps']
|
||||
host_info['type'] = info_tmp['type']
|
||||
if 'password' in info_tmp:
|
||||
host_info['password'] = info_tmp['password']
|
||||
if 'pkey' in info_tmp:
|
||||
host_info['pkey'] = info_tmp['pkey']
|
||||
if 'pkey_passwd' in info_tmp:
|
||||
host_info['pkey_passwd'] = info_tmp['pkey_passwd']
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, '错误:' + str(e))
|
||||
|
||||
return mw.returnJson(True, 'ok!', host_info)
|
||||
return mw.returnJson(False, '不存在此配置')
|
||||
|
||||
def get_server_list(self):
|
||||
host_list = []
|
||||
for name in os.listdir(self.__host_dir):
|
||||
info_file = self.__host_dir + '/' + name + '/info.json'
|
||||
if not os.path.exists(info_file):
|
||||
continue
|
||||
try:
|
||||
info_tmp = self.getSshInfo(info_file)
|
||||
host_info = {}
|
||||
host_info['host'] = name
|
||||
host_info['port'] = info_tmp['port']
|
||||
host_info['ps'] = info_tmp['ps']
|
||||
# host_info['sort'] = int(info_tmp['sort'])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
# if os.path.exists(info_file):
|
||||
# os.remove(info_file)
|
||||
# continue
|
||||
|
||||
host_list.append(host_info)
|
||||
|
||||
host_list = sorted(host_list, key=lambda x: x['host'], reverse=False)
|
||||
return mw.returnJson(True, 'ok!', host_list)
|
||||
|
||||
def del_server(self):
|
||||
args = self.getArgs()
|
||||
check = self.checkArgs(args, ['host'])
|
||||
if not check[0]:
|
||||
return check[1]
|
||||
host = args['host']
|
||||
info_file = self.__host_dir + '/' + host
|
||||
mw.execShell('rm -rf {}'.format(info_file))
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
|
||||
def add_server(self):
|
||||
args = self.getArgs()
|
||||
check = self.checkArgs(
|
||||
args, ['host', 'port', 'type', 'username', 'ps'])
|
||||
if not check[0]:
|
||||
return check[1]
|
||||
|
||||
host = args['host']
|
||||
info = {
|
||||
'port': args['port'],
|
||||
'username': args['username'],
|
||||
'ps': args['ps'],
|
||||
'type': args['type'],
|
||||
}
|
||||
|
||||
if args['type'] == '0':
|
||||
info['password'] = args['password']
|
||||
else:
|
||||
info['pkey'] = args['pkey']
|
||||
info['pkey_passwd'] = args['pkey_passwd']
|
||||
|
||||
dst_host_dir = self.__host_dir + '/' + host
|
||||
if not os.path.exists(dst_host_dir):
|
||||
os.makedirs(dst_host_dir)
|
||||
|
||||
enstr = mw.enCrypt('mdserver-web', json.dumps(info))
|
||||
mw.writeFile(dst_host_dir + '/info.json', enstr)
|
||||
return mw.returnJson(True, '添加成功!')
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'status':
|
||||
print(status())
|
||||
classApp = App()
|
||||
try:
|
||||
data = eval("classApp." + func + "()")
|
||||
print(data)
|
||||
except Exception as e:
|
||||
print(mw.getTracebackInfo())
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
{
|
||||
"hook":[
|
||||
{
|
||||
"tag":"menu",
|
||||
"menu": {
|
||||
"title":"终端管理",
|
||||
"name":"webssh",
|
||||
"path":"menu/index.html",
|
||||
"css_path":"menu/index.css",
|
||||
"js_path":"js/webssh.js"
|
||||
}
|
||||
}
|
||||
],
|
||||
"id":3,
|
||||
"title":"SSH终端",
|
||||
"tip":"lib",
|
||||
"name":"webssh",
|
||||
"type":"扩展",
|
||||
"ps":"完整功能的SSH客户端,仅用于连接本服务器",
|
||||
"versions":"1.0",
|
||||
"versions":"2.0",
|
||||
"shell":"install.sh",
|
||||
"checks":"server/webssh",
|
||||
"path": "server/webssh",
|
||||
|
||||
@@ -11,12 +11,13 @@ serverPath=$(dirname "$rootPath")
|
||||
|
||||
install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
|
||||
VERSION=$2
|
||||
|
||||
Install_webssh()
|
||||
{
|
||||
echo '正在安装脚本文件...' > $install_tmp
|
||||
mkdir -p $serverPath/webssh
|
||||
echo '1.0' > $serverPath/webssh/version.pl
|
||||
echo "${VERSION}" > $serverPath/webssh/version.pl
|
||||
echo '安装完成' > $install_tmp
|
||||
|
||||
}
|
||||
|
||||
+580
-159
@@ -1,179 +1,600 @@
|
||||
function web_shell2() {
|
||||
var termCols = 83;
|
||||
var termRows = 21;
|
||||
var sendTotal = 0;
|
||||
if(!socket)socket = io.connect();
|
||||
var term = new Terminal({ cols: termCols, rows: termRows, screenKeys: true, useStyle: true});
|
||||
|
||||
term.open();
|
||||
term.setOption('cursorBlink', true);
|
||||
term.setOption('fontSize', 14);
|
||||
gterm = term
|
||||
//全局
|
||||
var host_ssh_list = [];
|
||||
|
||||
socket.on('server_response', function (data) {
|
||||
term.write(data.data);
|
||||
if (data.data == '\r\n登出\r\n' ||
|
||||
data.data == '登出\r\n' ||
|
||||
data.data == '\r\nlogout\r\n' ||
|
||||
data.data == 'logout\r\n') {
|
||||
setTimeout(function () {
|
||||
layer.closeAll();
|
||||
term.destroy();
|
||||
clearInterval(interval);
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
|
||||
if (socket) {
|
||||
socket.emit('connect_event', '');
|
||||
interval = setInterval(function () {
|
||||
socket.emit('connect_event', '');
|
||||
}, 1000);
|
||||
//刷新页面
|
||||
$(window).unload(function(){
|
||||
for (i in host_ssh_list) {
|
||||
host_ssh_list[i].close();
|
||||
}
|
||||
|
||||
term.on('data', function (data) {
|
||||
socket.emit('webssh', data);
|
||||
return false;
|
||||
});
|
||||
|
||||
function appPost(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:'webssh', 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 appAsyncPost(method,args){
|
||||
var _args = null;
|
||||
if (typeof(args) == 'string'){
|
||||
_args = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
_args = JSON.stringify(args);
|
||||
}
|
||||
return syncPost('/plugins/run', {name:'webssh', func:method, args:_args});
|
||||
}
|
||||
|
||||
function appPostCallbak(method, args, callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'webssh';
|
||||
req_data['func'] = method;
|
||||
args['version'] = '1.0';
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/callback', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
var tag = $.getUrlParam('tag');
|
||||
if(tag == 'webssh'){
|
||||
webShell_Load();
|
||||
}
|
||||
});
|
||||
|
||||
function webShell_Resize(){
|
||||
var cur_ssh = $('.term_item_tab .list .active');
|
||||
if (cur_ssh.length > 0){
|
||||
var data = $(cur_ssh).data();
|
||||
var item = host_ssh_list[data.id];
|
||||
item.term.fit();
|
||||
item.resize({ cols: item.term.cols, rows: item.term.rows});
|
||||
item.term.focus();
|
||||
}
|
||||
}
|
||||
|
||||
function webShell_Load(){
|
||||
changeDivH();
|
||||
$(window).resize(function(){
|
||||
changeDivH();
|
||||
webShell_Resize();
|
||||
});
|
||||
|
||||
$('.term_content_tab .term-tool-button').click(function(){
|
||||
if ($(this).hasClass('tool-show')){
|
||||
$('#term_box_view').css('margin-right',300);
|
||||
$('.term_tootls').css('display',"block");
|
||||
$(this).removeClass('tool-show').addClass('tool-hide');
|
||||
$(this).find('span').removeClass('glyphicon-menu-left').addClass('glyphicon-menu-right');
|
||||
} else {
|
||||
$('#term_box_view').css('margin-right',0);
|
||||
$('.term_tootls').css('display',"none");
|
||||
$(this).removeClass('tool-hide').addClass('tool-show');
|
||||
$(this).find('span').removeClass('glyphicon-menu-right').addClass('glyphicon-menu-left');
|
||||
}
|
||||
webShell_Resize();
|
||||
});
|
||||
|
||||
|
||||
var term_box = layer.open({
|
||||
type: 1,
|
||||
title: "本地终端",
|
||||
area: ['685px','435px'],
|
||||
closeBtn: 2,
|
||||
shadeClose: false,
|
||||
content: '<div class="term-box"><div id="term"></div></div>\
|
||||
<div class="shell-text-input">\
|
||||
<textarea type="text" class="bt-input-text-shell" placeholder="请将命令粘贴到此处.." value="" name="ssh_copy" />\
|
||||
<div class="shell-btn-group">\
|
||||
<button class="shellbutton btn btn-success btn-sm pull-right shell_btn_1">发送(Ctrl+Enter)</button>\
|
||||
<button class="shellbutton btn btn-default btn-sm pull-right shell_btn_close">关闭</button>\
|
||||
</div>\
|
||||
</div>',
|
||||
cancel: function () {
|
||||
term.destroy();
|
||||
clearInterval(interval);
|
||||
|
||||
$('.full_exit_screen').click(function(ele){
|
||||
if($(this).hasClass('glyphicon-resize-full')){
|
||||
requestFullScreen($('#term_box_view')[0]);
|
||||
$(this).removeClass('glyphicon-resize-full').addClass('glyphicon-resize-small');
|
||||
} else{
|
||||
exitFull();
|
||||
$(this).removeClass('glyphicon-resize-small').addClass('glyphicon-resize-full');
|
||||
}
|
||||
});
|
||||
$(".shell_btn_close").click(function(){
|
||||
layer.close(term_box);
|
||||
term.destroy();
|
||||
clearInterval(interval);
|
||||
})
|
||||
|
||||
setTimeout(function () {
|
||||
$('.terminal').detach().appendTo('#term');
|
||||
$("#term").show();
|
||||
socket.emit('webssh', "\n");
|
||||
term.focus();
|
||||
|
||||
// 鼠标右键事件
|
||||
var can = $("#term");
|
||||
can.contextmenu(function (e) {
|
||||
var winWidth = can.width();
|
||||
var winHeight = can.height();
|
||||
var mouseX = e.pageX;
|
||||
var mouseY = e.pageY;
|
||||
var menuWidth = $(".contextmenu").width();
|
||||
var menuHeight = $(".contextmenu").height();
|
||||
var minEdgeMargin = 10;
|
||||
if (mouseX + menuWidth + minEdgeMargin >= winWidth &&
|
||||
mouseY + menuHeight + minEdgeMargin >= winHeight) {
|
||||
menuLeft = mouseX - menuWidth - minEdgeMargin + "px";
|
||||
menuTop = mouseY - menuHeight - minEdgeMargin + "px";
|
||||
$('.addServer').click(function(){
|
||||
webShell_addServer();
|
||||
});
|
||||
|
||||
$('.tootls_host_btn').click(function(){
|
||||
webShell_addServer();
|
||||
});
|
||||
|
||||
$('.tootls_commonly_btn').click(function(){
|
||||
webShell_cmd();
|
||||
});
|
||||
|
||||
// 切换服务器终端视图
|
||||
$('.term_item_tab .list').on('click', 'span.item', function (ev) {
|
||||
var index = $(this).index();
|
||||
var data = $(this).data();
|
||||
if ($(this).hasClass('addServer')) {
|
||||
} else if ($(this).hasClass('tab_tootls')) {
|
||||
} else {
|
||||
$(this).addClass('active').siblings().removeClass('active');
|
||||
$('.term_content_tab .term_item:eq(' + index + ')').addClass('active').siblings().removeClass('active');
|
||||
webShell_Resize();
|
||||
}
|
||||
});
|
||||
|
||||
$('.term_item_tab').on('click', '.icon-trem-close', function () {
|
||||
var id = $(this).parent().data('id');
|
||||
webShell_removeTermView(id);
|
||||
})
|
||||
|
||||
//服务器列表和常用命令
|
||||
webShell_getHostList();
|
||||
|
||||
//服务器列表和命令切换
|
||||
$('.term_tootls .tab-nav span').click(function(){
|
||||
var list_type = $(this).attr('data-type');
|
||||
if (!$(this).hasClass('on')){
|
||||
|
||||
$('.term_tootls .tab-nav span').removeClass('on');
|
||||
$(this).addClass('on');
|
||||
|
||||
$('.term_tootls .tab-con .tab-block').removeClass('on')
|
||||
if (list_type == 'host'){
|
||||
$('.term_tootls .tab-con .tab-block').eq(0).addClass('on');
|
||||
webShell_getHostList();
|
||||
}
|
||||
else if (mouseX + menuWidth + minEdgeMargin >= winWidth) {
|
||||
menuLeft = mouseX - menuWidth - minEdgeMargin + "px";
|
||||
menuTop = mouseY + minEdgeMargin + "px";
|
||||
|
||||
if (list_type == 'shell'){
|
||||
$('.term_tootls .tab-con .tab-block').eq(1).addClass('on');
|
||||
webShell_getCmdList();
|
||||
}
|
||||
else if (mouseY + menuHeight + minEdgeMargin >= winHeight) {
|
||||
menuLeft = mouseX + minEdgeMargin + "px";
|
||||
menuTop = mouseY - menuHeight - minEdgeMargin + "px";
|
||||
}
|
||||
});
|
||||
|
||||
webShell_Menu();
|
||||
}
|
||||
|
||||
|
||||
function changeDivH(){
|
||||
var l = $(window).height();
|
||||
$('#term_box_view').parent().css('height',l-80);
|
||||
$('#xterm-viewport').css('height',l-80);
|
||||
|
||||
$('.tootls_host_list').css('display','block').css('height',l-192);
|
||||
$('.tootls_commonly_list').css('display','block').css('height',l-192);
|
||||
}
|
||||
|
||||
|
||||
// 窗口状态改变
|
||||
function fullScreenChange(el, callback) {
|
||||
el.addEventListener("fullscreenchange", function () {
|
||||
callback && callback(document.fullscreen);
|
||||
});
|
||||
|
||||
el.addEventListener("webkitfullscreenchange", function () {
|
||||
callback && callback(document.webkitIsFullScreen);
|
||||
});
|
||||
|
||||
el.addEventListener("mozfullscreenchange", function () {
|
||||
callback && callback(document.mozFullScreen);
|
||||
});
|
||||
|
||||
el.addEventListener("msfullscreenchange", function () {
|
||||
callback && callback(document.msFullscreenElement);
|
||||
});
|
||||
}
|
||||
|
||||
// 全屏
|
||||
function requestFullScreen(element) {
|
||||
var requestMethod = element.requestFullScreen || //W3C
|
||||
element.webkitRequestFullScreen || //Chrome等
|
||||
element.mozRequestFullScreen || //FireFox
|
||||
element.msRequestFullScreen; //IE11
|
||||
if (requestMethod) {
|
||||
requestMethod.call(element);
|
||||
}else if (typeof window.ActiveXObject !== "undefined") {//for Internet Explorer
|
||||
var wscript = new ActiveXObject("WScript.Shell");
|
||||
if (wscript !== null) {
|
||||
wscript.SendKeys("{F11}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//退出全屏
|
||||
function exitFull() {
|
||||
var exitMethod = document.exitFullscreen || //W3C
|
||||
document.mozCancelFullScreen || //Chrome等
|
||||
document.webkitExitFullscreen || //FireFox
|
||||
document.webkitExitFullscreen; //IE11
|
||||
if (exitMethod) {
|
||||
exitMethod.call(document);
|
||||
}else if (typeof window.ActiveXObject !== "undefined") {//for Internet Explorer
|
||||
var wscript = new ActiveXObject("WScript.Shell");
|
||||
if (wscript !== null) {
|
||||
wscript.SendKeys("{F11}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function webShell_getCmdList(){
|
||||
appPost('get_cmd_list', {}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
var alist = rdata.data;
|
||||
|
||||
var tli = '';
|
||||
for (var i = 0; i < alist.length; i++) {
|
||||
tli+='<li class="data-cmd-list" data-index="'+i+'" data-clipboard-text="'+alist[i]['cmd']+'">\
|
||||
<i></i>\
|
||||
<span class="span_title">'+alist[i]['title']+'</span>\
|
||||
<span class="tootls">\
|
||||
<span class="glyphicon glyphicon-edit" aria-hidden="true" title="编辑常用命令信息"></span>\
|
||||
<span class="glyphicon glyphicon-trash" aria-hidden="true" title="删除常用命令信息"></span>\
|
||||
</span>\
|
||||
</li>';
|
||||
}
|
||||
|
||||
$('.tootls_commonly_list').html(tli);
|
||||
|
||||
$('.data-cmd-list .glyphicon-edit').click(function(){
|
||||
var index = $(this).parent().parent().attr('data-index');
|
||||
var t = alist[index];
|
||||
webShell_cmd(t['title'],t['cmd']);
|
||||
});
|
||||
|
||||
$('.data-cmd-list .glyphicon-trash').click(function(){
|
||||
var index = $(this).parent().parent().attr('data-index');
|
||||
var t = alist[index];
|
||||
appPost('del_cmd', {title:t['title']}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg, function(){
|
||||
webShell_getCmdList();
|
||||
},{ icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
});
|
||||
|
||||
$('.data-cmd-list .span_title').click(function(e){
|
||||
copyText($(this).parent().attr('data-clipboard-text'));
|
||||
e.preventDefault();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var n;
|
||||
function Terms_WebSocketIO_Create(ip, random){
|
||||
n = new Terms_WebSocketIO('#'+random, { ssh_info: { host: ip, ps: "22", id: random } });
|
||||
n.registerCloseCallBack(function(){
|
||||
webShell_removeTermView(random);
|
||||
layer.msg('已经关闭【'+ip+'】', { icon: 1, time: 3000 });
|
||||
});
|
||||
|
||||
n.registerConnectedCallBack(function(){
|
||||
var that = this;
|
||||
$('.term_item_tab .item').each(function(){
|
||||
var id = $(this).data('id');
|
||||
if (id == that.id){
|
||||
$(this).find('.icon').removeClass('icon-warning').addClass('icon-sucess');
|
||||
}
|
||||
else {
|
||||
menuLeft = mouseX + minEdgeMargin + "px";
|
||||
menuTop = mouseY + minEdgeMargin + "px";
|
||||
});
|
||||
webShell_Resize();
|
||||
});
|
||||
|
||||
n.registerExitCallBack(function(){
|
||||
var that = this;
|
||||
$('.term_item_tab .item').each(function(){
|
||||
var id = $(this).data('id');
|
||||
if (id == that.id){
|
||||
$(this).find('.icon').removeClass('icon-sucess').addClass('icon-warning');
|
||||
}
|
||||
});
|
||||
});
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
function webShell_Menu(){
|
||||
var random = 'localhost';
|
||||
// host_ssh_list[random] = new Terms_WebSocketIO('#'+random, { ssh_info: { host: "38.6.224.67", ps: "22", id: random } });
|
||||
host_ssh_list[random] = Terms_WebSocketIO_Create('127.0.0.1',random);
|
||||
}
|
||||
|
||||
function webShell_openTermView(info) {
|
||||
if (typeof info === "undefined") {
|
||||
info = { host: '127.0.0.1', ps: '本地服务器' }
|
||||
}
|
||||
var random = getRandomString(9);
|
||||
var tab_content = $('.term_content_tab');
|
||||
var item_list = $('.term_item_tab .list');
|
||||
tab_content.find('.term_item').removeClass('active').siblings().removeClass('active');
|
||||
tab_content.append('<div class="term_item active" id="' + random + '" data-host="' + info.host + '"></div>');
|
||||
item_list.find('.item').removeClass('active');
|
||||
if (info.ps == ''){
|
||||
info.ps = info.host;
|
||||
}
|
||||
item_list.append('<span class="active item ' + (info.host == '127.0.0.1' ? 'localhost_item' : '') + '" data-host="' + info.host + '" data-id="' + random + '"><i class="icon icon-sucess"></i><div class="content"><span>' + info.ps + '</span></div><span class="icon-trem-close"></span></span>');
|
||||
host_ssh_list[random] = Terms_WebSocketIO_Create(info.host, random);
|
||||
}
|
||||
|
||||
|
||||
function webShell_removeTermView(id){
|
||||
var item = $('[data-id="' + id + '"]');
|
||||
var next = item.next();
|
||||
var prev = item.prev();
|
||||
$('#' + id).remove();
|
||||
item.remove();
|
||||
try {
|
||||
host_ssh_list[id].close();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
delete host_ssh_list[id];
|
||||
if (item.hasClass('active')) {
|
||||
if (next.length > 0) {
|
||||
next.click();
|
||||
} else {
|
||||
prev.click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function webShell_getHostList(info){
|
||||
appPost('get_server_list', {}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
var alist = rdata.data;
|
||||
|
||||
var tli = '';
|
||||
for (var i = 0; i < alist.length; i++) {
|
||||
tli+='<li class="data-host-list" data-index="'+i+'" data-host="'+alist[i]['host']+'">\
|
||||
<i></i>\
|
||||
<span class="host">'+alist[i]['host']+'</span>\
|
||||
<span class="tootls">\
|
||||
<span class="glyphicon glyphicon-edit" aria-hidden="true" title="编辑常用命令信息"></span>\
|
||||
<span class="glyphicon glyphicon-trash" aria-hidden="true" title="删除常用命令信息"></span>\
|
||||
</span>\
|
||||
</li>';
|
||||
}
|
||||
|
||||
$('.tootls_host_list').html(tli);
|
||||
|
||||
$('.data-host-list .glyphicon-edit').click(function(e){
|
||||
var index = $(this).parent().parent().attr('data-index');
|
||||
var info = alist[index];
|
||||
webShell_addServer(info);
|
||||
});
|
||||
|
||||
$('.data-host-list .glyphicon-trash').click(function(e){
|
||||
var index = $(this).parent().parent().attr('data-index');
|
||||
var t = alist[index];
|
||||
appPost('del_server', {host:t['host']}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg, function(){
|
||||
webShell_getHostList();
|
||||
},{ icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$('.tootls_host_list .host').on('click', function (e) {
|
||||
var _this = $(this).parent();
|
||||
var index = $(_this).index();
|
||||
var host = $(_this).data('host');
|
||||
$(_this).find('i').addClass('active');
|
||||
if ($('.item[data-host="' + host + '"]').length > 0) {
|
||||
layer.msg('已经打开!', { icon: 0, time: 3000 });
|
||||
} else {
|
||||
webShell_openTermView(alist[index]);
|
||||
}
|
||||
e.preventDefault();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function webShell_addServer(info=[]){
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '添加主机信息',
|
||||
area: '500px',
|
||||
btn:["确定","取消"],
|
||||
content:'<div class="bt-form pd20 c6">\
|
||||
<div class="line input_group">\
|
||||
<span class="tname">服务器IP</span>\
|
||||
<div class="info-r">\
|
||||
<input type="text" name="host" class="bt-input-text mr5" style="width:240px" value="127.0.0.1" placeholder="输入服务器IP" val="" autocomplete="off" />\
|
||||
<input type="text" name="port" class="bt-input-text mr5" style="width:60px" placeholder="端口" value="22" autocomplete="off"/>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class="line">\
|
||||
<span class="tname">SSH账号</span>\
|
||||
<div class="info-r">\
|
||||
<input type="text" name="username" class="bt-input-text mr5" style="width:305px" placeholder="输入SSH账号" value="root" autocomplete="off"/>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class="line">\
|
||||
<span class="tname">验证方式</span>\
|
||||
<div class="info-r">\
|
||||
<div class="btn-group">\
|
||||
<button type="button" tabindex="-1" class="btn btn-sm auth_type_checkbox btn-default btn-success" data-ctype="0">密码验证</button>\
|
||||
<button type="button" tabindex="-1" class="btn btn-sm auth_type_checkbox btn-default" data-ctype="1">私钥验证\
|
||||
</button>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class="line c_password_view show">\
|
||||
<span class="tname">密码</span>\
|
||||
<div class="info-r">\
|
||||
<input type="text" name="password" class="bt-input-text mr5" placeholder="请输入SSH密码" style="width:305px;" value="" autocomplete="off"/>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class="line c_pkey_view hide">\
|
||||
<span class="tname">私钥</span>\
|
||||
<div class="info-r">\
|
||||
<textarea rows="4" name="pkey" class="bt-input-text mr5" placeholder="请输入SSH私钥" style="width:305px;height: 80px;line-height: 18px;padding-top:10px;"></textarea>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class="line key_pwd_line hide" style="display:none;">\
|
||||
<span class="tname">私钥密码</span>\
|
||||
<div class="info-r">\
|
||||
<input type="text" name="pkey_passwd" class="bt-input-text mr5" placeholder="请输入私钥密码" style="width:305px;" value="" autocomplete="off"/>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class="line ssh_ps_tips">\
|
||||
<span class="tname">备注</span>\
|
||||
<div class="info-r">\
|
||||
<input type="text" name="ps" class="bt-input-text mr5" placeholder="请输入备注,可为空" style="width:305px;" value="" autocomplete="off"/>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>',
|
||||
success:function(){
|
||||
if (typeof(info['host'])!='undefined'){
|
||||
$('input[name="host"]').val(info['host']);
|
||||
appPost('get_server_by_host',{host:info['host']},function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
var jdata = rdata.data;
|
||||
if (jdata['type'] == 0){
|
||||
$('input[name="password"]').val(jdata['password']);
|
||||
} else{
|
||||
$('textarea[name="pkey"]').val(jdata['pkey']);
|
||||
// $('input[name="pkey_passwd"]').val(jdata['pkey_passwd']);
|
||||
}
|
||||
$('input[name="ps"]').val(jdata['ps']);
|
||||
$('.auth_type_checkbox').each(function(){
|
||||
if ($(this).data('ctype') == jdata['type']){
|
||||
$(this).addClass('btn-success');
|
||||
} else {
|
||||
$(this).removeClass('btn-success');
|
||||
}
|
||||
});
|
||||
if (jdata['type'] == 0){
|
||||
$('.c_password_view').removeClass('show').addClass('show');
|
||||
$('.c_pkey_view').removeClass('show').addClass('hide');
|
||||
// $('.key_pwd_line').removeClass('show').addClass('hide');
|
||||
|
||||
}else{
|
||||
$('.c_password_view').removeClass('show').addClass('hide');
|
||||
$('.c_pkey_view').removeClass('show').addClass('show');
|
||||
// $('.key_pwd_line').removeClass('show').addClass('show');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$('.auth_type_checkbox').click(function(){
|
||||
var ctype = $(this).attr('data-ctype');
|
||||
$('.auth_type_checkbox').removeClass('btn-success');
|
||||
$(this).addClass('btn-success');
|
||||
|
||||
if (ctype == 0){
|
||||
$('.c_password_view').removeClass('show').addClass('show');
|
||||
$('.c_pkey_view').removeClass('show').addClass('hide');
|
||||
// $('.key_pwd_line').removeClass('show').addClass('hide');
|
||||
|
||||
}else{
|
||||
$('.c_password_view').removeClass('show').addClass('hide');
|
||||
$('.c_pkey_view').removeClass('show').addClass('show');
|
||||
// $('.key_pwd_line').removeClass('show').addClass('show');
|
||||
}
|
||||
});
|
||||
},
|
||||
yes:function(l,layeror){
|
||||
var host = $('input[name="host"]').val();
|
||||
var port = $('input[name="port"]').val();
|
||||
var username = $('input[name="username"]').val();
|
||||
var password = '';
|
||||
var pkey = '';
|
||||
var pkey_passwd = '';
|
||||
var type = $('.btn-group .btn-success').attr('data-ctype');
|
||||
if (type == "0"){
|
||||
password = $('input[name="password"]').val();
|
||||
} else{
|
||||
pkey = $('textarea[name="pkey"]').val();
|
||||
pkey_passwd = $('input[name="pkey_passwd"]').val();
|
||||
}
|
||||
|
||||
var ps = $('input[name="ps"]').val();
|
||||
|
||||
var req_data = {
|
||||
type:type,
|
||||
host:host,
|
||||
port:port,
|
||||
username:username,
|
||||
password:password,
|
||||
pkey:pkey,
|
||||
pkey_passwd:pkey_passwd,
|
||||
ps:ps,
|
||||
};
|
||||
|
||||
var selectText = term.getSelection()
|
||||
var style_str = '';
|
||||
var paste_str = '';
|
||||
if (!selectText) {
|
||||
if (!getCookie('shell_copy_body')) {
|
||||
paste_str = 'style="color: #bbb;" disable';
|
||||
}
|
||||
style_str = 'style="color: #bbb;" disable';
|
||||
} else {
|
||||
setCookie('ssh_selection', selectText);
|
||||
}
|
||||
console.log(req_data);
|
||||
|
||||
appPost('add_server',req_data,function(rdata){
|
||||
layer.close(l);
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg, function(){
|
||||
webShell_getHostList();
|
||||
},{ icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
var menudiv = '<ul class="contextmenu">\
|
||||
<li><a class="shell_copy_btn menu_ssh" data-clipboard-text="'+ selectText + '" ' + style_str + '>复制到剪切板</a></li>\
|
||||
<li><a onclick="shell_paste_text()" '+ paste_str+'>粘贴选中项</a></li>\
|
||||
<li><a onclick="shell_to_baidu()" ' + style_str + '>百度搜索</a></li>\
|
||||
</ul>';
|
||||
$("body").append(menudiv);
|
||||
$(".contextmenu").css({
|
||||
"left": menuLeft,
|
||||
"top": menuTop
|
||||
function webShell_cmd(title='', cmd=''){
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '添加常用命令信息',
|
||||
area: '500px',
|
||||
btn:["确定","取消"],
|
||||
content:'<div class="bt-form pd20 c6">\
|
||||
<div class="line">\
|
||||
<span class="tname">命令名称</span>\
|
||||
<div class="info-r">\
|
||||
<input type="text" name="title" class="bt-input-text mr5" style="width:305px" placeholder="请输入常用命令描述,必填项" value="'+title+'" autocomplete="off"/>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class="line">\
|
||||
<span class="tname">命令内容</span>\
|
||||
<div class="info-r">\
|
||||
<textarea rows="4" name="cmd" class="bt-input-text mr5" placeholder="请输入常用命令信息,必填项" style="width:305px;height: 150px;line-height: 18px;padding-top:10px;">'+cmd+'</textarea>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>',
|
||||
success:function(){
|
||||
},
|
||||
yes:function(l,layer_id){
|
||||
var title = $('input[name="title"]').val();
|
||||
var cmd = $('textarea[name="cmd"]').val();
|
||||
|
||||
appPost('add_cmd', {title:title,cmd:cmd}, function(rdata){
|
||||
layer.close(l);
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg, function(){
|
||||
webShell_getCmdList();
|
||||
},{ icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
return false;
|
||||
});
|
||||
can.click(function () {
|
||||
remove_ssh_menu();
|
||||
});
|
||||
|
||||
clipboard = new ClipboardJS('.shell_copy_btn');
|
||||
clipboard.on('success', function (e) {
|
||||
layer.msg('复制成功!');
|
||||
setCookie('shell_copy_body', e.text)
|
||||
remove_ssh_menu();
|
||||
term.focus();
|
||||
});
|
||||
|
||||
clipboard.on('error', function (e) {
|
||||
layer.msg('复制失败,浏览器不兼容!');
|
||||
setCookie('shell_copy_body', e.text)
|
||||
remove_ssh_menu();
|
||||
term.focus();
|
||||
});
|
||||
|
||||
$(".shellbutton").click(function () {
|
||||
var tobj = $("textarea[name='ssh_copy']");
|
||||
var ptext = tobj.val();
|
||||
tobj.val('');
|
||||
if ($(this).text().indexOf('Alt') != -1) {
|
||||
ptext +="\n";
|
||||
}
|
||||
socket.emit('webssh', ptext);
|
||||
term.focus();
|
||||
})
|
||||
$("textarea[name='ssh_copy']").keydown(function (e) {
|
||||
|
||||
if (e.ctrlKey && e.keyCode == 13) {
|
||||
$(".shell_btn_1").click();
|
||||
} else if (e.altKey && e.keyCode == 13) {
|
||||
$(".shell_btn_1").click();
|
||||
}
|
||||
});
|
||||
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function shell_to_baidu() {
|
||||
var selectText = getCookie('ssh_selection');
|
||||
remove_ssh_menu();
|
||||
window.open('https://www.baidu.com/s?wd=' + selectText)
|
||||
gterm.focus();
|
||||
}
|
||||
|
||||
function shell_paste_text(){
|
||||
socket.emit('webssh', getCookie('ssh_selection'));
|
||||
remove_ssh_menu();
|
||||
gterm.focus();
|
||||
}
|
||||
|
||||
function remove_ssh_menu() {
|
||||
$(".contextmenu").remove();
|
||||
}
|
||||
@@ -0,0 +1,593 @@
|
||||
|
||||
/* menu start */
|
||||
.menu .menu_plugin_webssh {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAMBJREFUeNpiYKAQMM6cOVMASBuQozk9Pf0AC5AuAOJ6cgwAWt7IhMQvRKIfAPEEYgxhQWL3o9EFpBgwAeifQhKdD7YI5oWPUMEEmAQR4COyATBwAIgdgIacB2IFYkxhQouWB0BsCA3E86QGIsxvDSBXgGIDSxr5ALTgAk4XADWAQt4fiB2BChcAaQVoGoHheLwuAGqagBz/UNsciQ4DEgE/zAUfQM4DOt+eRANAYTMRZADIrxfIdMUFSnMzA0CAAQD0hjqnYxWD2gAAAABJRU5ErkJggg==");
|
||||
}
|
||||
|
||||
.menu .current .menu_plugin_webssh:hover {
|
||||
background-image: url("data:image/gif;base64,R0lGODlhEAAQAMQXAJ6ipUFITySRPIiMkDFRQuXm5jJKQy1kQCGeOzBXQSl3PiWLPVhfZSxrQCpxP9na2yeEPTREQ8LExmRqb5OXm/Hx8TU9RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjMyQjYwMTU3OTA2QTExRUJCN0NFQUUwQjRDMzM4RDJDIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjMyQjYwMTU4OTA2QTExRUJCN0NFQUUwQjRDMzM4RDJDIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MzJCNjAxNTU5MDZBMTFFQkI3Q0VBRTBCNEMzMzhEMkMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MzJCNjAxNTY5MDZBMTFFQkI3Q0VBRTBCNEMzMzhEMkMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFMgAXACwAAAAAEAAQAAAFU6AljmRpBVOqrqtFVXAsy9Q7VMMDzHHN/5UawGQC1CiWg4IoOiINAoGB6BxBENSjyIE4oFKMprYhIFgYkvTQpWWOqm6RcVBI2+/2AuXL6k8CcSYhACH5BAUyABcALAcACQAFAAIAAAUIYJSMBLGcSggAIfkEBTIAFwAsBwAJAAUAAgAABQhgMI0MI51ACAAh+QQFMgAXACwHAAkABQACAAAFCGCUjASxnEoIADs=");
|
||||
}
|
||||
/* menu end */
|
||||
|
||||
|
||||
.xterm{
|
||||
position: inherit;
|
||||
}
|
||||
|
||||
.tab-nav {
|
||||
border-bottom: #cacad9 1px solid;
|
||||
}
|
||||
|
||||
.tab-nav span {
|
||||
background-color: #f0f0f1;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
padding: 0 15px;
|
||||
border: #cacad9 1px solid;
|
||||
color: #444;
|
||||
display: inline-block;
|
||||
margin: 0 -1px -1px 0;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tab-nav .on {
|
||||
background: #fff;
|
||||
border-bottom: #fff 1px solid;
|
||||
color: #444
|
||||
}
|
||||
|
||||
.tab-con {
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tab-con .tab-block {
|
||||
display: none;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tab-con .tab-block.on {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tab-con ul.cmdlist {
|
||||
list-style-type: decimal
|
||||
}
|
||||
ul.cmdlist {
|
||||
list-style-type: decimal;
|
||||
height: 505px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.tab-con ul.cmdlist li {
|
||||
position: relative;
|
||||
list-style-type: decimal;
|
||||
list-style-position: inside;
|
||||
line-height: 40px;
|
||||
border-bottom: #dbdbea 1px solid;
|
||||
margin-top: 6px
|
||||
}
|
||||
|
||||
.tab-con ul.cmdlist li .com-progress,
|
||||
.tab-con ul.cmdlist li .state,
|
||||
.opencmd {
|
||||
float: right;
|
||||
margin-left: 20px;
|
||||
color: #535362
|
||||
}
|
||||
|
||||
.tab-con ul.cmdlist li .line-progress {
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
left: 0;
|
||||
height: 1px;
|
||||
background-color: #20a53a
|
||||
}
|
||||
|
||||
.tab-con ul.cmdlist li .cmd {
|
||||
border: 0 none;
|
||||
border-radius: 0;
|
||||
display: block;
|
||||
width: 570px;
|
||||
height: 200px;
|
||||
line-height: 22px;
|
||||
padding: 0 10px;
|
||||
background-color: #333;
|
||||
color: #eee;
|
||||
overflow: auto
|
||||
}
|
||||
|
||||
.term_item_tab .list {
|
||||
display: inline-block;
|
||||
webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
/* display: flex; */
|
||||
height: 38px;
|
||||
overflow: hidden;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.term_item_tab .list>span {
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
text-align: left;
|
||||
padding: 0 25px 0 22px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
border-right: 1px solid #e2e2e2;
|
||||
cursor: pointer;
|
||||
width: 150px;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.icon-trem-close {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -8.5px;
|
||||
right: 8px;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.icon-trem-close::after,
|
||||
.icon-trem-close::before {
|
||||
content: '';
|
||||
height: 14px;
|
||||
width: 2px;
|
||||
display: inline-block;
|
||||
background: #fb0000;
|
||||
z-index: 999;
|
||||
position: absolute;
|
||||
position: absolute;
|
||||
top: 1.5px;
|
||||
left: 8px;
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.icon-trem-close::after {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.icon-trem-close::before {
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
/*term_item_tab*/
|
||||
.term_item_tab .list>span.active .icon-trem-close,
|
||||
.term_item_tab .list>span:hover .icon-trem-close {
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
.term_item_tab {
|
||||
background: #f1f1f1;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.term_item_tab .list>span i {
|
||||
transition: all 50ms;
|
||||
}
|
||||
|
||||
.term_item_tab .list>span .icon {
|
||||
height: 9px;
|
||||
width: 9px;
|
||||
border-radius: 5px;
|
||||
display: inline-block;
|
||||
background: #fff;
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 50%;
|
||||
margin-top: -4.5px;
|
||||
}
|
||||
|
||||
.term_item_tab .list>span .icon.icon-sucess {
|
||||
background-color: #10952a;
|
||||
}
|
||||
|
||||
.term_item_tab .list>span .icon.icon-info {
|
||||
background-color: #fc6d26;
|
||||
}
|
||||
|
||||
.term_item_tab .list>span .icon.icon-warning {
|
||||
background-color: #ff5d2c;
|
||||
}
|
||||
|
||||
.term_item_tab .list>span:hover {
|
||||
background-color: #dadada;
|
||||
}
|
||||
|
||||
.term_item_tab .list>span.active {
|
||||
background-color: #424242;
|
||||
}
|
||||
|
||||
.term_item_tab .list span.active span {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.term_item_tab .list .content {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.term_item_tab .item .glyphicon {
|
||||
vertical-align: top;
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
font-size: 15px;
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
color: #ff7070 !important;
|
||||
display: none;
|
||||
transition: all 500ms;
|
||||
}
|
||||
|
||||
.term_item_tab .item .glyphicon:hover {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.term_item_tab .list span span {
|
||||
vertical-align: middle;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #666;
|
||||
display: inline-block;
|
||||
line-height: 37px;
|
||||
}
|
||||
|
||||
.term_item_tab .glyphicon {
|
||||
font-size: 15px;
|
||||
margin-left: 8px;
|
||||
vertical-align: middle;
|
||||
transition: all 500ms;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
color: #ea7575;
|
||||
}
|
||||
|
||||
.term_item_tab span.glyphicon {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.term_item_tab .glyphicon:hover {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.term_item_tab .tab_tootls {
|
||||
padding: 0;
|
||||
float: right;
|
||||
/* padding-right: 15px; */
|
||||
/* display: inline-block; */
|
||||
/* display: none; */
|
||||
}
|
||||
|
||||
|
||||
/* .term_item_tab .tab_tootls span{
|
||||
margin-left: 0;
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
height: 19px;
|
||||
color: #bbb;
|
||||
/* vertical-align: bottom;
|
||||
} */
|
||||
|
||||
.term_item_tab .tab_tootls .glyphicon-resize-full,
|
||||
.term_item_tab .tab_tootls .glyphicon-resize-small {
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
margin: 0
|
||||
}
|
||||
|
||||
.term_item_tab .addServer {
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.term_item_tab .addServer span {
|
||||
margin: 0;
|
||||
color: #20a53a;
|
||||
}
|
||||
|
||||
.term_item_tab>span:hover {
|
||||
background-color: #ececec;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.term_item_tab .addServer span:hover {
|
||||
color: #4c4c4c;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.term_content_tab {
|
||||
background-color: rgb(51, 51, 51);
|
||||
position: absolute;
|
||||
top: 38px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 10px 10px 10px 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.term_content_tab .term_item {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.term_content_tab .term_item.active {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.term-tool-button {
|
||||
position: absolute;
|
||||
right: -7px;
|
||||
top: 50%;
|
||||
width: 28px;
|
||||
height: 60px;
|
||||
background-color: #565656;
|
||||
border-top-left-radius: 30px;
|
||||
border-bottom-left-radius: 30px;
|
||||
cursor: pointer;
|
||||
z-index: 999;
|
||||
margin-top: -30px;
|
||||
}
|
||||
|
||||
.term-tool-button span {
|
||||
font-size: 12px;
|
||||
position: relative;
|
||||
left: 15%;
|
||||
top: 35%;
|
||||
font-size: 18px;
|
||||
color: #FFFFFF;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.term-tool-button:hover {
|
||||
background-color: #D8D8D8;
|
||||
}
|
||||
|
||||
.term_box {
|
||||
height: 100%;
|
||||
border-radius: 3px;
|
||||
margin-right: 300px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.term_tootls {
|
||||
width: 290px;
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
top: 15px;
|
||||
/* border: 1px solid #ececec; */
|
||||
}
|
||||
|
||||
.term_tootls .tootls_tab {
|
||||
display: inline-block;
|
||||
/* border: 1px solid #20a53a; */
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.term_tootls .tootls_tab a {
|
||||
display: inline-block;
|
||||
width: 38px;
|
||||
text-align: center;
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
color: #ececec;
|
||||
background-color: #c7c7c71a;
|
||||
}
|
||||
|
||||
.term_tootls .tab-con {
|
||||
/* position: absolute;*/
|
||||
top: 10px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.term_tootls .tootls_tab a:hover {
|
||||
background-color: #8282824d;
|
||||
}
|
||||
|
||||
.term_tootls .tootls_tab i {
|
||||
font-style: normal;
|
||||
font-size: 12px;
|
||||
color: #ececec;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.main-content .safe {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.term_content_tab .xterm .xterm-viewport{
|
||||
/* On OS X this is required in order for the scroll bar to appear fully opaque */
|
||||
background-color: #000;
|
||||
overflow-y: auto;
|
||||
cursor: default;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.term_content_tab .xterm .composition-view.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.term_content_tab .xterm .xterm-viewport::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 5px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.term_content_tab .xterm .xterm-viewport::-webkit-scrollbar-thumb {
|
||||
border-radius: 0;
|
||||
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
|
||||
background: #666;
|
||||
border-radius: 4px;
|
||||
transition: all 1s;
|
||||
}
|
||||
|
||||
.term_content_tab .xterm .xterm-viewport:hover::-webkit-scrollbar-thumb {
|
||||
background: #aaa;
|
||||
}
|
||||
|
||||
.term_content_tab .xterm .xterm-viewport::-webkit-scrollbar-track {
|
||||
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 0;
|
||||
background: #222;
|
||||
border-radius: 4px;
|
||||
transition: all 1s;
|
||||
}
|
||||
|
||||
.term_content_tab .xterm .xterm-viewport:hover::-webkit-scrollbar-track {
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
.tootls_host_list,
|
||||
.tootls_commonly_list {
|
||||
list-style: none;
|
||||
border-top: none;
|
||||
border: 1px solid #ececec;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.tootls_host_list,
|
||||
.tootls_commonly_list {
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
font-size: 12px;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.tootls_commonly_list li,
|
||||
.tootls_host_list li {
|
||||
display: inline-block;
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
color: #444;
|
||||
border-bottom: 1px solid #ececec;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tootls_commonly_list li {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.tootls_commonly_list li:hover,
|
||||
.tootls_host_list li:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.tootls_commonly_list li:hover .tootls,
|
||||
.tootls_host_list li:hover .tootls {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tootls_host_list li i {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
width: 38px;
|
||||
background-size: 16px;
|
||||
background: url('/plugins/file?name=webssh&f=img/ico-cmd.png') no-repeat center center;
|
||||
}
|
||||
|
||||
.tootls_commonly_list li>span,
|
||||
.tootls_host_list li>span {
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tootls_commonly_list li:hover>span:nth-child(2),
|
||||
.tootls_host_list li:hover>span:nth-child(2) {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.tootls_commonly_list li>span:nth-child(2),
|
||||
.tootls_host_list li>span:nth-child(2) {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 12px;
|
||||
max-width: 190px;
|
||||
}
|
||||
|
||||
|
||||
/* .tootls_host_list li>span:nth-child(2){
|
||||
margin-left: 35px;
|
||||
} */
|
||||
|
||||
.tootls_commonly_list li>span:nth-child(2) {
|
||||
max-width: 190px;
|
||||
}
|
||||
|
||||
.tootls_commonly_list li span:nth-child(3),
|
||||
.tootls_host_list li span:nth-child(3) {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.tootls_commonly_list li .tootls,
|
||||
.tootls_host_list li .tootls {
|
||||
width: 70px;
|
||||
text-align: right;
|
||||
padding-right: 10px;
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
.tootls_commonly_list li .tootls {
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
.tootls_commonly_list li .tootls span,
|
||||
.tootls_host_list li .tootls span {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tootls_commonly_list li .tootls span:nth-child(1),
|
||||
.tootls_host_list li .tootls span:nth-child(1) {
|
||||
color: #888;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tootls_commonly_list li .tootls span:nth-child(2),
|
||||
.tootls_host_list li .tootls span:nth-child(2) {
|
||||
color: #ea7575;
|
||||
}
|
||||
|
||||
.tootls_commonly_list li .tootls span:nth-child(2):hover,
|
||||
.tootls_host_list li .tootls span:nth-child(2):hover {
|
||||
color: red;
|
||||
}
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
|
||||
|
||||
<div class="main-content">
|
||||
<div class="container-fluid">
|
||||
<div class="safe bgw mtb15 pd15 radius4" style="height: 300px;">
|
||||
|
||||
<div class="term_box" id="term_box_view">
|
||||
<div class="term_item_tab">
|
||||
<div class="list">
|
||||
<span class="item active" data-host="127.0.0.1" data-id="localhost">
|
||||
<i class="icon-sucess icon"></i>
|
||||
<div class="content">
|
||||
<span>本地服务器</span>
|
||||
</div>
|
||||
<span class="icon-trem-close"></span>
|
||||
</div>
|
||||
<span class="addServer" title="添加服务器SSH信息">
|
||||
<span class="glyphicon glyphicon-plus" aria-hidden="true" ></span>
|
||||
</span>
|
||||
<span class="tab_tootls">
|
||||
<span class="glyphicon full_exit_screen glyphicon-resize-full" aria-hidden="true"></span>
|
||||
<!-- <span>全屏显示</span> -->
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 控制台 -->
|
||||
<div class="term_content_tab">
|
||||
<div class="term-tool-button tool-hide"><span class="glyphicon glyphicon-menu-right"></span></div>
|
||||
<div class="term_item active" id="localhost" data-host="127.0.0.1"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="term_tootls">
|
||||
|
||||
<div class="tab-nav">
|
||||
<span class="on" data-type="host">服务器列表</span>
|
||||
<span data-type="shell">常用命令</span>
|
||||
</div>
|
||||
<div class="tab-con">
|
||||
<div class="tab-block on">
|
||||
<div>
|
||||
<button class="btn btn-success btn-sm tootls_host_btn" type="button" data-type="host">添加服务器</button>
|
||||
</div>
|
||||
<ul class="tootls_host_list"></ul>
|
||||
</div>
|
||||
<div class="tab-block">
|
||||
<div>
|
||||
<button class="btn btn-success tootls_commonly_btn btn-sm" type="button" data-type="shell">添加命令</button>
|
||||
</div>
|
||||
<ul class="tootls_commonly_list"></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,2 +1,3 @@
|
||||
lua_shared_dict mw_total 100m;
|
||||
lua_need_request_body off;
|
||||
include {$SERVER_APP}/lua/webstats_log.lua;
|
||||
@@ -194,7 +194,7 @@
|
||||
<p onclick="wsOverview();">概览</p>
|
||||
<p onclick="wsSitesList();">网站列表</p>
|
||||
<p onclick="wsSpiderStat();">蜘蛛统计</p>
|
||||
<p onclick="wsClientStat();">客服端统计</p>
|
||||
<p onclick="wsClientStat();">客户端统计</p>
|
||||
<p onclick="wsIpStat();">IP统计</p>
|
||||
<p onclick="wsUriStat();">URI统计</p>
|
||||
<p onclick="wsSitesErrorLog();">错误日志</p>
|
||||
|
||||
@@ -5,6 +5,7 @@ import io
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import re
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
@@ -81,6 +82,18 @@ def loadLuaFile(name):
|
||||
lua_dir = getServerDir() + "/lua"
|
||||
lua_dst = lua_dir + "/" + name
|
||||
|
||||
if not os.path.exists(lua_dst):
|
||||
lua_tpl = getPluginDir() + '/lua/' + name
|
||||
content = mw.readFile(lua_tpl)
|
||||
content = content.replace('{$SERVER_APP}', getServerDir())
|
||||
content = content.replace('{$ROOT_PATH}', mw.getServerDir())
|
||||
mw.writeFile(lua_dst, content)
|
||||
|
||||
|
||||
def loadLuaFileReload(name):
|
||||
lua_dir = getServerDir() + "/lua"
|
||||
lua_dst = lua_dir + "/" + name
|
||||
|
||||
lua_tpl = getPluginDir() + '/lua/' + name
|
||||
content = mw.readFile(lua_tpl)
|
||||
content = content.replace('{$SERVER_APP}', getServerDir())
|
||||
@@ -221,6 +234,15 @@ def initDreplace():
|
||||
content = content.replace('{$ROOT_PATH}', mw.getServerDir())
|
||||
mw.writeFile(path, content)
|
||||
|
||||
# 已经安装的
|
||||
al_config = getServerDir() + "/lua/config.json"
|
||||
if os.path.exists(al_config):
|
||||
tmp = json.loads(mw.readFile(al_config))
|
||||
if tmp['global']['record_post_args'] or tmp['global']['record_get_403_args']:
|
||||
openLuaNeedRequestBody()
|
||||
else:
|
||||
closeLuaNeedRequestBody()
|
||||
|
||||
lua_dir = getServerDir() + "/lua"
|
||||
if not os.path.exists(lua_dir):
|
||||
mw.execShell('mkdir -p ' + lua_dir)
|
||||
@@ -244,6 +266,11 @@ def initDreplace():
|
||||
return 'ok'
|
||||
|
||||
|
||||
def luaRestart():
|
||||
mw.opWeb("stop")
|
||||
mw.opWeb("start")
|
||||
|
||||
|
||||
def start():
|
||||
initDreplace()
|
||||
|
||||
@@ -253,7 +280,8 @@ def start():
|
||||
if not mw.isAppleSystem():
|
||||
mw.execShell("chown -R www:www " + getServerDir())
|
||||
|
||||
mw.opWeb("reload")
|
||||
# issues:326
|
||||
luaRestart()
|
||||
return 'ok'
|
||||
|
||||
|
||||
@@ -265,22 +293,30 @@ def stop():
|
||||
import tool_task
|
||||
tool_task.removeBgTask()
|
||||
|
||||
mw.opWeb("restart")
|
||||
luaRestart()
|
||||
return 'ok'
|
||||
|
||||
|
||||
def restart():
|
||||
initDreplace()
|
||||
|
||||
mw.opWeb("reload")
|
||||
luaRestart()
|
||||
return 'ok'
|
||||
|
||||
|
||||
def reload():
|
||||
initDreplace()
|
||||
|
||||
file_list = [
|
||||
'webstats_common.lua',
|
||||
'webstats_log.lua',
|
||||
]
|
||||
for fl in file_list:
|
||||
loadLuaFileReload(fl)
|
||||
|
||||
loadDebugLogFile()
|
||||
|
||||
mw.opWeb("restart")
|
||||
luaRestart()
|
||||
return 'ok'
|
||||
|
||||
|
||||
@@ -291,6 +327,22 @@ def getGlobalConf():
|
||||
return mw.returnJson(True, 'ok', content)
|
||||
|
||||
|
||||
def openLuaNeedRequestBody():
|
||||
conf = luaConf()
|
||||
content = mw.readFile(conf)
|
||||
content = re.sub("lua_need_request_body (.*);",
|
||||
'lua_need_request_body on;', content)
|
||||
mw.writeFile(conf, content)
|
||||
|
||||
|
||||
def closeLuaNeedRequestBody():
|
||||
conf = luaConf()
|
||||
content = mw.readFile(conf)
|
||||
content = re.sub("lua_need_request_body (.*);",
|
||||
'lua_need_request_body off;', content)
|
||||
mw.writeFile(conf, content)
|
||||
|
||||
|
||||
def setGlobalConf():
|
||||
args = getArgs()
|
||||
|
||||
@@ -298,14 +350,23 @@ def setGlobalConf():
|
||||
content = mw.readFile(conf)
|
||||
content = json.loads(content)
|
||||
|
||||
open_force_get_request_body = False
|
||||
for v in ['record_post_args', 'record_get_403_args']:
|
||||
data = checkArgs(args, [v])
|
||||
if data[0]:
|
||||
rval = False
|
||||
if args[v] == "true":
|
||||
rval = True
|
||||
open_force_get_request_body = True
|
||||
|
||||
content['global'][v] = rval
|
||||
|
||||
# 开启强制获取日志配置
|
||||
if open_force_get_request_body:
|
||||
openLuaNeedRequestBody()
|
||||
else:
|
||||
closeLuaNeedRequestBody()
|
||||
|
||||
for v in ['ip_top_num', 'uri_top_num', 'save_day']:
|
||||
data = checkArgs(args, [v])
|
||||
if data[0]:
|
||||
@@ -333,7 +394,7 @@ def setGlobalConf():
|
||||
mw.writeFile(conf, json.dumps(content))
|
||||
conf_lua = getServerDir() + "/lua/webstats_config.lua"
|
||||
listToLuaFile(conf_lua, content)
|
||||
mw.restartWeb()
|
||||
luaRestart()
|
||||
return mw.returnJson(True, '设置成功')
|
||||
|
||||
|
||||
@@ -405,7 +466,7 @@ def setSiteConf():
|
||||
for v in ['cdn_headers', 'exclude_extension', 'exclude_status', 'exclude_ip']:
|
||||
data = checkArgs(args, [v])
|
||||
if data[0]:
|
||||
site_conf[v] = args[v].split("\\n")
|
||||
site_conf[v] = args[v].strip().split("\\n")
|
||||
|
||||
data = checkArgs(args, ['exclude_url'])
|
||||
if data[0]:
|
||||
@@ -426,7 +487,7 @@ def setSiteConf():
|
||||
mw.writeFile(conf, json.dumps(content))
|
||||
conf_lua = getServerDir() + "/lua/webstats_config.lua"
|
||||
listToLuaFile(conf_lua, content)
|
||||
mw.restartWeb()
|
||||
luaRestart()
|
||||
return mw.returnJson(True, '设置成功')
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"name": "webstats",
|
||||
"title": "网站统计",
|
||||
"shell": "install.sh",
|
||||
"versions":["0.2.2"],
|
||||
"versions":["0.2.3"],
|
||||
"tip": "soft",
|
||||
"install_pre_inspection":true,
|
||||
"checks": "server/webstats",
|
||||
|
||||
@@ -14,6 +14,12 @@ install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
VERSION=$2
|
||||
sys_os=`uname`
|
||||
|
||||
cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
|
||||
HTTP_PREFIX="https://"
|
||||
if [ ! -z "$cn" ];then
|
||||
HTTP_PREFIX="https://ghproxy.com/"
|
||||
fi
|
||||
|
||||
if [ "$sys_os" == "Darwin" ];then
|
||||
BAK='_bak'
|
||||
else
|
||||
@@ -97,7 +103,7 @@ Install_App()
|
||||
# 缓存数据
|
||||
GEO_VERSION=$(get_latest_release "P3TERX/GeoLite.mmdb")
|
||||
if [ ! -f $serverPath/source/webstats/GeoLite2-City.mmdb ];then
|
||||
wget --no-check-certificate -O $serverPath/source/webstats/GeoLite2-City.mmdb https://github.com/P3TERX/GeoLite.mmdb/releases/download/${GEO_VERSION}/GeoLite2-City.mmdb
|
||||
wget --no-check-certificate -O $serverPath/source/webstats/GeoLite2-City.mmdb ${HTTP_PREFIX}github.com/P3TERX/GeoLite.mmdb/releases/download/${GEO_VERSION}/GeoLite2-City.mmdb
|
||||
fi
|
||||
|
||||
if [ -f $serverPath/source/webstats/GeoLite2-City.mmdb ];then
|
||||
@@ -108,6 +114,10 @@ Install_App()
|
||||
echo '安装完成' > $install_tmp
|
||||
|
||||
cd $rootPath && python3 ${rootPath}/plugins/webstats/index.py start
|
||||
|
||||
|
||||
# delete install data
|
||||
rm -rf $serverPath/source/webstats
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
|
||||
@@ -853,16 +853,14 @@ function wsSitesListRequest(page){
|
||||
});
|
||||
},
|
||||
yes:function(){
|
||||
var select = $('#webstats .tab-nav span');
|
||||
var select_pos = 0;
|
||||
$('#webstats .tab-nav span').each(function(i){
|
||||
$('#site_conf .tab-nav span').each(function(i){
|
||||
if ($(this).hasClass('on')){select_pos = i;}
|
||||
});
|
||||
var args = {"site":domain};
|
||||
if ( [0,1,2,4].indexOf(select_pos)>-1 ){
|
||||
var setting_cdn = $('textarea[name="setting-cdn"]').val();
|
||||
// var list = setting_cdn.split('\n')
|
||||
|
||||
if ( select_pos == 0 ){
|
||||
args['cdn_headers'] = setting_cdn;
|
||||
} else if ( select_pos == 1 ){
|
||||
|
||||
@@ -94,7 +94,7 @@ function _M.setInputSn(self, input_sn)
|
||||
if config[input_sn] == nil then
|
||||
auto_config = global_config
|
||||
else
|
||||
auto_config = config[site]
|
||||
auto_config = config[input_sn]
|
||||
for k, v in pairs(global_config) do
|
||||
if auto_config[k] == nil then
|
||||
auto_config[k] = v
|
||||
@@ -202,19 +202,28 @@ function _M.get_http_origin(self)
|
||||
local data = ""
|
||||
local headers = ngx.req.get_headers()
|
||||
if not headers then return data end
|
||||
if method ~='GET' then
|
||||
local req_method = ngx.req.get_method()
|
||||
if req_method ~='GET' then
|
||||
-- API disabled in the context of log_by_lua*
|
||||
-- ngx.req.read_body()
|
||||
data = ngx.req.get_body_data()
|
||||
|
||||
-- proxy_pass, fastcgi_pass, uwsgi_pass, and scgi_pass
|
||||
data = ngx.var.request_body
|
||||
if not data then
|
||||
data = ngx.req.get_body_data()
|
||||
end
|
||||
|
||||
if not data then
|
||||
data = ngx.req.get_post_args(1000000)
|
||||
end
|
||||
|
||||
if "string" == type(data) then
|
||||
headers["payload"] = data
|
||||
end
|
||||
|
||||
if "table" == type(data) then
|
||||
headers = table.concat(headers, data)
|
||||
-- headers = table.concat(headers, data)
|
||||
headers["payload"] = table.concat(data, "&")
|
||||
end
|
||||
end
|
||||
return json.encode(headers)
|
||||
@@ -616,7 +625,8 @@ end
|
||||
|
||||
|
||||
function _M._update_stat_pre(self, db, stat_table, key)
|
||||
local update_stat_stmt = db:prepare(string.format("INSERT INTO %s(time) SELECT :time WHERE NOT EXISTS(SELECT time FROM %s WHERE time=:time);", stat_table, stat_table))
|
||||
local local_sql = string.format("INSERT INTO %s(time) SELECT :time WHERE NOT EXISTS(SELECT time FROM %s WHERE time=:time);", stat_table, stat_table)
|
||||
local update_stat_stmt = db:prepare(local_sql)
|
||||
update_stat_stmt:bind_names{time=key}
|
||||
update_stat_stmt:step()
|
||||
update_stat_stmt:finalize()
|
||||
@@ -632,7 +642,8 @@ end
|
||||
function _M.update_stat(self, db, stat_table, key, columns)
|
||||
-- 根据指定表名,更新统计数据
|
||||
if not columns then return end
|
||||
local stmt = db:prepare(string.format("INSERT INTO %s(time) SELECT :time WHERE NOT EXISTS(SELECT time FROM %s WHERE time=:time);", stat_table, stat_table))
|
||||
local local_sql = string.format("INSERT INTO %s(time) SELECT :time WHERE NOT EXISTS(SELECT time FROM %s WHERE time=:time);", stat_table, stat_table)
|
||||
local stmt = db:prepare(local_sql)
|
||||
stmt:bind_names{time=key}
|
||||
local res, err = stmt:step()
|
||||
stmt:finalize()
|
||||
|
||||
@@ -8,7 +8,7 @@ log_by_lua_block {
|
||||
package.path = cpath .. "?.lua;" .. package.path
|
||||
end
|
||||
|
||||
local ver = '0.2.2'
|
||||
local ver = '0.2.3'
|
||||
local debug_mode = true
|
||||
|
||||
local __C = require "webstats_common"
|
||||
|
||||
@@ -95,7 +95,7 @@ def removeBgTask():
|
||||
|
||||
data = api.delete(cfg["task_id"])
|
||||
if data[0]:
|
||||
print(data[1])
|
||||
# print(data[1])
|
||||
cfg["task_id"] = -1
|
||||
mw.writeFile(getTaskConf(), json.dumps(cfg))
|
||||
return True
|
||||
|
||||
@@ -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 xhPost(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);
|
||||
}
|
||||
@@ -31,12 +21,10 @@ function xhPost(method,args,callback){
|
||||
},'json');
|
||||
}
|
||||
|
||||
|
||||
function xhAsyncPost(method,args){
|
||||
|
||||
var _args = null;
|
||||
if (typeof(args) == 'string'){
|
||||
_args = JSON.stringify(str2Obj(args));
|
||||
_args = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
_args = JSON.stringify(args);
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
setuptools>=33.1.1
|
||||
Werkzeug>=1.0.1
|
||||
wheel==0.33.6
|
||||
wheel==0.37.1
|
||||
gunicorn==20.1.0
|
||||
requests==2.27.1
|
||||
urllib3>=1.21.1
|
||||
@@ -19,6 +19,7 @@ configparser==5.2.0
|
||||
python-engineio==4.3.2
|
||||
python-socketio>=4.2.0
|
||||
flask-socketio==5.2.0
|
||||
flask_sockets==0.2.1
|
||||
zmq==0.0.0
|
||||
paramiko>=2.8.0
|
||||
pymongo
|
||||
@@ -27,4 +28,4 @@ Jinja2>=2.11.2
|
||||
flask-caching>=1.10.1
|
||||
bcrypt==3.1.3
|
||||
PyMySQL==1.0.2
|
||||
whitenoise==5.3.0
|
||||
whitenoise==5.3.0
|
||||
|
||||
+56
-126
@@ -8,11 +8,11 @@ import shutil
|
||||
import uuid
|
||||
import json
|
||||
import traceback
|
||||
import socket
|
||||
|
||||
# reload(sys)
|
||||
# sys.setdefaultencoding('utf-8')
|
||||
|
||||
|
||||
import paramiko
|
||||
from datetime import timedelta
|
||||
|
||||
from flask import Flask
|
||||
@@ -27,6 +27,7 @@ from flask import render_template_string, abort
|
||||
from flask_caching import Cache
|
||||
from flask_session import Session
|
||||
|
||||
|
||||
from whitenoise import WhiteNoise
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
@@ -67,6 +68,10 @@ app.config['SESSION_USE_SIGNER'] = True
|
||||
app.config['SESSION_KEY_PREFIX'] = 'MW_:'
|
||||
app.config['SESSION_COOKIE_NAME'] = "MW_VER_1"
|
||||
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=31)
|
||||
|
||||
if mw.isAppleSystem():
|
||||
app.config['DEBUG'] = True
|
||||
|
||||
# Session(app)
|
||||
|
||||
# 设置BasicAuth
|
||||
@@ -75,7 +80,7 @@ app.config['BASIC_AUTH_OPEN'] = False
|
||||
if os.path.exists(basic_auth_conf):
|
||||
try:
|
||||
ba_conf = json.loads(mw.readFile(basic_auth_conf))
|
||||
print(ba_conf)
|
||||
# print(ba_conf)
|
||||
app.config['BASIC_AUTH_USERNAME'] = ba_conf['basic_user']
|
||||
app.config['BASIC_AUTH_PASSWORD'] = ba_conf['basic_pwd']
|
||||
app.config['BASIC_AUTH_OPEN'] = ba_conf['open']
|
||||
@@ -88,6 +93,10 @@ from flask_socketio import SocketIO, emit, send
|
||||
socketio = SocketIO()
|
||||
socketio.init_app(app)
|
||||
|
||||
# sockets
|
||||
from flask_sockets import Sockets
|
||||
sockets = Sockets(app)
|
||||
|
||||
# from gevent.pywsgi import WSGIServer
|
||||
# from geventwebsocket.handler import WebSocketHandler
|
||||
# http_server = WSGIServer(('0.0.0.0', '7200'), app,
|
||||
@@ -210,7 +219,8 @@ def publicObject(toObject, func, action=None, get=None):
|
||||
return mw.getJson(data)
|
||||
except Exception as e:
|
||||
# API发生错误记录
|
||||
print(traceback.print_exc())
|
||||
if mw.isDebugMode():
|
||||
print(traceback.print_exc())
|
||||
data = {'msg': '访问异常:' + str(e) + '!', "status": False}
|
||||
return mw.getJson(data)
|
||||
|
||||
@@ -223,14 +233,29 @@ def publicObject(toObject, func, action=None, get=None):
|
||||
# print(os)
|
||||
# return mw.getLocalIp()
|
||||
|
||||
# 仅针对webhook插件
|
||||
@app.route("/hook")
|
||||
@app.route("/.well-known/acme-challenge/<val>")
|
||||
def wellknow(val=None):
|
||||
# 申请面板ssl使用
|
||||
f = mw.getRunDir() + "/tmp/.well-known/acme-challenge/" + val
|
||||
if os.path.exists(f):
|
||||
return mw.readFile(f)
|
||||
return ''
|
||||
|
||||
|
||||
@app.route("/hook", methods=['POST', 'GET'])
|
||||
def webhook():
|
||||
# 仅针对webhook插件
|
||||
input_args = {
|
||||
'access_key': request.args.get('access_key', '').strip(),
|
||||
'params': request.args.get('params', '').strip()
|
||||
}
|
||||
|
||||
if request.method == 'POST':
|
||||
input_args = {
|
||||
'access_key': request.form.get('access_key', '').strip(),
|
||||
'params': request.form.get('params', '').strip()
|
||||
}
|
||||
|
||||
wh_install_path = mw.getServerDir() + '/webhook'
|
||||
if not os.path.exists(wh_install_path):
|
||||
return mw.returnJson(False, '请先安装WebHook插件!')
|
||||
@@ -531,140 +556,45 @@ def index(reqClass=None, reqAction=None, reqData=None):
|
||||
|
||||
|
||||
##################### ssh start ###########################
|
||||
ssh = None
|
||||
shell = None
|
||||
shell_client = None
|
||||
|
||||
|
||||
def create_rsa():
|
||||
# mw.execShell("rm -f /root/.ssh/*")
|
||||
if not os.path.exists('/root/.ssh/authorized_keys'):
|
||||
mw.execShell('touch /root/.ssh/authorized_keys')
|
||||
@socketio.on('webssh_websocketio')
|
||||
def webssh_websocketio(data):
|
||||
if not isLogined():
|
||||
emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'})
|
||||
return
|
||||
|
||||
if not os.path.exists('/root/.ssh/id_rsa.pub') and os.path.exists('/root/.ssh/id_rsa'):
|
||||
mw.execShell(
|
||||
'echo y | ssh-keygen -q -t rsa -P "" -f /root/.ssh/id_rsa')
|
||||
else:
|
||||
mw.execShell('ssh-keygen -q -t rsa -P "" -f /root/.ssh/id_rsa')
|
||||
global shell_client
|
||||
if not shell_client:
|
||||
import ssh_terminal
|
||||
shell_client = ssh_terminal.ssh_terminal()
|
||||
|
||||
mw.execShell('cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys')
|
||||
mw.execShell('chmod 600 /root/.ssh/authorized_keys')
|
||||
|
||||
|
||||
def clear_ssh():
|
||||
# 服务器IP
|
||||
ip = mw.getHostAddr()
|
||||
sh = '''
|
||||
#!/bin/bash
|
||||
PLIST=`who | grep localhost | awk '{print $2}'`
|
||||
for i in $PLIST
|
||||
do
|
||||
ps -t /dev/$i |grep -v TTY | awk '{print $1}' | xargs kill -9
|
||||
done
|
||||
|
||||
#getHostAddr
|
||||
PLIST=`who | grep "${ip}" | awk '{print $2}'`
|
||||
for i in $PLIST
|
||||
do
|
||||
ps -t /dev/$i |grep -v TTY | awk '{print $1}' | xargs kill -9
|
||||
done
|
||||
'''
|
||||
if not mw.isAppleSystem():
|
||||
info = mw.execShell(sh)
|
||||
print(info[0], info[1])
|
||||
|
||||
|
||||
def connect_ssh():
|
||||
# print 'connect_ssh ....'
|
||||
# clear_ssh()
|
||||
global shell, ssh
|
||||
if not os.path.exists('/root/.ssh/id_rsa') or not os.path.exists('/root/.ssh/id_rsa.pub'):
|
||||
create_rsa()
|
||||
|
||||
# 检查是否写入authorized_keys
|
||||
data = mw.execShell("cat /root/.ssh/id_rsa.pub | awk '{print $3}'")
|
||||
if data[0] != "":
|
||||
ak_data = mw.execShell(
|
||||
"cat /root/.ssh/authorized_keys | grep " + data[0])
|
||||
if ak_data[0] == "":
|
||||
mw.execShell(
|
||||
'cat /root/.ssh/id_rsa.pub >> /root/.ssh/authorized_keys')
|
||||
mw.execShell('chmod 600 /root/.ssh/authorized_keys')
|
||||
|
||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
|
||||
try:
|
||||
ssh.connect(mw.getHostAddr(), mw.getSSHPort(), timeout=5)
|
||||
except Exception as e:
|
||||
ssh.connect('127.0.0.1', mw.getSSHPort())
|
||||
except Exception as e:
|
||||
ssh.connect('localhost', mw.getSSHPort())
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
shell = ssh.invoke_shell(term='xterm', width=83, height=21)
|
||||
shell.setblocking(0)
|
||||
return True
|
||||
shell_client.run(request.sid, data)
|
||||
return
|
||||
|
||||
|
||||
@socketio.on('webssh')
|
||||
def webssh(msg):
|
||||
# print('webssh ...')
|
||||
global shell
|
||||
if not isLogined():
|
||||
emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'})
|
||||
return None
|
||||
global shell, ssh
|
||||
ssh_success = True
|
||||
|
||||
if not shell:
|
||||
ssh_success = connect_ssh()
|
||||
if not shell:
|
||||
emit('server_response', {'data': '连接SSH服务失败!\r\n'})
|
||||
return
|
||||
shell = mw.connectSsh()
|
||||
|
||||
if shell.exit_status_ready():
|
||||
ssh_success = connect_ssh()
|
||||
if not ssh_success:
|
||||
emit('server_response', {'data': '连接SSH服务失败!\r\n'})
|
||||
return
|
||||
shell.send(msg)
|
||||
try:
|
||||
time.sleep(0.005)
|
||||
recv = shell.recv(4096)
|
||||
emit('server_response', {'data': recv.decode("utf-8")})
|
||||
except Exception as ex:
|
||||
pass
|
||||
# print 'webssh:' + str(ex)
|
||||
|
||||
|
||||
@socketio.on('connect_event')
|
||||
def connected_msg(msg):
|
||||
if not isLogined():
|
||||
emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'})
|
||||
return None
|
||||
global shell, ssh
|
||||
ssh_success = True
|
||||
if not shell:
|
||||
ssh_success = connect_ssh()
|
||||
# print(ssh_success)
|
||||
if not ssh_success:
|
||||
emit('server_response', {'data': '连接SSH服务失败!\r\n'})
|
||||
return
|
||||
try:
|
||||
recv = shell.recv(8192)
|
||||
# print recv.decode("utf-8")
|
||||
emit('server_response', {'data': recv.decode("utf-8")})
|
||||
except Exception as e:
|
||||
pass
|
||||
# print 'connected_msg:' + str(e)
|
||||
|
||||
|
||||
if not mw.isAppleSystem():
|
||||
try:
|
||||
import paramiko
|
||||
ssh = paramiko.SSHClient()
|
||||
|
||||
# 启动尝试时连接
|
||||
# connect_ssh()
|
||||
except Exception as e:
|
||||
print("本地终端无法使用")
|
||||
shell = mw.connectSsh()
|
||||
|
||||
if shell:
|
||||
shell.send(msg)
|
||||
try:
|
||||
time.sleep(0.005)
|
||||
recv = shell.recv(4096)
|
||||
emit('server_response', {'data': recv.decode("utf-8")})
|
||||
except Exception as ex:
|
||||
emit('server_response', {'data': str(ex)})
|
||||
|
||||
##################### ssh end ###########################
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user