Merge pull request #577 from midoks/dev

0.16.9
pull/580/head 0.16.9
Mr Chen 12 months ago committed by GitHub
commit dcae829a1c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      .gitignore
  2. 7
      README.md
  3. 2
      class/core/config_api.py
  4. 15
      class/core/files_api.py
  5. 3
      class/core/mw.py
  6. 34
      class/plugin/orm.py
  7. 3
      cmd.md
  8. 1
      plugins/clean/index.py
  9. 10
      plugins/data_query/index.html
  10. 13
      plugins/data_query/static/html/index.html
  11. 6
      plugins/data_query/static/js/app.js
  12. 1
      plugins/mongodb/index.html
  13. 36
      plugins/mongodb/index.py
  14. 145
      plugins/mongodb/js/mongodb.js
  15. 2
      plugins/mysql-apt/scripts/backup.py
  16. 2
      plugins/mysql-yum/scripts/backup.py
  17. 27
      plugins/mysql/README.md
  18. 2
      plugins/mysql/conf/mysql.sql
  19. 547
      plugins/mysql/index.py
  20. 7
      plugins/mysql/install.sh
  21. 134
      plugins/mysql/js/mysql.js
  22. 2
      plugins/openresty/conf/nginx.conf
  23. 4
      plugins/php/versions/common/mongodb.sh
  24. 13
      plugins/redis/config/redis.conf
  25. 2
      plugins/redis/init.d/redis.tpl
  26. 23
      plugins/redis/install.sh
  27. 2
      plugins/redis/tpl/redis_cluster.conf
  28. 2
      plugins/redis/tpl/redis_simple.conf
  29. 19
      plugins/redis/tpl/redis_slave.conf
  30. 75
      plugins/redis/tpl/redis_slave_mem.conf
  31. 486
      plugins/sphinx/class/sphinx_make.py
  32. 20
      plugins/sphinx/index.html
  33. 94
      plugins/sphinx/index.py
  34. 3
      plugins/sphinx/info.json
  35. 2
      plugins/sphinx/init.d/sphinx.tpl
  36. 120
      plugins/sphinx/install.sh
  37. 228
      plugins/sphinx/js/sphinx.js
  38. 4
      plugins/tgbot/startup/extend/push_ad.py
  39. 4
      plugins/tgbot/startup/extend/push_notice_msg.py
  40. 4
      plugins/webstats/index.html
  41. 15
      plugins/webstats/index.py
  42. 106
      plugins/webstats/js/stats.js
  43. 15
      plugins/webstats/webstats_index.py
  44. 2
      requirements.txt
  45. 22
      route/static/app/files.js
  46. 2
      route/static/app/index.js
  47. 22
      route/static/app/public.js
  48. 3
      route/static/css/site.css
  49. 35
      route/templates/default/layout.html
  50. 19
      scripts/backup.py
  51. 39
      scripts/init.d/mw.tpl
  52. 1
      scripts/install/alma.sh
  53. 1
      scripts/install/amazon.sh
  54. 1
      scripts/install/arch.sh
  55. 1
      scripts/install/centos.sh
  56. 1
      scripts/install/debian.sh
  57. 1
      scripts/install/euler.sh
  58. 1
      scripts/install/fedora.sh
  59. 1
      scripts/install/freebsd.sh
  60. 1
      scripts/install/opensuse.sh
  61. 4
      scripts/install/rhel.sh
  62. 1
      scripts/install/rocky.sh
  63. 1
      scripts/install/ubuntu.sh
  64. 23
      tools.py
  65. 3
      version/r3.6.txt
  66. 3
      version/r3.7.txt

1
.gitignore vendored

@ -173,6 +173,7 @@ plugins/v2ray
plugins/frp
plugins/file_search
plugins/proxysql
plugins/tidb
debug.out

@ -110,9 +110,12 @@ docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:
```
### 版本更新 0.16.8
### 版本更新 0.16.9
- 首页对网络/磁盘IO进行更细致的展示。
- mysql同步优化,享受丝滑般感觉。
- 网站统计 - 实时-可调节1-10s。
- 网站统计 - 加入大小条件。
- Sphinx优化。
### JSDelivr安装地址

@ -28,7 +28,7 @@ from flask import request
class config_api:
__version = '0.16.8'
__version = '0.16.9'
__api_addr = 'data/api.json'
# 统一默认配置文件

@ -121,6 +121,7 @@ class files_api:
def fileAccessApi(self):
filename = request.form.get('filename', '')
data = self.getAccess(filename)
data['sys_users'] = self.getSysUserList()
return mw.getJson(data)
def setFileAccessApi(self):
@ -933,6 +934,20 @@ class files_api:
data['chown'] = 'www'
return data
def getSysUserList(self):
pwd_file = '/etc/passwd'
if os.path.exists(pwd_file):
content = mw.readFile(pwd_file)
clist = content.split('\n')
sys_users = []
for line in clist:
if line.find(":")<0:
continue
lines = line.split(":",1)
sys_users.append(lines[0])
return sys_users
return ['root','mysql','www']
# 计算文件数量
def getCount(self, path, search):
i = 0

@ -79,10 +79,11 @@ def getRootDir():
def getPluginDir():
return getRunDir() + '/plugins'
def getPanelDataDir():
return getRunDir() + '/data'
def getMWLogs():
return getRunDir() + '/logs'
def getPanelTmp():
return getRunDir() + '/tmp'

@ -17,37 +17,50 @@ class ORM:
__DB_CUR = None
__DB_ERR = None
__DB_CNF = '/etc/my.cnf'
__DB_TIMEOUT=1
__DB_SOCKET = '/www/server/mysql/mysql.sock'
__DB_CHARSET = "utf8"
def __Conn(self):
# print(self.__DB_HOST, self.__DB_USER)
'''连接数据库'''
try:
if os.path.exists(self.__DB_SOCKET):
if self.__DB_HOST != 'localhost':
try:
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT,
cursorclass=pymysql.cursors.DictCursor)
except Exception as e:
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT,
cursorclass=pymysql.cursors.DictCursor)
elif os.path.exists(self.__DB_SOCKET):
try:
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT,
unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor)
except Exception as e:
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT,
unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor)
else:
try:
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT,
cursorclass=pymysql.cursors.DictCursor)
except Exception as e:
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT,
cursorclass=pymysql.cursors.DictCursor)
self.__DB_CUR = self.__DB_CONN.cursor()
@ -80,6 +93,10 @@ class ORM:
def getPwd(self):
return self.__DB_PASS
def setTimeout(self, timeout = 1):
self.__DB_TIMEOUT = timeout
return True
def setDbName(self, name):
self.__DB_NAME = name
@ -95,6 +112,13 @@ class ORM:
except Exception as ex:
return ex
def ping(self):
try:
self.__DB_CONN.ping()
except Exception as e:
print(e)
return True
def query(self, sql):
# 执行SQL语句返回数据集
if not self.__Conn():

@ -5,7 +5,8 @@
```
/etc/init.d/mw default | 显示登录信息
/etc/init.d/mw db | 快捷连接数据库
/etc/init.d/mw db | 快捷连接MySQL
/etc/init.d/mw redis | 快捷连接Redis
----------------------------------------
mw open | 开启面板
mw close | 关闭面板

@ -116,6 +116,7 @@ def initConf():
"/www/server/php/82/var/log",
"/www/server/php/83/var/log",
"/www/server/php/84/var/log",
"/www/server/openresty/nginx/logs",
"/www/server/phpmyadmin",
"/www/server/redis/data",
"/www/server/cron",

@ -5,6 +5,16 @@
white-space: nowrap;
vertical-align: middle;
}
#select_db xm-select{
min-height: 30px;
line-height: 30px;
}
#select_db xm-select *{
font-size: 12px;
}
</style>
<div class="bt-form">

@ -123,9 +123,16 @@
<input name="mongodb_field_value" type="text" class="bt-input-text" placeholder="请输入查找内容">
</div>
<button type="button" class="mongodb_find btn btn-success btn-sm mr5 ml5" style="float:right;">
<span>查找</span>
</button>
<div style="float:right;">
<button type="button" class="mongodb_find btn btn-success btn-sm mr5 ml5" >
<span>查找</span>
</button>
<button type="button" class="mongodb_refresh btn btn-success btn-sm mr5 ml5">
<span>刷新</span>
</button>
</div>
</div>
<div class="pull-right" style="padding-top: 5px;">

@ -738,6 +738,10 @@ function mongodbInitField(f, data){
}
mongodbDataList(1);
});
$('#mongodb .mongodb_refresh').unbind('click').click(function(){
mongodbDataList(1);
});
}
var mogodb_db_list;
@ -1252,7 +1256,7 @@ function redisBatchClear(){
xm_db_list = xmSelect.render({
el: '#select_db',
repeat: true,
repeat: false,
toolbar: {show: true},
data: idx_db,
});

@ -63,6 +63,7 @@
<p onclick="mongoStatus();">负载状态</p>
<p onclick="mongoReplStatus();">复制状态</p>
<p onclick="pluginLogs('mongodb','','run_log');">日志</p>
<p onclick="mgdbReadme();">相关说明</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con" style="height:455px;overflow: auto;"></div>

@ -18,7 +18,6 @@ if mw.isAppleSystem():
# /usr/lib/systemd/system/mongod.service
# /var/lib/mongo
# python3 /www/server/mdserver-web/plugins/mongodb/index.py repl_init
# python3 /www/server/mdserver-web/plugins/mongodb/index.py run_repl_info
@ -107,6 +106,9 @@ def getConfIp():
data = getConfigData()
return data['net']['bindIp']
def getConfLocalIp():
return '127.0.0.1'
def getConfPort():
data = getConfigData()
return data['net']['port']
@ -186,7 +188,7 @@ def mongdbClientS():
import pymongo
port = getConfPort()
auth = getConfAuth()
ip = getConfIp()
ip = getConfLocalIp()
mg_root = pSqliteDb('config').where('id=?', (1,)).getField('mg_root')
if auth == 'disabled':
@ -200,7 +202,7 @@ def mongdbClient():
import pymongo
port = getConfPort()
auth = getConfAuth()
ip = getConfIp()
ip = getConfLocalIp()
mg_root = pSqliteDb('config').where('id=?', (1,)).getField('mg_root')
# print(ip,port,auth,mg_root)
if auth == 'disabled':
@ -488,14 +490,13 @@ def runDocInfo():
def runReplInfo():
client = mongdbClient()
db = client.admin
result = {}
try:
serverStatus = db.command('serverStatus')
except Exception as e:
return mw.returnJson(False, str(e))
d = getConfigData()
result = {}
if 'replication' in d and 'replSetName' in d['replication']:
result['repl_name'] = d['replication']['replSetName']
@ -518,7 +519,22 @@ def runReplInfo():
hosts = mw.getDefault(repl,'hosts', '')
result['hosts'] = ','.join(hosts)
result['members'] = []
try:
members_list = []
replStatus = db.command('replSetGetStatus')
if 'members' in replStatus:
members = replStatus['members']
for m in members:
t = {}
t['name'] = m['name']
t['stateStr'] = m['stateStr']
t['uptime'] = m['uptime']
members_list.append(t)
result['members'] = members_list
except Exception as e:
pass
return mw.returnJson(True, 'OK', result)
def getDbList():
@ -602,7 +618,7 @@ def addDb():
username = data_name
client[data_name].chat.insert_one({})
client[data_name].zchat.insert_one({})
user_roles = [{'role': 'dbOwner', 'db': data_name}, {'role': 'userAdmin', 'db': data_name}]
if auth_status:
# db.command("dropUser", username)
@ -787,7 +803,7 @@ def toDbBase(find):
data_name = find['name']
db = client[data_name]
db.chat.insert_one({})
db.zchat.insert_one({})
user_roles = [{'role': 'dbOwner', 'db': data_name}, {'role': 'userAdmin', 'db': data_name}]
try:
db_admin.command("createUser", find['username'], pwd=find['password'], roles=user_roles)
@ -1011,10 +1027,6 @@ def replSetNode():
add_node = args['node'].strip()
idx = int(args['idx'])
priority = -1
if 'priority' in args:
priority = args['priority'].strip()

@ -159,6 +159,12 @@ function mongoReplStatus() {
<tr><th>me</th><td>' + rdata.me + '</td><td></td></tr>';
}
var tbody_members = '';
var member_list = rdata['members'];
for (var i = 0; i < member_list.length; i++) {
tbody_members += '<tr><th>'+member_list[i]['name']+'</th><td>' + member_list[i]['stateStr'] + '</td><td>'+member_list[i]['uptime']+'</td></tr>';
}
// console.log(rdata);
var repl_on = 'btn-danger';
var repl_on_title = '未开启';
@ -176,9 +182,14 @@ function mongoReplStatus() {
con += '<div class="divtable">\
<table class="table table-hover table-bordered" style="width: 660px;">\
<thead><th>字段</th><th></th><th></th></thead>\
<tbody>\
'+tbody+'\
<tbody>\
<tbody>'+tbody+'<tbody>\
</table>\
</div>';
con += '<div class="divtable" style="margin-top:5px;">\
<table class="table table-hover table-bordered" style="width: 660px;">\
<thead><th>IP</th><th></th><th>线</th></thead>\
<tbody>'+tbody_members+'<tbody>\
</table>\
</div>';
@ -188,67 +199,66 @@ function mongoReplStatus() {
//设置副本名称
function mongoReplCfgReplSetName(){
mgPost('run_doc_info', '', '', function(rdata){
var rdata = $.parseJSON(rdata.data);
// <select class='bt-input-text mr5' name='replSetName' style='width:100px'><option value=''></option></select>
layer.open({
type: 1,
area: '300px',
title: '设置副本名称',
closeBtn: 1,
shift: 5,
shadeClose: false,
btn:["提交","关闭"],
content: "<form class='bt-form pd20' id='mod_pwd'>\
<div class='line'>\
<span class='tname'>同步副本名称</span>\
<div class='info-r'>\
<input class='bt-input-text mr5' name='replSetName' style='width:100px' />\
</div>\
</div>\
</form>",
layer.open({
type: 1,
area: '300px',
title: '设置副本名称',
closeBtn: 1,
shift: 5,
shadeClose: false,
btn:["提交","关闭"],
content: "<form class='bt-form pd20' id='mod_pwd'>\
<div class='line'>\
<span class='tname'>同步副本</span>\
<div class='info-r'>\
<select class='bt-input-text mr5' name='replSetName' style='width:100px'>\
<option value=''></option>\
</select>\
</div>\
</div>\
</form>",
success: function(){
// console.log(rdata);
var rlist = rdata['dbs'];
var dbs = [];
var selectHtml = '';
for (var i = 0; i < rlist.length; i++) {
// console.log(rlist[i]['db']);
var dbname = rlist[i]['db'];
if (['admin','local','config'].includes(dbname)){
} else {
dbs.push(dbname);
}
}
if (dbs.length == 0 ){
selectHtml += "<option value=''>无</option>";
}
for (index in dbs) {
selectHtml += "<option value='"+dbs[index]+"'>"+dbs[index]+"</option>";
}
$('select[name="replSetName"]').html(selectHtml);
},
yes:function(index){
var data = {};
data['name'] = $('select[name=replSetName]').val();
mgPost('repl_set_name', '',data, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
if (rdata['status']){
layer.close(index);
mongoReplCfgInit();
}
},{icon: rdata.status ? 1 : 2});
});
}
});
success: function(){
// // console.log(rdata);
// var rlist = rdata['dbs'];
// var dbs = [];
// var selectHtml = '';
// for (var i = 0; i < rlist.length; i++) {
// // console.log(rlist[i]['db']);
// var dbname = rlist[i]['db'];
// if (['admin','local','config'].includes(dbname)){
// } else {
// dbs.push(dbname);
// }
// }
// if (dbs.length == 0 ){
// selectHtml += "<option value=''>无</option>";
// }
// for (index in dbs) {
// selectHtml += "<option value='"+dbs[index]+"'>"+dbs[index]+"</option>";
// }
// $('select[name="replSetName"]').html(selectHtml);
},
yes:function(index){
var data = {};
data['name'] = $('input[name=replSetName]').val();
if (data['name'] == ''){
layer.msg("副本名称不能为空");
return;
}
mgPost('repl_set_name', '',data, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
if (rdata['status']){
layer.close(index);
mongoReplCfgInit();
}
},{icon: rdata.status ? 1 : 2});
});
}
});
}
@ -1193,3 +1203,12 @@ function importDbExternal(file,name){
});
}
function mgdbReadme(){
var readme = '<ul class="help-info-text c7">';
readme += '<li>认证同步说明</li>';
readme += '<li>root/用户,配置Key完全一致才能同步。</li>';
readme += '</ul>';
$('.soft-man-con').html(readme);
}

