diff --git a/class/core/config_api.py b/class/core/config_api.py index c08e129e9..1002d8840 100755 --- a/class/core/config_api.py +++ b/class/core/config_api.py @@ -27,7 +27,7 @@ from flask import request class config_api: - __version = '0.16.2' + __version = '0.16.2.2' __api_addr = 'data/api.json' def __init__(self): diff --git a/class/core/system_api.py b/class/core/system_api.py index f6a395a13..939b7634b 100755 --- a/class/core/system_api.py +++ b/class/core/system_api.py @@ -299,7 +299,8 @@ class system_api: data_list = data.strip().split("\n") mac_version = '' for x in data_list: - mac_version += x.split("\t")[1] + ' ' + xlist = x.split("\t") + mac_version += xlist[len(xlist)-1] + ' ' arch_ver = mw.execShell("arch") return mac_version + " (" + arch_ver[0].strip() + ")" @@ -366,15 +367,15 @@ class system_api: mem = psutil.virtual_memory() if mw.getOs() == 'darwin': memInfo = { - 'memTotal': mem.total / 1024 / 1024 + 'memTotal': mem.total, } memInfo['memRealUsed'] = memInfo['memTotal'] * (mem.percent / 100) else: memInfo = { - 'memTotal': mem.total / 1024 / 1024, - 'memFree': mem.free / 1024 / 1024, - 'memBuffers': mem.buffers / 1024 / 1024, - 'memCached': mem.cached / 1024 / 1024 + 'memTotal': mem.total, + 'memFree': mem.free, + 'memBuffers': mem.buffers, + 'memCached': mem.cached } memInfo['memRealUsed'] = memInfo['memTotal'] - \ @@ -558,9 +559,9 @@ class system_api: networkInfo['upTotal'] = networkIo[0] networkInfo['downTotal'] = networkIo[1] networkInfo['up'] = round(float( - networkIo[0] - session['up']) / 1024 / (ntime - session['otime']), 2) + networkIo[0] - session['up']) / (ntime - session['otime']), 2) networkInfo['down'] = round( - float(networkIo[1] - session['down']) / 1024 / (ntime - session['otime']), 2) + float(networkIo[1] - session['down']) / (ntime - session['otime']), 2) networkInfo['downPackets'] = networkIo[3] networkInfo['upPackets'] = networkIo[2] diff --git a/plugins/mariadb/index.html b/plugins/mariadb/index.html index 167114618..17aeace3f 100755 --- a/plugins/mariadb/index.html +++ b/plugins/mariadb/index.html @@ -12,6 +12,7 @@

性能优化

日志

慢日志

+

BINLOG

管理列表

主从配置