@ -63,7 +63,7 @@ class backupTools:
if len(mycnf) > 100:
mw.writeFile(db_path + '/etc/my.cnf', mycnf)
cmd = db_path + "/bin/usr/bin/mysqldump --defaults-file=" + my_conf_path + " --single-transaction --quick --default-character-set=utf8 " + \
cmd = db_path + "/bin/usr/bin/mysqldump --defaults-file=" + my_conf_path + " --single-transaction -q --default-character-set=utf8 " + \
name + " | gzip > " + filename
mw.execShell(cmd)

@ -65,7 +65,7 @@ class backupTools:
if len(content) > 100:
mw.writeFile(my_cnf, content)
cmd = db_path + "/bin/usr/bin/mysqldump --defaults-file=" + my_cnf + " --single-transaction --quick --default-character-set=utf8 " + \
cmd = db_path + "/bin/usr/bin/mysqldump --defaults-file=" + my_cnf + " --single-transaction -q --default-character-set=utf8 " + \
name + " | gzip > " + filename
mw.execShell(cmd)

@ -0,0 +1,27 @@
```
show global variables like '%gtid%';
show global variables like 'server_uuid';
```
```
# 不锁表,需要删除原来数据表
# tables = db.query('show tables from `%s`' % sync_db_import)
# table_key = "Tables_in_" + sync_db_import
# for tname in tables:
# drop_db_cmd = 'drop table if exists '+sync_db_import+'.'+tname[table_key]
# # print(drop_db_cmd)
# db.query(drop_db_cmd)
```
```
# 修改同步位置
# master_info = sync_mdb.query('show master status')
# slave_info = db.query('show slave status')
# if len(master_info)>0:
# channel_name = slave_info[0]['Channel_Name']
# change_cmd = "CHANGE MASTER TO MASTER_LOG_FILE='"+master_info[0]['File']+"', MASTER_LOG_POS="+str(master_info[0]['Position'])+" for channel '"+channel_name+"';"
# print(change_cmd)
# r = db.execute(change_cmd)
# print(r)
```

@ -50,7 +50,9 @@ CREATE TABLE IF NOT EXISTS `slave_sync_user` (
`pass` TEXT,
`mode` TEXT,
`cmd` TEXT,
`db` TEXT,
`addtime` TEXT
);
ALTER TABLE `slave_sync_user` ADD COLUMN `db` TEXT DEFAULT '';

@ -63,12 +63,12 @@ def getArgs():
if t.strip() == '':
tmp = []
else:
t = t.split(':', 1)
t = t.split(':',1)
tmp[t[0]] = t[1]
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':', 1)
t = args[i].split(':',1)
tmp[t[0]] = t[1]
return tmp
@ -2349,7 +2349,7 @@ def addMasterRepSlaveUser(version=''):
if isError != None:
return isError
sql_select = "grant select,lock tables,PROCESS on *.* to " + username + "@'%';"
sql_select = "grant select,reload,REPLICATION CLIENT,PROCESS on *.* to " + username + "@'%';"
pdb.execute(sql_select)
pdb.execute('FLUSH PRIVILEGES;')
@ -2394,29 +2394,31 @@ def getMasterRepSlaveUserCmd(version):
if sid != '':
channel_name = " for channel 'r{}';".format(sid)
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" + channel_name
if version == '8.0':
sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \
clist[0]['username'] + "', SOURCE_PASSWORD='" + \
clist[0]['password'] + \
"', MASTER_AUTO_POSITION=1" + channel_name
mdb8 = ['8.0','8.1','8.2','8.3','8.4']
sql = ''
if not mw.inArray(mdb8,version):
base_sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
clist[0]['username'] + "', MASTER_PASSWORD='" + \
clist[0]['password'] + "'"
sql += base_sql +';'
sql += "<br/><hr/>"
# sql += base_sql + ", MASTER_AUTO_POSITION=1" + channel_name
sql += base_sql + channel_name
sql += "<br/><hr/>"
sql += base_sql + "', MASTER_LOG_FILE='" + mstatus[0]["File"] + "',MASTER_LOG_POS=" + str(mstatus[0]["Position"]) + channel_name
else:
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
clist[0]['username'] + "', MASTER_PASSWORD='" + \
clist[0]['password'] + \
"', MASTER_LOG_FILE='" + mstatus[0]["File"] + \
"',MASTER_LOG_POS=" + str(mstatus[0]["Position"]) + channel_name
if version == "8.0":
sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \
base_sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \
clist[0]['username'] + "', SOURCE_PASSWORD='" + \
clist[0]['password'] + \
"', SOURCE_LOG_FILE='" + mstatus[0]["File"] + \
"',SOURCE_LOG_POS=" + \
str(mstatus[0]["Position"]) + channel_name
clist[0]['password']+"'"
sql += base_sql +';'
sql += "<br/><hr/>"
# sql += base_sql + ", MASTER_AUTO_POSITION=1" + channel_name
sql += base_sql + channel_name
sql += "<br/><hr/>"
sql += base_sql + "', SOURCE_LOG_FILE='" + mstatus[0]["File"] + "',SOURCE_LOG_POS=" + str(mstatus[0]["Position"]) + channel_name
data = {}
data['cmd'] = sql
@ -2769,6 +2771,7 @@ def initSlaveStatus(version=''):
return mw.returnJson(False, 'MySQL未启动', [])
mode_file = getSyncModeFile()
# print(mode_file)
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
@ -2842,7 +2845,7 @@ def initSlaveStatusSyncUser(version=''):
if slave_t['mode'] == '1':
mode_name = 'gtid'
# print(local_mode, mode_name)
if local_mode != mode_name:
msg += base_t + '->同步模式不一致'
continue
@ -2856,8 +2859,9 @@ def initSlaveStatusSyncUser(version=''):
pinfo = parseSlaveSyncCmd(cmd_sql)
except Exception as e:
return mw.returnJson(False, base_t + '->CMD同步命令不合规范!')
# print(u['cmd'])
# print(cmd_sql)
t = pdb.query(cmd_sql)
# print(t)
isError = isSqlError(t)
if isError:
return isError
@ -2972,18 +2976,21 @@ def setSlaveStatus(version=''):
return mw.returnJson(True, '设置成功!')
def deleteSlave(version=''):
args = getArgs()
def deleteSlaveFunc(sign = ''):
db = pMysqlDb()
if 'sign' in args:
sign = args['sign']
if sign != '':
db.query("stop slave for channel '{}'".format(sign))
db.query("reset slave all for channel '{}'".format(sign))
else:
db.query('stop slave')
db.query('reset slave all')
def deleteSlave(version=''):
args = getArgs()
sign = ''
if 'sign' in args:
sign = args['sign']
deleteSlaveFunc(sign)
return mw.returnJson(True, '删除成功!')
@ -3018,6 +3025,293 @@ def dumpMysqlData(version=''):
return 'fail'
############### --- 重要 数据补足同步 ---- ###########
def getSyncMysqlDB(dbname,sign = ''):
conn = pSqliteDb('slave_sync_user')
if sign != '':
data = conn.field('ip,port,user,pass,mode,cmd').where('ip=?', (sign,)).find()
else:
data = conn.field('ip,port,user,pass,mode,cmd').find()
user = data['user']
apass = data['pass']
port = data['port']
ip = data['ip']
# 远程数据
sync_db = mw.getMyORM()
# MySQLdb |
sync_db.setPort(port)
sync_db.setHost(ip)
sync_db.setUser(user)
sync_db.setPwd(apass)
sync_db.setDbName(dbname)
sync_db.setTimeout(60)
return sync_db
def syncDatabaseRepairTempFile():
tmp_log = mw.getMWLogs()+ '/mysql-check.log'
return tmp_log
def syncDatabaseRepairLog(version=''):
import subprocess
args = getArgs()
data = checkArgs(args, ['db','sign','op'])
if not data[0]:
return data[1]
sync_args_db = args['db']
sync_args_sign = args['sign']
op = args['op']
tmp_log = syncDatabaseRepairTempFile()
cmd = 'cd '+mw.getServerDir()+'/mdserver-web && source bin/activate && python3 plugins/mysql/index.py sync_database_repair {"db":"'+sync_args_db+'","sign":"'+sync_args_sign+'"}'
# print(cmd)
if op == 'get':
log = mw.getLastLine(tmp_log, 15)
return mw.returnJson(True, log)
if op == 'cmd':
return mw.returnJson(True, 'ok', cmd)
if op == 'do':
os.system(' echo "开始执行" > '+ tmp_log)
subprocess.Popen(cmd +' >> '+ tmp_log +' &')
# time.sleep(10)
# mw.execShell('rm -rf '+tmp_log)
return mw.returnJson(True, 'ok')
return mw.returnJson(False, '无效请求!')
def syncDatabaseRepair(version=''):
time_stats_s = time.time()
tmp_log = syncDatabaseRepairTempFile()
from pymysql.converters import escape_string
args = getArgs()
data = checkArgs(args, ['db','sign'])
if not data[0]:
return data[1]
sync_args_db = args['db']
sync_args_sign = args['sign']
# 本地数据
local_db = pMysqlDb()
# 远程数据
sync_db = getSyncMysqlDB(sync_args_db,sync_args_sign)
tables = local_db.query('show tables from `%s`' % sync_args_db)
table_key = "Tables_in_" + sync_args_db
inconsistent_table = []
tmp_dir = '/tmp/sync_db_repair'
mw.execShell('mkdir -p '+tmp_dir)
for tb in tables:
table_name = sync_args_db+'.'+tb[table_key]
table_check_file = tmp_dir+'/'+table_name+'.txt'
if os.path.exists(table_check_file):
# print(table_name+', 已检查OK')
continue
primary_key_sql = "SHOW INDEX FROM "+table_name+" WHERE Key_name = 'PRIMARY';";
primary_key_data = local_db.query(primary_key_sql)
# print(primary_key_sql,primary_key_data)
pkey_name = '*'
if len(primary_key_data) == 1:
pkey_name = primary_key_data[0]['Column_name']
# print(pkey_name)
if pkey_name != '*' :
# 智能校验(由于服务器同步可能会慢,比较总数总是对不上)
cmd_local_newpk_sql = 'select ' + pkey_name + ' from ' + table_name + " order by " + pkey_name + " desc limit 1"
cmd_local_newpk_data = local_db.query(cmd_local_newpk_sql)
# print(cmd_local_newpk_data)
if len(cmd_local_newpk_data) == 1:
# 比较总数
cmd_count_sql = 'select count('+pkey_name+') as num from '+table_name + ' where '+pkey_name + ' <= '+ str(cmd_local_newpk_data[0][pkey_name])
local_count_data = local_db.query(cmd_count_sql)
sync_count_data = sync_db.query(cmd_count_sql)
if local_count_data != sync_count_data:
print(cmd_count_sql)
print("all data compare: ",local_count_data, sync_count_data)
else:
print(table_name+' smart compare check ok.')
mw.writeFile(tmp_log, table_name+' smart compare check ok.\n','a+')
mw.execShell("echo 'ok' > "+table_check_file)
continue
# 比较总数
cmd_count_sql = 'select count('+pkey_name+') as num from '+table_name
local_count_data = local_db.query(cmd_count_sql)
sync_count_data = sync_db.query(cmd_count_sql)
if local_count_data != sync_count_data:
print("all data compare: ",local_count_data, sync_count_data)
inconsistent_table.append(table_name)
diff = sync_count_data[0]['num'] - local_count_data[0]['num']
print(table_name+', need sync. diff,'+str(diff))
mw.writeFile(tmp_log, table_name+', need sync. diff,'+str(diff)+'\n','a+')
else:
print(table_name+' check ok.')
mw.writeFile(tmp_log, table_name+' check ok.\n','a+')
mw.execShell("echo 'ok' > "+table_check_file)
# inconsistent_table = ['xx.xx']
# 数据对齐
for table_name in inconsistent_table:
is_break = False
while not is_break:
local_db.ping()
# 远程数据
sync_db.ping()
print("check table:"+table_name)
mw.writeFile(tmp_log, "check table:"+table_name+'\n','a+')
table_name_pos = 0
table_name_pos_file = tmp_dir+'/'+table_name+'.pos.txt'
primary_key_sql = "SHOW INDEX FROM "+table_name+" WHERE Key_name = 'PRIMARY';";
primary_key_data = local_db.query(primary_key_sql)
pkey_name = primary_key_data[0]['Column_name']
if os.path.exists(table_name_pos_file):
table_name_pos = mw.readFile(table_name_pos_file)
data_select_sql = 'select * from '+table_name + ' where '+pkey_name+' > '+str(table_name_pos)+' limit 10000'
print(data_select_sql)
local_select_data = local_db.query(data_select_sql)
time_s = time.time()
sync_select_data = sync_db.query(data_select_sql)
print(f'sync query cos:{time.time() - time_s:.4f}s')
mw.writeFile(tmp_log, f'sync query cos:{time.time() - time_s:.4f}s\n','a+')
# print(local_select_data)
# print(sync_select_data)
# print(len(local_select_data))
# print(len(sync_select_data))
print('pos:',str(table_name_pos),'local compare sync,',local_select_data == sync_select_data)
cmd_count_sql = 'select count('+pkey_name+') as num from '+table_name
local_count_data = local_db.query(cmd_count_sql)
time_s = time.time()
sync_count_data = sync_db.query(cmd_count_sql)
print(f'sync count data cos:{time.time() - time_s:.4f}s')
print(local_count_data,sync_count_data)
# 数据同步有延迟,相等即任务数据补足完成
if local_count_data[0]['num'] == sync_count_data[0]['num']:
is_break = True
break
diff = sync_count_data[0]['num'] - local_count_data[0]['num']
print("diff," + str(diff)+' line data!')
if local_select_data == sync_select_data:
data_count = len(local_select_data)
if data_count == 0:
# mw.writeFile(table_name_pos_file, '0')
print(table_name+",data is equal ok..")
is_break = True
break
# print(table_name,data_count)
pos = local_select_data[data_count-1][pkey_name]
print('pos',pos)
progress = pos/sync_count_data[0]['num']
print('progress,%.2f' % progress+'%')
mw.writeFile(table_name_pos_file, str(pos))
else:
sync_select_data_len = len(sync_select_data)
skip_idx = 0
# 主库PK -> 查询本地 | 保证一致
if sync_select_data_len > 0:
for idx in range(sync_select_data_len):
sync_idx_data = sync_select_data[idx]
local_idx_data = None
if idx in local_select_data:
local_idx_data = local_select_data[idx]
if sync_select_data[idx] == local_idx_data:
skip_idx = idx
pos = local_select_data[idx][pkey_name]
mw.writeFile(table_name_pos_file, str(pos))
# print(insert_data)
local_inquery_sql = 'select * from ' + table_name+ ' where ' +pkey_name+' = '+ str(sync_idx_data[pkey_name])
# print(local_inquery_sql)
ldata = local_db.query(local_inquery_sql)
# print('ldata:',ldata)
if len(ldata) == 0:
print("id:"+ str(sync_idx_data[pkey_name])+ " no exists, insert")
insert_sql = 'insert into ' + table_name
field_str = ''
value_str = ''
for field in sync_idx_data:
field_str += '`'+field+'`,'
value_str += '\''+escape_string(str(sync_idx_data[field]))+'\','
field_str = '(' +field_str.strip(',')+')'
value_str = '(' +value_str.strip(',')+')'
insert_sql = insert_sql+' '+field_str+' values'+value_str+';'
print(insert_sql)
r = local_db.execute(insert_sql)
print(r)
else:
# print('compare sync->local:',sync_idx_data == ldata[0] )
if ldata[0] == sync_idx_data:
continue
print("id:"+ str(sync_idx_data[pkey_name])+ " data is not equal, update")
update_sql = 'update ' + table_name
field_str = ''
value_str = ''
for field in sync_idx_data:
if field == pkey_name:
continue
field_str += '`'+field+'`=\''+escape_string(str(sync_idx_data[field]))+'\','
field_str = field_str.strip(',')
update_sql = update_sql+' set '+field_str+' where '+pkey_name+'=\''+str(sync_idx_data[pkey_name])+'\';'
print(update_sql)
r = local_db.execute(update_sql)
print(r)
# 本地PK -> 查询主库 | 保证一致
# local_select_data_len = len(local_select_data)
# if local_select_data_len > 0:
# for idx in range(local_select_data_len):
# if idx < skip_idx:
# continue
# local_idx_data = local_select_data[idx]
# print('local idx check', idx, skip_idx)
# local_inquery_sql = 'select * from ' + table_name+ ' where ' +pkey_name+' = '+ str(local_idx_data[pkey_name])
# print(local_inquery_sql)
# sdata = sync_db.query(local_inquery_sql)
# sdata_len = len(sdata)
# print('sdata:',sdata,sdata_len)
# if sdata_len == 0:
# delete_sql = 'delete from ' + table_name + ' where ' +pkey_name+' = '+ str(local_idx_data[pkey_name])
# print(delete_sql)
# r = local_db.execute(delete_sql)
# print(r)
# break
if is_break:
print("break all")
break
time.sleep(3)
print(f'data check cos:{time.time() - time_stats_s:.4f}s')
print("data supplementation completed")
mw.execShell('rm -rf '+tmp_dir)
return 'ok'
############### --- 重要 同步---- ###########
def asyncTmpfile():
@ -3028,7 +3322,20 @@ def asyncTmpfile():
def writeDbSyncStatus(data):
path = asyncTmpfile()
mw.writeFile(path, json.dumps(data))
return True
def fullSyncCmd():
time_all_s = time.time()
args = getArgs()
data = checkArgs(args, ['db', 'sign'])
if not data[0]:
return data[1]
db = args['db']
sign = args['sign']
cmd = 'cd '+mw.getServerDir()+'/mdserver-web && source bin/activate && python3 plugins/mysql/index.py do_full_sync {"db":"'+db+'","sign":"'+sign+'"}'
return mw.returnJson(True,'ok',cmd)
def doFullSync(version=''):
mode_file = getSyncModeFile()
@ -3042,7 +3349,53 @@ def doFullSync(version=''):
return doFullSyncUser(version)
def isSimpleSyncCmd(sql):
new_sql = sql.lower()
if new_sql.find('master_auto_position') > 0:
return False
return True
def getChannelNameForCmd(cmd):
cmd = cmd.lower()
cmd_arr = cmd.split('channel')
if len(cmd_arr) == 2:
cmd_channel_info = cmd_arr[1]
channel_name = cmd_channel_info.strip()
channel_name = channel_name.strip(';')
channel_name = channel_name.strip("'")
return channel_name
return ''
def doFullSyncUserImportContentForChannel(file, channel_name):
# print(file, channel_name)
content = mw.readFile(file)
content = content.replace('STOP SLAVE;', "STOP SLAVE for channel '{}';".format(channel_name))
content = content.replace('START SLAVE;', "START SLAVE for channel '{}';".format(channel_name))
find_head = "CHANGE MASTER TO "
find_re = find_head+"(.*?);"
find_r = re.search(find_re, content, re.I|re.M)
if find_r:
find_rg = find_r.groups()
if len(find_rg)>0:
find_str = find_head+find_rg[0]
if find_str.lower().find('channel')==-1:
content = content.replace(find_str+';', find_str+" for channel '{}';".format(channel_name))
mw.writeFile(file,content)
return True
def doFullSyncUser(version=''):
which_pv = mw.execShell('which pv')
is_exist_pv = False
if not os.path.exists(which_pv[0]):
is_exist_pv = True
time_all_s = time.time()
args = getArgs()
data = checkArgs(args, ['db', 'sign'])
if not data[0]:
@ -3063,6 +3416,9 @@ def doFullSyncUser(version=''):
db = pMysqlDb()
# 重置
# deleteSlaveFunc(sync_sign)
conn = pSqliteDb('slave_sync_user')
if sync_sign != '':
data = conn.field('ip,port,user,pass,mode,cmd').where(
@ -3070,10 +3426,15 @@ def doFullSyncUser(version=''):
else:
data = conn.field('ip,port,user,pass,mode,cmd').find()
# print(data)
user = data['user']
apass = data['pass']
port = data['port']
ip = data['ip']
cmd = data['cmd']
channel_name = getChannelNameForCmd(cmd)
sync_mdb = getSyncMysqlDB(sync_db,sync_sign)
bak_file = '/tmp/tmp.sql'
if os.path.exists(bak_file):
@ -3087,41 +3448,111 @@ def doFullSyncUser(version=''):
time.sleep(1)
writeDbSyncStatus({'code': 1, 'msg': '正在停止从库...', 'progress': 15})
if version == '8.0':
mdb8 = ['8.0','8.1','8.2','8.3','8.4']
if mw.inArray(mdb8,version):
db.query("stop slave user='{}' password='{}';".format(user, apass))
else:
db.query("stop slave")
time.sleep(2)
time.sleep(1)
writeDbSyncStatus({'code': 2, 'msg': '远程导出数据...', 'progress': 20})
# --master-data=2表示在dump过程中记录主库的binlog和pos点,并在dump文件中注释掉这一行
# --master-data=1表示在dump过程中记录主库的binlog和pos点,并在dump文件中不注释掉这一行,即恢复时会执行
# --dump-slave=2表示在dump过程中,在从库dump,mysqldump进程也要在从库执行,记录当时主库的binlog和pos点,并在dump文件中注释掉这一行
# --dump-slave=1表示在dump过程中,在从库dump,mysqldump进程也要在从库执行,记录当时主库的binlog和pos点,并在dump文件中不注释掉这一行
# --force --opt --single-transaction
# --skip-opt --create-options
# --master-data=1
find_run_dump = mw.execShell('ps -ef | grep mysqldump | grep -v grep')
if find_run_dump[0] != "":
print("正在远程导出数据中,别着急...")
writeDbSyncStatus({'code': 3.1, 'msg': '正在远程导出数据中,别着急...', 'progress': 19})
return False
time_s = time.time()
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 + \
"' --ssl-mode=DISABLED " + sync_db + " > " + bak_file
print(dump_sql_data)
mw.execShell(dump_sql_data)
if isSimpleSyncCmd(cmd):
dmp_option += " --master-data=1 --apply-slave-statements --include-master-host-port "
else:
dmp_option += ' '
dump_sql_data = getServerDir() + "/bin/mysqldump --single-transaction --default-character-set=utf8mb4 --compress -q " + dmp_option + " -h" + ip + " -P" + \
port + " -u" + user + " -p'" + apass + "' --ssl-mode=DISABLED " + sync_db + " > " + bak_file
print(dump_sql_data)
time_s = time.time()
r = mw.execShell(dump_sql_data)
print(r)
time_e = time.time()
export_cos = time_e - time_s
print("export cos:", export_cos)
writeDbSyncStatus({'code': 3, 'msg': '正在到本地导入数据中...', 'progress': 40})
writeDbSyncStatus({'code': 3, 'msg': '导出耗时:'+str(int(export_cos))+'秒,正在到本地导入数据中...', 'progress': 40})
find_run_import = mw.execShell('ps -ef | grep mysql| grep '+ bak_file +' | grep -v grep')
if find_run_import[0] != "":
print("正在导入数据中,别着急...")
writeDbSyncStatus({'code': 4.1, 'msg': '正在导入数据中,别着急...', 'progress': 39})
return False
time_s = time.time()
if os.path.exists(bak_file):
# 重置
db.execute('reset master')
# 加快导入 - 开始
# db.execute('set global innodb_flush_log_at_trx_commit = 2')
# db.execute('set global sync_binlog = 2000')
if channel_name != '':
doFullSyncUserImportContentForChannel(bak_file, channel_name)
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile()
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + " -uroot -p'" + pwd + \
"' " + sync_db_import + ' < ' + bak_file
print(my_import_cmd)
mw.execShell(my_import_cmd)
if version == '8.0':
if is_exist_pv:
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + " -uroot -p'" + pwd + "' " + sync_db_import
my_import_cmd = "pv -t -p " + bak_file + '|' + my_import_cmd
print(my_import_cmd)
os.system(my_import_cmd)
else:
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + " -uroot -p'" + pwd + "' " + sync_db_import + ' < ' + bak_file
print(my_import_cmd)
mw.execShell(my_import_cmd)
# 加快导入 - 结束
# db.execute('set global innodb_flush_log_at_trx_commit = 1')
# db.execute('set global sync_binlog = 1')
time_e = time.time()
import_cos = time_e - time_s
print("import cos:", import_cos)
writeDbSyncStatus({'code': 4, 'msg': '导入耗时:'+str(int(import_cos))+'', 'progress': 60})
time.sleep(3)
# print(cmd)
# r = db.query(cmd)
# print(r)
mdb8 = ['8.0','8.1','8.2','8.3','8.4']
if mw.inArray(mdb8,version):
db.query("start slave user='{}' password='{}';".format(user, apass))
else:
db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
db.query("start all slaves")
time_all_e = time.time()
cos = time_all_e - time_all_s
writeDbSyncStatus({'code': 6, 'msg': '总耗时:'+str(int(cos))+'秒,从库重启完成...', 'progress': 100})
if os.path.exists(bak_file):
os.system("rm -rf " + bak_file)
return True
@ -3252,10 +3683,13 @@ def doFullSyncSSH(version=''):
print(import_data[0])
writeDbSyncStatus({'code': 5, 'msg': '导入数据失败...', 'progress': 100})
return 'fail'
# "start slave user='{}' password='{}';".format(uinfo['username'], uinfo['password'])
db.query("start slave")
mdb8 = ['8.0','8.1','8.2','8.3','8.4']
if mw.inArray(mdb8,version):
db.query("start slave user='{}' password='{}';".format(uinfo['username'], uinfo['password']))
else:
db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + SSH_PRIVATE_KEY)
@ -3300,9 +3734,16 @@ def fullSync(version=''):
def installPreInspection(version):
import psutil
mem = psutil.virtual_memory()
memTotal = mem.total
memG = memTotal/1024/1024/1024
if memG > 2:
return 'ok'
swap_path = mw.getServerDir() + "/swap"
if not os.path.exists(swap_path):
return "为了稳定安装MySQL,先安装swap插件!"
return "内存小,为了稳定安装MySQL,先安装swap插件!"
return 'ok'
@ -3494,7 +3935,13 @@ if __name__ == "__main__":
print(fullSync(version))
elif func == 'do_full_sync':
print(doFullSync(version))
elif func == 'full_sync_cmd':
print(fullSyncCmd())
elif func == 'dump_mysql_data':
print(dumpMysqlData(version))
elif func == 'sync_database_repair':
print(syncDatabaseRepair())
elif func == 'sync_database_repair_log':
print(syncDatabaseRepairLog())
else:
print('error')

@ -6,8 +6,11 @@ export PATH
# https://www.cnblogs.com/whiteY/p/17331882.html
# cd /www/server/mdserver-web/plugins/mysql && bash install.sh install 8.2
# cd /www/server/mdserver-web && source bin/activate && python3 /www/server/mdserver-web/plugins/mysql/index.py try_slave_sync_bugfix {}
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mysql/index.py try_slave_sync_bugfix {}
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mysql/index.py do_full_sync {"db":"xxx","sign":"","begin":1}
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mysql/index.py sync_database_repair {"db":"xxx","sign":""}
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mysql/index.py init_slave_status
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mysql/index.py install_pre_inspection
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")