diff --git a/plugins/mariadb/index.py b/plugins/mariadb/index.py index dd5988e50..c8d3996fc 100755 --- a/plugins/mariadb/index.py +++ b/plugins/mariadb/index.py @@ -90,6 +90,12 @@ def getDataDir(): tmp = re.search(rep, content) return tmp.groups()[0].strip() +def getLogBinName(): + file = getConf() + content = mw.readFile(file) + rep = 'log-bin\s*=\s*(.*)' + tmp = re.search(rep, content) + return tmp.groups()[0].strip() def getPidFile(): file = getConf() @@ -294,6 +300,53 @@ def binLog(): mw.writeFile(conf, con) return mw.returnJson(True, '设置成功!') +def binLogList(): + args = getArgs() + data = checkArgs(args, ['page', 'page_size', 'tojs']) + if not data[0]: + return data[1] + + page = int(args['page']) + page_size = int(args['page_size']) + + data_dir = getDataDir() + log_bin_name = getLogBinName() + + alist = os.listdir(data_dir) + log_bin_l = [] + for x in range(len(alist)): + f = alist[x] + t = {} + if f.startswith(log_bin_name) and not f.endswith('.index'): + abspath = data_dir + '/' + f + t['name'] = f + t['size'] = os.path.getsize(abspath) + t['time'] = mw.getDataFromInt(os.path.getctime(abspath)) + log_bin_l.append(t) + + log_bin_l = sorted(log_bin_l, key=lambda x: x['time'], reverse=True) + + # print(log_bin_l) + # print(data_dir, log_bin_name) + + count = len(log_bin_l) + + page_start = (page - 1) * page_size + page_end = page_start + page_size + if page_end > count: + page_end = count + + data = {} + page_args = {} + page_args['count'] = count + page_args['p'] = page + page_args['row'] = page_size + page_args['tojs'] = args['tojs'] + data['page'] = mw.getPage(page_args) + data['data'] = log_bin_l[page_start:page_end] + + return mw.getJson(data) + def cleanBinLog(): db = pMysqlDb() @@ -302,21 +355,6 @@ def cleanBinLog(): return mw.returnJson(True, '清理BINLOG成功!') -def setSkipGrantTables(v): - ''' - 设置是否密码验证 - ''' - conf = getConf() - con = mw.readFile(conf) - if v: - if con.find('#skip-grant-tables') != -1: - con = con.replace('#skip-grant-tables', 'skip-grant-tables') - else: - con = con.replace('skip-grant-tables', '#skip-grant-tables') - mw.writeFile(conf, con) - return True - - def getErrorLog(): args = getArgs() path = getDataDir() @@ -579,7 +617,7 @@ def setMyPort(): restart() return mw.returnJson(True, '编辑成功!') - +# python3 plugins/mariadb/index.py run_info {} def runInfo(version): if status(version) == 'stop': @@ -760,6 +798,8 @@ def rootPwd(): 'id=?', (1,)).getField('mysql_root') +# python3 plugins/mariadb/index.py import_db_external {"file":"xx.sql","name":"demo1"} +# python3 plugins/mariadb/index.py import_db_external {"file":"db_demo1_20231221_203614 2.sql","name":"demo1"} def importDbExternal(): args = getArgs() data = checkArgs(args, ['file', 'name']) @@ -806,6 +846,9 @@ def importDbExternal(): mw.execShell(cmd) import_sql = import_dir + tmpFile + if file.find(".sql") > -1 and file.find(".sql.gz") == -1: + import_sql = import_dir + file + if import_sql == "": return mw.returnJson(False, '未找SQL文件') @@ -813,12 +856,14 @@ def importDbExternal(): sock = getSocketFile() os.environ["MYSQL_PWD"] = pwd - mysql_cmd = getServerDir() + '/bin/mariadb -S ' + sock + ' -uroot -p' + \ - pwd + ' ' + name + ' < ' + import_sql + mysql_cmd = getServerDir() + '/bin/mariadb -S ' + sock + ' -uroot -p"' + \ + pwd + '" ' + name + ' < "' + import_sql+'"' # print(mysql_cmd) os.system(mysql_cmd) - os.remove(import_sql) + + if ext != 'sql': + os.remove(import_sql) return mw.returnJson(True, 'ok') @@ -982,7 +1027,7 @@ def syncGetDatabases(): host = user["Host"] break - ps = mw.getMsg('INPUT_PS') + ps = vdb_name if vdb_name == 'test': ps = mw.getMsg('DATABASE_TEST') addTime = time.strftime('%Y-%m-%d %X', time.localtime()) @@ -1054,12 +1099,21 @@ def setRootPwd(version=''): if not data[0]: return data[1] + #强制修改 + force = 0 + if 'force' in args and args['force'] == '1': + force = 1 + + password = args['password'] try: pdb = pMysqlDb('mysql') result = pdb.query("show databases") isError = isSqlError(result) if isError != None: + if force == 1: + pSqliteDb('config').where('id=?', (1,)).save('mysql_root', (password,)) + return mw.returnJson(True, '【强制修改】数据库root密码修改成功(不意为成功连接数据)!') return isError cmd = "ALTER USER 'root'@'localhost' IDENTIFIED BY '" + password + "';" @@ -1069,7 +1123,12 @@ def setRootPwd(version=''): pSqliteDb('config').where('id=?', (1,)).save('mysql_root', (password,)) orm = pMysqlDb() orm.execute("flush privileges") - return mw.returnJson(True, '数据库root密码修改成功!') + + msg = '' + if force == 1: + msg = ',无须强制!' + + return mw.returnJson(True, '数据库root密码修改成功!' + msg) except Exception as ex: return mw.returnJson(False, '修改错误:' + str(ex)) @@ -1276,16 +1335,58 @@ def setDbAccess(): psdb.where('username=?', (name,)).save('accept,rw', (access, 'rw',)) return mw.returnJson(True, '设置成功!') +def openSkipGrantTables(): + mycnf = getConf() + content = mw.readFile(mycnf) + content = content.replace('#skip-grant-tables','skip-grant-tables') + mw.writeFile(mycnf, content) + return True + +def closeSkipGrantTables(): + mycnf = getConf() + content = mw.readFile(mycnf) + content = content.replace('skip-grant-tables','#skip-grant-tables') + mw.writeFile(mycnf, content) + return True + + +def resetDbRootPwd(version): + serverdir = getServerDir() + myconf = serverdir + "/etc/my.cnf" + pwd = mw.getRandomString(16) + pSqliteDb('config').where('id=?', (1,)).save('mysql_root', (pwd,)) + + db_option = "-S " + serverdir + "/mysql.sock" + cmd_pass = serverdir + '/bin/mariadb ' + db_option + ' -uroot -e' + cmd_pass = cmd_pass + \ + "\"flush privileges;use mysql;grant all privileges on *.* to 'root'@'localhost' identified by '" + pwd + "';" + cmd_pass = cmd_pass + "flush privileges;\"" + + data = mw.execShell(cmd_pass) + # print(data) + return True def fixDbAccess(version): + + pdb = pMysqlDb() + mdb_ddir = getDataDir() + if not os.path.exists(mdb_ddir): + return mw.returnJson(False, '数据目录不存在,尝试重启重建!') + try: - pdb = pMysqlDb() psdb = pSqliteDb('databases') data = pdb.query('show databases') isError = isSqlError(data) if isError != None: + # 重置密码 appCMD(version, 'stop') - mw.execShell("rm -rf " + getServerDir() + "/data") + openSkipGrantTables() + appCMD(version, 'start') + time.sleep(3) + resetDbRootPwd(version) + + appCMD(version, 'stop') + closeSkipGrantTables() appCMD(version, 'start') return mw.returnJson(True, '修复成功!') return mw.returnJson(True, '正常无需修复!') @@ -1588,6 +1689,13 @@ def setDbMasterAccess(): psdb.where('username=?', (username,)).save('accept', (access,)) return mw.returnJson(True, '设置成功!') +def resetMaster(version=''): + pdb = pMysqlDb() + r = pdb.execute('reset master') + isError = isSqlError(r) + if isError != None: + return isError + return mw.returnJson(True, '重置成功!') def getMasterDbList(version=''): args = getArgs() @@ -1899,8 +2007,7 @@ def getMasterRepSlaveUserCmd(version): if mode == "gtid": sql = "CHANGE MASTER " + connection_name + "TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \ clist[0]['username'] + "', MASTER_PASSWORD='" + \ - clist[0]['password'] + "', " + "MASTER_LOG_FILE='" + mstatus[0]["File"] + \ - "',MASTER_USE_GTID=no,MASTER_CONNECT_RETRY=10;" + clist[0]['password'] + "',MASTER_USE_GTID=no,MASTER_CONNECT_RETRY=10;" else: sql = "CHANGE MASTER " + connection_name + "TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \ clist[0]['username'] + "', MASTER_PASSWORD='" + \ @@ -2186,6 +2293,58 @@ def getSlaveList(version=''): data['data'] = dlist return mw.getJson(data) +def trySlaveSyncBugfix(version=''): + if status(version) == 'stop': + return mw.returnJson(False, 'MySQL未启动', []) + + mode_file = getSyncModeFile() + if not os.path.exists(mode_file): + return mw.returnJson(False, '需要先设置同步配置') + + mode = mw.readFile(mode_file) + if mode != 'sync-user': + return mw.returnJson(False, '仅支持【同步账户】模式') + + conn = pSqliteDb('slave_sync_user') + slave_sync_data = conn.field('ip,port,user,pass,mode,cmd').select() + if len(slave_sync_data) < 1: + return mw.returnJson(False, '需要先添加【同步用户】配置!') + + # print(slave_sync_data) + # 本地从库 + sdb = pMysqlDb() + + gtid_purged = '' + + for i in range(len(slave_sync_data)): + port = slave_sync_data[i]['port'] + password = slave_sync_data[i]['pass'] + host = slave_sync_data[i]['ip'] + user = slave_sync_data[i]['user'] + + # print(port, password, host) + + mdb = mw.getMyORM() + mdb.setHost(host) + mdb.setPort(port) + mdb.setUser(user) + mdb.setPwd(password) + mdb.setSocket('') + + # var_gtid = mdb.query('show VARIABLES like "%gtid_purged%"') + var_gtid = mdb.query('select @@global.gtid_current_pos as Value') + #print(var_gtid) + if len(var_gtid) > 0: + gtid_purged += var_gtid[0]['Value'] + ',' + + gtid_purged = gtid_purged.strip(',') + sql = "set @@global.gtid_slave_pos='" + gtid_purged + "'" + + sdb.query('stop all slaves') + # print(sql) + sdb.query(sql) + sdb.query('start all slaves') + return mw.returnJson(True, '修复成功!') def getSlaveSyncCmd(version=''): root = mw.getRunDir() @@ -2226,7 +2385,7 @@ def parseSlaveSyncCmd(cmd): a[kv[0]] = kv[1].replace("'", '').replace("'", '').replace(";", '') return a - +# python3 plugins/mariadb/index.py init_slave_status {} def initSlaveStatusSyncUser(version=''): conn = pSqliteDb('slave_sync_user') @@ -2267,7 +2426,7 @@ def initSlaveStatusSyncUser(version=''): try: pinfo = parseSlaveSyncCmd(cmd_sql) except Exception as e: - return mw.returnJson(False, base_t + '->CMD同步命令不合规范!') + return mw.returnJson(False, base_t + '->CMD同步命令不合规范:'+str(e)) pdb.query(cmd_sql) @@ -2452,8 +2611,7 @@ def fullSync(version=''): if args['begin'] == '1': cmd = 'cd ' + mw.getRunDir() + ' && python3 ' + \ getPluginDir() + \ - '/index.py do_full_sync {"db":"' + \ - args['db'] + '","sign":"' + sign + '"} &' + '/index.py do_full_sync {"db":"' + args['db'] + '","sign":"' + sign + '"} &' # print(cmd) mw.execShell(cmd) return json.dumps({'code': 0, 'msg': '同步数据中!', 'progress': 0}) @@ -2475,6 +2633,7 @@ def fullSync(version=''): return json.dumps({'code': 0, 'msg': '点击开始,开始同步!', 'progress': 0}) +# python3 plugins/mariadb/index.py do_full_sync {"db":"demo1","sign":"","beigin":"1"} def doFullSync(version=''): mode_file = getSyncModeFile() if not os.path.exists(mode_file): @@ -2512,13 +2671,16 @@ def doFullSyncUser(version=''): ip = data['ip'] bak_file = '/tmp/tmp.sql' + if os.path.exists(bak_file): + os.system("rm -rf " + bak_file) writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0}) writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 10}) if not os.path.exists(bak_file): - dump_sql_data = getServerDir() + "/bin/mysqldump --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \ - port + " -u" + user + " -p" + apass + " " + sync_db + " > " + bak_file + # https://mariadb.com/kb/zh-cn/mariadb-dump/ + dump_sql_data = getServerDir() + "/bin/mariadb-dump -f --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \ + port + " -u" + user + " -p'" + apass + "' " + sync_db + ">" + bak_file print(dump_sql_data) mw.execShell(dump_sql_data) @@ -2526,8 +2688,9 @@ def doFullSyncUser(version=''): if os.path.exists(bak_file): pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root') sock = getSocketFile() - my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \ - ' ' + sync_db + ' < ' + bak_file + my_import_cmd = getServerDir() + '/bin/mariadb -S ' + sock + " -uroot -p'" + pwd + \ + "' " + sync_db + '<' + bak_file + print(my_import_cmd) mw.execShell(my_import_cmd) pinfo = parseSlaveSyncCmd(data['cmd']) @@ -2729,6 +2892,8 @@ if __name__ == "__main__": print(getConf()) elif func == 'bin_log': print(binLog()) + elif func == 'binlog_list': + print(binLogList()) elif func == 'clean_bin_log': print(cleanBinLog()) elif func == 'error_log': @@ -2797,6 +2962,8 @@ if __name__ == "__main__": print(getDbrunMode(version)) elif func == 'set_dbrun_mode': print(setDbrunMode(version)) + elif func == 'reset_master': + print(resetMaster(version)) elif func == 'get_masterdb_list': print(getMasterDbList(version)) elif func == 'get_master_status': @@ -2821,6 +2988,8 @@ if __name__ == "__main__": print(getMasterRepSlaveUserCmd(version)) elif func == 'get_slave_list': print(getSlaveList(version)) + elif func == 'try_slave_sync_bugfix': + print(trySlaveSyncBugfix(version)) elif func == 'get_slave_sync_cmd': print(getSlaveSyncCmd(version)) elif func == 'get_slave_ssh_list': diff --git a/plugins/mariadb/index_mariadb.py b/plugins/mariadb/index_mariadb.py new file mode 100644 index 000000000..2eed99a8e --- /dev/null +++ b/plugins/mariadb/index_mariadb.py @@ -0,0 +1,186 @@ +# 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 + + +if mw.isAppleSystem(): + cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\'' + info = mw.execShell(cmd) + p = "/usr/local/lib/" + info[0].strip() + "/site-packages" + sys.path.append(p) + + +app_debug = False +if mw.isAppleSystem(): + app_debug = True + + +def getPluginName(): + return 'mariadb' + + +def getPluginDir(): + return mw.getPluginDir() + '/' + getPluginName() + + +def getSPluginDir(): + return '/www/server/mdserver-web/plugins/' + getPluginName() + + +def getServerDir(): + return mw.getServerDir() + '/' + getPluginName() + + +def getConf(): + path = getServerDir() + '/etc/my.cnf' + return path + + +def getDataDir(): + file = getConf() + content = mw.readFile(file) + rep = 'datadir\s*=\s*(.*)' + tmp = re.search(rep, content) + return tmp.groups()[0].strip() + + +def getRelayLogName(): + file = getConf() + content = mw.readFile(file) + rep = 'relay-log\s*=\s*(.*)' + tmp = re.search(rep, content) + return tmp.groups()[0].strip() + + +def getLogBinName(): + file = getConf() + content = mw.readFile(file) + rep = 'log-bin\s*=\s*(.*)' + tmp = re.search(rep, content) + return tmp.groups()[0].strip() + + +def binLogListLook(args): + + file = args['file'] + line = args['line'] + + data_dir = getDataDir() + my_bin = getServerDir() + '/bin' + my_binlog_cmd = my_bin + '/mysqlbinlog' + + cmd = my_binlog_cmd + ' --no-defaults ' + \ + data_dir + '/' + file + '|tail -' + str(line) + + data = mw.execShell(cmd) + + rdata = {} + rdata['cmd'] = cmd + rdata['data'] = data[0] + + return rdata + + +def binLogListLookDecode(args): + + file = args['file'] + line = args['line'] + + data_dir = getDataDir() + my_bin = getServerDir() + '/bin' + my_binlog_cmd = my_bin + '/mysqlbinlog' + + cmd = my_binlog_cmd + ' --no-defaults --base64-output=decode-rows -vvvv ' + \ + data_dir + '/' + file + '|tail -' + str(line) + + data = mw.execShell(cmd) + + rdata = {} + rdata['cmd'] = cmd + rdata['data'] = data[0] + + return rdata + + +def binLogListTraceRelay(args): + rdata = {} + file = args['file'] + line = args['line'] + + relay_name = getRelayLogName() + data_dir = getDataDir() + alist = os.listdir(data_dir) + relay_list = [] + for x in range(len(alist)): + f = alist[x] + t = {} + if f.startswith(relay_name) and not f.endswith('.index'): + relay_list.append(f) + + relay_list = sorted(relay_list, reverse=True) + if len(relay_list) == 0: + rdata['cmd'] = '' + rdata['data'] = '无Relay日志' + return rdata + + file = relay_list[0] + + my_bin = getServerDir() + '/bin' + my_binlog_cmd = my_bin + '/mysqlbinlog' + + cmd = my_binlog_cmd + ' --no-defaults --base64-output=decode-rows -vvvv ' + \ + data_dir + '/' + file + '|tail -' + str(line) + + data = mw.execShell(cmd) + + rdata['cmd'] = cmd + rdata['data'] = data[0] + + return rdata + + +def binLogListTraceBinLog(args): + rdata = {} + file = args['file'] + line = args['line'] + + data_dir = getDataDir() + log_bin_name = getLogBinName() + + alist = os.listdir(data_dir) + log_bin_l = [] + for x in range(len(alist)): + f = alist[x] + t = {} + if f.startswith(log_bin_name) and not f.endswith('.index'): + log_bin_l.append(f) + + if len(log_bin_l) == 0: + rdata['cmd'] = '' + rdata['data'] = '无BINLOG' + return rdata + + log_bin_l = sorted(log_bin_l, reverse=True) + file = log_bin_l[0] + + my_bin = getServerDir() + '/bin' + my_binlog_cmd = my_bin + '/mysqlbinlog' + + cmd = my_binlog_cmd + ' --no-defaults --base64-output=decode-rows -vvvv ' + \ + data_dir + '/' + file + '|tail -' + str(line) + + data = mw.execShell(cmd) + + rdata['cmd'] = cmd + rdata['data'] = data[0] + + return rdata diff --git a/plugins/mariadb/js/mariadb.js b/plugins/mariadb/js/mariadb.js index ca786a3c1..a5fa5b685 100755 --- a/plugins/mariadb/js/mariadb.js +++ b/plugins/mariadb/js/mariadb.js @@ -58,6 +58,62 @@ function myAsyncPost(method,args){ return syncPost('/plugins/run', {name:'mysql', func:method, args:_args}); } +function myPostCallbak(method, version, args,callback){ + var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); + + var req_data = {}; + req_data['name'] = 'mariadb'; + req_data['func'] = method; + req_data['script']='index_mariadb'; + args['version'] = version; + + + if (typeof(args) == 'string' && args == ''){ + req_data['args'] = JSON.stringify(toArrayObject(args)); + } else { + req_data['args'] = JSON.stringify(args); + } + + $.post('/plugins/callback', req_data, function(data) { + layer.close(loadT); + if (!data.status){ + layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']}); + return; + } + + if(typeof(callback) == 'function'){ + callback(data); + } + },'json'); +} + +function myPostCallbakN(method, version, args,callback){ + + var req_data = {}; + req_data['name'] = 'mariadb'; + req_data['func'] = method; + req_data['script']='index_mariadb'; + args['version'] = version; + + + if (typeof(args) == 'string' && args == ''){ + req_data['args'] = JSON.stringify(toArrayObject(args)); + } else { + req_data['args'] = JSON.stringify(args); + } + + $.post('/plugins/callback', req_data, 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 runInfo(){ myPost('run_info','',function(data){ @@ -83,7 +139,7 @@ function runInfo(){ 活动/峰值连接数' + rdata.Threads_running + '/' + rdata.Max_used_connections + '若值过大,增加max_connections\ 线程缓存命中率' + ((1 - rdata.Threads_created / rdata.Connections) * 100).toFixed(2) + '%若过低,增加thread_cache_size\ 索引命中率' + ((1 - rdata.Key_reads / rdata.Key_read_requests) * 100).toFixed(2) + '%若过低,增加key_buffer_size\ - Innodb索引命中率' + ((1 - rdata.Innodb_buffer_pool_reads / rdata.Innodb_buffer_pool_read_requests) * 100).toFixed(2) + '%若过低,增加innodb_buffer_pool_size\ + Innodb索引命中率' + (rdata.Innodb_buffer_pool_read_requests / (rdata.Innodb_buffer_pool_read_requests+rdata.Innodb_buffer_pool_reads)).toFixed(2) + '%若过低,增加innodb_buffer_pool_size\ 查询缓存命中率' + cache_size + '' + lan.soft.mysql_status_ps5 + '\ 创建临时表到磁盘' + ((rdata.Created_tmp_disk_tables / rdata.Created_tmp_tables) * 100).toFixed(2) + '%若过大,尝试增加tmp_table_size\ 已打开的表' + rdata.Open_tables + '若过大,增加table_cache_size\ @@ -438,13 +494,24 @@ function syncToDatabase(type){ } function setRootPwd(type, pwd){ + if (type==1){ + var password = $("#MyPassword").val(); + myPost('set_root_pwd', {password:password}, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + dbList(); + },{icon: rdata.status ? 1 : 2}); + }); + return; + } + var index = layer.open({ type: 1, area: '500px', title: '修改数据库密码', closeBtn: 1, shift: 5, - btn:["提交","关闭"], + btn:["提交", "关闭", "复制ROOT密码", "强制修改"], shadeClose: true, content: "
\
\ @@ -454,16 +521,36 @@ function setRootPwd(type, pwd){
\ \
", - yes:function(index,layero){ + yes:function(layerIndex){ var password = $("#MyPassword").val(); myPost('set_root_pwd', {password:password}, function(data){ var rdata = $.parseJSON(data.data); showMsg(rdata.msg,function(){ + layer.close(layerIndex); dbList(); - layer.close(index); },{icon: rdata.status ? 1 : 2}); }); - return; + }, + btn3:function(){ + var password = $("#MyPassword").val(); + copyText(password); + return false; + }, + btn4:function(layerIndex){ + layer.confirm('强制修改,是为了在重建时使用,确定强制?', { + btn: ['确定', '取消'] + }, function(index, layero){ + layer.close(index); + var password = $("#MyPassword").val(); + myPost('set_root_pwd', {password:password,force:'1'}, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + layer.close(layerIndex); + dbList(); + },{icon: rdata.status ? 1 : 2}); + }); + }); + return false; } }); } @@ -1160,6 +1247,146 @@ function dbList(page, search){ }); } +function myBinRollingLogs(_name, func, _args, line){ + + var file_line = 100; + if ( typeof(line) != 'undefined' ){ + file_line = line; + } + + var reqTimer = null; + + function requestLogs(func,file,line){ + myPostCallbakN(func,'',{'file':file,"line":line}, function(rdata){ + var data = rdata.data.data; + var cmd = rdata.data.cmd; + if(data == '') { + data = '当前没有日志!'; + } + + $('#my_rolling_cmd').html(cmd); + + $('#my_rolling_copy').click(function(){ + copyText(cmd); + }); + + var ebody = ''; + $("#my_rolling_logs").html(ebody); + var ob = document.getElementById('roll_info_log'); + ob.scrollTop = ob.scrollHeight; + }); + } + + + layer.open({ + type: 1, + title: _name + '日志', + area: ['800px','700px'], + end: function(){ + if (reqTimer){ + clearInterval(reqTimer); + } + }, + content:'
\ +
\ + \ + \ + \ + \ +
cmd
\ +
\ +
\ +
\ + \ +
', + success:function(){ + var fileName = _args['file']; + requestLogs(func,fileName,file_line); + reqTimer = setInterval(function(){ + requestLogs(func,fileName,file_line); + },1000); + } + }); +} + +function myBinLogsRender(page){ + var _data = {}; + if (typeof(page) =='undefined'){ + var page = 1; + } + + _data['page'] = page; + _data['page_size'] = 10; + _data['tojs'] = 'myBinLogsRender'; + myPost('binlog_list', _data, function(data){ + var rdata = $.parseJSON(data.data); + // console.log(rdata); + var list = ''; + for(i in rdata.data){ + list += ''; + + list += '' + rdata.data[i]['name'] +''; + list += '' + toSize(rdata.data[i]['size'])+''; + list += '' + rdata.data[i]['time'] +''; + + + list += ''; + list += '查看 | '; + list += '解码查看'; + list += ''; + } + + if (rdata.data.length ==0){ + list = '无数据'; + } + + $("#binlog_list tbody").html(list); + $('#binlog_page').html(rdata.page); + + + $('#binlog_list .look').click(function(){ + var i = $(this).data('index'); + var file = rdata.data[i]['name']; + myBinRollingLogs('查看BINLOG','binLogListLook',{'file':file },100); + }); + + $('#binlog_list .look_decode').click(function(){ + var i = $(this).data('index'); + var file = rdata.data[i]['name']; + myBinRollingLogs('查看解码BINLOG','binLogListLookDecode',{'file':file },100); + }); + }); +} + +function myBinLogs(){ + var con = '
\ + \ + \ +
\ +
\ + \ + \ + \ + \ + \ + \ + \ +
文件名称大小时间操作
\ +
\ +
\ +
\ +
'; + $(".soft-man-con").html(con); + myBinLogsRender(1); + + $('.soft-man-con .relay_trace').click(function(){ + myBinRollingLogs('中继日志跟踪','binLogListTraceRelay',{'file':''},100); + }); + + $('.soft-man-con .binlog_trace').click(function(){ + myBinRollingLogs('最新BINLOG日志跟踪','binLogListTraceBinLog',{'file':''},100); + }); +} function myLogs(){ @@ -1603,6 +1830,14 @@ function setDbMasterAccess(username){ }); } +function resetMaster(){ + myPost('reset_master', '', function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + },{icon: rdata.status ? 1 : 2}); + },'正在执行重置master命令[reset master]'); +} + function getMasterRepSlaveList(){ var _data = {}; if (typeof(page) =='undefined'){ @@ -2329,11 +2564,16 @@ function masterOrSlaveConf(version=''){ "+(info['Slave_SQL_Running_State'] == '' ? '无':info['Slave_SQL_Running_State']) +"\ "; + var btn_list = ['复制错误',"取消"]; + if (info['Last_IO_Error'].search(/1236/i)>0){ + btn_list = ['复制错误',"取消","尝试修复"]; + } + layer.open({ type: 1, title: '同步异常信息', area: ['600px','300px'], - btn:['复制错误',"取消"], + btn:btn_list, content:"
\
\
\ @@ -2348,10 +2588,20 @@ function masterOrSlaveConf(version=''){
\ ", success:function(){ - // copyText(v['Error']); - // $('.class-copy-db-err').click(function(){ - // copyText(v['Error']); - // }); + if (info['Last_IO_Error'] != ''){ + copyText(info['Last_IO_Error']); + return; + } + + if (info['Last_SQL_Error'] != ''){ + copyText(info['Last_SQL_Error']); + return; + } + + if (info['Slave_SQL_Running_State'] != ''){ + copyText(info['Slave_SQL_Running_State']); + return; + } }, yes:function(){ if (info['Last_IO_Error'] != ''){ @@ -2368,6 +2618,14 @@ function masterOrSlaveConf(version=''){ copyText(info['Slave_SQL_Running_State']); return; } + }, + btn3:function(){ + myPost('try_slave_sync_bugfix', {}, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg, function(){ + masterOrSlaveConf(); + },{ icon: rdata.status ? 1 : 5 },2000); + }); } }); }); @@ -2437,6 +2695,7 @@ function masterOrSlaveConf(version=''){

\ Master[主]配置\ \ + \

\
\ \ diff --git a/plugins/mariadb/versions/10.11/install.sh b/plugins/mariadb/versions/10.11/install.sh index a433e5431..268a58f5a 100755 --- a/plugins/mariadb/versions/10.11/install.sh +++ b/plugins/mariadb/versions/10.11/install.sh @@ -14,7 +14,7 @@ sysName=`uname` install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb -MY_VER=10.11.2 +MY_VER=10.11.6 Install_app() { diff --git a/plugins/mariadb/versions/10.6/install.sh b/plugins/mariadb/versions/10.6/install.sh index ee514a658..585928bd1 100755 --- a/plugins/mariadb/versions/10.6/install.sh +++ b/plugins/mariadb/versions/10.6/install.sh @@ -13,7 +13,7 @@ sysName=`uname` install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb -MY_VER=10.6.12 +MY_VER=10.6.16 Install_app() { diff --git a/plugins/mariadb/versions/10.9/install.sh b/plugins/mariadb/versions/10.9/install.sh index a41092daa..e34acb0a4 100755 --- a/plugins/mariadb/versions/10.9/install.sh +++ b/plugins/mariadb/versions/10.9/install.sh @@ -14,7 +14,7 @@ sysName=`uname` install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb -MY_VER=10.9.5 +MY_VER=10.9.8 Install_app() { diff --git a/plugins/mariadb/versions/11.0/install.sh b/plugins/mariadb/versions/11.0/install.sh index c352af3d9..2564badba 100755 --- a/plugins/mariadb/versions/11.0/install.sh +++ b/plugins/mariadb/versions/11.0/install.sh @@ -14,7 +14,7 @@ sysName=`uname` install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb -MY_VER=11.0.3 +MY_VER=11.0.4 Install_app() { diff --git a/plugins/mariadb/versions/11.1/install.sh b/plugins/mariadb/versions/11.1/install.sh index 0a757fbe3..5c5bdc7d4 100755 --- a/plugins/mariadb/versions/11.1/install.sh +++ b/plugins/mariadb/versions/11.1/install.sh @@ -14,7 +14,7 @@ sysName=`uname` install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb -MY_VER=11.1.2 +MY_VER=11.1.3 Install_app() { diff --git a/plugins/mariadb/versions/11.2/install.sh b/plugins/mariadb/versions/11.2/install.sh index 0284e2e3f..f9c505ffa 100755 --- a/plugins/mariadb/versions/11.2/install.sh +++ b/plugins/mariadb/versions/11.2/install.sh @@ -14,7 +14,7 @@ sysName=`uname` install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb -MY_VER=11.2.1 +MY_VER=11.2.2 Install_app() { diff --git a/plugins/mariadb/versions/11.3/install.sh b/plugins/mariadb/versions/11.3/install.sh index bae361cd9..f8fa5cfba 100755 --- a/plugins/mariadb/versions/11.3/install.sh +++ b/plugins/mariadb/versions/11.3/install.sh @@ -14,7 +14,7 @@ sysName=`uname` install_tmp=${rootPath}/tmp/mw_install.pl mariadbDir=${serverPath}/source/mariadb -MY_VER=11.3.0 +MY_VER=11.3.1 Install_app() { diff --git a/plugins/mysql-apt/conf/my8.2.cnf b/plugins/mysql-apt/conf/my8.2.cnf new file mode 100644 index 000000000..ed3a116fb --- /dev/null +++ b/plugins/mysql-apt/conf/my8.2.cnf @@ -0,0 +1,108 @@ +[client] +user = root +#password = your_password +port = 33206 +socket = {$SERVER_APP_PATH}/mysql.sock +default-character-set = UTF8MB4 + +[mysqld] +!include {$SERVER_APP_PATH}/etc/mode/classic.cnf + +authentication_policy=mysql_native_password +pid-file = {$SERVER_APP_PATH}/data/mysql.pid +user = mysql +port = 33206 +mysqlx_port = 33260 +socket = {$SERVER_APP_PATH}/mysql.sock +datadir = {$SERVER_APP_PATH}/data +log-error = {$SERVER_APP_PATH}/data/error.log +server-id = {$SERVER_ID} +sql-mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES + +default_storage_engine = InnoDB + +key_buffer_size = 8M +table_open_cache = 32 +sort_buffer_size = 256K +net_buffer_length = 4K +read_buffer_size = 128K +read_rnd_buffer_size = 256K +myisam_sort_buffer_size = 4M +thread_cache_size = 4 +lower_case_table_names=1 +tmp_table_size = 8M +character-set-server = UTF8MB4 + +max_connections = 500 +max_connect_errors = 100 +open_files_limit = 2560 +max_allowed_packet = 128M + +#skip-external-locking +#skip-grant-tables +#loose-skip-innodb +#skip-networking +#skip-name-resolve + +log-bin=mysql-bin +binlog_format=mixed +slow_query_log=1 +slow-query-log-file={$SERVER_APP_PATH}/data/mysql-slow.log +long_query_time=3 +#log_queries_not_using_indexes=on + +relay-log=mdserver +relay-log-index=mdserver + +#master +#sync_binlog=1 + +#binlog-do-db +binlog-ignore-db = test +binlog-ignore-db = mysql +binlog-ignore-db = information_schema +binlog-ignore-db = performance_schema + +#slave +log_replica_updates +#replicate-do-db +slave_skip_errors=1062,1396 +replicate-ignore-db = information_schema +replicate-ignore-db = performance_schema +replicate-ignore-db = mysql +replicate-ignore-db = test + +master_info_repository = table +relay_log_info_repository = table + +default_storage_engine = InnoDB +innodb_data_home_dir = {$SERVER_APP_PATH}/data +innodb_data_file_path = ibdata1:10M:autoextend +innodb_log_group_home_dir = {$SERVER_APP_PATH}/data +innodb_buffer_pool_size = 16M +innodb_redo_log_capacity=10485760 +innodb_log_buffer_size = 8M +innodb_flush_log_at_trx_commit = 1 +innodb_lock_wait_timeout = 120 +innodb_max_dirty_pages_pct = 90 +innodb_read_io_threads = 1 +innodb_write_io_threads = 1 +innodb_file_per_table=1 +binlog_expire_logs_seconds=2592000 + +secure-file-priv={$SERVER_APP_PATH}/tmp + +[mysqldump] +quick + +[mysql] +no-auto-rehash + +[myisamchk] +key_buffer_size = 20M +sort_buffer_size = 20M +read_buffer = 2M +write_buffer = 2M + +[mysqlhotcopy] +interactive-timeout \ No newline at end of file diff --git a/plugins/mysql-apt/info.json b/plugins/mysql-apt/info.json index de3f7d77e..be352333e 100755 --- a/plugins/mysql-apt/info.json +++ b/plugins/mysql-apt/info.json @@ -6,7 +6,7 @@ "type":"运行环境", "ps":"一种关系数据库管理系统[debian,ubuntu](极速安装)", "todo_versions":["5.7","8.0"], - "versions":["5.7","8.0"], + "versions":["5.7","8.0","8.2"], "shell":"install.sh", "install_pre_inspection":true, "checks":"server/mysql-apt", diff --git a/plugins/mysql-apt/versions/8.2/install.sh b/plugins/mysql-apt/versions/8.2/install.sh new file mode 100755 index 000000000..3d286b2b9 --- /dev/null +++ b/plugins/mysql-apt/versions/8.2/install.sh @@ -0,0 +1,140 @@ +# -*- coding: utf-8 -*- +#!/bin/bash + +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH +export DEBIAN_FRONTEND=noninteractive + +# https://downloads.mysql.com/archives/community/ + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") +sysName=`uname` + +install_tmp=${rootPath}/tmp/mw_install.pl +myDir=${serverPath}/source/mysql-apt + +bash ${rootPath}/scripts/getos.sh +OSNAME=`cat ${rootPath}/data/osname.pl` +VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` + +# cd /www/server/mdserver-web/plugins/mysql-apt && bash install.sh install 8.0 + +# 暂时debian12没有标准版,先用11使用 +if [ "$OSNAME" == 'debian' ] && [ "$VERSION_ID" == '12' ] ;then + echo "暂时不支持该${OSNAME}${VERSION_ID}" > $install_tmp + exit 1 +fi + + +ARCH="amd64" +TMP_ARCH=`arch` +if [ "$TMP_ARCH" == "x86_64" ];then + ARCH="amd64" +elif [ "$TMP_ARCH" == "aarch64" ];then + ARCH="arm64" +else + ARCH="amd64" +fi + +if [ "$ARCH" != "amd64" ];then + echo "暂时不支持该${ARCH}" > $install_tmp + exit 1 +fi + + +MYSQL_VER=8.2.0 +SUFFIX_NAME=${MYSQL_VER}-1${OSNAME}${VERSION_ID}_${ARCH} + + +# /lib/systemd/system/mysql.service +# /etc/mysql/my.cnf + +APT_INSTALL() +{ +######## +mkdir -p $myDir +mkdir -p $serverPath/mysql-apt/bin + +wget --no-check-certificate -O ${myDir}/mysql-server_${SUFFIX_NAME}.deb-bundle.tar https://cdn.mysql.com/archives/mysql-8.2/mysql-server_${SUFFIX_NAME}.deb-bundle.tar +chmod +x ${myDir}/mysql-server_${SUFFIX_NAME}.deb-bundle.tar +cd ${myDir} && tar vxf ${myDir}/mysql-server_${SUFFIX_NAME}.deb-bundle.tar + +apt update -y +apt install -y libnuma1 libaio1 libmecab2 + +# 安装 +dpkg -X mysql-common_${SUFFIX_NAME}.deb $serverPath/mysql-apt/bin + + + +dpkg -X mysql-community-client-plugins_${SUFFIX_NAME}.deb $serverPath/mysql-apt/bin +dpkg -X mysql-community-client-core_${SUFFIX_NAME}.deb $serverPath/mysql-apt/bin +dpkg -X mysql-community-client_${SUFFIX_NAME}.deb $serverPath/mysql-apt/bin +dpkg -X mysql-client_${SUFFIX_NAME}.deb $serverPath/mysql-apt/bin + +dpkg -X mysql-community-server-core_${SUFFIX_NAME}.deb $serverPath/mysql-apt/bin + +dpkg -X mysql-community-server_${SUFFIX_NAME}.deb $serverPath/mysql-apt/bin +dpkg -X mysql-server_${SUFFIX_NAME}.deb $serverPath/mysql-apt/bin + +# 测试时可关闭 +rm -rf $myDir +####### +} + +APT_UNINSTALL() +{ +### +rm -rf $myDir +# apt remove -y mysql-server +### +} + + +Install_mysql() +{ + echo '正在安装脚本文件...' > $install_tmp + if id mysql &> /dev/null ;then + echo "mysql uid is `id -u mysql`" + echo "mysql shell is `grep "^mysql:" /etc/passwd |cut -d':' -f7 `" + else + groupadd mysql + useradd -g mysql mysql + fi + + isApt=`which apt` + if [ "$isApt" != "" ];then + APT_INSTALL + fi + + if [ "$?" == "0" ];then + mkdir -p $serverPath/mysql-apt + echo '8.0' > $serverPath/mysql-apt/version.pl + echo '安装完成' > $install_tmp + else + echo '8.0' > $serverPath/mysql-apt/version.pl + echo "暂时不支持该系统" > $install_tmp + fi +} + +Uninstall_mysql() +{ + + isApt=`which apt` + if [ "$isApt" != "" ];then + APT_UNINSTALL + fi + + rm -rf $serverPath/mysql-apt + echo '卸载完成' > $install_tmp +} + +action=$1 +if [ "${1}" == 'install' ];then + Install_mysql +else + Uninstall_mysql +fi diff --git a/plugins/mysql/index.py b/plugins/mysql/index.py index e11d884fb..4db972978 100755 --- a/plugins/mysql/index.py +++ b/plugins/mysql/index.py @@ -192,7 +192,11 @@ def pMysqlDb(): def makeInitRsaKey(version=''): - datadir = getServerDir() + "/data" + try: + datadir = getDataDir() + except Exception as e: + datadir = getServerDir() + "/data" + mysql_pem = datadir + "/mysql.pem" if not os.path.exists(mysql_pem): rdata = mw.execShell( @@ -405,22 +409,6 @@ def cleanBinLog(): db.execute("PURGE MASTER LOGS BEFORE '" + cleanTime + "';") return mw.returnJson(True, '清理BINLOG成功!') - -def setSkipGrantTables(v): - ''' - 设置是否密码验证 - ''' - conf = getConf() - con = mw.readFile(conf) - if v: - if con.find('#skip-grant-tables') != -1: - con = con.replace('#skip-grant-tables', 'skip-grant-tables') - else: - con = con.replace('skip-grant-tables', '#skip-grant-tables') - mw.writeFile(conf, con) - return True - - def getErrorLog(): args = getArgs() filename = getErrorLogsFile() @@ -536,7 +524,6 @@ def initMysqlPwd(): pSqliteDb('config').where('id=?', (1,)).save('mysql_root', (pwd,)) return True - def initMysql8Pwd(): time.sleep(8) @@ -1067,7 +1054,7 @@ def importDbExternal(): myPass(True, pwd) mysql_cmd = getServerDir() + '/bin/mysql --defaults-file=' + my_cnf + \ - ' -uroot -p\"' + pwd + '\" -f ' + name + ' < ' + import_sql + ' -uroot -p"' + pwd + '" -f ' + name + ' < ' + import_sql # print(mysql_cmd) rdata = mw.execShell(mysql_cmd) @@ -1105,8 +1092,8 @@ def importDbBackup(): pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root') sock = getSocketFile() - mysql_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \ - ' ' + name + ' < ' + file_path_sql + mysql_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p"' + pwd + \ + '" ' + name + ' < ' + file_path_sql # print(mysql_cmd) # os.system(mysql_cmd) @@ -1274,7 +1261,7 @@ def syncGetDatabases(): host = user["Host"] break - ps = mw.getMsg('INPUT_PS') + ps = vdb_name if vdb_name == 'test': ps = mw.getMsg('DATABASE_TEST') addTime = time.strftime('%Y-%m-%d %X', time.localtime()) @@ -1346,12 +1333,20 @@ def setRootPwd(version=''): if not data[0]: return data[1] + #强制修改 + force = 0 + if 'force' in args and args['force'] == '1': + force = 1 + password = args['password'] try: pdb = pMysqlDb() result = pdb.query("show databases") isError = isSqlError(result) if isError != None: + if force == 1: + pSqliteDb('config').where('id=?', (1,)).save('mysql_root', (password,)) + return mw.returnJson(True, '【强制修改】数据库root密码修改成功(不意为成功连接数据)!') return isError if version.find('5.7') > -1 or version.find('8.0') > -1: @@ -1366,7 +1361,12 @@ def setRootPwd(version=''): "update mysql.user set Password=password('" + password + "') where User='root'") pdb.execute("flush privileges") pSqliteDb('config').where('id=?', (1,)).save('mysql_root', (password,)) - return mw.returnJson(True, '数据库root密码修改成功!') + + msg = '' + if force == 1: + msg = ',无须强制!' + + return mw.returnJson(True, '数据库root密码修改成功!'+msg) except Exception as ex: return mw.returnJson(False, '修改错误:' + str(ex)) @@ -1576,15 +1576,79 @@ def setDbAccess(): return mw.returnJson(True, '设置成功!') +def openSkipGrantTables(): + mycnf = getConf() + content = mw.readFile(mycnf) + content = content.replace('#skip-grant-tables','skip-grant-tables') + mw.writeFile(mycnf, content) + return True + +def closeSkipGrantTables(): + mycnf = getConf() + content = mw.readFile(mycnf) + content = content.replace('skip-grant-tables','#skip-grant-tables') + mw.writeFile(mycnf, content) + return True + + +def resetDbRootPwd(version): + serverdir = getServerDir() + myconf = serverdir + "/etc/my.cnf" + pwd = mw.getRandomString(16) + + pSqliteDb('config').where('id=?', (1,)).save('mysql_root', (pwd,)) + + if float(version) < 5.7: + cmd_pass = serverdir + '/bin/mysql --defaults-file=' + myconf + ' -uroot -e' + cmd_pass = cmd_pass + '"UPDATE mysql.user SET password=PASSWORD(\'' + pwd + "') WHERE user='root';" + cmd_pass = cmd_pass + 'flush privileges;"' + data = mw.execShell(cmd_pass) + # print(data) + else: + auth_policy = getAuthPolicy() + + reset_pwd = 'flush privileges;' + reset_pwd = reset_pwd + \ + "UPDATE mysql.user SET authentication_string='' WHERE user='root';" + reset_pwd = reset_pwd + "flush privileges;" + reset_pwd = reset_pwd + \ + "alter user 'root'@'localhost' IDENTIFIED by '" + pwd + "';" + reset_pwd = reset_pwd + \ + "alter user 'root'@'localhost' IDENTIFIED WITH "+auth_policy+" by '" + pwd + "';" + reset_pwd = reset_pwd + "flush privileges;" + + tmp_file = "/tmp/mysql_init_tmp.log" + mw.writeFile(tmp_file, reset_pwd) + cmd_pass = serverdir + '/bin/mysql --defaults-file=' + myconf + ' -uroot -proot < ' + tmp_file + + data = mw.execShell(cmd_pass) + # print(data) + os.remove(tmp_file) + return True + def fixDbAccess(version): + + pdb = pMysqlDb() + mdb_ddir = getDataDir() + if not os.path.exists(mdb_ddir): + return mw.returnJson(False, '数据目录不存在,尝试重启重建!') + try: - pdb = pMysqlDb() data = pdb.query('show databases') isError = isSqlError(data) if isError != None: + + # 重置密码 appCMD(version, 'stop') - mw.execShell("rm -rf " + getServerDir() + "/data") + openSkipGrantTables() appCMD(version, 'start') + + resetDbRootPwd(version) + + appCMD(version, 'stop') + closeSkipGrantTables() + appCMD(version, 'start') + return mw.returnJson(True, '修复成功!') return mw.returnJson(True, '正常无需修复!') except Exception as e: @@ -2882,6 +2946,8 @@ def doFullSyncUser(version=''): ip = data['ip'] bak_file = '/tmp/tmp.sql' + if os.path.exists(bak_file): + os.system("rm -rf " + bak_file) writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0}) dmp_option = '' @@ -2893,18 +2959,18 @@ def doFullSyncUser(version=''): 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) + port + " -u" + user + " -p'" + apass + \ + "' --ssl-mode=DISABLED " + sync_db + " > " + bak_file + print(dump_sql_data) mw.execShell(dump_sql_data) writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40}) if os.path.exists(bak_file): pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root') sock = getSocketFile() - my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \ - ' ' + sync_db_import + ' < ' + bak_file - # print(my_import_cmd) + 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': @@ -3150,8 +3216,6 @@ if __name__ == "__main__": print(binLog()) elif func == 'binlog_list': print(binLogList()) - elif func == 'binlog_look': - print(binLogListLook()) elif func == 'clean_bin_log': print(cleanBinLog()) elif func == 'error_log': diff --git a/plugins/mysql/js/mysql.js b/plugins/mysql/js/mysql.js index c39747c03..bc33dba8f 100755 --- a/plugins/mysql/js/mysql.js +++ b/plugins/mysql/js/mysql.js @@ -161,7 +161,7 @@ function runInfo(){ 活动/峰值连接数' + rdata.Threads_running + '/' + rdata.Max_used_connections + '若值过大,增加max_connections\ 线程缓存命中率' + ((1 - rdata.Threads_created / rdata.Connections) * 100).toFixed(2) + '%若过低,增加thread_cache_size\ 索引命中率' + ((1 - rdata.Key_reads / rdata.Key_read_requests) * 100).toFixed(2) + '%若过低,增加key_buffer_size\ - Innodb索引命中率' + ((1 - rdata.Innodb_buffer_pool_reads / rdata.Innodb_buffer_pool_read_requests) * 100).toFixed(2) + '%若过低,增加innodb_buffer_pool_size\ + Innodb索引命中率' + (rdata.Innodb_buffer_pool_read_requests / (rdata.Innodb_buffer_pool_read_requests+rdata.Innodb_buffer_pool_reads)).toFixed(2) + '%若过低,增加innodb_buffer_pool_size\ 查询缓存命中率' + cache_size + '' + lan.soft.mysql_status_ps5 + '\ 创建临时表到磁盘' + ((rdata.Created_tmp_disk_tables / rdata.Created_tmp_tables) * 100).toFixed(2) + '%若过大,尝试增加tmp_table_size\ 已打开的表' + rdata.Open_tables + '若过大,增加table_cache_size\ @@ -507,7 +507,7 @@ function setRootPwd(type, pwd){ title: '修改数据库密码', closeBtn: 1, shift: 5, - btn:["提交", "复制ROOT密码", "关闭"], + btn:["提交", "关闭", "复制ROOT密码", "强制修改"], shadeClose: true, content: "
\
\ @@ -523,13 +523,30 @@ function setRootPwd(type, pwd){ var rdata = $.parseJSON(data.data); showMsg(rdata.msg,function(){ layer.close(layerIndex); + dbList(); },{icon: rdata.status ? 1 : 2}); }); }, - btn2:function(){ + btn3:function(){ var password = $("#MyPassword").val(); copyText(password); return false; + }, + btn4:function(layerIndex){ + layer.confirm('强制修改,是为了在重建时使用,确定强制?', { + btn: ['确定', '取消'] + }, function(index, layero){ + layer.close(index); + var password = $("#MyPassword").val(); + myPost('set_root_pwd', {password:password,force:'1'}, function(data){ + var rdata = $.parseJSON(data.data); + showMsg(rdata.msg,function(){ + layer.close(layerIndex); + dbList(); + },{icon: rdata.status ? 1 : 2}); + }); + }); + return false; } }); } @@ -684,7 +701,7 @@ function setDbPass(id, username, password){
\
\ \ - ", + ", yes:function(index){ // var data = $("#mod_pwd").serialize(); var data = {}; @@ -1291,6 +1308,7 @@ function myBinRollingLogs(_name, func, _args, line){ } }); } + function myBinLogsRender(page){ var _data = {}; if (typeof(page) =='undefined'){ @@ -2577,10 +2595,20 @@ function masterOrSlaveConf(version=''){ \ ", success:function(){ - // copyText(v['Error']); - // $('.class-copy-db-err').click(function(){ - // copyText(v['Error']); - // }); + if (info['Last_IO_Error'] != ''){ + copyText(info['Last_IO_Error']); + return; + } + + if (info['Last_SQL_Error'] != ''){ + copyText(info['Last_SQL_Error']); + return; + } + + if (info['Slave_SQL_Running_State'] != ''){ + copyText(info['Slave_SQL_Running_State']); + return; + } }, yes:function(){ if (info['Last_IO_Error'] != ''){ @@ -2675,7 +2703,7 @@ function masterOrSlaveConf(version=''){

\ Master[主]配置\ \ - \ + \

\
\ \ diff --git a/plugins/php-apt/info.json b/plugins/php-apt/info.json index dbd8ba244..d8fc8540d 100755 --- a/plugins/php-apt/info.json +++ b/plugins/php-apt/info.json @@ -5,7 +5,7 @@ "name": "php-apt", "title": "PHP[APT]", "coexist": true, - "versions": ["56","70","71","72","73","74","80","81","82"], + "versions": ["56","70","71","72","73","74","80","81","82","83"], "install_pre_inspection":true, "tip": "soft", "checks": "server/php-apt/VERSION", diff --git a/plugins/php-apt/versions/83/install.sh b/plugins/php-apt/versions/83/install.sh new file mode 100755 index 000000000..adce1b9a2 --- /dev/null +++ b/plugins/php-apt/versions/83/install.sh @@ -0,0 +1,49 @@ +#!/bin/bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH + +curPath=`pwd` +rootPath=$(dirname "$curPath") +rootPath=$(dirname "$rootPath") +serverPath=$(dirname "$rootPath") +sourcePath=${serverPath}/source +sysName=`uname` +install_tmp=${rootPath}/tmp/mw_install.pl + + +#获取信息和版本 +# bash /www/server/mdsever-web/scripts/getos.sh +bash ${rootPath}/scripts/getos.sh +OSNAME=`cat ${rootPath}/data/osname.pl` +VERSION_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` + +version=8.3 +PHP_VER=83 + + +Install_php() +{ +#------------------------ install start ------------------------------------# +apt -y install php${version} php${version}-fpm php${version}-dev +if [ "$?" == "0" ];then + mkdir -p $serverPath/php-apt/${PHP_VER} +fi + +#------------------------ install end ------------------------------------# +} + +Uninstall_php() +{ +#------------------------ uninstall start ------------------------------------# +apt -y remove php${version} php${version}-* +rm -rf $serverPath/php-apt/${PHP_VER} +echo "卸载php-${version}..." > $install_tmp +#------------------------ uninstall start ------------------------------------# +} + +action=${1} +if [ "${1}" == 'install' ];then + Install_php +else + Uninstall_php +fi diff --git a/plugins/php-apt/versions/phplib.conf b/plugins/php-apt/versions/phplib.conf index 986584b0f..fcf941d86 100755 --- a/plugins/php-apt/versions/phplib.conf +++ b/plugins/php-apt/versions/phplib.conf @@ -10,7 +10,8 @@ "74", "80", "81", - "82" + "82", + "83" ], "type": "数据库", "msg": "数据库访问抽象模块!", @@ -28,7 +29,8 @@ "74", "80", "81", - "82" + "82", + "83" ], "type": "数据库", "msg": "用于使用MySQL数据库的模块!", @@ -46,7 +48,8 @@ "74", "80", "81", - "82" + "82", + "83" ], "type": "数据库", "msg": "用于使用MySQL数据库的模块!", @@ -64,7 +67,8 @@ "74", "80", "81", - "82" + "82", + "83" ], "type": "数据库", "msg": "用于使用sqlite3数据库的模块!", @@ -90,7 +94,8 @@ "74", "80", "81", - "82" + "82", + "83" ], "type": "数据库", "msg": "用于使用ODBC数据库的模块!", @@ -142,7 +147,8 @@ "74", "80", "81", - "82" + "82", + "83" ], "type": "缓存器", "msg": "用于加速PHP脚本!", @@ -175,7 +181,8 @@ "74", "80", "81", - "82" + "82", + "83" ], "type": "通用扩展", "msg": "高精度计算!", @@ -239,7 +246,8 @@ "74", "80", "81", - "82" + "82", + "83" ], "type": "通用扩展", "msg": "通用CURL库!", @@ -252,7 +260,8 @@ "74", "80", "81", - "82" + "82", + "83" ], "type": "通用扩展", "msg": "通用GD库!", @@ -270,7 +279,8 @@ "74", "80", "81", - "82" + "82", + "83" ], "type": "通用扩展", "msg": "提供国际化支持", @@ -300,7 +310,8 @@ "74", "80", "81", - "82" + "82", + "83" ], "type": "缓存器", "msg": "强大的内容缓存器,支持集群", @@ -317,7 +328,9 @@ "73", "74", "80", - "81" + "81", + "82", + "83" ], "type": "缓存器", "msg": "更强大的内容缓存器,支持集群", @@ -334,7 +347,9 @@ "73", "74", "80", - "81" + "81", + "82", + "83" ], "type": "缓存器", "msg": "脚本缓存器", @@ -352,7 +367,8 @@ "74", "80", "81", - "82" + "82", + "83" ], "type": "通用扩展", "msg": "比GD更强大的图形库", @@ -369,7 +385,9 @@ "73", "74", "80", - "81" + "81", + "82", + "83" ], "type": "调试器", "msg": "不多说,不了解的不要安装", @@ -386,7 +404,9 @@ "73", "74", "80", - "81" + "81", + "82", + "83" ], "type": "性能分析", "msg": "不多说,不了解的不要安装!", @@ -403,7 +423,9 @@ "73", "74", "80", - "81" + "81", + "82", + "83" ], "type": "通用扩展", "msg": "异步、并行、高性能网络通信引擎", @@ -463,7 +485,9 @@ "73", "74", "80", - "81" + "81", + "82", + "83" ], "type": "框架", "msg": "Yar是一个RPC框架", @@ -541,7 +565,9 @@ "73", "74", "80", - "81" + "81", + "82", + "83" ], "type": "日志", "msg": "SeasLog高性能日志记录", @@ -559,7 +585,8 @@ "74", "80", "81", - "82" + "82", + "83" ], "type": "通用扩展", "msg": "用于需要多字节字符串处理的模块", @@ -577,7 +604,8 @@ "74", "80", "81", - "82" + "82", + "83" ], "type": "压缩", "msg": "压缩组件", @@ -594,7 +622,8 @@ "74", "80", "81", - "82" + "82", + "83" ], "type": "压缩", "msg": "压缩组件", diff --git a/route/static/app/index.js b/route/static/app/index.js index 81c06fdf2..8ef4a3b8e 100755 --- a/route/static/app/index.js +++ b/route/static/app/index.js @@ -6,7 +6,7 @@ $(function() { $(this).find(".mask").css({ "color": "#d2edd8" }); $(this).find(".mem-re-con").css({ "display": "block" }); $(this).find(".mem-re-con").animate({ "top": "0", opacity: 1 }); - $("#memory").text(lan.index.memre); + $("#memory").text('内存释放'); } }, function() { $(this).removeClass("shine_green"); @@ -29,6 +29,7 @@ $(function() { } }); }); + //获取负载 function getLoad(data) { $("#LoadList .mask").html("获取中.."); @@ -83,19 +84,18 @@ function showCpuTips(rdata){ $('#cpuChart .mask').unbind(); $('#cpuChart .mask').hover(function() { var cpuText = ''; - if (rdata.cpu[2].length == 1){ var cpuUse = parseFloat(rdata.cpu[2][0] == 0 ? 0 : rdata.cpu[2][0]).toFixed(1); - cpuText += 'CPU-1:' + cpuUse + '%' - } else{ + cpuText += 'CPU-1:' + cpuUse + '%'; + } else { for (var i = 1; i < rdata.cpu[2].length + 1; i++) { - var cpuUse = parseFloat(rdata.cpu[2][i - 1] == 0 ? 0 : rdata.cpu[2][i - 1]).toFixed(1); - if (i % 2 != 0) { - cpuText += 'CPU-' + i + ':' + cpuUse + '% | ' - } else { - cpuText += 'CPU-' + i + ':' + cpuUse + '%' - cpuText += '\n' - } + var cpuUse = parseFloat(rdata.cpu[2][i - 1] == 0 ? 0 : rdata.cpu[2][i - 1]).toFixed(1); + if (i % 2 != 0) { + cpuText += 'CPU-' + i + ':' + cpuUse + '% | '; + } else { + cpuText += 'CPU-' + i + ':' + cpuUse + '%'; + cpuText += '\n'; + } } } layer.tips(rdata.cpu[3] + "
" + rdata.cpu[5] + "个物理CPU," + (rdata.cpu[4]) + "个物理核心," + rdata.cpu[1] + "个逻辑核心
" + cpuText, this, { time: 0, tips: [1, '#999'] }); @@ -109,6 +109,7 @@ function rocket(sum, m) { var n = sum - m; $(".mem-release").find(".mask span").text(n); } + //释放内存 function reMemory() { setTimeout(function() { @@ -199,27 +200,34 @@ function clearSystem() { } function setMemImg(info){ - setCookie("memRealUsed", parseInt(info.memRealUsed)); - $("#memory").html(parseInt(info.memRealUsed) + '/' + parseInt(info.memTotal) + ' (MB)'); - setCookie("mem-before", $("#memory").text()); - if (!getCookie('memSize')) setCookie('memSize', parseInt(info.memTotal)); + + var memRealUsed = toSize(info.memRealUsed); + var memTotal = toSize(info.memTotal); + + var memRealUsedVal = memRealUsed.split(' ')[0]; + var memTotalVal = memTotal.split(' ')[0]; + var unit = memTotal.split(' ')[1]; + + var mem_txt = memRealUsedVal + '/' + memTotalVal + ' ('+ unit +')'; + setCookie("mem-before", mem_txt); + $("#memory").html(mem_txt); + var memPre = Math.floor(info.memRealUsed / (info.memTotal / 100)); $("#left").html(memPre); setcolor(memPre, "#left", 75, 90, 95); - $("#state").html(info.cpuRealUsed); - setcolor(memPre, "#state", 30, 70, 90); - setImg(); + + var memFree = info.memTotal - info.memRealUsed; + if (memFree/(1024*1024) < 64) { + $("#messageError").show(); + $("#messageError").append('

当前可用物理内存小于64M,这可能导致MySQL自动停止,站点502等错误,请尝试释放内存!

') + } } function getInfo() { $.get("/system/system_total", function(info) { - setCookie("memRealUsed", parseInt(info.memRealUsed)); - $("#memory").html(parseInt(info.memRealUsed) + '/' + parseInt(info.memTotal) + ' (MB)'); - setCookie("mem-before", $("#memory").text()); - if (!getCookie('memSize')) setCookie('memSize', parseInt(info.memTotal)); - var memPre = Math.floor(info.memRealUsed / (info.memTotal / 100)); - $("#left").html(memPre); - setcolor(memPre, "#left", 75, 90, 95); + + setMemImg(info); + $("#info").html(info.system); $("#running").html(info.time); var _system = info.system; @@ -240,13 +248,8 @@ function getInfo() { } $("#core").html(info.cpuNum + ' 核心'); $("#state").html(info.cpuRealUsed); - setcolor(memPre, "#state", 30, 70, 90); - var memFree = info.memTotal - info.memRealUsed; - - if (memFree < 64) { - $("#messageError").show(); - $("#messageError").append('

' + lan.index.mem_warning + '

') - } + setcolor(info.cpuRealUsed, "#state", 30, 70, 90); + // if (info.isuser > 0) { // $("#messageError").show(); @@ -280,9 +283,10 @@ function setcolor(pre, s, s1, s2, s3) { function getNet() { var up, down; $.get("/system/network", function(net) { + $("#InterfaceSpeed").html(lan.index.interfacespeed + ": 1.0Gbps"); - $("#upSpeed").html(net.up + ' KB'); - $("#downSpeed").html(net.down + ' KB'); + $("#upSpeed").html(toSize(net.up)); + $("#downSpeed").html(toSize(net.down)); $("#downAll").html(toSize(net.downTotal)); $("#downAll").attr('title', lan.index.package + ':' + net.downPackets) $("#upAll").html(toSize(net.upTotal)); @@ -292,18 +296,23 @@ function getNet() { setcolor(net.cpu[0], "#state", 30, 70, 90); setCookie("upNet", net.up); setCookie("downNet", net.down); + + //负载 getLoad(net.load); - - // setMemImg(net.mem); - setImg(); + //内存 + setMemImg(net.mem); + + //绑定hover事件 + setImg(); showCpuTips(net); + },'json'); } //网络Io function netImg() { - var myChartNetwork = echarts.init(document.getElementById('netImg')); + var echartsNetImg = echarts.init(document.getElementById('netImg')); var xData = []; var yData = []; var zData = []; @@ -333,10 +342,27 @@ function netImg() { return ts(h) + ':' + ts(mm) + ':' + ts(s); } + var default_unit = 'KB/s'; function addData(shift) { xData.push(getTime()); - yData.push(getCookie("upNet")); - zData.push(getCookie("downNet")); + + if (getCookie("upNet") > getCookie("downNet") ){ + tmp = getCookie("upNet"); + } else { + tmp = getCookie("downNet"); + } + var tmpSize = toSize(tmp); + default_unit = tmpSize.split(' ')[1] + '/s'; + + + var upNetTmp = toSize(getCookie("upNet")); + var downNetTmp = toSize(getCookie("downNet")); + + var upNetTmpSize = upNetTmp.split(' ')[0]; + var downNetTmp = downNetTmp.split(' ')[0] + + yData.push(upNetTmpSize); + zData.push(downNetTmp); if (shift) { xData.shift(); yData.shift(); @@ -379,20 +405,16 @@ function netImg() { } }, yAxis: { - name: lan.index.unit + 'KB/s', + name: '单位 '+ default_unit, splitLine: { - lineStyle: { - color: "#eee" - } + lineStyle: { color: "#eee" } }, axisLine: { - lineStyle: { - color: "#666" - } + lineStyle: { color: "#666" } } }, series: [{ - name: lan.index.net_up, + name: '上行', type: 'line', data: yData, smooth: true, @@ -420,8 +442,9 @@ function netImg() { width: 1 } } - }, { - name: lan.index.net_down, + }, + { + name: '下行', type: 'line', data: zData, smooth: true, @@ -432,21 +455,21 @@ function netImg() { normal: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{ offset: 0, - color: 'rgba(30, 144, 255,0.5)' + color: 'rgba(30, 144, 255,0.5)', }, { offset: 1, - color: 'rgba(30, 144, 255,0.8)' + color: 'rgba(30, 144, 255,0.8)', }], false) } }, itemStyle: { normal: { - color: '#52a9ff' + color: '#52a9ff', } }, lineStyle: { normal: { - width: 1 + width: 1, } } }] @@ -454,7 +477,12 @@ function netImg() { setInterval(function() { getNet(); addData(true); - myChartNetwork.setOption({ + echartsNetImg.setOption({ + yAxis: { + name: '单位 '+ default_unit, + splitLine: { lineStyle: { color: "#eee" } }, + axisLine: { lineStyle: { color: "#666" } } + }, xAxis: { data: xData }, @@ -467,10 +495,11 @@ function netImg() { }] }); }, 3000); + // 使用刚指定的配置项和数据显示图表。 - myChartNetwork.setOption(option); + echartsNetImg.setOption(option); window.addEventListener("resize", function() { - myChartNetwork.resize(); + echartsNetImg.resize(); }); } @@ -612,7 +641,6 @@ function reBoot() { content: '
重启服务器
重启面板
' }); - $('.rebt-con a').click(function () { var type = $(this).attr('data-id'); switch (type) { @@ -737,6 +765,7 @@ function setSafeHide() { setCookie('safeMsg', '1'); $("#safeMsg").remove(); } + //查看报告 function showDanger(num, port) { var atxt = "因未使用安全隔离登录,所有IP都可以尝试连接,存在较高风险,请立即处理。"; @@ -750,15 +779,15 @@ function showDanger(num, port) { closeBtn: 1, shift: 5, content: '
\ - \ - \ - \ - \ - \ - \ -
风险类型:暴力破解 说明
累计遭遇攻击总数:' + num + ' 详细(数据直接来源本服务器日志)
风险等级:较高风险
风险描述:' + atxt + '
可参考解决方案:

方案一:修改SSH默认端口,修改SSH验证方式为数字证书,清除近期登陆日志。

方案二:购买宝塔企业运维版,一键部署安全隔离服务,高效且方便。

\ -
立即部署隔离防护
\ -
' + \ + \ + \ + \ + \ + \ +
风险类型:暴力破解 说明
累计遭遇攻击总数:' + num + ' 详细(数据直接来源本服务器日志)
风险等级:较高风险
风险描述:' + atxt + '
可参考解决方案:

方案一:修改SSH默认端口,修改SSH验证方式为数字证书,清除近期登陆日志。

方案二:购买宝塔企业运维版,一键部署安全隔离服务,高效且方便。

\ +
立即部署隔离防护
\ + ' }); $(".showDanger td").css("padding", "8px") } diff --git a/route/static/app/public.js b/route/static/app/public.js index 909c02d35..75bb3f489 100755 --- a/route/static/app/public.js +++ b/route/static/app/public.js @@ -12,7 +12,7 @@ function toSize(a) { if(a < e) { return(b == 0 ? a : a.toFixed(2)) + d[b] } - a /= e + a /= e; } } diff --git a/route/static/app/soft.js b/route/static/app/soft.js index 3dd364025..eacbfcf5f 100755 --- a/route/static/app/soft.js +++ b/route/static/app/soft.js @@ -403,7 +403,12 @@ function indexListHtml(callback){ //首页软件列表 function indexSoft() { indexListHtml(function(){ - $("#indexsoft").dragsort({ dragSelector: ".spanmove", dragBetween: true, dragEnd: saveOrder, placeHolderTemplate: "
" }); + $("#indexsoft").dragsort({ + dragSelector: ".spanmove", + dragBetween: true, + dragEnd: saveOrder, + placeHolderTemplate: "
" + }); }); function saveOrder() { diff --git a/route/templates/default/index.html b/route/templates/default/index.html index 26f627483..a2c233e85 100755 --- a/route/templates/default/index.html +++ b/route/templates/default/index.html @@ -135,7 +135,7 @@ setTimeout(function() { setTimeout(function() { getInfo(); -}, 1500); +}, 200); {% endblock %} \ No newline at end of file