@ -1985,6 +1985,7 @@ function getFullSyncStatus(db){
</div>\
<div class='table_toolbar' style='left:0px;'>\
<span data-status='init' class='sync btn btn-default btn-sm' id='begin_full_sync'>开始</span>\
<span data-status='init' class='btn btn-default btn-sm' id='full_sync_cmd'>手动命令</span>\
</div>\
</div>",
cancel: function(){
@ -2003,10 +2004,36 @@ function getFullSyncStatus(db){
fullSync(db,sign,0);
}, 1000);
$(this).data('status','starting');
$('#begin_full_sync').text("同步中");
} else {
layer.msg("正在同步中..",{icon:0});
}
});
$('#full_sync_cmd').click(function(){
myPostN('full_sync_cmd', {'db':db,'sign':''}, function(rdata){
var rdata = $.parseJSON(rdata.data);
layer.open({
title: "手动执行命令CMD",
area: ['600px', '180px'],
type:1,
closeBtn: 1,
shadeClose: false,
btn:["复制","取消"],
content: '<div class="pd15">\
<div class="divtable">\
<pre class="layui-code">'+rdata.data+'</pre>\
</div>\
</div>',
success:function(){
copyText(rdata.data);
},
yes:function(){
copyText(rdata.data);
}
});
});
});
}
});
});
@ -2022,12 +2049,91 @@ function getFullSyncStatus(db){
if (rdata['code']==6 ||rdata['code']<0){
layer.msg(rdata['msg']);
clearInterval(timeId);
$('#begin_full_sync').text("同步结束,再次同步?");
$("#begin_full_sync").attr('data-status','init');
}
});
}
}
function dataSyncVerify(db){
var reqTimer = null;
function requestLogs(layerIndex){
myPostN('sync_database_repair_log', {db:db, sign:'',op:'get'}, function(rdata){
var rdata = $.parseJSON(rdata.data);
if(!rdata.status) {
layer.close(layerIndex);
layer.msg(rdata.msg,{icon:2, time:2000});
clearInterval(reqTimer);
return;
};
if (rdata.msg == ''){
rdata.msg = '暂无数据!';
}
$("#data_verify_log").html(rdata.msg);
//滚动到最低
var ob = document.getElementById('data_verify_log');
ob.scrollTop = ob.scrollHeight;
});
}
layer.open({
type: 1,
title: '同步数据库['+db+']数据校验',
area: '500px',
btn:[ "开始","取消","手动"],
content:"<div class='bt-form'>\
"+'<pre id="data_verify_log" style="overflow: auto; border: 0px none; line-height:23px;padding: 5px; margin: 0px; white-space: pre-wrap; height: 395px; background-color: rgb(51,51,51);color:#f1f1f1;border-radius:0px;font-family:"></pre>'+"\
</div>",
cancel: function(){
if (reqTimer){
clearInterval(reqTimer);
}
},
yes:function(index,layer_index){
myPostN('sync_database_repair_log', {db:db, sign:'',op:'do'}, function(data){});
layer.msg("执行成功");
requestLogs(layer_index);
reqTimer = setInterval(function(){
requestLogs(layer_index);
},3000);
},
success:function(){
},
btn3: function(){
myPostN('sync_database_repair_log', {db:db, sign:'',op:'cmd'}, function(rdata){
var rdata = $.parseJSON(rdata.data);
layer.open({
title: "手动执行命令CMD",
area: ['600px', '180px'],
type:1,
closeBtn: 1,
shadeClose: false,
btn:["复制","取消"],
content: '<div class="pd15">\
<div class="divtable">\
<pre class="layui-code">'+rdata.data+'</pre>\
</div>\
</div>',
success:function(){
copyText(rdata.data);
},
yes:function(){
copyText(rdata.data);
}
});
});
return false;
}
});
}
function addSlaveSSH(ip=''){
myPost('get_slave_ssh_by_ip', {ip:ip}, function(rdata){
@ -2183,7 +2289,7 @@ function addSlaveSyncUser(ip=''){
var index = layer.open({
type: 1,
area: ['500px','470px'],
area: ['500px','510px'],
title: '同步账户',
closeBtn: 1,
shift: 5,
@ -2194,6 +2300,15 @@ function addSlaveSyncUser(ip=''){
<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'>同步模式</span>\
<div class='info-r'>\
<select class='bt-input-text mr5' name='mode'>\
<option value='0' "+( mode == '0' ? 'selected="selected"' : '')+">经典</option>\
<option value='1' "+( mode == '1' ? 'selected="selected"' : '')+">GTID</option>\
</select>\
</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>\
@ -2204,6 +2319,7 @@ function addSlaveSyncUser(ip=''){
$('textarea[name="cmd"]').html(cmd);
$('textarea[name="cmd"]').change(function(){
var val = $(this).val();
val = val.replace(';','');
var a = {};
if (val.toLowerCase().indexOf('for')>0){
cmd_tmp = val.split('for');
@ -2230,11 +2346,6 @@ function addSlaveSyncUser(ip=''){
$('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){
@ -2243,7 +2354,7 @@ function addSlaveSyncUser(ip=''){
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 mode = $('select[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){
@ -2521,7 +2632,7 @@ function masterOrSlaveConf(version=''){
for(i in rdata.data){
var v = rdata.data[i];
if ('Channel_Name' in v){
if ('Channel_Name' in v && v['Channel_Name'] !=''){
isHasSign = true;
}
@ -2692,7 +2803,8 @@ function masterOrSlaveConf(version=''){
list += '<td>' + rdata.data[i]['name'] +'</td>';
list += '<td style="text-align:right">' +
'<a href="javascript:;" class="btlink" onclick="setDbSlave(\''+rdata.data[i]['name']+'\')" title="加入|退出">'+(rdata.data[i]['slave']?'退出':'加入')+'</a> | ' +
'<a href="javascript:;" class="btlink" onclick="getFullSyncStatus(\''+rdata.data[i]['name']+'\')" title="同步">同步</a>' +
'<a href="javascript:;" class="btlink" onclick="getFullSyncStatus(\''+rdata.data[i]['name']+'\')" title="同步">同步</a> | ' +
'<a href="javascript:;" class="btlink" onclick="dataSyncVerify(\''+rdata.data[i]['name']+'\')" title="数据校验">数据校验</a>' +
'</td>';
list += '</tr>';
}
@ -2824,10 +2936,10 @@ function masterOrSlaveConf(version=''){
getMasterDbList();
}
// if (rdata.slave_status){
if (rdata.slave_status){
getAsyncMasterDbList();
getAsyncDataList()
// }
}
});
}
getMasterStatus();

@ -51,7 +51,7 @@ http
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 2;
gzip_comp_level 9;
gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;

@ -17,6 +17,10 @@ sysName=`uname`
actionType=$1
version=$2
if [ "$version" -ge '74' ];then
LIBV=1.19.0
fi
if [ "$version" == '71' ];then
LIBV=1.11.1
fi

@ -1,5 +1,5 @@
daemonize yes
pidfile {$SERVER_PATH}/redis/redis_6379.pid
pidfile {$SERVER_PATH}/redis/redis.pid
bind 127.0.0.1
port 6379
@ -40,15 +40,20 @@ maxclients 10000
#maxmemory-samples 3
maxmemory 218mb
maxmemory-policy volatile-ttl
#maxmemory-policy allkeys-lru
############################## APPEND ONLY MODE ###############################
#appendonly no
# appendonly no
# appendfsync always
#appendfsync everysec
# appendfsync everysec
# appendfsync no
#no-appendfsync-on-rewrite no
# appendfilename "appendonly.aof"
# no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

@ -23,7 +23,7 @@ if [ "$REDISPASS" != "" ];then
fi
EXEC={$SERVER_PATH}/redis/bin/redis-server
CLIEXEC="{$SERVER_PATH}/redis/bin/redis-cli -p $REDISPORT$REDISPASS"
PIDFILE={$SERVER_PATH}/redis/redis_6379.pid
PIDFILE={$SERVER_PATH}/redis/redis.pid
echo $REDISPASS
echo $REDISPORT

@ -25,14 +25,16 @@ Install_App()
{
echo '正在安装脚本文件...' > $install_tmp
mkdir -p $serverPath/source
mkdir -p $serverPath/source/redis
if [ ! -f $serverPath/source/redis-${VERSION}.tar.gz ];then
wget -O $serverPath/source/redis-${VERSION}.tar.gz http://download.redis.io/releases/redis-${VERSION}.tar.gz
fi
cd $serverPath/source && tar -zxvf redis-${VERSION}.tar.gz
FILE_TGZ=redis-${VERSION}.tar.gz
REDIS_DIR=$serverPath/source/redis
if [ ! -f $REDIS_DIR/${FILE_TGZ} ];then
wget -O $REDIS_DIR/${FILE_TGZ} http://download.redis.io/releases/${FILE_TGZ}
fi
cd $REDIS_DIR && tar -zxvf ${FILE_TGZ}
CMD_MAKE=`which gmake`
if [ "$?" == "0" ];then
@ -55,8 +57,8 @@ Install_App()
echo '安装失败!'
fi
if [ -d $serverPath/source/redis-${VERSION} ];then
rm -rf $serverPath/source/redis-${VERSION}
if [ -d ${REDIS_DIR}/redis-${VERSION} ];then
rm -rf ${REDIS_DIR}/redis-${VERSION}
fi
}
@ -80,8 +82,11 @@ Uninstall_App()
$serverPath/redis/initd/redis stop
fi
rm -rf $serverPath/redis
echo "Uninstall_redis" > $install_tmp
if [ -d $serverPath/redis ];then
rm -rf $serverPath/redis
fi
echo "卸载redis成功"
}
action=$1

@ -1,5 +1,5 @@
daemonize yes
pidfile {$SERVER_PATH}/redis/redis_6379.pid
pidfile {$SERVER_PATH}/redis/redis.pid
loglevel notice
logfile {$SERVER_PATH}/redis/data/redis.log

@ -1,5 +1,5 @@
daemonize yes
pidfile {$SERVER_PATH}/redis/redis_6379.pid
pidfile {$SERVER_PATH}/redis/redis.pid
bind 127.0.0.1
port 6379

@ -1,16 +1,17 @@
daemonize yes
pidfile {$SERVER_PATH}/redis/redis_6379.pid
pidfile {$SERVER_PATH}/redis/redis.pid
loglevel notice
logfile {$SERVER_PATH}/redis/data/redis.log
databases 16
bind 127.0.0.1
port 6379
requirepass {$REDIS_PASS}
timeout 0
tcp-keepalive 0
bind 127.0.0.1
port 6379
requirepass {$REDIS_PASS}
loglevel notice
logfile {$SERVER_PATH}/redis/data/redis.log
databases 16
################################ SNAPSHOTTING #################################
@ -33,7 +34,7 @@ slave-priority 100
# 填写主库信息
#slaveof 127.0.0.1 6379
#masterauth master_pwd
#masterauth 123123
################################## SECURITY ###################################
@ -41,7 +42,7 @@ slave-priority 100
################################### LIMITS ####################################
maxclients 10000
#maxmemory-samples 3
maxmemory 218mb
maxmemory 0mb
maxmemory-policy volatile-ttl
############################## APPEND ONLY MODE ###############################

@ -0,0 +1,75 @@
daemonize yes
pidfile {$SERVER_PATH}/redis/redis.pid
loglevel notice
logfile {$SERVER_PATH}/redis/data/redis.log
databases 16
timeout 0
tcp-keepalive 0
bind 127.0.0.1
port 6379
requirepass {$REDIS_PASS}
################################ SNAPSHOTTING #################################
save ""
stop-writes-on-bgsave-error no
################################# REPLICATION #################################
slave-serve-stale-data yes
slave-read-only yes
repl-disable-tcp-nodelay no
slave-priority 100
# 填写主库信息
#slaveof 127.0.0.1 6379
#masterauth 123123
################################## SECURITY ###################################
################################### LIMITS ####################################
maxclients 10000
#maxmemory-samples 3
maxmemory 218mb
maxmemory-policy allkeys-lru
############################## APPEND ONLY MODE ###############################
################################ LUA SCRIPTING ###############################
lua-time-limit 5000
################################## SLOW LOG ###################################
slowlog-log-slower-than 10000
slowlog-max-len 128
############################### ADVANCED CONFIG ###############################
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
activerehashing no
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes

@ -0,0 +1,486 @@
# coding:utf-8
import sys
import io
import os
import time
import subprocess
import re
import json
sys.path.append(os.getcwd() + "/class/core")
import mw
def getServerDir():
return mw.getServerDir() + '/mysql'
def getPluginDir():
return mw.getPluginDir() + '/mysql'
def getConf():
path = getServerDir() + '/etc/my.cnf'
return path
def getDbPort():
file = getConf()
content = mw.readFile(file)
rep = 'port\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
def getSocketFile():
file = getConf()
content = mw.readFile(file)
rep = 'socket\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
def pSqliteDb(dbname='databases'):
file = getServerDir() + '/mysql.db'
name = 'mysql'
conn = mw.M(dbname).dbPos(getServerDir(), name)
return conn
def pMysqlDb():
# pymysql
db = mw.getMyORM()
db.setPort(getDbPort())
db.setSocket(getSocketFile())
# db.setCharset("utf8")
db.setPwd(pSqliteDb('config').where('id=?', (1,)).getField('mysql_root'))
return db
class sphinxMake():
pdb = None
psdb = None
pkey_name_cache = {}
delta = 'sph_counter'
ver = ''
def __init__(self):
self.pdb = pMysqlDb()
def setDeltaName(self, name):
self.delta = name
return True
def setVersion(self, ver):
self.ver = ver
def createSql(self, db):
conf = '''
CREATE TABLE IF NOT EXISTS `{$DB_NAME}`.`{$TABLE_NAME}` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`table` varchar(200) NOT NULL,
`max_id` bigint(20) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `table_uniq` (`table`),
KEY `table` (`table`)
) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4;
'''
conf = conf.replace("{$TABLE_NAME}", self.delta)
conf = conf.replace("{$DB_NAME}", db)
return conf
def eqVerField(self, field):
ver = self.ver.replace(".1",'')
if float(ver) >= 3.6:
if field == 'sql_attr_timestamp':
return 'attr_bigint'
if field == 'sql_attr_bigint':
return 'attr_bigint'
if field == 'sql_attr_float':
return 'attr_float'
if field == 'sql_field_string':
return 'field_string'
if float(ver) >= 3.3:
if field == 'sql_attr_timestamp':
return 'sql_attr_bigint'
return field
def pathVerName(self):
ver = self.ver.replace(".1",'')
# if float(ver) >= 3.6:
# return 'datadir'
return 'path'
def getTablePk(self, db, table):
key = db+'_'+table
if key in self.pkey_name_cache:
return self.pkey_name_cache[key]
# SHOW INDEX FROM bbs.bbs_ucenter_vars WHERE Key_name = 'PRIMARY'
pkey_sql = "SHOW INDEX FROM {}.{} WHERE Key_name = 'PRIMARY';".format(db,table,);
pkey_data = self.pdb.query(pkey_sql)
# print(db, table)
# print(pkey_data)
key = ''
if len(pkey_data) == 1:
pkey_name = pkey_data[0]['Column_name']
sql = "select COLUMN_NAME,DATA_TYPE from information_schema.COLUMNS where `TABLE_SCHEMA`='{}' and `TABLE_NAME` = '{}' and `COLUMN_NAME`='{}';"
sql = sql.format(db,table,pkey_name,)
# print(sql)
fields = self.pdb.query(sql)
if len(fields) == 1:
# print(fields[0]['DATA_TYPE'])
if mw.inArray(['bigint','smallint','tinyint','int','mediumint'], fields[0]['DATA_TYPE']):
key = pkey_name
return key
def getTableFieldStr(self, db, table):
sql = "select COLUMN_NAME,DATA_TYPE from information_schema.COLUMNS where `TABLE_SCHEMA`='{}' and `TABLE_NAME` = '{}';"
sql = sql.format(db,table,)
fields = self.pdb.query(sql)
field_str = ''
for x in range(len(fields)):
field_str += '`'+fields[x]['COLUMN_NAME']+'`,'
field_str = field_str.strip(',')
return field_str
def makeSphinxHeader(self):
conf = '''
indexer
{
mem_limit = 128M
}
searchd
{
listen = 9312
listen = 9306:mysql41
log = {$server_dir}/sphinx/index/searchd.log
query_log = {$server_dir}/sphinx/index/query.log
read_timeout = 5
max_children = 0
pid_file = {$server_dir}/sphinx/index/searchd.pid
seamless_rotate = 1
preopen_indexes = 1
unlink_old = 1
#workers = threads # for RT to work
binlog_path = {$server_dir}/sphinx/index/binlog
}
'''
conf = conf.replace("{$server_dir}", mw.getServerDir())
return conf
def makeSphinxDbSourceRangeSql(self, db, table):
pkey_name = self.getTablePk(db,table)
sql = "SELECT min("+pkey_name+"), max("+pkey_name+") FROM "+table
return sql
def makeSphinxDbSourceQuerySql(self, db, table):
pkey_name = self.getTablePk(db,table)
field_str = self.getTableFieldStr(db,table)
# print(field_str)
if pkey_name == 'id':
sql = "SELECT " + field_str + " FROM " + table + " where id >= $start AND id <= $end"
else:
sql = "SELECT `"+pkey_name+'` as `id`,' + field_str + " FROM " + table + " where "+pkey_name+" >= $start AND "+pkey_name+" <= $end"
return sql
def makeSphinxDbSourceDeltaRange(self, db, table):
pkey_name = self.getTablePk(db,table)
conf = "SELECT (SELECT max_id FROM `{$SPH_TABLE}` where `table`='{$TABLE_NAME}') as min, (SELECT max({$PK_NAME}) FROM {$TABLE_NAME}) as max"
conf = conf.replace("{$DB_NAME}", db)
conf = conf.replace("{$TABLE_NAME}", table)
conf = conf.replace("{$SPH_TABLE}", self.delta)
conf = conf.replace("{$PK_NAME}", pkey_name)
return conf
def makeSphinxDbSourcePost(self, db, table):
pkey_name = self.getTablePk(db,table)
conf = "sql_query_post = UPDATE {$SPH_TABLE} SET max_id=(SELECT MAX({$PK_NAME}) FROM {$TABLE_NAME}) where `table`='{$TABLE_NAME}'"
# conf = "REPLACE INTO {$SPH_TABLE} (`table`,`max_id`) VALUES ('{$TABLE_NAME}',(SELECT MAX({$PK_NAME}) FROM {$TABLE_NAME}))"
conf = conf.replace("{$DB_NAME}", db)
conf = conf.replace("{$TABLE_NAME}", table)
conf = conf.replace("{$SPH_TABLE}", self.delta)
conf = conf.replace("{$PK_NAME}", pkey_name)
return conf
def makeSphinxDbSourceDelta(self, db, table):
conf = '''
source {$DB_NAME}_{$TABLE_NAME}_delta:{$DB_NAME}_{$TABLE_NAME}
{
sql_query_pre = SET NAMES utf8
sql_query_range = {$DELTA_RANGE}
sql_query = {$DELTA_QUERY}
{$DELTA_UPDATE}
{$SPH_FIELD}
}
index {$DB_NAME}_{$TABLE_NAME}_delta:{$DB_NAME}_{$TABLE_NAME}
{
source = {$DB_NAME}_{$TABLE_NAME}_delta
{$PATH_NAME} = {$server_dir}/sphinx/index/db/{$DB_NAME}.{$TABLE_NAME}/delta
html_strip = 1
ngram_len = 1
ngram_chars = U+3000..U+2FA1F
{$SPH_FIELD_INDEX}
}
''';
conf = conf.replace("{$server_dir}", mw.getServerDir())
conf = conf.replace("{$PATH_NAME}", self.pathVerName())
conf = conf.replace("{$DB_NAME}", db)
conf = conf.replace("{$TABLE_NAME}", table)
delta_range = self.makeSphinxDbSourceDeltaRange(db, table)
conf = conf.replace("{$DELTA_RANGE}", delta_range)
delta_query = self.makeSphinxDbSourceQuerySql(db, table)
conf = conf.replace("{$DELTA_QUERY}", delta_query)
delta_update = self.makeSphinxDbSourcePost(db, table)
conf = conf.replace("{$DELTA_UPDATE}", delta_update)
sph_field = self.makeSqlToSphinxTable(db, table)
conf = self.makeSphinxDbFieldRepalce(conf, sph_field)
return conf;
def makeSphinxDbSource(self, db, table, create_sphinx_table = False):
db_info = pSqliteDb('databases').field('username,password').where('name=?', (db,)).find()
port = getDbPort()
conf = '''
source {$DB_NAME}_{$TABLE_NAME}
{
type = mysql
sql_host = 127.0.0.1
sql_user = {$DB_USER}
sql_pass = {$DB_PASS}
sql_db = {$DB_NAME}
sql_port = {$DB_PORT}
sql_query_pre = SET NAMES utf8
{$UPDATE}
sql_query_range = {$DB_RANGE_SQL}
sql_range_step = 1000
sql_query = {$DB_QUERY_SQL}
{$SPH_FIELD}
}
index {$DB_NAME}_{$TABLE_NAME}
{
source = {$DB_NAME}_{$TABLE_NAME}
{$PATH_NAME} = {$server_dir}/sphinx/index/db/{$DB_NAME}.{$TABLE_NAME}/index
ngram_len = 1
ngram_chars = U+3000..U+2FA1F
{$SPH_FIELD_INDEX}
}
'''
conf = conf.replace("{$server_dir}", mw.getServerDir())
conf = conf.replace("{$PATH_NAME}", self.pathVerName())
conf = conf.replace("{$DB_NAME}", db)
conf = conf.replace("{$TABLE_NAME}", table)
conf = conf.replace("{$DB_USER}", db_info['username'])
conf = conf.replace("{$DB_PASS}", db_info['password'])
conf = conf.replace("{$DB_PORT}", port)
range_sql = self.makeSphinxDbSourceRangeSql(db, table)
conf = conf.replace("{$DB_RANGE_SQL}", range_sql)
query_sql = self.makeSphinxDbSourceQuerySql(db, table)
conf = conf.replace("{$DB_QUERY_SQL}", query_sql)
sph_field = self.makeSqlToSphinxTable(db, table)
# conf = conf.replace("{$SPH_FIELD}", sph_field)
conf = self.makeSphinxDbFieldRepalce(conf, sph_field)
if create_sphinx_table:
update = self.makeSphinxDbSourcePost(db, table)
conf = conf.replace("{$UPDATE}", update)
else:
conf = conf.replace("{$UPDATE}", '')
if create_sphinx_table:
sph_sql = self.createSql(db)
self.pdb.query(sph_sql)
sql_find = "select * from {}.{} where `table`='{}'".format(db,self.delta,table)
find_data = self.pdb.query(sql_find)
if len(find_data) == 0:
insert_sql = "insert into `{}`.`{}`(`table`,`max_id`) values ('{}',{}) ".format(db,self.delta,table,0)
# print(insert_sql)
self.pdb.execute(insert_sql)
conf += self.makeSphinxDbSourceDelta(db,table)
# print(ver)
# print(conf)
return conf
def makeSphinxDbFieldRepalce(self, content, sph_field):
ver = self.ver.replace(".1",'')
ver = float(ver)
if ver >= 3.6:
content = content.replace("{$SPH_FIELD}", '')
content = content.replace("{$SPH_FIELD_INDEX}", '')
else:
content = content.replace("{$SPH_FIELD}", sph_field)
content = content.replace("{$SPH_FIELD_INDEX}", '')
return content
def makeSqlToSphinxDb(self, db, table = [], is_delta = False):
conf = ''
for tn in table:
pkey_name = self.getTablePk(db,tn)
if pkey_name == '':
continue
conf += self.makeSphinxDbSource(db, tn,is_delta)
if len(table) == 0:
tables = self.pdb.query("show tables in "+ db)
for x in range(len(tables)):
key = 'Tables_in_'+db
table_name = tables[x][key]
pkey_name = self.getTablePk(db, table_name, is_delta)
if pkey_name == '':
continue
if self.makeSqlToSphinxTableIsHaveFulltext(db, table_name):
conf += self.makeSphinxDbSource(db, table_name)
return conf
def makeSqlToSphinxTableIsHaveFulltext(self, db, table):
sql = "select COLUMN_NAME,DATA_TYPE from information_schema.COLUMNS where `TABLE_SCHEMA`='{}' and `TABLE_NAME` = '{}';"
sql = sql.format(db,table,)
cols = self.pdb.query(sql)
cols_len = len(cols)
for x in range(cols_len):
data_type = cols[x]['DATA_TYPE']
column_name = cols[x]['COLUMN_NAME']
if mw.inArray(['varchar'], data_type):
return True
if mw.inArray(['text','mediumtext','tinytext','longtext'], data_type):
return True
return False
def makeSqlToSphinxTable(self,db,table):
pkey_name = self.getTablePk(db,table)
sql = "select COLUMN_NAME,DATA_TYPE from information_schema.COLUMNS where `TABLE_SCHEMA`='{}' and `TABLE_NAME` = '{}';"
sql = sql.format(db,table,)
cols = self.pdb.query(sql)
cols_len = len(cols)
conf = ''
run_pos = 0
for x in range(cols_len):
data_type = cols[x]['DATA_TYPE']
column_name = cols[x]['COLUMN_NAME']
# print(column_name+":"+data_type)
# if mw.inArray(['tinyint'], data_type):
# conf += 'sql_attr_bool = '+ column_name + "\n"
if pkey_name == column_name:
# run_pos += 1
# conf += '\tsql_attr_bigint = '+column_name+"\n"
continue
if mw.inArray(['enum'], data_type):
run_pos += 1
conf += '\t'+self.eqVerField('sql_attr_string')+' = '+ column_name + "\n"
continue
if mw.inArray(['decimal'], data_type):
run_pos += 1
conf += '\t'+self.eqVerField('sql_attr_float')+' = '+ column_name + "\n"
continue
if mw.inArray(['bigint','smallint','tinyint','int','mediumint'], data_type):
run_pos += 1
conf += '\t'+self.eqVerField('sql_attr_bigint')+' = '+ column_name + "\n"
continue
if mw.inArray(['float'], data_type):
run_pos += 1
conf += '\t'+self.eqVerField('sql_attr_float')+' = '+ column_name + "\n"
continue
if mw.inArray(['char'], data_type):
conf += '\t'+self.eqVerField('sql_attr_string')+' = '+ column_name + "\n"
continue
if mw.inArray(['varchar'], data_type):
run_pos += 1
conf += '\t'+self.eqVerField('sql_field_string')+' = '+ column_name + "\n"
continue
if mw.inArray(['text','mediumtext','tinytext','longtext'], data_type):
run_pos += 1
conf += '\t'+self.eqVerField('sql_field_string')+' = '+ column_name + "\n"
continue
if mw.inArray(['datetime','date'], data_type):
run_pos += 1
conf += '\t'+self.eqVerField('sql_attr_timestamp')+' = '+ column_name + "\n"
continue
return conf
def checkDbName(self, db):
filter_db = ['information_schema','performance_schema','sys','mysql']
if db in filter_db:
return False
return True
def makeSqlToSphinx(self, db, tables = [], is_delta = False):
conf = ''
conf += self.makeSphinxHeader()
conf += self.makeSqlToSphinxDb(db, tables, is_delta)
return conf
def makeSqlToSphinxAll(self):
filter_db = ['information_schema','performance_schema','sys','mysql']
dblist = self.pdb.query('show databases')
conf = ''
conf += self.makeSphinxHeader()
# conf += makeSqlToSphinxDb(pdb, 'bbs')
for x in range(len(dblist)):
dbname = dblist[x]['Database']
if mw.inArray(filter_db, dbname):
continue
conf += self.makeSqlToSphinxDb(dbname)
return conf

@ -1,21 +1,35 @@
<style>
#table xm-select{
min-height: 30px;
line-height: 30px;
}
#table xm-select *{
font-size: 12px;
}
</style>
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('sphinx');">服务</p>
<p onclick="pluginInitD('sphinx');">自启动</p>
<p onclick="pluginConfigTpl('sphinx');">配置修改</p>
<p onclick="pluginConfigTpl('sphinx','', 'conf','config_tpl','read_config_tpl',tryRebuildIndex);">配置修改</p>
<p onclick="pluginLogs('sphinx','','run_log',30);">运行日志</p>
<p onclick="pluginLogs('sphinx','','query_log', 30);">查询日志</p>
<p onclick="runStatus();">运行状态</p>
<p onclick="rebuild();">重建索引</p>
<p onclick="commonFunc();">常用功能</p>
<p onclick="readme();">说明</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
<div class="soft-man-con" style="height:425px;overflow: auto;"></div>
</div>
</div>
</div>
<script type="text/javascript">
resetPluginWinHeight(500);
$.getScript( "/plugins/file?name=sphinx&f=js/sphinx.js", function(){
pluginService('sphinx');
});

@ -15,14 +15,13 @@ app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'sphinx'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
sys.path.append(getPluginDir() +"/class")
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
@ -210,9 +209,10 @@ def reload():
def rebuild():
file = initDreplace()
subprocess.Popen(file + ' rebuild &',
stdout=subprocess.PIPE, shell=True)
# data = mw.execShell(file + ' rebuild')
cmd = file + ' rebuild'
data = mw.execShell(cmd)
if data[0].find('successfully')<0:
return data[0].replace("\n","<br/>")
return 'ok'
@ -295,18 +295,35 @@ def sphinxConfParse():
sindex = re.findall(rep, content)
indexlen = len(sindex)
cmd = {}
cmd['cmd'] = bin_dir + '/bin/bin/indexer -c ' + bin_dir + '/sphinx.conf'
cmd['index'] = []
cmd_index = []
cmd_delta = []
if indexlen > 0:
cmd_index = []
cmd_delta = []
for x in range(indexlen):
if sindex[x].find(':') != -1:
cmd_delta.append(sindex[x])
name = sindex[x].strip()
if name == '':
continue
if name.find(':') != -1:
cmd_delta.append(name.strip())
else:
cmd_index.append(sindex[x])
cmd_index.append(name.strip())
# print(cmd_index)
# print(cmd_delta)
for ci in cmd_index:
val = {}
val['index'] = ci
cmd['index'] = cmd_index
cmd['delta'] = cmd_delta
cmd['cmd'] = bin_dir + '/bin/bin/indexer -c ' + bin_dir + '/sphinx.conf'
for cd in cmd_delta:
cd = cd.replace(" ", '')
if cd.find(":"+ci) > -1:
val['delta'] = cd.split(":")[0].strip()
break
cmd['index'].append(val)
return cmd
@ -317,6 +334,55 @@ def sphinxCmd():
else:
return mw.returnJson(False, 'no index')
def makeDbToSphinxTest():
conf_file = getConf()
import sphinx_make
sph_make = sphinx_make.sphinxMake()
conf = sph_make.makeSqlToSphinxAll()
mw.writeFile(conf_file,conf)
print(conf)
# makeSqlToSphinxTable()
return True
def makeDbToSphinx():
args = getArgs()
check = checkArgs(args, ['db','tables','is_delta','is_cover'])
if not check[0]:
return check[1]
db = args['db']
tables = args['tables']
is_delta = args['is_delta']
is_cover = args['is_cover']
if is_cover != 'yes':
return mw.returnJson(False,'暂时仅支持覆盖!')
sph_file = getConf()
import sphinx_make
sph_make = sphinx_make.sphinxMake()
version_pl = getServerDir() + "/version.pl"
if os.path.exists(version_pl):
version = mw.readFile(version_pl).strip()
sph_make.setVersion(version)
if not sph_make.checkDbName(db):
return mw.returnJson(False,'保留数据库名称,不可用!')
is_delta_bool = False
if is_delta == 'yes':
is_delta_bool = True
if is_cover == 'yes':
tables = tables.split(',')
content = sph_make.makeSqlToSphinx(db, tables, is_delta_bool)
mw.writeFile(sph_file,content)
return mw.returnJson(True,'设置成功!')
return mw.returnJson(True,'测试中')
if __name__ == "__main__":
func = sys.argv[1]
@ -352,5 +418,7 @@ if __name__ == "__main__":
print(runStatus())
elif func == 'sphinx_cmd':
print(sphinxCmd())
elif func == 'db_to_sphinx':
print(makeDbToSphinx())
else:
print('error')

@ -4,8 +4,7 @@
"name": "sphinx",
"title": "sphinx",
"shell": "install.sh",
"versions":["3.1.1"],
"updates":["3.1.1"],
"versions":["3.1.1","3.2.1","3.3.1","3.4.1","3.5.1","3.6.1","3.7.1"],
"tip": "soft",
"checks": "server/sphinx",
"path": "server/sphinx",

@ -33,7 +33,7 @@ start () {
}
rebuild () {
${APP_PATH}/bin/bin/indexer -c ${APP_CONF} --all --rotate &
${APP_PATH}/bin/bin/indexer -c ${APP_CONF} --all --rotate
}

@ -7,45 +7,108 @@ rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
sysArch=`arch`
install_tmp=${rootPath}/tmp/mw_install.pl
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/sphinx/index.py rebuild
# cd /www/server/mdserver-web/plugins/sphinx && bash install.sh install 3.6.1
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/sphinx/index.py db_to_sphinx && /www/server/sphinx/bin/bin/indexer -c /www/server/sphinx/sphinx.conf --all --rotate
# /Users/midoks/Desktop/mwdev/server/sphinx/bin/bin/indexer /Users/midoks/Desktop/mwdev/server/sphinx/sphinx.conf --all --rotate
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/sphinx/index.py sphinx_cmd
# /Users/midoks/Desktop/mwdev/server/sphinx/bin/bin/indexer /Users/midoks/Desktop/mwdev/server/sphinx/sphinx.conf --all --rotate
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/sphinx/index.py start
bash ${rootPath}/scripts/getos.sh
OSNAME=`cat ${rootPath}/data/osname.pl`
OSNAME_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
# echo "bash ${rootPath}/scripts/getos.sh"
OSNAME="macos"
if [ -f ${rootPath}/data/osname.pl ];then
OSNAME=`cat ${rootPath}/data/osname.pl`
fi
if [ ${OSNAME} == "centos" ] ||
[ ${OSNAME} == "fedora" ] ||
[ ${OSNAME} == "alma" ]; then
if [ "${OSNAME}" == "centos" ] ||
[ "${OSNAME}" == "fedora" ] ||
[ "${OSNAME}" == "alma" ]; then
yum install -y postgresql-libs unixODBC
fi
# http://sphinxsearch.com/files/sphinx-3.7.1-da9f8a4-linux-amd64.tar.gz
VERSION=$2
# echo $VERSION
if [ "$VERSION" == "3.1.1" ];then
VERSION_NUM=${VERSION}-612d99f
elif [ "$VERSION" == "3.2.1" ]; then
VERSION_NUM=${VERSION}-f152e0b
elif [ "$VERSION" == "3.3.1" ]; then
VERSION_NUM=${VERSION}-b72d67b
elif [ "$VERSION" == "3.4.1" ]; then
VERSION_NUM=${VERSION}-efbcc65
elif [ "$VERSION" == "3.5.1" ]; then
VERSION_NUM=${VERSION}-82c60cb
elif [ "$VERSION" == "3.6.1" ]; then
VERSION_NUM=${VERSION}-c9dbeda
elif [ "$VERSION" == "3.7.1" ]; then
VERSION_NUM=${VERSION}-da9f8a4
fi
# echo $VERSION_NUM
VERSION=3.1.1
Install_sphinx()
{
echo '正在安装Sphinx...'
mkdir -p $serverPath/sphinx
SPHINX_DIR=${serverPath}/source/sphinx
mkdir -p $SPHINX_DIR
if [ ! -f ${SPHINX_DIR}/sphinx-${VERSION}.tar.gz ];then
if [ $sysName == 'Darwin' ]; then
wget -O ${SPHINX_DIR}/sphinx-${VERSION}.tar.gz http://sphinxsearch.com/files/sphinx-${VERSION}-612d99f-darwin-amd64.tar.gz
else
curl -sSLo ${SPHINX_DIR}/sphinx-${VERSION}.tar.gz http://sphinxsearch.com/files/sphinx-${VERSION}-612d99f-linux-amd64.tar.gz
fi
SPH_NAME=amd64
if [ "$sysArch" == "arm64" ];then
SPH_NAME=amd64
elif [ "$sysArch" == "x86_64" ]; then
SPH_NAME=amd64
elif [ "$sysArch" == "aarch64" ]; then
SPH_NAME=aarch64
fi
if [ ! -f ${SPHINX_DIR}/sphinx-${VERSION}.tar.gz ];then
curl -sSLo ${SPHINX_DIR}/sphinx-${VERSION}.tar.gz https://github.com/midoks/mdserver-web/releases/download/init/sphinx-${VERSION}.tar.gz
if [ "$sysName" == "Darwin" ] && [ "$VERSION" == "3.7.1" ];then
SPH_NAME=aarch64
fi
SPH_SYSNAME=linux
if [ $sysName == 'Darwin' ]; then
SPH_SYSNAME=darwin
elif [ "$sysName" == "aarch64" ]; then
SPH_NAME=aarch64
elif [ "$sysName" == "freebsd" ]; then
SPH_NAME=freebsd
fi
cd ${SPHINX_DIR} && tar -zxvf sphinx-${VERSION}.tar.gz
if [ "$SPH_SYSNAME" == "linux" ];then
glibc_ver=`ldd --version | grep libc | awk -F ')' '{print $2}'|awk '{gsub(/^\s+|\s+$/, "");print}'`
if [ "$VERSION" == "3.7.1" ] && [ `echo "2.29 > $glibc_ver " | bc` -eq 1 ];then
SPH_NAME=${SPH_NAME}-glibc2.17
fi
if [ "$VERSION" == "3.6.1" ] && [ `echo "2.29 > $glibc_ver " | bc` -eq 1 ];then
SPH_NAME=${SPH_NAME}-glibc2.17
fi
fi
FILE_NAME=sphinx-${VERSION_NUM}-${SPH_SYSNAME}-${SPH_NAME}
FILE_TGZ=${FILE_NAME}.tar.gz
echo $FILE_TGZ
# curl -sSLo ${SPHINX_DIR}/${FILE_TGZ} http://sphinxsearch.com/files/${FILE_TGZ}
if [ ! -f ${SPHINX_DIR}/${FILE_TGZ} ];then
wget --no-check-certificate -O ${SPHINX_DIR}/${FILE_TGZ} http://sphinxsearch.com/files/${FILE_TGZ}
fi
cd ${SPHINX_DIR} && tar -zxvf ${FILE_TGZ}
if [ "$?" == "0" ];then
mkdir -p $SPHINX_DIR
@ -56,7 +119,10 @@ Install_sphinx()
echo "${VERSION}" > $serverPath/sphinx/version.pl
echo '安装Sphinx完成'
cd ${rootPath} && python3 ${rootPath}/plugins/sphinx/index.py start
cd ${rootPath} && python3 ${rootPath}/plugins/sphinx/index.py initd_install
if [ $sysName != 'Darwin' ]; then
cd ${rootPath} && python3 ${rootPath}/plugins/sphinx/index.py initd_install
fi
fi
if [ -d ${SPHINX_DIR}/sphinx-${VERSION} ];then
@ -69,8 +135,14 @@ Uninstall_sphinx()
if [ -f /usr/lib/systemd/system/sphinx.service ] || [ -f /lib/systemd/system/sphinx.service ];then
systemctl stop sphinx
systemctl disable sphinx
rm -rf /usr/lib/systemd/system/sphinx.service
rm -rf /lib/systemd/system/sphinx.service
if [ -f /usr/lib/systemd/system/sphinx.service ];then
rm -rf /usr/lib/systemd/system/sphinx.service
fi
if [ -f /lib/systemd/system/sphinx.service ];then
rm -rf /lib/systemd/system/sphinx.service
fi
systemctl daemon-reload
fi
@ -78,8 +150,12 @@ Uninstall_sphinx()
$serverPath/sphinx/initd/sphinx stop
fi
rm -rf $serverPath/sphinx
echo "Uninstall_sphinx" > $install_tmp
if [ -d $serverPath/sphinx ];then
echo "rm -rf $serverPath/sphinx"
rm -rf $serverPath/sphinx
fi
echo "卸载sphinx成功"
}
action=$1

@ -20,6 +20,32 @@ function spPostMin(method, args, callback){
},'json');
}
function myPost(method, args, callback, title){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(toArrayObject(args));
} else {
_args = JSON.stringify(args);
}
var _title = '正在获取...';
if (typeof(title) != 'undefined'){
_title = title;
}
$.post('/plugins/run', {name:'mysql', func:method, args:_args}, function(data) {
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function spPost(method, args, callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
spPostMin(method,args,function(data){
@ -30,21 +56,167 @@ function spPost(method, args, callback){
});
}
function rebuild(){
var con = '<button class="btn btn-default btn-sm" onclick="rebuildIndex();">重建索引</button>';
function commonFunc(){
var con = '<button class="btn btn-default btn-sm" onclick="confirmRebuildIndex();">重建索引</button>';
con += '&nbsp;&nbsp; <button class="btn btn-default btn-sm" onclick="autoMakeConf();">自动创建配置</button>';
$(".soft-man-con").html(con);
}
function autoMakeConf(){
var xm_db_list;
var con = '<ul class="help-info-text c7">';
con += '<li style="color:red;">如果数据量比较大,第一次启动会失败!(可通过手动建立索引)</li>';
con += '<li style="color:red;">以下内容,需手动加入计划任务。</li>';
layer.open({
type: 1,
area: ['380px','350px'],
title: '自动创建配置',
closeBtn: 1,
shift: 5,
shadeClose: true,
btn:["提交","关闭"],
content: "<form class='bt-form pd20'>\
<div class='line'>\
<span class='tname'>选择数据库</span>\
<div class='info-r'>\
<select class='bt-input-text mr5' name='dbname' style='width:100%'>\
<option value=''></option>\
</select>\
</div>\
</div>\
<div class='line'>\
<span class='tname'>选择表</span>\
<div class='info-r'>\
<div id='table'></div>\
</div>\
</div>\
<div class='line'>\
<span class='tname'>是否增量</span>\
<div class='info-r'>\
<select class='bt-input-text mr5' name='is_delta' style='width:100px'>\
<option value='no'></option>\
<option value='yes'></option>\
</select>\
</div>\
</div>\
<div class='line'>\
<span class='tname'>是否覆盖配置</span>\
<div class='info-r'>\
<select class='bt-input-text mr5' name='is_cover' style='width:100px'>\
<option value='yes'></option>\
<option value='no'></option>\
</select>\
</div>\
</div>\
<ul class='help-info-text c7'>\
<li style='color:red;'>具体配置仍须手动修改!!!</li>'\
</ul>\
</form>\
",
success:function(l,i){
$(l).find('.layui-layer-content').css('overflow','visible');
xm_db_list = xmSelect.render({
el: '#table',
repeat: false,
toolbar: {show: true},
data: [],
});
myPost('get_db_list', {"page":1,"page_size":20}, function(data){
var rdata = $.parseJSON(data.data);
var dblist = rdata.data;
var db_html = '';
for (var i = 0; i < dblist.length; i++) {
db_html += "<option value='"+dblist[i]['name']+"'>"+dblist[i]['name']+"</option>";
}
if (dblist.length > 0){
initDbSelect(dblist[0]['name']);
}
$('select[name="dbname"]').html(db_html);
});
$('select[name="dbname"]').change(function(){
var db = $('select[name="dbname"]').val();
initDbSelect(db);
});
},
yes:function(index){
var args = {}
args['db'] = $('select[name="dbname"]').val();
args['is_delta'] = $('select[name="is_delta"]').val();
args['is_cover'] = $('select[name="is_cover"]').val();
args['tables'] = xm_db_list.getValue('value').join(',');
// console.log(args);
spPost('db_to_sphinx', args, function(rdata){
var rdata = $.parseJSON(rdata.data);
// console.log(rdata);
showMsg(rdata.msg,function(){
if (rdata.status){
layer.close(index);
confirmRebuildIndex();
}
},{icon: rdata.status ? 1 : 2}, 2000);
});
}
});
function initDbSelect(db){
if (db == ''){
return;
}
getDbInfo(db, function(rdata){
var rdata = $.parseJSON(rdata.data);
var tables = rdata.tables;
var idx_db = [];
for (var i = 0; i < tables.length; i++) {
var t = {};
t['name'] = tables[i]['table_name'];
t['value'] = tables[i]['table_name'];
idx_db.push(t);
}
xm_db_list = xmSelect.render({el: '#table', filterable: true,repeat: false,toolbar: {show: true},data: idx_db,});
});
}
function getDbInfo(db_name, callback){
myPost('get_db_info', {name:db_name}, function(data){
callback(data);
});
}
}
function rebuildIndex(){
spPost('rebuild', '', function(data){
if (data.data == 'ok'){
layer.msg('在重建中..',{icon:1,time:2000,shade: [0.3, '#000']});
layer.msg('重建成功!',{icon:1,time:2000,shade: [0.3, '#000']});
} else {
layer.msg(data.data,{icon:2,time:2000,shade: [0.3, '#000']});
layer.msg(data.data,{icon:2,time:10000,shade: [0.3, '#000']});
}
});
}
function confirmRebuildIndex(){
layer.confirm("是否重建索引?", {icon:3,closeBtn: 1} , function(){
rebuildIndex();
});
}
function tryRebuildIndex(){
layer.confirm("修改配置后,是否尝试重建索引!", {icon:3,closeBtn: 1} , function(){
rebuildIndex();
});
}
function secToTime(s) {
var t;
if(s > -1){
@ -74,7 +246,12 @@ function runStatus(){
return;
}
var idata = rdata.data;
// console.log(idata);
var tbody = '';
for (var i in idata) {
tbody += '<tr><th>'+i+'</th><td>' + idata[i] + '</td><td colspan="2">'+i+'</td></tr>';
}
var con = '<div class="divtable"><table class="table table-hover table-bordered" style="margin-bottom:10px;background-color:#fafafa">\
<tbody>\
<tr><th>运行时间</th><td>' + secToTime(idata.uptime) + '</td><th></th><td>' + parseInt(parseInt(idata.queries) / parseInt(idata.uptime)) + '</td></tr>\
@ -84,18 +261,12 @@ function runStatus(){
</tbody>\
</table>\
<table class="table table-hover table-bordered">\
<thead style="display:none;"><th></th><th></th><th></th><th></th></thead>\
<tbody>\
<tr><th>command_delete</th><td>' + idata.command_delete + '</td><td colspan="2">command_delete</td></tr>\
<tr><th>command_excerpt</th><td>' + idata.command_excerpt + '</td><td colspan="2">command_excerpt</td></tr>\
<tr><th>command_flushattrs</th><td>' + idata.command_flushattrs + '</td><td colspan="2">command_flushattrs</td></tr>\
<tr><th>command_keywords</th><td>' + idata.command_keywords + '</td><td colspan="2">command_keywords</td></tr>\
<tr><th>command_persist</th><td>' + idata.command_persist + '</td><td colspan="2">command_persist</td></tr>\
<tr><th>command_search</th><td>' + idata.command_search + '</td><td colspan="2">command_search</td></tr>\
<tr><th>command_status</th><td>' + idata.command_status + '</td><td colspan="2">command_status</td></tr>\
<tr><th>command_update</th><td>' + idata.command_update + '</td><td colspan="2">command_update</td></tr>\
<tbody>\
</table></div>';
<thead style="display:none;"><th></th><th></th><th></th><th></th></thead>\
<tbody>\
'+tbody+'\
<tbody>\
</table>\
</div>';
$(".soft-man-con").html(con);
});
@ -110,21 +281,24 @@ function readme(){
return;
}
// console.log(rdata['data']);
var con = '<ul class="help-info-text c7">';
con += '<li style="color:red;">如果数据量比较大,第一次启动会失败!(可通过手动建立索引)</li>';
con += '<li style="color:red;">以下内容,需手动加入计划任务。</li>';
con += '<li>全量:' + rdata['data']['cmd'] + ' --all --rotate</li>';
//主索引
for (var i = 0; i < rdata['data']['index'].length; i++) {
var index_t = rdata['data']['index'][i];
con += '<li>主索引:' + rdata['data']['cmd'] + ' '+ index_t +' --rotate</li>';
}
for (var i = 0; i < rdata['data']['delta'].length; i++) {
var delta_t = rdata['data']['delta'][i];
var list = delta_t.split(':');
// console.log(list);
con += '<li>增量索引:' + rdata['data']['cmd'] + ' '+ list[0] +' --rotate</li>';
con += '<li>合并索引:' + rdata['data']['cmd'] + ' --merge '+ list[1] + ' ' + list[0] +' --rotate</li>';
var index_kv = rdata['data']['index'][i];
var index = index_kv['index'];
var delta = index_kv['delta'];
// console.log(index,delta);
con += '<li>主索引 :' + rdata['data']['cmd'] + ' '+ index +' --rotate</li>';
con += '<li>增量索引 :' + rdata['data']['cmd'] + ' '+ delta +' --rotate</li>';
con += '<li>合并索引 :' + rdata['data']['cmd'] + ' --merge '+ index + ' ' + delta +' --rotate</li>';
}
con += '</ul>';

@ -42,10 +42,10 @@ def send_msg(bot, tag='ad', trigger_time=300):
# 信号只在一个周期内执行一次|end
# https://t.me/gjgzs2022 | 22/m | @GJ_gzs
# https://zhaoziyuan1.cc | web | 15/m | 2m | next,5/15 | @baleite
# https://zhaoziyuan1.cc | web | 15/m | 2m | next,7/15 | @baleite
# 综合包网/NG接口开户 | 28/m | 6m | next,10/28 | @aabbcx888
# 实名认证/过人脸🕵各种账号处理✅ | 30/m| next,6/30 | @nngzs
## 海外服务器 高防CDN 解决移动屏蔽 | 19/m | next,4/19 | @YYCDNFW
# 海外服务器 高防CDN 解决移动屏蔽 | 群内置顶AD| 16/m | @YYCDNFW
keyboard = [
[
types.InlineKeyboardButton(

@ -51,11 +51,9 @@ def send_msg(bot, tag='ad', trigger_time=300):
# 信号只在一个周期内执行一次|end
# https://t.me/gjgzs2022 | 22/m | @GJ_gzs
# https://zhaoziyuan1.cc | web | 15/m | 2m | next,5/15 | @baleite
# https://zhaoziyuan1.cc | web | 15/m | 2m | next,7/15 | @baleite
# 综合包网/NG接口开户 | 28/m | 6m | next,10/28 | @aabbcx888
# 实名认证/过人脸🕵各种账号处理✅ | 30/m| next,6/30 | @nngzs
## 海外服务器 高防CDN 解决移动屏蔽 | 19/m | next,4/19 | @YYCDNFW
# 群内置顶AD| 16/m | @YYCDNFW
keyboard = [
[
types.InlineKeyboardButton(

@ -86,13 +86,13 @@
.indicators-label {
display: inline-block;
width: 95px;
width: 88px;
height: 26px;
line-height: 26px;
cursor: pointer;
}
.indicators-label input {
.indicators-label input[type=radio] {
height: 13px;
margin: 0 5px 0 0;
vertical-align: -2px;

@ -654,17 +654,19 @@ def getLogsRealtimeInfo():
'''
import datetime
args = getArgs()
check = checkArgs(args, ['site', 'type'])
check = checkArgs(args, ['site', 'type','second'])
if not check[0]:
return check[1]
domain = args['site']
dtype = args['type']
second = int(args['second'])
conn = pSqliteDb('web_logs', domain)
timeInt = time.mktime(datetime.datetime.now().timetuple())
conn = conn.where("time>=?", (int(timeInt) - 10,))
conn = conn.where("time>=?", (int(timeInt) - second,))
field = 'time,body_length'
field_sum = toSumField(field.replace("time,", ""))
@ -714,6 +716,7 @@ def getLogsList():
method = args['method']
status_code = args['status_code']
request_time = args['request_time']
request_size = args['request_size']
spider_type = args['spider_type']
query_date = args['query_date']
search_uri = args['search_uri']
@ -752,6 +755,14 @@ def getLogsList():
if len(request_time_s) == 1:
conn = conn.andWhere("request_time>=?", (request_time,))
if request_size != "all":
request_size_s = request_size.strip().split('-')
# print(int(request_size_s[0])*1024)
if len(request_size_s) == 2:
conn = conn.andWhere("body_length>=? and body_length<?", (int(request_size_s[0])*1024,int(request_size_s[1])*1024,))
if len(request_size_s) == 1:
conn = conn.andWhere("body_length>=?", (int(request_size_s[0])*1024,))
if spider_type == "normal":
pass
elif spider_type == "only_spider":

@ -192,7 +192,7 @@ function wsOverviewRequest(page){
var data = rdata.data.data;
var statData = rdata.data.stat_list;
console.log(statData, data);
// console.log(statData, data);
var stat_pv = statData['pv'] == null?0:statData['pv'];
var stat_uv = statData['uv'] == null?0:statData['uv'];
@ -383,11 +383,13 @@ function wsOverviewRequest(page){
var select_option = $('.indicators-container input:checked').parent().attr('data-name');
if (select_option != "realtime_traffic" && select_option != 'realtime_request' ){
clearInterval(ovTimer);
console.log("get_logs_realtime_info over:"+select_option);
// console.log("get_logs_realtime_info over:"+select_option);
return;
}
wsOriginPost("get_logs_realtime_info",'',{"site":args["site"], "type":select_option} , function(rdata){
var second = $('#check_realtime_second').val();
wsOriginPost("get_logs_realtime_info",'',{"site":args["site"], "type":select_option,'second':second} , function(rdata){
var rdata = $.parseJSON(rdata.data);
@ -488,11 +490,11 @@ var html = '<div>\
<p class="ov_num">0</p>\
</div>\
<div class="overview_box">\
<p class="ov_title">实时流量<i class="tips" data-toggle="tooltip" data-placement="top" title="当前10秒内您网站的实时流量大小。包括已排除的请求。">?</i></p>\
<p class="ov_title">实时流量<i class="tips" data-toggle="tooltip" data-placement="top" title="当前X秒内您网站的实时流量大小。包括已排除的请求。">?</i></p>\
<p class="ov_num">0</p>\
</div>\
<div class="overview_box">\
<p class="ov_title">每秒请求<i class="tips" data-toggle="tooltip" data-placement="top" title="当前10秒内您网站的实时请求数量。包括已排除的请求。">?</i></p>\
<p class="ov_title"><span id="ov_title_req_second">每秒请求<span><i class="tips" data-toggle="tooltip" data-placement="top" title="当前1-10秒内您网站的实时请求数量。包括已排除的请求。">?</i></p>\
<p class="ov_num">0</p>\
</div>\
</div>\
@ -525,7 +527,11 @@ var html = '<div>\
</div>\
<div class="indicators-label" bt-event-click="indicatorsType" data-name="realtime_request">\
<input type="radio" id="check_realtime_request" name="check_realtime_request">\
<span class="check_realtime_request" style="font-weight:normal">每秒请求</span>\
<span class="check_realtime_request" style="font-weight:normal">每X秒请求</span>\
</div>\
<div class="indicators-label" bt-event-click="indicatorsType">\
<input class="bt-input-text mr5" type="number" id="check_realtime_second" name="check_realtime_second" value="1" style="width:40px;outline:none;height:23px;border-radius:3px;">\
<span style="font-weight:normal"></span>\
</div>\
</div>\
</div>\
@ -592,8 +598,34 @@ $('#search_time button').click(function(){
});
$('.indicators-container input').click(function(){
$('.indicators-container input').each(function(){
function initRealtimeTraffic(){
var check_realtime_second = $('#check_realtime_second').val();
if (check_realtime_second<1){
check_realtime_second = 1;
$('#check_realtime_second').val(check_realtime_second);
}
if (check_realtime_second>10){
check_realtime_second = 10;
$('#check_realtime_second').val(check_realtime_second);
}
var title = "每秒请求";
if (check_realtime_second > 1){
title = '每'+check_realtime_second+'秒请求'
}
$('#ov_title_req_second').text(title)
$('.check_realtime_request').text(title);
}
initRealtimeTraffic();
$('#check_realtime_second').change(function(){
initRealtimeTraffic();
});
$('.indicators-container input[type=radio]').click(function(){
$('.indicators-container input[type=radio]').each(function(){
$(this).removeAttr('checked');
});
$(this).prop({'checked':true});
@ -2081,6 +2113,7 @@ var html = '<div>\
<option value="503">503</option>\
<option value="403">403</option>\
<option value="404">404</option>\
<option value="499">499</option>\
</select>\
<span style="margin-left:10px">时间: </span>\
<div class="input-group" style="margin-left:10px;width:350px;display: inline-table;vertical-align: top;">\
@ -2179,6 +2212,7 @@ function wsTableLogRequest(page){
args['method'] = $('select[name="method"]').val();
args['status_code'] = $('select[name="status_code"]').val();
args['request_time'] = $('select[name="request_time"]').val();
args['request_size'] = $('select[name="request_size"]').val();
args['spider_type'] = $('select[name="spider_type"]').val();
args['referer'] = $('select[name="referer"]').val();
args['ip'] = $('input[name="ip"]').val();
@ -2213,8 +2247,18 @@ function wsTableLogRequest(page){
"12":"其他",
}
var req_status = $('#logs_search').attr('req');
// console.log(req_status);
if (typeof(req_status) != 'undefined'){
if (req_status == 'start'){
layer.msg("正在请求中,请稍候!");
return;
}
}
$('#logs_search').attr('req','start');
wsPostCallbak('get_logs_list', '' ,args, function(rdata){
$('#logs_search').attr('req','end');
var rdata = $.parseJSON(rdata.data);
var list = '';
var data = rdata.data.data;
@ -2267,22 +2311,6 @@ function wsTableLogRequest(page){
$('#ws_table').html(table);
$('#wsPage').html(rdata.data.page);
$('input[name="ip"]').bind('focus', function(e){
$(this).keyup(function(e){
if(e.keyCode == 13) {
wsTableLogRequest(1);
}
});
});
$('input[name="search_uri"]').bind('focus', function(e){
$(this).keyup(function(e){
if(e.keyCode == 13) {
wsTableLogRequest(1);
}
});
});
$(".tablescroll .details").click(function(){
var index = $(this).attr('data-id');
var res = data[index];
@ -2388,6 +2416,7 @@ var html = '<div>\
<option value="500">500</option>\
<option value="502">502</option>\
<option value="503">503</option>\
<option value="499">499</option>\
<option value="404">404</option>\
<option value="301">301</option>\
<option value="302">302</option>\
@ -2432,6 +2461,15 @@ var html = '<div>\
<option value="500-1000">500ms-1s</option>\
<option value="1000">大于1s</option>\
</select>\
<span style="margin-left:10px;">大小: </span>\
<select class="bt-input-text" name="request_size" style="margin-left:5px;">\
<option value="all">所有</option>\
<option value="0-1">0-1(kb)</option>\
<option value="1-20">1-20(kb)</option>\
<option value="20-50">20-50(kb)</option>\
<option value="50-100">50-100(kb)</option>\
<option value="100">大于100kb</option>\
</select>\
<span style="margin-left:10px;">URL过滤: </span>\
<div class="input-group" style="width:210px;display:inline-flex;">\
<input type="text" name="search_uri" class="form-control btn-group-sm" autocomplete="off" placeholder="URI搜索" style="font-size: 12px;padding: 0 10px;height:30px;">\
@ -2444,6 +2482,22 @@ var html = '<div>\
</div>';
$(".soft-man-con").html(html);
$('input[name="ip"]').bind('focus', function(e){
$(this).keyup(function(e){
if(e.keyCode == 13) {
wsTableLogRequest(1);
}
});
});
$('input[name="search_uri"]').bind('focus', function(e){
$(this).keyup(function(e){
if(e.keyCode == 13) {
wsTableLogRequest(1);
}
});
});
//日期范围
laydate.render({
elem: '#time_choose',
@ -2507,6 +2561,10 @@ $('select[name="request_time"]').change(function(){
wsTableLogRequest(1);
});
$('select[name="request_size"]').change(function(){
wsTableLogRequest(1);
});
$('#logs_search').click(function(){
wsTableLogRequest(1);
});

@ -705,7 +705,7 @@ def get_logs_list(args):
start_time = time.time()
check = checkArgs(args, ['page', 'page_size','site', 'method',
'status_code', 'spider_type', 'request_time', 'query_date', 'search_uri'])
'status_code', 'spider_type', 'request_time', 'request_size', 'query_date', 'search_uri'])
if not check[0]:
return check[1]
@ -716,6 +716,7 @@ def get_logs_list(args):
method = args['method']
status_code = args['status_code']
request_time = args['request_time']
request_size = args['request_size']
spider_type = args['spider_type']
query_date = args['query_date']
search_uri = args['search_uri']
@ -726,7 +727,7 @@ def get_logs_list(args):
limit = str(page_size) + ' offset ' + str(page_size * (page - 1))
conn = pSqliteDb('web_logs', domain)
field = 'time,ip,domain,server_name,method,is_spider,protocol,status_code,request_headers,ip_list,client_port,body_length,user_agent,referer,request_time,uri,body_length'
field = 'time,ip,domain,server_name,method,is_spider,protocol,status_code,request_headers,ip_list,client_port,body_length,user_agent,referer,request_time,uri'
condition = ''
conn = conn.field(field)
conn = conn.where("1=1", ())
@ -762,6 +763,14 @@ def get_logs_list(args):
if len(request_time_s) == 1:
conn = conn.andWhere("request_time>=?", (request_time,))
if request_size != "all":
request_size_s = request_size.strip().split('-')
# print(int(request_size_s[0])*1024)
if len(request_size_s) == 2:
conn = conn.andWhere("body_length>=? and body_length<?", (int(request_size_s[0])*1024,int(request_size_s[1])*1024,))
if len(request_size_s) == 1:
conn = conn.andWhere("body_length>=?", (int(request_size_s[0])*1024,))
if spider_type == "normal":
pass
elif spider_type == "only_spider":
@ -824,7 +833,7 @@ def getLogsErrorList():
limit = str(page_size) + ' offset ' + str(page_size * (page - 1))
conn = pSqliteDb('web_logs', domain)
field = 'time,ip,domain,server_name,method,protocol,status_code,ip_list,client_port,body_length,user_agent,referer,request_time,uri,body_length'
field = 'time,ip,domain,server_name,method,protocol,status_code,ip_list,client_port,body_length,user_agent,referer,request_time,uri'
conn = conn.field(field)
conn = conn.where("1=1", ())

@ -29,7 +29,7 @@ pymemcache
redis
pillow
Jinja2>=2.11.2
PyMySQL==1.0.2
PyMySQL==1.1.1
whitenoise==5.3.0
pyotp
pytz

@ -1592,6 +1592,22 @@ function setChmod(action,fileName){
var toExec = fileName == lan.files.all?'batch(3,1)':'setChmod(1,\''+fileName+'\')';
$.post('/files/file_access','filename='+encodeURIComponent(fileName),function(rdata){
// console.log(rdata);
var sys_users = rdata.sys_users;
var own_html = '';
var is_find_own_option = false;
for (var i = 0; i < sys_users.length; i++) {
var own = sys_users[i];
if (rdata.chown==own){
is_find_own_option = true;
own_html += '<option value="'+own+'" selected="selected">'+own+'</option>';
} else {
own_html += '<option value="'+own+'">'+own+'</option>';
}
}
if (!is_find_own_option){
own_html += '<option value="'+rdata.chown+'" selected="selected">'+rdata.chown+'</option>';
}
layer.open({
type:1,
closeBtn: 1,
@ -1619,10 +1635,8 @@ function setChmod(action,fileName){
</fieldset>\
<div class="setchmodnum"><input class="bt-input-text" type="text" id="access" maxlength="3" value="'+rdata.chmod+'">权限\
<span>所有者\
<select id="chown" class="bt-input-text">\
<option value="www" '+(rdata.chown=='www'?'selected="selected"':'')+'>www</option>\
<option value="mysql" '+(rdata.chown=='mysql'?'selected="selected"':'')+'>mysql</option>\
<option value="root" '+(rdata.chown=='root'?'selected="selected"':'')+'>root</option>\
<select id="chown" class="bt-input-text" style="width:100px;">\
'+own_html+'\
</select></span></div>\
<div class="bt-form-submit-btn">\
<button type="button" class="btn btn-danger btn-sm btn-title" onclick="layer.closeAll()">关闭</button>\

@ -1427,7 +1427,7 @@ var index = {
},'json');
},
task:function(){
index.getData();
// index.getData();
setInterval(function() {
index.getData();
}, 3000);

@ -2255,7 +2255,7 @@ function pluginConfig(_name, version, func){
//配置修改模版 --- start
function pluginConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_func){
function pluginConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_func, save_callback_func){
if ( typeof(version) == 'undefined' ){
version = '';
}
@ -2281,7 +2281,7 @@ function pluginConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_
<textarea class="bt-input-text" style="height: 320px; line-height:18px;" id="textBody"></textarea>\
<button id="onlineEditFileBtn" class="btn btn-success btn-sm" style="margin-top:10px;">保存</button>\
<ul class="help-info-text c7 ptb15">\
<li>此处为'+ _name + version +'主配置文件,若您不了解配置规则,请勿随意修改</li>\
<li>此处为'+ _name + version +'主配置文件,若您不了解配置规则,请勿随意修改</li>\
</ul>';
$(".soft-man-con").html(con);
@ -2321,7 +2321,7 @@ function pluginConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_
"Ctrl-H": "replaceAll",
"Ctrl-S": function() {
$("#textBody").text(editor.getValue());
pluginConfigSave(fileName);
pluginConfigSave(fileName,save_callback_func);
}
},
lineNumbers: true,
@ -2332,7 +2332,7 @@ function pluginConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_
$("#onlineEditFileBtn").unbind('click');
$("#onlineEditFileBtn").click(function(){
$("#textBody").text(editor.getValue());
pluginConfigSave(fileName);
pluginConfigSave(fileName, save_callback_func);
});
},'json');
}
@ -2361,7 +2361,7 @@ function pluginConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_
"Ctrl-H": "replaceAll",
"Ctrl-S": function() {
$("#textBody").text(editor.getValue());
pluginConfigSave(fileName);
pluginConfigSave(fileName,save_callback_func);
}
},
lineNumbers: true,
@ -2371,7 +2371,7 @@ function pluginConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_
$(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0});
$("#onlineEditFileBtn").click(function(){
$("#textBody").text(editor.getValue());
pluginConfigSave(fileName);
pluginConfigSave(fileName,save_callback_func);
});
},'json');
},'json');
@ -2498,13 +2498,19 @@ function pluginConfigListTpl(_name, version, config_tpl_func, read_config_tpl_fu
//配置保存
function pluginConfigSave(fileName) {
function pluginConfigSave(fileName, callback) {
var data = encodeURIComponent($("#textBody").val());
var encoding = 'utf-8';
var loadT = layer.msg('保存中...', {icon: 16,time: 0});
$.post('/files/save_body', 'data=' + data + '&path=' + fileName + '&encoding=' + encoding, function(rdata) {
layer.close(loadT);
layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2});
showMsg(rdata.msg, function(){
if ( rdata.status && typeof(callback) == 'function'){
callback();
}
},{icon: rdata.status ? 1 : 2});
},'json');
}

@ -9,7 +9,8 @@ input,
textarea,
select {
font-size: 100%;
font-family: inherit
font-family: inherit;
outline: none;
}
body,

@ -14,15 +14,6 @@
<!--[if lte IE 9]>
<script src="/static/js/requestAnimationFrame.js"></script>
<![endif]-->
<!-- 统计一下用户量,让我的开发更有激情 -->
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-FC74BB2RGD"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-FC74BB2RGD');
</script>
{% for menu in data['hook_menu'] %}
@ -188,4 +179,30 @@ if (thisPath.indexOf('?')>-1){
{% endif %}
{% endfor %}
<!-- 统计一下用户量,让我的开发更有激情 -->
<script type="text/javascript">
$(document).ready(function(){
setTimeout(function(){
$.getScript('https://www.googletagmanager.com/gtag/js?id=G-FC74BB2RGD', function(){
console.log("load google ad!");
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-FC74BB2RGD');
});
},3000);
});
</script>
<!-- Google tag (gtag.js) -->
<!-- <script async src="https://www.googletagmanager.com/gtag/js?id=G-FC74BB2RGD"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-FC74BB2RGD');
</script> -->
</html>

@ -87,6 +87,18 @@ class backupTools:
path = mw.getServerDir() + '/' + mtype + '/etc/my.cnf'
return path
def recognizeDbMode(self, mtype='mysql'):
conf = self.getConf(mtype)
con = mw.readFile(conf)
rep = r"!include %s/(.*)?\.cnf" % (mw.getServerDir() +'/'+ mtype +"/etc/mode",)
mode = 'none'
try:
data = re.findall(rep, con, re.M)
mode = data[0]
except Exception as e:
pass
return mode
# 数据库密码处理
def mypass(self, act, root):
conf_file = self.getConf('mysql')
@ -140,9 +152,14 @@ class backupTools:
# 开启一致性事务 会lock表
# cmd = db_path + "/bin/mysqldump --defaults-file=" + my_cnf + " --force --opt --default-character-set=utf8 " + \
# name + " | gzip > " + filename
option = ''
mode = self.recognizeDbMode('mysql')
if mode == 'gtid':
option = ' --set-gtid-purged=off '
# skip-opt 不会lock表
cmd = db_path + "/bin/mysqldump --defaults-file=" + my_cnf + " --skip-opt --create-options --default-character-set=utf8 " + \
# --skip-opt --create-options
cmd = db_path + "/bin/mysqldump --defaults-file=" + my_cnf +" " + option +" --single-transaction -q --default-character-set=utf8mb4 " + \
name + " | gzip > " + filename
# print(cmd)
mw.execShell(cmd)

@ -258,7 +258,7 @@ mw_mirror()
else
bash <(curl --insecure -sSL https://gitee.com/SuperManito/LinuxMirrors/raw/main/ChangeMirrors.sh)
fi
cd /www/server/mdserver-web
cd ${ROOT_PATH}/mdserver-web
}
mw_install_app()
@ -297,8 +297,8 @@ mw_debug(){
port=$(cat $mw_path/data/port.pl)
fi
if [ -d /www/server/mdserver-web ];then
cd /www/server/mdserver-web
if [ -d ${ROOT_PATH}/mdserver-web ];then
cd ${ROOT_PATH}/mdserver-web
fi
gunicorn -b :$port -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
}
@ -373,7 +373,8 @@ mw_connect_mysql(){
INPUT_KEY=${SOURCE_LIST_KEY[$INPUT]}
CHOICE_DB=${DB_TYPE[$INPUT_KEY]}
echo "login to ${CHOICE_DB}:"
pwd=$(cd /www/server/mdserver-web && python3 /www/server/mdserver-web/plugins/${CHOICE_DB}/index.py root_pwd)
pwd=$(cd ${ROOT_PATH}/mdserver-web && python3 ${ROOT_PATH}/mdserver-web/plugins/${CHOICE_DB}/index.py root_pwd)
if [ "$CHOICE_DB" == "mysql" ];then
${ROOT_PATH}/mysql/bin/mysql -uroot -p"${pwd}"
fi
@ -392,15 +393,34 @@ mw_connect_mysql(){
}
mw_redis(){
CONF="${ROOT_PATH}/redis/redis.conf"
if [ ! -f "$CONF" ]; then
echo -e "not install redis!"
exit 1
fi
REDISPORT=$(cat $CONF |grep port|grep -v '#'|awk '{print $2}')
REDISPASS=$(cat $CONF |grep requirepass|grep -v '#'|awk '{print $2}')
if [ "$REDISPASS" != "" ];then
REDISPASS=" -a $REDISPASS"
fi
CLIEXEC="${ROOT_PATH}/redis/bin/redis-cli -p $REDISPORT$REDISPASS"
echo $CLIEXEC
${CLIEXEC}
}
mw_venv(){
cd /www/server/mdserver-web && source bin/activate
cd ${ROOT_PATH}/mdserver-web && source bin/activate
}
mw_clean_lib(){
cd /www/server/mdserver-web && rm -rf lib
cd /www/server/mdserver-web && rm -rf lib64
cd /www/server/mdserver-web && rm -rf bin
cd /www/server/mdserver-web && rm -rf include
cd ${ROOT_PATH}/mdserver-web && rm -rf lib
cd ${ROOT_PATH}/mdserver-web && rm -rf lib64
cd ${ROOT_PATH}/mdserver-web && rm -rf bin
cd ${ROOT_PATH}/mdserver-web && rm -rf include
}
case "$1" in
@ -428,6 +448,7 @@ case "$1" in
'debug') mw_debug;;
'mirror') mw_mirror;;
'db') mw_connect_mysql;;
'redis') mw_redis;;
'venv') mw_venv;;
'clean_lib') mw_clean_lib;;
'default')

@ -95,6 +95,7 @@ yum install -y bison re2c cmake
yum install -y cmake3
yum install -y autoconf
yum install -y expect
yum install -y bc
yum install -y curl curl-devel
yum install -y zlib zlib-devel

@ -22,6 +22,7 @@ yum install -y curl-devel libmcrypt libmcrypt-devel
yum install -y mysql-devel
yum install -y expect
yum install -y pv
yum install -y bc
SSH_PORT=`netstat -ntpl|grep sshd|grep -v grep | sed -n "1,1p" | awk '{print $4}' | awk -F : '{print $2}'`
if [ "$SSH_PORT" == "" ];then

@ -41,6 +41,7 @@ echo y | pacman -Sy lemon
echo y | pacman -Sy which
echo y | pacman -Sy expect
echo y | pacman -Sy pv
echo y | pacman -Sy bc
## gd start

@ -32,6 +32,7 @@ yum install -y libmcrypt-devel
yum install -y mysql-devel
yum install -y expect
yum install -y pv
yum install -y bc
# if [ -f /usr/sbin/iptables ];then

@ -57,6 +57,7 @@ apt install -y wget curl lsof unzip tar cron expect locate lrzsz
apt install -y rar
apt install -y unrar
apt install -y pv
apt install -y bc
apt install -y python3-pip python3-dev python3-venv

@ -32,6 +32,7 @@ yum install -y libmcrypt-devel
yum install -y mysql-devel
yum install -y expect
yum install -y pv
yum install -y bc
# if [ -f /usr/sbin/iptables ];then

@ -74,6 +74,7 @@ yum install -y epel-release
yum install -y libevent libevent-devel zip libmcrypt libmcrypt-devel
yum install -y rar unrar
yum install -y pv
yum install -y bc
yum install -y gcc libffi-devel python-devel openssl-devel
yum install -y libmcrypt libmcrypt-devel python3-devel

@ -61,6 +61,7 @@ pkg install -y libevent
pkg install -y pidof
pkg install -y pstree
pkg install -y pv
pkg install -y bc
# curl https://sh.rustup.rs -sSf | sh
pkg install -y rust

@ -29,6 +29,7 @@ zypper install -y libtirpc-devel
zypper install -y rpcgen
zypper install -y expect
zypper install -y pv
zypper install -y bc
zypper install -y libzip libzip-devel
zypper install -y unrar rar

@ -184,7 +184,7 @@ if [ "$VERSION_ID" -ge "8" ];then
oniguruma oniguruma-devel patch pcre pcre-devel perl perl-Data-Dumper perl-devel procps psmisc python3-devel \
openssl openssl-devel patchelf libargon2-devel\
ImageMagick ImageMagick-devel libyaml-devel \
pv \
pv bc\
readline-devel rpcgen sqlite-devel rar unrar tar unzip vim-minimal wget zip zlib zlib-devel;
do
# dnf --enablerepo=remi,appstream,baseos,epel,extras,powertools install -y oniguruma5php-devel
@ -204,7 +204,7 @@ else
libwebp libwebp-devel libxml2 libxml2-devel libxslt libxslt-devel libzip libzip-devel libzstd-devel \
make mysql-devel ncurses ncurses-devel net-tools oniguruma oniguruma-devel openldap openldap-devel \
openssl openssl-devel patch pcre pcre-devel perl perl-Data-Dumper perl-devel psmisc python-devel \
pv \
pv bc\
python3-devel python3-pip re2c readline-devel rpcgen sqlite-devel tar unzip rar unrar vim-minimal vixie-cron \
wget zip zlib zlib-devel ImageMagick ImageMagick-devel libyaml-devel patchelf libargon2-devel;
do

@ -16,6 +16,7 @@ sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
yum install -y wget lsof
yum install -y unrar rar
yum install -y pv
yum install -y bc
yum install -y python3-devel
yum install -y crontabs
yum install -y expect

@ -19,6 +19,7 @@ apt install -y python3-venv
apt install -y python3-dev
apt install -y expect
apt install -y pv
apt install -y bc
apt install -y cron

@ -62,6 +62,7 @@ def mwcli(mw_input=0):
print("(23) 关闭IPV6支持")
print("(24) 开启防火墙SSH端口")
print("(25) 关闭二次验证")
print("(26) 查看防火墙信息")
print("(100) 开启PHP52显示")
print("(101) 关闭PHP52显示")
print("(200) 切换Linux系统软件源")
@ -75,7 +76,12 @@ def mwcli(mw_input=0):
except:
mw_input = 0
nums = [1, 2, 3, 4, 5, 10, 11, 12, 13, 20, 21, 22, 23, 24, 25, 100, 101, 200, 201]
nums = [
1, 2, 3, 4, 5, 10, 11, 12, 13,
20, 21, 22, 23, 24, 25, 26,
100, 101,
200, 201
]
if not mw_input in nums:
print(raw_tip)
print("已取消!")
@ -153,6 +159,21 @@ def mwcli(mw_input=0):
print("|-关闭二次验证成功!")
else:
print("|-二次验证已关闭!")
elif mw_input == 26:
cmd = 'which ufw'
run_cmd = False
find_cmd = mw.execShell(cmd)
if find_cmd[0].strip() != '':
run_cmd = True
os.system('ufw status')
cmd = 'which firewall-cmd'
find_cmd = mw.execShell(cmd)
if find_cmd[0].strip() != '':
run_cmd = True
os.system('firewall-cmd --list-all')
if not run_cmd:
mw.echoInfo("未检测到防火墙!")
elif mw_input == 100:
php_conf = 'plugins/php/info.json'
if os.path.exists(php_conf):

@ -35,4 +35,5 @@ whitenoise==5.3.0
pyotp
pytz
pyTelegramBotAPI
telebot
telebot
pyyaml

@ -1,3 +1,4 @@
urllib3==1.21.1
gevent==22.10.2
gunicorn==21.2.0
gunicorn==21.2.0
pyyaml
Loading…
Cancel
Save