Merge pull request #143 from midoks/dev

rsyncd更新
pull/159/head
Mr Chen 3 years ago committed by GitHub
commit 6e6fc3aa54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .gitignore
  2. 2
      class/core/config_api.py
  3. 9
      class/core/db.py
  4. 12
      class/core/mw.py
  5. 16
      class/core/site_api.py
  6. 13
      class/plugin/orm.py
  7. 109
      class/plugin/ormDb.py
  8. 248
      index.html
  9. 6
      plugins/mariadb/index.py
  10. 7
      plugins/mysql-yum/index.py
  11. 1
      plugins/mysql/conf/my.cnf
  12. 1
      plugins/mysql/conf/my5.7.cnf
  13. 2
      plugins/mysql/conf/my8.0.cnf
  14. 8
      plugins/mysql/index.py
  15. 12
      plugins/php/versions/73/install.sh
  16. 18
      plugins/php/versions/74/install.sh
  17. 14
      plugins/php/versions/80/install.sh
  18. 14
      plugins/php/versions/81/install.sh
  19. 15
      plugins/php/versions/82/install.sh
  20. 26
      plugins/rsyncd/conf/config.json
  21. 6
      plugins/rsyncd/conf/lsyncd.conf
  22. 3
      plugins/rsyncd/conf/rsyncd.conf
  23. 79
      plugins/rsyncd/index.html
  24. 679
      plugins/rsyncd/index.py
  25. 2
      plugins/rsyncd/info.json
  26. 10
      plugins/rsyncd/init.d/lsyncd.service.tpl
  27. 108
      plugins/rsyncd/init.d/lsyncd.tpl
  28. 17
      plugins/rsyncd/install.sh
  29. 149
      plugins/rsyncd/js/base64.js
  30. 680
      plugins/rsyncd/js/rsyncd.js
  31. 149
      plugins/rsyncd/tool_task.py
  32. 41
      plugins/webstats/index.py
  33. 8
      plugins/webstats/js/stats.js
  34. 12
      plugins/webstats/lua/webstats_log.lua
  35. 4
      plugins/webstats/tool_migrate.py
  36. 3
      plugins/webstats/tool_task.py
  37. 2
      plugins/xhprof/conf/xhprof.conf
  38. 44
      route/static/app/public.js
  39. 5
      route/templates/default/config.html
  40. 2
      route/templates/default/layout.html
  41. 4
      scripts/lib.sh

2
.gitignore vendored

@ -138,6 +138,7 @@ data/ipv6.pl
data/restart.pl
data/ssl.pl
data/edate.pl
data/osname.pl
plugins/l2tp
plugins/openlitespeed
@ -146,3 +147,4 @@ plugins/mtproxy
debug.out

@ -17,7 +17,7 @@ class config_api:
# 进行中.
# 兼容主流Linux系统
__version = '0.8.6.11'
__version = '0.8.6.12'
def __init__(self):
pass

@ -3,6 +3,7 @@
import sqlite3
import os
import sys
class Sql():
@ -31,6 +32,14 @@ class Sql():
except Exception as ex:
return "error: " + str(ex)
def autoTextFactory(self):
if sys.version_info[0] == 3:
self.__DB_CONN.text_factory = lambda x: str(
x, encoding="utf-8", errors='ignore')
else:
self.__DB_CONN.text_factory = lambda x: unicode(
x, "utf-8", "ignore")
def dbfile(self, name):
self.__DB_FILE = 'data/' + name + '.db'
return self

@ -733,7 +733,7 @@ def makeConf():
file = getRunDir() + '/data/json/config.json'
if not os.path.exists(file):
c = {}
c['title'] = 'Linux面板'
c['title'] = 'mdserver-web | linux面板'
c['home'] = 'http://github/midoks/mdserver-web'
c['recycle_bin'] = True
c['template'] = 'default'
@ -1022,3 +1022,13 @@ def getMyORM():
import orm
o = orm.ORM()
return o
def getMyORMDb():
'''
获取MySQL资源的ORM pip install mysqlclient==2.0.3 | pip install mysql-python
'''
sys.path.append(os.getcwd() + "/class/plugin")
import ormDb
o = ormDb.ORM()
return o

@ -387,9 +387,19 @@ class site_api:
os.system('mkdir -p ' + vpath)
data = []
for d in os.listdir(vpath):
# keyPath = self.sslDir + siteName + '/privkey.pem'
# certPath = self.sslDir + siteName + '/fullchain.pem'
keyPath = vpath + '/' + d + '/privkey.pem'
certPath = vpath + '/' + d + '/fullchain.pem'
if os.path.exists(keyPath) and os.path.exists(certPath):
self.saveCert(keyPath, certPath)
mpath = vpath + '/' + d + '/info.json'
if not os.path.exists(mpath):
continue
tmp = mw.readFile(mpath)
if not tmp:
continue
@ -479,8 +489,8 @@ class site_api:
mw.execShell('\\cp -a /tmp/backup2.conf ' + csrpath)
return mw.returnJson(False, 'ERROR: <br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
mw.restartWeb()
mw.writeLog('网站管理', '证书已保存!')
mw.restartWeb()
return mw.returnJson(True, '证书已保存!')
def setCertToSiteApi(self):
@ -2023,7 +2033,7 @@ location ^~ {from} {
def getPhpVersion(self):
phpVersions = ('00', '52', '53', '54', '55',
'56', '70', '71', '72', '73', '74', '80', '81')
'56', '70', '71', '72', '73', '74', '80', '81', '82')
data = []
for val in phpVersions:
tmp = {}
@ -2280,7 +2290,7 @@ location /{
certInfo = self.getCertName(certPath)
if not certInfo:
return mw.returnData(False, '证书解析失败!')
vpath = self.sslDir + certInfo['subject']
vpath = self.sslDir + certInfo['subject'].strip()
if not os.path.exists(vpath):
os.system('mkdir -p ' + vpath)
mw.writeFile(vpath + '/privkey.pem',

@ -18,6 +18,8 @@ class ORM:
__DB_CNF = '/etc/my.cnf'
__DB_SOCKET = '/www/server/mysql/mysql.sock'
__DB_CHARSET = "utf8"
def __Conn(self):
'''连接MYSQL数据库'''
try:
@ -25,19 +27,19 @@ class ORM:
if os.path.exists(self.__DB_SOCKET):
try:
self.__DB_CONN = connector.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=self.__DB_PORT, charset="utf8", connect_timeout=1, unix_socket=self.__DB_SOCKET)
port=self.__DB_PORT, charset=self.__DB_CHARSET, connect_timeout=1, unix_socket=self.__DB_SOCKET)
except Exception as e:
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = connector.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=self.__DB_PORT, charset="utf8", connect_timeout=1, unix_socket=self.__DB_SOCKET)
port=self.__DB_PORT, charset=self.__DB_CHARSET, connect_timeout=1, unix_socket=self.__DB_SOCKET)
else:
try:
self.__DB_CONN = connector.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=self.__DB_PORT, charset="utf8", connect_timeout=1)
port=self.__DB_PORT, charset=self.__DB_CHARSET, connect_timeout=1)
except Exception as e:
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = connector.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=self.__DB_PORT, charset="utf8", connect_timeout=1)
port=self.__DB_PORT, charset=self.__DB_CHARSET, connect_timeout=1)
self.__DB_CUR = self.__DB_CONN.cursor()
return True
@ -51,6 +53,9 @@ class ORM:
def setSocket(self, sock):
self.__DB_SOCKET = sock
def setCharset(self, charset):
self.__DB_CHARSET = charset
def setPort(self, port):
self.__DB_PORT = port

@ -0,0 +1,109 @@
# coding: utf-8
import re
import os
import sys
class ORM:
__DB_PASS = None
__DB_USER = 'root'
__DB_PORT = 3306
__DB_HOST = 'localhost'
__DB_CONN = None
__DB_CUR = None
__DB_ERR = None
__DB_CNF = '/etc/my.cnf'
__DB_SOCKET = '/www/server/mysql/mysql.sock'
__DB_CHARSET = 'utf8'
def __Conn(self):
'''连接MYSQL数据库'''
try:
try:
import MySQLdb
except Exception as ex:
self.__DB_ERR = ex
return False
# print(self.__DB_HOST)
# print(self.__DB_USER)
# print(self.__DB_PASS)
# print(self.__DB_PORT)
# print(self.__DB_CHARSET)
# print(self.__DB_SOCKET)
if os.path.exists(self.__DB_SOCKET):
try:
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, unix_socket=self.__DB_SOCKET)
except Exception as e:
print(e)
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, unix_socket=self.__DB_SOCKET)
else:
try:
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1)
except Exception as e:
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1)
self.__DB_CUR = self.__DB_CONN.cursor()
return True
except MySQLdb.Error as e:
self.__DB_ERR = e
return False
def setDbConf(self, conf):
self.__DB_CNF = conf
def setSocket(self, sock):
self.__DB_SOCKET = sock
def setCharset(self, charset):
self.__DB_CHARSET = charset
def setPort(self, port):
self.__DB_PORT = port
def setPwd(self, pwd):
self.__DB_PASS = pwd
def getPwd(self):
return self.__DB_PASS
def execute(self, sql):
# 执行SQL语句返回受影响行
if not self.__Conn():
return self.__DB_ERR
try:
result = self.__DB_CUR.execute(sql)
self.__DB_CONN.commit()
self.__Close()
return result
except Exception as ex:
return ex
def query(self, sql):
# 执行SQL语句返回数据集
if not self.__Conn():
return self.__DB_ERR
try:
self.__DB_CUR.execute(sql)
result = self.__DB_CUR.fetchall()
# 将元组转换成列表
data = map(list, result)
self.__Close()
return data
except Exception as ex:
return ex
# 关闭连接
def __Close(self):
self.__DB_CUR.close()
self.__DB_CONN.close()

File diff suppressed because one or more lines are too long

@ -144,10 +144,12 @@ def pSqliteDb(dbname='databases'):
def pMysqlDb():
db = mw.getMyORM()
# mysql.connector
# db = mw.getMyORM()
# MySQLdb |
db = mw.getMyORMDb()
db.setDbConf(getConf())
db.setPort(getDbPort())
db.setSocket(getSocketFile())

@ -132,7 +132,12 @@ def pSqliteDb(dbname='databases'):
def pMysqlDb():
db = mw.getMyORM()
# mysql.connector
# db = mw.getMyORM()
# MySQLdb |
db = mw.getMyORMDb()
# db = mw.getMyORM()
db.__DB_CNF = getConf()
db.setPort(getDbPort())
db.setSocket(getSocketFile())

@ -35,6 +35,7 @@ open_files_limit = 65535
#skip-external-locking
#loose-skip-innodb
#skip-grant-tables
skip-ssl
log-bin=mysql-bin

@ -37,6 +37,7 @@ max_allowed_packet = 128M
#loose-skip-innodb
#skip-networking
#skip-name-resolve
skip-ssl
log-bin=mysql-bin
binlog_format=mixed

@ -38,6 +38,8 @@ max_allowed_packet = 128M
#loose-skip-innodb
#skip-networking
#skip-name-resolve
skip-ssl
log-bin=mysql-bin
binlog_format=mixed

@ -134,9 +134,14 @@ def pSqliteDb(dbname='databases'):
def pMysqlDb():
db = mw.getMyORM()
# mysql.connector
# db = mw.getMyORM()
# MySQLdb |
db = mw.getMyORMDb()
db.setPort(getDbPort())
db.setSocket(getSocketFile())
# db.setCharset("utf8")
db.setPwd(pSqliteDb('config').where('id=?', (1,)).getField('mysql_root'))
return db
@ -1495,6 +1500,7 @@ def getMasterStatus(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
dlist = list(dlist)
# print(dlist, len(dlist))
if len(dlist) > 0 and (dlist[0][10] == 'Yes' or dlist[0][11] == 'Yes'):
data['slave_status'] = True

@ -10,6 +10,10 @@ sourcePath=${serverPath}/source
sysName=`uname`
install_tmp=${rootPath}/tmp/mw_install.pl
function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; }
function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; }
function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; }
function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; }
version=7.3.33
PHP_VER=73
@ -49,6 +53,12 @@ if [ "$IS_64BIT" == "64" ];then
OPTIONS="${OPTIONS} --with-libdir=lib64"
fi
ZIP_OPTION='--enable-zip'
libzip_version=`pkg-config libzip --modversion`
if version_lt "$libzip_version" "0.11.0" ;then
export PKG_CONFIG_PATH=$serverPath/lib/libzip/lib/pkgconfig
ZIP_OPTION="--with-libzip=$serverPath/lib/libzip"
fi
if [ ! -d $serverPath/php/73 ];then
cd $sourcePath/php/php${PHP_VER} && ./configure \
@ -60,7 +70,7 @@ if [ ! -d $serverPath/php/73 ];then
--with-pdo-mysql=mysqlnd \
--with-zlib-dir=$serverPath/lib/zlib \
--enable-ftp \
--enable-zip \
$ZIP_OPTION\
--enable-sockets \
--enable-simplexml \
--enable-mbstring \

@ -11,6 +11,11 @@ sysName=`uname`
install_tmp=${rootPath}/tmp/mw_install.pl
function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; }
function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; }
function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; }
function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; }
version=7.4.26
PHP_VER=74
Install_php()
@ -22,6 +27,7 @@ mkdir -p $serverPath/php
cd $serverPath/mdserver-web/plugins/php/lib && /bin/bash freetype_new.sh
cd $serverPath/mdserver-web/plugins/php/lib && /bin/bash zlib.sh
cd $serverPath/mdserver-web/plugins/php/lib && /bin/bash libzip.sh
if [ ! -d $sourcePath/php/php${PHP_VER} ];then
@ -49,9 +55,7 @@ if [ $sysName == 'Darwin' ]; then
export LDFLAGS="-L/usr/local/opt/libxml2/lib"
else
OPTIONS='--without-iconv'
# OPTIONS="--with-iconv=${serverPath}/lib/libiconv"
OPTIONS="${OPTIONS} --with-curl"
# OPTIONS="${OPTIONS} --with-zip=${serverPath}/lib/libzip"
fi
IS_64BIT=`getconf LONG_BIT`
@ -61,6 +65,14 @@ fi
echo "$sourcePath/php/php${PHP_VER}"
ZIP_OPTION='--with-zip'
libzip_version=`pkg-config libzip --modversion`
if version_lt "$libzip_version" "0.11.0" ;then
export PKG_CONFIG_PATH=$serverPath/lib/libzip/lib/pkgconfig
ZIP_OPTION="--with-zip=$serverPath/lib/libzip"
fi
if [ ! -d $serverPath/php/${PHP_VER} ];then
cd $sourcePath/php/php${PHP_VER} && make clean
./buildconf --force
@ -72,7 +84,7 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then
--with-mysqli=mysqlnd \
--with-pdo-mysql=mysqlnd \
--with-zlib-dir=$serverPath/lib/zlib \
--with-zip \
$ZIP_OPTION \
--enable-ftp \
--enable-mbstring \
--enable-sockets \

@ -10,6 +10,11 @@ sourcePath=${serverPath}/source
sysName=`uname`
install_tmp=${rootPath}/tmp/mw_install.pl
function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; }
function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; }
function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; }
function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; }
version=8.0.21
PHP_VER=80
@ -55,6 +60,13 @@ if [ "$IS_64BIT" == "64" ];then
OPTIONS="${OPTIONS} --with-libdir=lib64"
fi
ZIP_OPTION='--with-zip'
libzip_version=`pkg-config libzip --modversion`
if version_lt "$libzip_version" "0.11.0" ;then
export PKG_CONFIG_PATH=$serverPath/lib/libzip/lib/pkgconfig
ZIP_OPTION="--with-zip=$serverPath/lib/libzip"
fi
echo "$sourcePath/php/php${PHP_VER}"
if [ ! -d $serverPath/php/${PHP_VER} ];then
@ -68,7 +80,7 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then
--with-mysqli=mysqlnd \
--with-pdo-mysql=mysqlnd \
--with-zlib-dir=$serverPath/lib/zlib \
--with-zip \
$ZIP_OPTION \
--enable-ftp \
--enable-mbstring \
--enable-sockets \

@ -10,6 +10,11 @@ sourcePath=${serverPath}/source
sysName=`uname`
install_tmp=${rootPath}/tmp/mw_install.pl
function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; }
function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; }
function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; }
function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; }
version=8.1.8
PHP_VER=81
@ -49,6 +54,13 @@ else
OPTIONS="${OPTIONS} --with-curl"
fi
ZIP_OPTION='--with-zip'
libzip_version=`pkg-config libzip --modversion`
if version_lt "$libzip_version" "0.11.0" ;then
export PKG_CONFIG_PATH=$serverPath/lib/libzip/lib/pkgconfig
ZIP_OPTION="--with-zip=$serverPath/lib/libzip"
fi
echo "$sourcePath/php/php${PHP_VER}"
@ -63,7 +75,7 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then
--with-mysqli=mysqlnd \
--with-pdo-mysql=mysqlnd \
--with-zlib-dir=$serverPath/lib/zlib \
--with-zip \
$ZIP_OPTION \
--enable-ftp \
--enable-mbstring \
--enable-sockets \

@ -10,6 +10,11 @@ sourcePath=${serverPath}/source
sysName=`uname`
install_tmp=${rootPath}/tmp/mw_install.pl
function version_gt() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1"; }
function version_le() { test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" == "$1"; }
function version_lt() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; }
function version_ge() { test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1"; }
version=8.2.0alpha2
PHP_VER=82
@ -50,6 +55,14 @@ if [ "$IS_64BIT" == "64" ];then
OPTIONS="${OPTIONS} --with-libdir=lib64"
fi
ZIP_OPTION='--with-zip'
libzip_version=`pkg-config libzip --modversion`
if version_lt "$libzip_version" "0.11.0" ;then
export PKG_CONFIG_PATH=$serverPath/lib/libzip/lib/pkgconfig
ZIP_OPTION="--with-zip=$serverPath/lib/libzip"
fi
echo "$sourcePath/php/php${PHP_VER}"
if [ ! -d $serverPath/php/${PHP_VER} ];then
@ -63,7 +76,7 @@ if [ ! -d $serverPath/php/${PHP_VER} ];then
--with-mysqli=mysqlnd \
--with-pdo-mysql=mysqlnd \
--with-zlib-dir=$serverPath/lib/zlib \
--with-zip \
$ZIP_OPTION \
--enable-mbstring \
--enable-ftp \
--enable-sockets \

@ -0,0 +1,26 @@
{
"receive":{
"default":{
"uid": "root",
"use chroot": "no",
"dont compress": "*.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2 *.mp4 *.avi *.swf *.rar",
"hosts allow": "",
"max connections": 200,
"gid": "root",
"timeout": 600,
"pid file": "/var/run/rsyncd.pid",
"log file": "/var/log/rsyncd.log",
"port": 873
},
"list":[]
},
"send":{
"default":{
"logfile": "/www/server/rsyncd/lsyncd.log",
"inotifyMode": "CloseWrite",
"maxProcesses": 8,
"statusFile": "/www/server/rsyncd/lsyncd.status"
},
"list":[]
}
}

@ -0,0 +1,6 @@
settings {
logfile = "{$SERVER_PATH}/rsyncd/lsyncd.log",
inotifyMode = "CloseWrite",
maxProcesses = 8,
statusFile = "{$SERVER_PATH}/rsyncd/lsyncd.status"
}

@ -5,5 +5,4 @@ max connections = 100
log file = /var/log/rsyncd.log
pid file = /var/run/rsyncd.pid
list = false
hosts allow = *
secrets file = /etc/rsyncd.passwd
hosts allow = *

@ -1,12 +1,79 @@
<style type="text/css">
.overflow_hide {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
vertical-align: middle;
}
.lsyncd_exclude label {
font-weight: normal;
margin-right: 20px
}
.lsyncd_exclude button {
vertical-align: 0
}
.lsyncd_exclude {
overflow: hidden;
margin: 15px;
}
.lsyncd_exclude span {
margin-right: 5px
}
.lsyncd_exclude fieldset {
border: 1px solid #ccc;
border-radius: 3px;
float: left;
padding-bottom: 0;
/* width: 240px; */
}
.wafConf fieldset:nth-of-type(2) {
margin: 0 10px
}
.lsyncd_exclude legend {
border: 0 none;
font-size: 14px;
margin: 0 6px;
padding: 3px;
width: auto
}
.lsyncd_exclude fieldset input {
margin-left: 4px
}
.lsyncd_exclude fieldset .table {
margin-top: -1px;
margin-bottom: 0
}
.lsyncd_exclude fieldset .table tr td:nth-of-type(2) {
width: 42px
}
.lsyncd_exclude fieldset .table-overflow {
height: 210px;
overflow: auto;
margin-top: 10px;
border-top: #ddd 1px solid
}
</style>
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('rsyncd');">服务</p>
<p onclick="pluginInitD('rsyncd');">自启动</p>
<p onclick="pluginConfig('rsyncd');">配置修改</p>
<p onclick="pluginConfig('rsyncd','','conf_pwd');">密钥配置</p>
<p onclick="lsyncdSend();">发送配置</p>
<p onclick="rsyncdReceive();">接收配置</p>
<p onclick="pluginLogs('rsyncd','','run_log');">日志</p>
<p onclick="rsRead()">说明</p>
</div>
<div class="bt-w-con pd15">
@ -16,8 +83,12 @@
</div>
<script type="text/javascript">
resetPluginWinWidth(700);
$.getScript( "/plugins/file?name=rsyncd&f=js/rsyncd.js", function() {
// $.getScript("/plugins/file?name=rsyncd&f=js/base64.js", function() {
// console.log('base64 load');
// });
$.getScript("/plugins/file?name=rsyncd&f=js/rsyncd.js", function() {
pluginService('rsyncd');
});
</script>

@ -73,18 +73,24 @@ def status():
"ps -ef|grep rsync |grep -v grep | grep -v python | awk '{print $2}'")
if data[0] == '':
return 'stop'
# data = mw.execShell(
# "ps -ef|grep lsyncd |grep -v grep | grep -v python | awk '{print $2}'")
# if data[0] == '':
# return 'stop'
return 'start'
def appConf():
return getServerDir() + '/rsyncd.conf'
# return '/etc/rsyncd.conf'
def appConfPwd():
# if mw.isAppleSystem():
return getServerDir() + '/rsyncd.passwd'
# return '/etc/rsyncd.passwd'
def appAuthPwd(name):
nameDir = getServerDir() + '/receive/' + name
if not os.path.exists(nameDir):
mw.execShell("mkdir -p " + nameDir)
return nameDir + '/auth.db'
def getLog():
@ -97,8 +103,17 @@ def getLog():
return tmp.groups()[0]
def initDreplace():
def getLsyncdLog():
path = getServerDir() + "/lsyncd.conf"
conf = mw.readFile(path)
rep = 'logfile\s*=\s*\"(.*)\"'
tmp = re.search(rep, conf)
if not tmp:
return ''
return tmp.groups()[0]
def initDReceive():
# conf
conf_path = appConf()
conf_tpl_path = getPluginDir() + '/conf/rsyncd.conf'
@ -106,18 +121,13 @@ def initDreplace():
content = mw.readFile(conf_tpl_path)
mw.writeFile(conf_path, content)
# pwd
confpwd_path = appConfPwd()
if not os.path.exists(confpwd_path):
mw.writeFile(confpwd_path, '')
mw.execShell('chmod 0600 ' + confpwd_path)
initD_path = getServerDir() + '/init.d'
if not os.path.exists(initD_path):
os.mkdir(initD_path)
file_bin = initD_path + '/' + getPluginName()
file_bin = initD_path + '/' + getPluginName()
file_tpl = getInitDTpl()
# print(file_bin, file_tpl)
# initd replace
if not os.path.exists(file_bin):
content = mw.readFile(file_tpl)
@ -132,14 +142,14 @@ def initDreplace():
if os.path.exists(systemDir) and not os.path.exists(systemService):
rsync_bin = mw.execShell('which rsync')[0].strip()
if rsync_bin == '':
print('rsync缺失!')
print('rsync missing!')
exit(0)
service_path = mw.getServerDir()
se_content = mw.readFile(systemServiceTpl)
se_content = se_content.replace('{$SERVER_PATH}', service_path)
se_content = se_content.replace('{$RSYNC_BIN}', rsync_bin)
mw.writeFile(systemService, se_content)
se = mw.readFile(systemServiceTpl)
se = se.replace('{$SERVER_PATH}', service_path)
se = se.replace('{$RSYNC_BIN}', rsync_bin)
mw.writeFile(systemService, se)
mw.execShell('systemctl daemon-reload')
rlog = getLog()
@ -148,6 +158,89 @@ def initDreplace():
return file_bin
def initDSend():
service_path = mw.getServerDir()
conf_path = getServerDir() + '/lsyncd.conf'
conf_tpl_path = getPluginDir() + '/conf/lsyncd.conf'
if not os.path.exists(conf_path):
content = mw.readFile(conf_tpl_path)
content = content.replace('{$SERVER_PATH}', service_path)
mw.writeFile(conf_path, content)
initD_path = getServerDir() + '/init.d'
if not os.path.exists(initD_path):
os.mkdir(initD_path)
# initd replace
file_bin = initD_path + '/lsyncd'
file_tpl = getPluginDir() + "/init.d/lsyncd.tpl"
if not os.path.exists(file_bin):
content = mw.readFile(file_tpl)
content = contentReplace(content)
mw.writeFile(file_bin, content)
mw.execShell('chmod +x ' + file_bin)
lock_file = getServerDir() + "/installed.pl"
# systemd
systemDir = mw.systemdCfgDir()
systemService = systemDir + '/lsyncd.service'
systemServiceTpl = getPluginDir() + '/init.d/lsyncd.service.tpl'
if not os.path.exists(lock_file):
lsyncd_bin = mw.execShell('which lsyncd')[0].strip()
if lsyncd_bin == '':
print('lsyncd missing!')
exit(0)
content = mw.readFile(systemServiceTpl)
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$LSYNCD_BIN}', lsyncd_bin)
mw.writeFile(systemService, content)
mw.execShell('systemctl daemon-reload')
mw.writeFile(lock_file, "ok")
lslog = getLsyncdLog()
if os.path.exists(lslog):
mw.writeFile(lslog, '')
return file_bin
def getDefaultConf():
path = getServerDir() + "/config.json"
data = mw.readFile(path)
data = json.loads(data)
return data
def setDefaultConf(data):
path = getServerDir() + "/config.json"
mw.writeFile(path, json.dumps(data))
return True
def initConfigJson():
path = getServerDir() + "/config.json"
tpl = getPluginDir() + "/conf/config.json"
if not os.path.exists(path):
data = mw.readFile(tpl)
data = json.loads(data)
mw.writeFile(path, json.dumps(data))
def initDreplace():
initDSend()
# conf
file_bin = initDReceive()
initConfigJson()
return file_bin
def rsyncOp(method):
file = initDreplace()
if not mw.isAppleSystem():
@ -186,6 +279,12 @@ def initdStatus():
data = mw.execShell(shell_cmd)
if data[0] == '':
return 'fail'
shell_cmd = 'systemctl status lsyncd | grep loaded | grep "enabled;"'
data = mw.execShell(shell_cmd)
if data[0] == '':
return 'fail'
return 'ok'
@ -193,6 +292,7 @@ def initdInstall():
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl enable lsyncd')
mw.execShell('systemctl enable rsyncd')
return 'ok'
@ -202,6 +302,7 @@ def initdUinstall():
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl diable lsyncd')
mw.execShell('systemctl diable rsyncd')
return 'ok'
@ -211,6 +312,7 @@ def getRecListData():
content = mw.readFile(path)
flist = re.findall("\[(.*)\]", content)
flist_len = len(flist)
ret_list = []
for i in range(flist_len):
@ -226,31 +328,27 @@ def getRecListData():
t1 = re.search(reg, content, re.S)
if t1:
args = t1.groups()[0]
# print 'args start', args, 'args_end'
t2 = re.findall('\s*(.*)\s*=\s*(.*)', args, re.M)
# print('args start', args, 'args_end')
t2 = re.findall('\s*(.*)\s*\=\s*?(.*)?', args, re.M | re.I)
for i in range(len(t2)):
tmp[t2[i][0].strip()] = t2[i][1]
tmp[t2[i][0].strip()] = t2[i][1].strip()
ret_list.append(tmp)
return ret_list
def getRecListDataBy(name):
l = getRecListData()
for x in range(len(l)):
if name == l[x]["name"]:
return l[x]
def getRecList():
ret_list = getRecListData()
return mw.returnJson(True, 'ok', ret_list)
def getUPwdList():
pwd_path = appConfPwd()
pwd_content = mw.readFile(pwd_path)
plist = pwd_content.strip().split('\n')
plist_len = len(plist)
data = {}
for x in range(plist_len):
tmp = plist[x].split(':')
data[tmp[0]] = tmp[1]
return data
def addRec():
args = getArgs()
data = checkArgs(args, ['name', 'path', 'pwd', 'ps'])
@ -262,10 +360,12 @@ def addRec():
args_path = args['path']
args_ps = args['ps']
pwd_path = appConfPwd()
pwd_content = mw.readFile(pwd_path)
pwd_content += args_name + ':' + args_pwd + "\n"
mw.writeFile(pwd_path, pwd_content)
delRecBy(args_name)
auth_path = appAuthPwd(args_name)
pwd_content = args_name + ':' + args_pwd + "\n"
mw.writeFile(auth_path, pwd_content)
mw.execShell("chmod 600 " + auth_path)
path = appConf()
content = mw.readFile(path)
@ -274,71 +374,498 @@ def addRec():
con += 'path = ' + args_path + "\n"
con += 'comment = ' + args_ps + "\n"
con += 'auth users = ' + args_name + "\n"
con += 'ignore errors' + "\n"
con += 'secrets file = ' + auth_path + "\n"
con += 'read only = false'
content = content + con
content = content.strip() + "\n" + con
mw.writeFile(path, content)
return mw.returnJson(True, '添加成功')
def delRec():
def getRec():
args = getArgs()
data = checkArgs(args, ['name'])
if not data[0]:
return data[1]
args_name = args['name']
cmd = "sed -i '_bak' '/" + args_name + "/d' " + appConfPwd()
mw.execShell(cmd)
name = args['name']
try:
if name == "":
tmp = {}
tmp["name"] = ""
tmp["comment"] = ""
tmp["path"] = mw.getWwwDir()
tmp["pwd"] = mw.getRandomString(16)
return mw.returnJson(True, 'OK', tmp)
data = getRecListDataBy(name)
content = mw.readFile(data['secrets file'])
pwd = content.strip().split(":")
data['pwd'] = pwd[1]
return mw.returnJson(True, 'OK', data)
def delRecBy(name):
try:
path = appConf()
content = mw.readFile(path)
ret_list = getRecListData()
ret_list_len = len(ret_list)
reclist = getRecListData()
ret_list_len = len(reclist)
is_end = False
next_name = ''
for x in range(ret_list_len):
tmp = ret_list[x]
if tmp['name'] == args_name:
tmp = reclist[x]
if tmp['name'] == name:
secrets_file = tmp['secrets file']
tp = os.path.dirname(secrets_file)
if os.path.exists(tp):
mw.execShell("rm -rf " + tp)
if x + 1 == ret_list_len:
is_end = True
else:
next_name = ret_list[x + 1]['name']
next_name = reclist[x + 1]['name']
reg = ''
if is_end:
reg = '\[' + args_name + '\]\s*(.*)'
reg = '\[' + name + '\]\s*(.*)'
else:
reg = '\[' + args_name + '\]\s*(.*)\s*\[' + next_name + '\]'
reg = '\[' + name + '\]\s*(.*)\s*\[' + next_name + '\]'
conre = re.search(reg, content, re.S)
content = content.replace(
"[" + args_name + "]\n" + conre.groups()[0], '')
"[" + name + "]\n" + conre.groups()[0], '')
mw.writeFile(path, content)
return mw.returnJson(True, '删除成功!')
except Exception as e:
return mw.returnJson(False, '删除失败!')
return False
return True
def cmdRec():
def delRec():
args = getArgs()
data = checkArgs(args, ['name'])
if not data[0]:
return data[1]
name = args['name']
ok = delRecBy(name)
if ok:
return mw.returnJson(True, '删除成功!')
return mw.returnJson(False, '删除失败!')
an = args['name']
pwd_list = getUPwdList()
def cmdRecSecretKey():
import base64
args = getArgs()
data = checkArgs(args, ['name'])
if not data[0]:
return data[1]
name = args['name']
info = getRecListDataBy(name)
secrets_file = info['secrets file']
content = mw.readFile(info['secrets file'])
pwd = content.strip().split(":")
m = {"A": info['name'], "B": pwd[1], "C": "873"}
m = json.dumps(m)
m = m.encode("utf-8")
m = base64.b64encode(m)
cmd = m.decode("utf-8")
return mw.returnJson(True, 'OK!', cmd)
def cmdRecCmd():
args = getArgs()
data = checkArgs(args, ['name'])
if not data[0]:
return data[1]
name = args['name']
info = getRecListDataBy(name)
ip = mw.getLocalIp()
cmd = 'echo "' + pwd_list[an] + '" > /tmp/p.pass' + "<br>"
cmd += 'chmod 600 /tmp/p.pass' + "<br>"
cmd += 'rsync -arv --password-file=/tmp/p.pass --progress --delete /project ' + \
an + '@' + ip + '::' + an
content = mw.readFile(info['secrets file'])
pwd = content.strip().split(":")
tmp_name = '/tmp/' + name + '.pass'
cmd = 'echo "' + pwd[1] + '" > ' + tmp_name + '<br>'
cmd += 'chmod 600 ' + tmp_name + ' <br>'
cmd += 'rsync -arv --password-file=' + tmp_name + \
' --progress --delete /project ' + name + '@' + ip + '::' + name
return mw.returnJson(True, 'OK!', cmd)
# rsyncdReceive
# ----------------------------- rsyncdSend start -------------------------
def lsyncdReload():
mw.execShell('systemctl reload lsyncd')
def makeLsyncdConf(data):
# print(data)
lsyncd_data = data['send']
lsyncd_setting = lsyncd_data['default']
content = "settings {\n"
for x in lsyncd_setting:
v = lsyncd_setting[x]
# print(v, type(v))
if type(v) == str:
content += "\t" + x + ' = "' + v + "\",\n"
elif type(v) == int:
content += "\t" + x + ' = ' + str(v) + ",\n"
content += "}\n\n"
lsyncd_list = lsyncd_data['list']
rsync_bin = mw.execShell('which rsync')[0].strip()
send_dir = getServerDir() + "/send"
if len(lsyncd_list) > 0:
for x in range(len(lsyncd_list)):
t = lsyncd_list[x]
name_dir = send_dir + "/" + t["name"]
if not os.path.exists(name_dir):
mw.execShell("mkdir -p " + name_dir)
cmd_exclude = name_dir + "/exclude"
cmd_exclude_txt = ""
for x in t['exclude']:
cmd_exclude_txt += x + "\n"
mw.writeFile(cmd_exclude, cmd_exclude_txt)
cmd_pass = name_dir + "/pass"
mw.writeFile(cmd_pass, t['password'])
mw.execShell("chmod 600 " + cmd_pass)
delete_ok = ' '
if t['delete'] == "true":
delete_ok = ' --delete '
remote_addr = t['name'] + '@' + t['ip'] + "::" + t['name']
cmd = rsync_bin + " -avzP " + "--port=" + str(t['rsync']['port']) + " --bwlimit=" + t['rsync'][
'bwlimit'] + delete_ok + " --exclude-from=" + cmd_exclude + " --password-file=" + cmd_pass + " " + t["path"] + " " + remote_addr
mw.writeFile(name_dir + "/cmd", cmd)
mw.execShell("cmod +x " + name_dir + "/cmd")
if t['realtime'] == "false":
continue
# print(x, t)
content += "sync {\n"
content += "\tdefault.rsync,\n"
content += "\tsource = \"" + t['path'] + "\",\n"
content += "\ttarget = \"" + remote_addr + "\",\n"
content += "\tdelete = " + t['delete'] + ",\n"
content += "\tdelay = " + t['delay'] + ",\n"
content += "\tinit = false,\n"
exclude_str = json.dumps(t['exclude'])
exclude_str = exclude_str.replace("[", "{")
exclude_str = exclude_str.replace("]", "}")
# print(exclude_str)
content += "\texclude = " + exclude_str + ",\n"
# rsync
content += "\trsync = {\n"
content += "\t\tbinary = \"" + rsync_bin + "\",\n"
content += "\t\tarchive = true,\n"
content += "\t\tverbose = true,\n"
content += "\t\tcompress = " + t['rsync']['compress'] + ",\n"
content += "\t\tpassword_file = \"" + cmd_pass + "\",\n"
content += "\t\t_extra = {\"--bwlimit=" + t['rsync'][
'bwlimit'] + "\", \"--port=" + str(t['rsync']['port']) + "\"},\n"
content += "\t}\n"
content += "}\n"
path = getServerDir() + "/lsyncd.conf"
mw.writeFile(path, content)
lsyncdReload()
import tool_task
tool_task.createBgTask(lsyncd_list)
def lsyncdListFindIp(slist, ip):
for x in range(len(slist)):
if slist[x]["ip"] == ip:
return (True, x)
return (False, -1)
def lsyncdListFindName(slist, name):
for x in range(len(slist)):
if slist[x]["name"] == name:
return (True, x)
return (False, -1)
def lsyncdList():
data = getDefaultConf()
send = data['send']
return mw.returnJson(True, "设置成功!", send)
def lsyncdGet():
import base64
args = getArgs()
data = checkArgs(args, ['name'])
if not data[0]:
return data[1]
name = args['name']
data = getDefaultConf()
slist = data['send']["list"]
res = lsyncdListFindName(slist, name)
rsync = {
'bwlimit': "1024",
"compress": "true",
"archive": "true",
"verbose": "true"
}
info = {
"secret_key": '',
"ip": '',
"path": mw.getServerDir(),
'rsync': rsync,
'realtime': "true",
'delete': "false",
}
if res[0]:
list_index = res[1]
info = slist[list_index]
m = {"A": info['name'], "B": info["password"], "C": "873"}
m = json.dumps(m)
m = m.encode("utf-8")
m = base64.b64encode(m)
info['secret_key'] = m.decode("utf-8")
return mw.returnJson(True, "OK", info)
def lsyncdDelete():
args = getArgs()
data = checkArgs(args, ['name'])
if not data[0]:
return data[1]
name = args['name']
data = getDefaultConf()
slist = data['send']["list"]
res = lsyncdListFindName(slist, name)
retdata = {}
if res[0]:
list_index = res[1]
slist.pop(list_index)
data['send']["list"] = slist
setDefaultConf(data)
makeLsyncdConf(data)
return mw.returnJson(True, "OK")
def lsyncdAdd():
import base64
args = getArgs()
data = checkArgs(args, ['ip', 'conn_type', 'path',
'secret_key', 'delay', 'period'])
if not data[0]:
return data[1]
ip = args['ip']
path = args['path']
conn_type = args['conn_type']
secret_key = args['secret_key']
delete = args['delete']
realtime = args['realtime']
delay = args['delay']
bwlimit = args['bwlimit']
compress = args['compress']
period = args['period']
hour = args['hour']
minute = args['minute']
minute_n = args['minute-n']
info = {
"ip": ip,
"path": path,
"delete": delete,
"realtime": realtime,
'delay': delay,
"conn_type": conn_type,
"period": period,
"hour": hour,
"minute": minute,
"minute-n": minute_n,
}
if conn_type == "key":
try:
m = base64.b64decode(secret_key)
m = json.loads(m)
info['name'] = m['A']
info['password'] = m['B']
info['port'] = m['C']
except Exception as e:
return mw.returnJson(False, "接收密钥格式错误!")
else:
data = checkArgs(args, ['uname'])
if not data[0]:
return data[1]
info['name'] = args['uname']
info['password'] = args['uname']
rsync = {
'bwlimit': bwlimit,
"port": info['port'],
"compress": compress,
"archive": "true",
"verbose": "true"
}
info['rsync'] = rsync
if not 'exclude' in info:
info["exclude"] = [
"/**.upload.tmp",
"**/*.log",
"**/*.tmp",
"**/*.temp",
".git",
".gitignore",
".user.ini",
]
data = getDefaultConf()
slist = data['send']["list"]
res = lsyncdListFindName(slist, info['name'])
if res[0]:
list_index = res[1]
slist[list_index] = info
else:
slist.append(info)
data['send']["list"] = slist
setDefaultConf(data)
makeLsyncdConf(data)
return mw.returnJson(True, "设置成功!")
def lsyncdRun():
args = getArgs()
data = checkArgs(args, ['name'])
if not data[0]:
return data[1]
send_dir = getServerDir() + "/send"
name = args['name']
app_dir = send_dir + "/" + name
cmd = "bash " + app_dir + "/cmd >> " + app_dir + "/run.log" + " 2>&1 &"
mw.execShell(cmd)
return mw.returnJson(True, "执行成功!")
def lsyncdConfLog():
logs_path = getServerDir() + "/lsyncd.log"
return logs_path
def lsyncdLog():
args = getArgs()
data = checkArgs(args, ['name'])
if not data[0]:
return data[1]
send_dir = getServerDir() + "/send"
name = args['name']
app_dir = send_dir + "/" + name
return app_dir + "/run.log"
def lsyncdGetExclude():
args = getArgs()
data = checkArgs(args, ['name'])
if not data[0]:
return data[1]
data = getDefaultConf()
slist = data['send']["list"]
res = lsyncdListFindName(slist, args['name'])
i = res[1]
info = slist[i]
return mw.returnJson(True, "OK!", info['exclude'])
def lsyncdRemoveExclude():
args = getArgs()
data = checkArgs(args, ['name', 'exclude'])
if not data[0]:
return data[1]
exclude = args['exclude']
data = getDefaultConf()
slist = data['send']["list"]
res = lsyncdListFindName(slist, args['name'])
i = res[1]
info = slist[i]
exclude_list = info['exclude']
exclude_pop_key = -1
for x in range(len(exclude_list)):
if exclude_list[x] == exclude:
exclude_pop_key = x
if exclude_pop_key > -1:
exclude_list.pop(exclude_pop_key)
data['send']["list"][i]['exclude'] = exclude_list
setDefaultConf(data)
makeLsyncdConf(data)
return mw.returnJson(True, "OK!", exclude_list)
def lsyncdAddExclude():
args = getArgs()
data = checkArgs(args, ['name', 'exclude'])
if not data[0]:
return data[1]
exclude = args['exclude']
data = getDefaultConf()
slist = data['send']["list"]
res = lsyncdListFindName(slist, args['name'])
i = res[1]
info = slist[i]
exclude_list = info['exclude']
exclude_list.append(exclude)
data['send']["list"][i]['exclude'] = exclude_list
setDefaultConf(data)
makeLsyncdConf(data)
return mw.returnJson(True, "OK!", exclude_list)
if __name__ == "__main__":
func = sys.argv[1]
if func == 'status':
@ -359,8 +886,6 @@ if __name__ == "__main__":
print(initdUinstall())
elif func == 'conf':
print(appConf())
elif func == 'conf_pwd':
print(appConfPwd())
elif func == 'run_log':
print(getLog())
elif func == 'rec_list':
@ -369,7 +894,31 @@ if __name__ == "__main__":
print(addRec())
elif func == 'del_rec':
print(delRec())
elif func == 'cmd_rec':
print(cmdRec())
elif func == 'get_rec':
print(getRec())
elif func == 'cmd_rec_secret_key':
print(cmdRecSecretKey())
elif func == 'cmd_rec_cmd':
print(cmdRecCmd())
elif func == 'lsyncd_list':
print(lsyncdList())
elif func == 'lsyncd_add':
print(lsyncdAdd())
elif func == 'lsyncd_get':
print(lsyncdGet())
elif func == 'lsyncd_delete':
print(lsyncdDelete())
elif func == 'lsyncd_run':
print(lsyncdRun())
elif func == 'lsyncd_log':
print(lsyncdLog())
elif func == 'lsyncd_conf_log':
print(lsyncdConfLog())
elif func == 'lsyncd_get_exclude':
print(lsyncdGetExclude())
elif func == 'lsyncd_remove_exclude':
print(lsyncdRemoveExclude())
elif func == 'lsyncd_add_exclude':
print(lsyncdAddExclude())
else:
print('error')

@ -5,7 +5,7 @@
"name":"rsyncd",
"type":"软件",
"ps":"rsyncd同步助手",
"versions":"1.0",
"versions":"2.0",
"shell":"install.sh",
"checks":"server/rsyncd",
"path": "server/rsyncd",

@ -0,0 +1,10 @@
[Unit]
Description=Lightweight inotify based sync daemon
ConditionPathExists={$SERVER_PATH}/rsyncd/lsyncd.conf
[Service]
Type=simple
ExecStart={$LSYNCD_BIN} -nodaemon {$SERVER_PATH}/rsyncd/lsyncd.conf
[Install]
WantedBy=multi-user.target

@ -0,0 +1,108 @@
#!/bin/bash
#
# chkconfig: - 85 15
# description: Lightweight inotify based sync daemon
#
# processname: lsyncd
# config: /etc/lsyncd.conf
# config: /etc/sysconfig/lsyncd
# pidfile: /var/run/lsyncd.pid
# Source function library
if [ -f /etc/init.d/functions ];then
. /etc/init.d/functions
fi
if [ -f /lib/lsb/init-functions ];then
. /lib/lsb/init-functions
fi
# Source networking configuration.
if [ -f /etc/sysconfig/network ];then
. /etc/sysconfig/network
fi
LSYNCD_OPTIONS="-pidfile /var/run/lsyncd.pid {$SERVER_PATH}/rsyncd/lsyncd.conf"
if [ -e /etc/sysconfig/lsyncd ]; then
. /etc/sysconfig/lsyncd
fi
RETVAL=0
prog="lsyncd"
thelock=/var/lock/subsys/lsyncd
LSYNCD_USER=root
start() {
[ -f {$SERVER_PATH}/rsyncd/lsyncd.conf ] || exit 6
echo -n $"Starting $prog: "
if [ $UID -ne 0 ]; then
RETVAL=1
failure
else
nohup /usr/bin/lsyncd $LSYNCD_OPTIONS > /dev/null &
RETVAL=$?
[ $RETVAL -eq 0 ] && touch $thelock
fi;
echo
return $RETVAL
}
stop() {
echo -n $"Stopping $prog: "
if [ $UID -ne 0 ]; then
RETVAL=1
failure
else
killproc lsyncd
RETVAL=$?
[ $RETVAL -eq 0 ] && rm -f $thelock
fi;
echo
return $RETVAL
}
reload(){
echo -n $"Reloading $prog: "
killproc lsyncd -HUP
RETVAL=$?
echo
return $RETVAL
}
restart(){
stop
start
}
condrestart(){
[ -e $thelock ] && restart
return 0
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
reload)
reload
;;
condrestart)
condrestart
;;
status)
status lsyncd
RETVAL=$?
;;
*)
echo $"Usage: $0 {start|stop|status|restart|condrestart|reload}"
RETVAL=1
esac
exit $RETVAL

@ -14,25 +14,35 @@ sysName=`uname`
# 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}'`
echo $OSNAME
install_tmp=${rootPath}/tmp/mw_install.pl
Install_rsyncd()
{
echo '正在安装脚本文件...' > $install_tmp
mkdir -p $serverPath/rsyncd
if [ "$OSNAME" == "debian'" ] || [ "$OSNAME" == "ubuntu'" ];then
apt install -y rsync
apt install -y lsyncd
elif [[ "$OSNAME" == "arch" ]]; then
echo y | pacman -Sy rsync
echo y | pacman -Sy lsyncd
elif [[ "$OSNAME" == "macos" ]]; then
# brew install rsync
# brew install lsyncd
echo "ok"
else
yum install -y rsync
yum install -y lsyncd
fi
mkdir -p $serverPath/rsyncd
mkdir -p $serverPath/rsyncd/receive
mkdir -p $serverPath/rsyncd/send
echo '1.0' > $serverPath/rsyncd/version.pl
echo '2.0' > $serverPath/rsyncd/version.pl
echo '安装完成' > $install_tmp
cd ${rootPath} && python3 ${rootPath}/plugins/rsyncd/index.py start
cd ${rootPath} && python3 ${rootPath}/plugins/rsyncd/index.py initd_install
@ -51,6 +61,7 @@ Uninstall_rsyncd()
if [ -f $serverPath/rsyncd/initd/rsyncd ];then
$serverPath/rsyncd/initd/rsyncd stop
fi
rm -rf $serverPath/rsyncd
echo "卸载完成" > $install_tmp
}

@ -0,0 +1,149 @@
//base64.js
function base64_encode(str) {
var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var out, i, len;
var c1, c2, c3;
len = str.length;
i = 0;
out = "";
while(i < len) {
c1 = str.charCodeAt(i++) & 0xff;
if(i == len)
{
out += base64EncodeChars.charAt(c1 >> 2);
out += base64EncodeChars.charAt((c1 & 0x3) << 4);
out += "==";
break;
}
c2 = str.charCodeAt(i++);
if(i == len)
{
out += base64EncodeChars.charAt(c1 >> 2);
out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
out += base64EncodeChars.charAt((c2 & 0xF) << 2);
out += "=";
break;
}
c3 = str.charCodeAt(i++);
out += base64EncodeChars.charAt(c1 >> 2);
out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6));
out += base64EncodeChars.charAt(c3 & 0x3F);
}
return out;
}
function base64_decode(str) {
var base64DecodeChars = new Array(
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1);
var c1, c2, c3, c4;
var i, len, out;
len = str.length;
i = 0;
out = "";
while(i < len) {
/* c1 */
do {
c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
} while(i < len && c1 == -1);
if(c1 == -1)
break;
/* c2 */
do {
c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
} while(i < len && c2 == -1);
if(c2 == -1)
break;
out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
/* c3 */
do {
c3 = str.charCodeAt(i++) & 0xff;
if(c3 == 61)
return out;
c3 = base64DecodeChars[c3];
} while(i < len && c3 == -1);
if(c3 == -1)
break;
out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));
/* c4 */
do {
c4 = str.charCodeAt(i++) & 0xff;
if(c4 == 61)
return out;
c4 = base64DecodeChars[c4];
} while(i < len && c4 == -1);
if(c4 == -1)
break;
out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
}
return out;
}
function utf16to8(str) {
var out, i, len, c;
out = "";
len = str.length;
for(i = 0; i < len; i++) {
c = str.charCodeAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
out += str.charAt(i);
} else if (c > 0x07FF) {
out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
} else {
out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
}
}
return out;
}
function utf8to16(str) {
var out, i, len, c;
var char2, char3;
out = "";
len = str.length;
i = 0;
while(i < len) {
c = str.charCodeAt(i++);
switch(c >> 4)
{
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
// 0xxxxxxx
out += str.charAt(i-1);
break;
case 12: case 13:
// 110x xxxx 10xx xxxx
char2 = str.charCodeAt(i++);
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
break;
case 14:
// 1110 xxxx 10xx xxxx 10xx xxxx
char2 = str.charCodeAt(i++);
char3 = str.charCodeAt(i++);
out += String.fromCharCode(((c & 0x0F) << 12) |
((char2 & 0x3F) << 6) |
((char3 & 0x3F) << 0));
break;
}
}
return out;
}

@ -36,6 +36,542 @@ function rsPost(method,args,callback, title){
},'json');
}
///////////////// ----------------- 发送配置 ---------------- //////////////
function createSendTask(name = ''){
var args = {};
args["name"] = name;
rsPost('lsyncd_get', args, function(rdata){
var rdata = $.parseJSON(rdata.data);
var data = rdata.data;
console.log(data);
var layerName = '创建';
if (name!=''){
layerName = '编辑';
}
var compress_true = "";
var compress_false = "";
if (data['rsync']['compress'] == 'true'){
compress_true = "selected";
compress_false = "";
} else {
compress_true = "";
compress_false = "selected";
}
var delete_true = "";
var delete_false = "";
if (data['delete'] == 'false'){
delete_true = "selected";
delete_false = "";
} else {
delete_true = "";
delete_false = "selected";
}
var realtime_true = "";
var realtime_false = "";
if (data['realtime'] == 'true'){
realtime_true = "selected";
realtime_false = "";
} else {
realtime_true = "";
realtime_false = "selected";
}
var period_day = "";
var period_minute_n = "";
if (data['period'] == 'day'){
period_day = "selected";
period_minute_n = "";
} else {
period_day = "";
period_minute_n = "selected";
}
var layerID = layer.open({
type: 1,
area: ['600px','500px'],
title: layerName+"发送任务",
closeBtn: 1,
shift: 0,
shadeClose: false,
btn: ['提交','取消'],
content:"<form class='bt-form pd20' id='fromServerPath' accept-charset='utf-8'>\
<div class='line'>\
<span class='tname'>服务器IP</span>\
<div class='info-r c4'>\
<input class='bt-input-text' type='text' name='ip' placeholder='请输入接收服务器IP' value='"+data["ip"]+"' style='width:310px' />\
</div>\
</div>\
<div class='line'>\
<span class='tname'>同步目录</span>\
<div class='info-r c4'>\
<input id='inputPath' class='bt-input-text mr5' type='text' name='path' value='"+data["path"]+"' placeholder='请选择同步目录' style='width:310px' /><span class='glyphicon glyphicon-folder-open cursor' onclick='changePath(\"inputPath\")'></span>\
<span data-toggle='tooltip' data-placement='top' title='【同步目录】若不以/结尾,则表示将数据同步到二级目录,一般情况下目录路径请以/结尾' class='bt-ico-ask' style='cursor: pointer;'>?</span>\
</div>\
</div>\
<div class='line'>\
<span class='tname'>同步方式</span>\
<div class='info-r c4'>\
<select class='bt-input-text' name='delete' style='width:100px'>\
<option value='false' "+delete_true+">增量</option>\
<option value='true' "+delete_false+">完全</option>\
</select>\
<span data-toggle='tooltip' data-placement='top' title='【同步方式】增量: 数据更改/增加时同步,且只追加和替换文件\n【同步方式】完全: 保持两端的数据与目录结构的一致性,会同步删除、追加和替换文件和目录' class='bt-ico-ask' style='cursor: pointer;'>?</span>\
<span style='margin-left: 20px;margin-right: 10px;'>同步周期</span>\
<select class='bt-input-text synchronization' name='realtime' style='width:100px'>\
<option value='true' "+realtime_true+">实时同步</option>\
<option value='false' "+realtime_false+">定时同步</option>\
</select>\
</div>\
</div>\
<div class='line' id='period' style='height:45px;display:none;'>\
<span class='tname'>定时周期</span>\
<div class='info-r c4'>\
<select class='bt-input-text pull-left mr20' name='period' style='width:100px;'>\
<option value='day' "+period_day+">每天</option>\
<option value='minute-n' "+period_minute_n+">N分钟</option>\
</select>\
<div class='plan_hms pull-left mr20 bt-input-text hour'>\
<span><input class='bt-input-text' type='number' name='hour' value='"+data["hour"]+"' maxlength='2' max='23' min='0'></span>\
<span class='name'>小时</span>\
</div>\
<div class='plan_hms pull-left mr20 bt-input-text minute'>\
<span><input class='bt-input-text' type='number' name='minute' value='"+data["minute"]+"' maxlength='2' max='59' min='0'></span>\
<span class='name'>分钟</span>\
</div>\
<div class='plan_hms pull-left mr20 bt-input-text minute-n' style='display:none;'>\
<span><input class='bt-input-text' type='number' name='minute-n' value='"+data["minute-n"]+"' maxlength='2' max='59' min='0'></span>\
<span class='name'>分钟</span>\
</div>\
</div>\
</div>\
<div class='line'>\
<span class='tname'>限速</span>\
<div class='info-r c4'>\
<input class='bt-input-text' type='number' name='bwlimit' min='0' value='1024' style='width:100px' /> KB\
<span data-toggle='tooltip' data-placement='top' title='【限速】限制数据同步任务的速度,防止因同步数据导致带宽跑高' class='bt-ico-ask' style='cursor: pointer;'>?</span>\
<span style='margin-left: 29px;margin-right: 10px;'>延迟</span><input class='bt-input-text' min='0' type='number' name='delay' value='3' style='width:100px' /> \
<span data-toggle='tooltip' data-placement='top' title='【延迟】在延迟时间周期内仅记录不同步,到达周期后一次性同步数据,以节省开销' class='bt-ico-ask' style='cursor: pointer;'>?</span>\
</div>\
</div>\
<div class='line'>\
<span class='tname'>连接方式</span>\
<div class='info-r c4'>\
<select class='bt-input-text' name='conn_type' style='width:100px'>\
<option value='key'>密钥</option>\
<option value='user'>帐号</option>\
</select>\
<span style='margin-left: 45px;margin-right: 10px;'>压缩传输</span>\
<select class='bt-input-text' name='compress' style='width:100px'>\
<option value='true' "+compress_true+">开启</option>\
<option value='false' "+compress_false+">关闭</option>\
</select>\
<span data-toggle='tooltip' data-placement='top' title='【压缩传输】开启后可减少带宽开销,但会增加CPU开销,如带宽充足,建议关闭此选项' class='bt-ico-ask' style='cursor: pointer;'>?</span>\
</div>\
</div>\
<div class='line conn-key'>\
<span class='tname'>接收密钥</span>\
<div class='info-r c4'>\
<textarea id='mainDomain' class='bt-input-text' name='secret_key' style='width:310px;height:75px;line-height:22px' placeholder='此密钥为 接收配置[接收账号] 的密钥'>"+data['secret_key']+"</textarea>\
</div>\
</div>\
<div class='line conn-user'>\
<span class='tname'>用户名</span>\
<div class='info-r c4'>\
<input class='bt-input-text' type='text' name='u_user' min='0' value='"+data["name"]+"' style='width:310px' />\
</div>\
</div>\
<div class='line conn-user'>\
<span class='tname'>密码</span>\
<div class='info-r c4'>\
<input class='bt-input-text' type='text' name='u_pass' min='0' value='"+data["password"]+"' style='width:310px' />\
</div>\
</div>\
<div class='line conn-user'>\
<span class='tname'>端口</span>\
<div class='info-r c4'>\
<input class='bt-input-text' type='number' name='u_port' min='0' value='"+data["rsync"]["port"]+"' style='width:310px' />\
</div>\
</div>\
<ul class=\"help-info-text c7\">\
</ul>\
</form>",
success:function(){
$('[data-toggle="tooltip"]').tooltip();
$(".conn-user").hide();
$("select[name='conn_type']").change(function(){
if($(this).val() == 'key'){
$(".conn-user").hide();
$(".conn-key").show();
}else{
$(".conn-user").show();
$(".conn-key").hide();
}
});
var selVal = $('.synchronization option:selected').val();
if (selVal == "false"){
$('#period').show();
}else{
$('#period').hide();
$('.hour input,.minute input').val('0');
$('.minute-n input').val('1');
}
$('.synchronization').change(function(event) {
var selVal = $('.synchronization option:selected').val();
if (selVal == "false"){
$('#period').show();
}else{
$('#period').hide();
$('.hour input,.minute input').val('0');
$('.minute-n input').val('1');
}
});
$("select[name='delete']").change(function(){
if($(this).val() == 'true'){
var mpath = $('input[name="path"]').val();
var msg = '<div><span style="color:orangered;">警告:您选择了完全同步,将会使本机同步与目标机器指定目录的文件保持一致,'
+'<br />请确认目录设置是否有误,一但设置错误,可能导致目标机器的目录文件被删除!</span>'
+'<br /><br /> <span style="color:red;">注意: 同步程序将本机目录:'
+mpath+'的所有数据同步到目标服务器,若目标服务器的同步目录存在其它文件将被删除!</span> <br /><br /> 已了解风险,请按确定继续</div>';
layer.confirm(msg,{title:'数据安全风险警告',icon:2,closeBtn: 1,shift: 5,
btn2:function(){
setTimeout(function(){$($("select[name='delete']").children("option")[0]).prop('selected',true);},100);
}
});
}
});
var selVal = $('#period select option:selected').val();
if (selVal == 'day'){
$('.hour,.minute').show();
if ($('.hour input').val() == ''){
$('.hour input,.minute input').val('0');
}
$('.minute-n').hide();
}else{
$('.hour,.minute').hide();
$('.minute-n').show();
if ($('.minute-n input').val() == ''){
$('.minute-n input').val('1');
}
}
$('#period').change(function(event) {
var selVal = $('#period select option:selected').val();
if (selVal == 'day'){
$('.hour,.minute').show();
if ($('.hour input').val() == ''){
$('.hour input,.minute input').val('0');
}
$('.minute-n').hide();
}else{
$('.hour,.minute').hide();
$('.minute-n').show();
if ($('.minute-n input').val() == ''){
$('.minute-n input').val('1');
}
}
});
},
yes:function(index){
var args = {};
var conn_type = $("select[name='conn_type']").val();
if(conn_type == 'key'){
if ( $('textarea[name="secret_key"]').val() != ''){
args['secret_key'] = $('textarea[name="secret_key"]').val();
} else {
layer.msg('请输入接收密钥!');
return false;
}
} else {
args['sname'] = $("input[name='u_user']").val();
args['password'] = $("input[name='u_pass']").val();
var port = Number($("input[name='u_port']").val());
args['port'] = port;
if (!args['sname'] || !args['password'] || !args['port']){
layer.msg('请输入帐号、密码、端口信息');
return false;
}
}
if ($('input[name="ip"]').val() == ''){
layer.msg('请输入服务器IP地址!');
return false;
}
args['sname'] = $("input[name='u_user']").val();
args['password'] = $("input[name='u_pass']").val();
var port = Number($("input[name='u_port']").val());
args['port'] = port;
args['ip'] = $('input[name="ip"]').val();
args['path'] = $('input[name="path"]').val();
args['delete'] = $('select[name="delete"]').val();
args['realtime'] = $('select[name="realtime"]').val();
args['delay'] = $('input[name="delay"]').val();
args['bwlimit'] = $('input[name="bwlimit"]').val();
args['conn_type'] = $('select[name="conn_type"]').val();
args['compress'] = $('select[name="compress"]').val();
args['period'] = $('select[name="period"]').val();
args['hour'] = $('input[name="hour"]').val();
args['minute'] = $('input[name="minute"]').val();
args['minute-n'] = $('input[name="minute-n"]').val();
rsPost('lsyncd_add', args, function(rdata){
var rdata = $.parseJSON(rdata.data);
layer.msg(rdata.msg,{icon:rdata.status?1:2,time:2000,shade: [0.3, '#000']});
if (rdata.status){
setTimeout(function(){layer.close(index);},2000);
return;
}
});
return true;
}
});
});
}
function lsyncdDelete(name){
safeMessage('删除['+name+']', '您真的要删除['+name+']吗?', function(){
var args = {};
args['name'] = name;
rsPost('lsyncd_delete', args, function(rdata){
var rdata = $.parseJSON(rdata.data);
layer.msg(rdata.msg,{icon:rdata.status?1:2,time:2000,shade: [0.3, '#000']});
setTimeout(function(){lsyncdSend();},2000);
});
});
}
function lsyncdRun(name){
var args = {};
args["name"] = name;
rsPost('lsyncd_run', args, function(rdata){
var rdata = $.parseJSON(rdata.data);
layer.msg(rdata.msg,{icon:rdata.status?1:2,time:2000,shade: [0.3, '#000']});
});
}
function lsyncdLog(name){
var args = {};
args["name"] = name;
pluginStandAloneLogs("rsyncd", '', "lsyncd_log", JSON.stringify(args));
}
function lsyncdExclude(name){
layer.open({
type:1,
title:'过滤器',
area: '400px',
shadeClose:false,
closeBtn:2,
content:'<div class="lsyncd_exclude">\
<div style="overflow:hidden;">\
<fieldset>\
<legend>排除的文件和目录</legend>\
<input type="text" class="bt-input-text mr5" data-type="exclude" title="例如:/home/www/" placeholder="例如:*.log" style="width:305px;">\
<button data-type="exclude" class=" addList btn btn-default btn-sm">添加</button>\
<div class="table-overflow">\
<table class="table table-hover BlockList"><tbody></tbody></table>\
</div>\
</fieldset>\
</div>\
<div>\
<ul class="help-info-text c7" style="list-style-type:decimal;">\
<li>排除的文件和目录是指当前目录下不需要同步的目录或者文件</li>\
<li>如果规则以斜线 <code>/</code></li>\
<li>如果规则以 <code>/</code></li>\
<li><code>?</code> 匹配任何字符,但不包括<code>/</code></li>\
<li><code>*</code> 匹配0或多个字符,但不包括<code>/</code></li>\
<li><code>**</code> 匹配0或多个字符,可以是<code>/</code></li>\
</ul>\
</div>\
</div>'
});
function getIncludeExclude(mName){
loadT = layer.msg('正在获取数据...',{icon:16,time:0,shade: [0.3, '#000']});
rsPost('lsyncd_get_exclude',{"name":mName}, function(rdata) {
layer.close(loadT);
var rdata = $.parseJSON(rdata.data);
var res = rdata.data;
var list=''
for (var i = 0; i < res.length; i++) {
list += '<tr><td>'+ res[i] +'</td><td><a href="javascript:;" data-type='+ mName +' class="delList">删除</a></td></tr>';
}
$('.lsyncd_exclude .BlockList tbody').empty().append(list);
});
}
getIncludeExclude(name);
function addArgs(name,exclude){
loadT = layer.msg('正在添加...',{icon:16,time:0,shade: [0.3, '#000']});
rsPost('lsyncd_add_exclude', {name:name,exclude:exclude}, function(res){
layer.close(loadT);
console.log('addArgs:',res);
if (res.status){
getIncludeExclude(name);
$('.lsyncd_exclude input').val('');
layer.msg(res.msg);
}else{
layer.msg(res.msg);
}
});
}
$('.addList').click(function(event) {
var val = $(this).prev().val();
if(val == ''){
layer.msg('当前输入内容为空,请输入');
return false;
}
addArgs(name,val);
});
$('.lsyncd_exclude input').keyup(function(event){
if (event.which == 13){
var val = $(this).val();
if(val == ''){
layer.msg('当前输入内容为空,请输入');
return false;
}
addArgs(name,val);
}
});
$('.lsyncd_exclude').on('click', '.delList', function(event) {
loadT = layer.msg('正在删除...',{icon:16,time:0,shade: [0.3, '#000']});
var val = $(this).parent().prev().text();
rsPost('lsyncd_remove_exclude',{"name":name,exclude:val}, function(rdata) {
layer.close(loadT);
console.log(rdata)
var rdata = $.parseJSON(rdata.data);
var res = rdata.data;
var list=''
for (var i = 0; i < res.length; i++) {
list += '<tr><td>'+ res[i] +'</td><td><a href="javascript:;" data-type='+ name +' class="delList">删除</a></td></tr>';
}
$('.lsyncd_exclude .BlockList tbody').empty().append(list);
});
});
}
function lsyncdConfLog(){
pluginStandAloneLogs("rsyncd","","lsyncd_conf_log");;
}
function lsyncdSend(){
rsPost('lsyncd_list', '', function(data){
var rdata = $.parseJSON(data.data);
console.log(rdata);
if (!rdata.status){
layer.msg(rdata.msg,{icon:rdata.status?1:2,time:2000,shade: [0.3, '#000']});
return;
}
var list = rdata.data.list;
var con = '';
con += '<div style="padding-top:1px;">\
<button class="btn btn-success btn-sm" onclick="createSendTask();">创建发送任务</button>\
<button class="btn btn-success btn-sm" onclick="lsyncdConfLog();">日志</button>\
</div>';
con += '<div class="divtable" style="margin-top:5px;"><table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0">';
con += '<thead><tr>';
con += '<th>名称(标识)</th>';
con += '<th>源目录</th>';
con += '<th>同步到</th>';
con += '<th>模式</th>';
con += '<th>周期</th>';
con += '<th>操作</th>';
con += '</tr></thead>';
con += '<tbody>';
for (var i = 0; i < list.length; i++) {
var mode = '增量';
if (list[i]['delete'] == 'true'){
mode = '完全';
} else {
mode = '增量';
}
var period = "实时";
if (list[i]['realtime'] == 'true'){
period = '实时';
} else {
period = '定时';
}
con += '<tr>'+
'<td>' + list[i]['name']+'</td>' +
'<td><a class="btlink overflow_hide" style="width:40px;" onclick="openPath(\''+list[i]['path']+'\')">' + list[i]['path']+'</a></td>' +
'<td>' + list[i]['ip']+":"+"cc"+'</td>' +
'<td>' + mode+'</td>' +
'<td>' + period +'</td>' +
'<td>\
<a class="btlink" onclick="lsyncdRun(\''+list[i]['name']+'\')">同步</a>\
| <a class="btlink" onclick="lsyncdLog(\''+list[i]['name']+'\')">日志</a>\
| <a class="btlink" onclick="lsyncdExclude(\''+list[i]['name']+'\')">过滤器</a>\
| <a class="btlink" onclick="createSendTask(\''+list[i]['name']+'\')">编辑</a>\
| <a class="btlink" onclick="lsyncdDelete(\''+list[i]['name']+'\')">删除</a>\
</td>\
</tr>';
}
con += '</tbody>';
con += '</table></div>';
$(".soft-man-con").html(con);
});
}
///////////////// ----------------- 接收配置 ---------------- //////////////
function rsyncdConf(){
rsPost('conf', {}, function(rdata){
rpath = rdata['data'];
if (rdata['status']){
onlineEditFile(0, rpath);
} else {
layer.msg(rdata.msg,{icon:1,time:2000,shade: [0.3, '#000']});
}
});
}
function rsyncdLog(){
pluginStandAloneLogs("rsyncd","","run_log");
}
function rsyncdReceive(){
rsPost('rec_list', '', function(data){
@ -47,6 +583,12 @@ function rsyncdReceive(){
// console.log(rdata);
var list = rdata.data;
var con = '';
con += '<div style="padding-top:1px;">\
<button class="btn btn-success btn-sm" onclick="rsyncdConf();">配置</button>\
<button class="btn btn-success btn-sm" onclick="rsyncdLog();">日志</button>\
</div>';
con += '<div class="divtable" style="margin-top:5px;"><table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0">';
con += '<thead><tr>';
con += '<th>服务名</th>';
@ -61,10 +603,12 @@ function rsyncdReceive(){
for (var i = 0; i < list.length; i++) {
con += '<tr>'+
'<td>' + list[i]['name']+'</td>' +
'<td>' + list[i]['path']+'</td>' +
'<td><a class="btlink overflow_hide" onclick="openPath(\''+list[i]['path']+'\')">' + list[i]['path']+'</a></td>' +
'<td>' + list[i]['comment']+'</td>' +
'<td>\
<a class="btlink" onclick="cmdReceive(\''+list[i]['name']+'\')">命令</a>\
<a class="btlink" onclick="cmdRecCmd(\''+list[i]['name']+'\')">命令</a>\
| <a class="btlink" onclick="cmdRecSecretKey(\''+list[i]['name']+'\')">密钥</a>\
| <a class="btlink" onclick="addReceive(\''+list[i]['name']+'\')">编辑</a>\
| <a class="btlink" onclick="delReceive(\''+list[i]['name']+'\')">删除</a></td>\
</tr>';
}
@ -76,61 +620,67 @@ function rsyncdReceive(){
});
}
function addReceive(){
var loadOpen = layer.open({
type: 1,
title: '创建接收',
area: '400px',
content:"<div class='bt-form pd20 pb70 c6'>\
<div class='line'>\
<span class='tname'>项目名</span>\
<div class='info-r c4'>\
<input id='name' class='bt-input-text' type='text' name='name' placeholder='项目名' style='width:200px' />\
</div>\
</div>\
<div class='line'>\
<span class='tname'>密钥</span>\
<div class='info-r c4'>\
<input id='MyPassword' class='bt-input-text' type='text' name='pwd' placeholder='密钥' style='width:200px' />\
<span title='随机密码' class='glyphicon glyphicon-repeat cursor' onclick='repeatPwd(16)'></span>\
</div>\
</div>\
<div class='line'>\
<span class='tname'>同步到</span>\
<div class='info-r c4'>\
<input id='inputPath' class='bt-input-text' type='text' name='path' placeholder='/' style='width:200px' />\
<span class='glyphicon glyphicon-folder-open cursor' onclick=\"changePath('inputPath')\"></span>\
</div>\
</div>\
<div class='line'>\
<span class='tname'>备注</span>\
<div class='info-r c4'>\
<input id='ps' class='bt-input-text' type='text' name='ps' placeholder='备注' style='width:200px' />\
</div>\
</div>\
<div class='bt-form-submit-btn'>\
<button type='button' id='add_ok' class='btn btn-success btn-sm btn-title bi-btn'>确认</button>\
</div>\
</div>",
success:function(layero, index){
repeatPwd(16);
function addReceive(name = ""){
rsPost('get_rec',{"name":name},function(rdata) {
var rdata = $.parseJSON(rdata.data);
var data = rdata.data;
var readonly = "";
if (name !=""){
readonly = 'readonly="readonly"'
}
});
$('#add_ok').click(function(){
_data = {};
_data['name'] = $('#name').val();
_data['pwd'] = $('#MyPassword').val();
_data['path'] = $('#inputPath').val();
_data['ps'] = $('#ps').val();
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
rsPost('add_rec', _data, function(data){
var rdata = $.parseJSON(data.data);
layer.close(loadOpen);
layer.msg(rdata.msg,{icon:rdata.status?1:2,time:2000,shade: [0.3, '#000']});
setTimeout(function(){rsyncdReceive();},2000);
var loadOpen = layer.open({
type: 1,
title: '创建接收',
area: '400px',
btn:['确认','取消'],
content:"<div class='bt-form pd20 c6'>\
<div class='line'>\
<span class='tname'>项目名</span>\
<div class='info-r c4'>\
<input id='name' value='"+data["name"]+"' class='bt-input-text' type='text' name='name' placeholder='项目名' style='width:200px' "+readonly+"/>\
</div>\
</div>\
<div class='line'>\
<span class='tname'>密钥</span>\
<div class='info-r c4'>\
<input id='MyPassword' value='"+data["pwd"]+"' class='bt-input-text' type='text' name='pwd' placeholder='密钥' style='width:200px'/>\
<span title='随机密码' class='glyphicon glyphicon-repeat cursor' onclick='repeatPwd(16)'></span>\
</div>\
</div>\
<div class='line'>\
<span class='tname'>同步到</span>\
<div class='info-r c4'>\
<input id='inputPath' value='"+data["path"]+"' class='bt-input-text' type='text' name='path' placeholder='/' style='width:200px'/>\
<span class='glyphicon glyphicon-folder-open cursor' onclick=\"changePath('inputPath')\"></span>\
</div>\
</div>\
<div class='line'>\
<span class='tname'>备注</span>\
<div class='info-r c4'>\
<input id='ps' class='bt-input-text' type='text' name='ps' value='"+data["comment"]+"' placeholder='备注' style='width:200px'/>\
</div>\
</div>\
</div>",
success:function(layero, index){},
yes:function(){
var args = {};
args['name'] = $('#name').val();
args['pwd'] = $('#MyPassword').val();
args['path'] = $('#inputPath').val();
args['ps'] = $('#ps').val();
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
rsPost('add_rec', args, function(data){
var rdata = $.parseJSON(data.data);
layer.close(loadOpen);
layer.msg(rdata.msg,{icon:rdata.status?1:2,time:2000,shade: [0.3, '#000']});
setTimeout(function(){rsyncdReceive();},2000);
});
}
});
});
})
}
@ -146,20 +696,34 @@ function delReceive(name){
});
}
function cmdReceive(name){
function cmdRecSecretKey(name){
var _data = {};
_data['name'] = name;
rsPost('cmd_rec', _data, function(data){
rsPost('cmd_rec_secret_key', _data, function(data){
var rdata = $.parseJSON(data.data);
layer.open({
type: 1,
title: '命令事例',
title: '接收密钥',
area: '400px',
content:"<div class='bt-form pd20 pb70 c6'>"+rdata.data+"</div>"
content:"<div class='bt-form pd20 pb70 c6'><textarea class='form-control' rows='6' readonly='readonly'>"+rdata.data+"</textarea></div>"
});
});
}
function cmdRecCmd(name){
var _data = {};
_data['name'] = name;
rsPost('cmd_rec_cmd', _data, function(data){
var rdata = $.parseJSON(data.data);
layer.open({
type: 1,
title: '接收命令例子',
area: '400px',
content:"<div class='bt-form pd20 pb70 c6'>"+rdata.data+"</div>"
});
});
}
function rsRead(){
var readme = '<ul class="help-info-text c7">';

@ -0,0 +1,149 @@
# coding:utf-8
import sys
import io
import os
import time
import json
sys.path.append(os.getcwd() + "/class/core")
import mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'rsyncd'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getTaskConf():
conf = getServerDir() + "/task_config.json"
return conf
def getConfigData():
try:
return json.loads(mw.readFile(getTaskConf()))
except:
pass
return []
def getConfigTpl():
tpl = {
"name": "",
"task_id": -1,
}
return tpl
def createBgTask(data):
removeBgTask()
for d in data:
if d['realtime'] == "false":
createBgTaskByName(d['name'], d)
def createBgTaskByName(name, args):
cfg = getConfigTpl()
_name = "[勿删]同步插件定时任务[" + name + "]"
res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
if res:
return True
if "task_id" in cfg.keys() and cfg["task_id"] > 0:
res = mw.M("crontab").field("id, name").where(
"id=?", (cfg["task_id"],)).find()
if res and res["id"] == cfg["task_id"]:
print("计划任务已经存在!")
return True
import crontab_api
api = crontab_api.crontab_api()
period = args['period']
_hour = ''
_minute = ''
_where1 = ''
_type_day = "day"
if period == 'day':
_type_day = 'day'
_hour = args['hour']
_minute = args['minute']
elif period == 'minute-n':
_type_day = 'minute-n'
_where1 = args['minute-n']
_minute = ''
cmd = '''
rname=%s
plugin_path=%s
logs_file=$plugin_path/send/${rname}/run.log
''' % (name, getServerDir())
cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT" >> $logs_file' + "\n"
cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n"
cmd += 'bash $plugin_path/send/${rname}/cmd >> $logs_file 2>&1' + "\n"
cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n"
cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n"
params = {
'name': _name,
'type': _type_day,
'week': "",
'where1': _where1,
'hour': _hour,
'minute': _minute,
'save': "",
'backup_to': "",
'stype': "toShell",
'sname': '',
'sbody': cmd,
'urladdress': '',
}
task_id = api.add(params)
if task_id > 0:
cfg["task_id"] = task_id
cfg["name"] = name
_dd = getConfigData()
_dd.append(cfg)
mw.writeFile(getTaskConf(), json.dumps(_dd))
def removeBgTask():
cfg_list = getConfigData()
for x in range(len(cfg_list)):
cfg = cfg_list[x]
if "task_id" in cfg.keys() and cfg["task_id"] > 0:
res = mw.M("crontab").field("id, name").where(
"id=?", (cfg["task_id"],)).find()
if res and res["id"] == cfg["task_id"]:
import crontab_api
api = crontab_api.crontab_api()
data = api.delete(cfg["task_id"])
if data[0]:
cfg["task_id"] = -1
cfg_list[x] = cfg
mw.writeFile(getTaskConf(), '[]')
return True
return False
if __name__ == "__main__":
if len(sys.argv) > 1:
action = sys.argv[1]
if action == "remove":
removeBgTask()
elif action == "add":
createBgTask()

@ -988,29 +988,32 @@ def getIpStatList():
if ip == "127.0.0.1":
clist[i]['area'] = "本地"
elif geoip_exists:
response = reader.city(ip)
country = response.country.names["zh-CN"]
# print(ip, response.subdivisions)
_subdivisions = response.subdivisions
try:
if len(_subdivisions) < 1:
response = reader.city(ip)
country = response.country.names["zh-CN"]
# print(ip, response.subdivisions)
_subdivisions = response.subdivisions
try:
if len(_subdivisions) < 1:
subdivisions = ""
else:
subdivisions = "," + response.subdivisions.most_specific.names[
"zh-CN"]
except Exception as e:
subdivisions = ""
else:
subdivisions = "," + response.subdivisions.most_specific.names[
"zh-CN"]
except Exception as e:
subdivisions = ""
try:
if 'zh-CN' in response.city.names:
city = "," + response.city.names["zh-CN"]
else:
city = "," + response.city.names["en"]
except Exception as e:
city = ""
try:
if 'zh-CN' in response.city.names:
city = "," + response.city.names["zh-CN"]
else:
city = "," + response.city.names["en"]
except Exception as e:
city = ""
clist[i]['area'] = country + subdivisions + city
clist[i]['area'] = country + subdivisions + city
except Exception as e:
clist[i]['area'] = "内网?"
return mw.returnJson(True, 'ok', clist)

@ -1996,8 +1996,8 @@ function wsTableErrorLogRequest(page){
list += '<td><span class="overflow_hide" style="width:100px;">' + data[i]['ip'] +'</span></td>';
list += '<td>' + toSize(data[i]['body_length']) +'</td>';
list += '<td>' + toSecond(data[i]['request_time']) +'</td>';
list += '<td><span class="overflow_hide" style="width:150px;">' + data[i]['uri'] +'</span></td>';
list += '<td>' + data[i]['status_code']+'/' + data[i]['method'] +'</td>';
list += '<td><span class="overflow_hide" style="width:130px;">' + data[i]['uri'] +'</span></td>';
list += '<td><span class="overflow_hide" style="width:60px;">' + data[i]['status_code']+'/' + data[i]['method'] +'</span></td>';
list += '<td><a data-id="'+i+'" href="javascript:;" class="btlink details" title="详情">详情</a></td>';
list += '</tr>';
}
@ -2205,8 +2205,8 @@ function wsTableLogRequest(page){
list += '<td><span class="overflow_hide" style="width:100px;">' + data[i]['ip'] +'</span></td>';
list += '<td>' + toSize(data[i]['body_length']) +'</td>';
list += '<td>' + toSecond(data[i]['request_time']) +'</td>';
list += '<td><span class="overflow_hide" style="width:150px;">' + data[i]['uri'] +'</span></td>';
list += '<td>' + data[i]['status_code']+'/' + data[i]['method'] +'</td>';
list += '<td><span class="overflow_hide" style="width:130px;">' + data[i]['uri'] +'</span></td>';
list += '<td><span class="overflow_hide" style="width:60px;">' + data[i]['status_code']+'/' + data[i]['method'] +'</span></td>';
list += '<td><a data-id="'+i+'" href="javascript:;" class="btlink details" title="详情">详情</a></td>';
list += '</tr>';
}

@ -174,6 +174,12 @@ log_by_lua_block {
return count
end
local function split_bylog(str,reps )
local resultStrList = {}
string.gsub(str,'[^'..reps..']+',function(w) table.insert(resultStrList,w) end)
return resultStrList
end
local function is_ipaddr_bylog(client_ip)
local cipn = split_bylog(client_ip,'.')
if arrlen_bylog(cipn) < 4 then return false end
@ -186,12 +192,6 @@ log_by_lua_block {
return true
end
local function split_bylog(str,reps )
local resultStrList = {}
string.gsub(str,'[^'..reps..']+',function(w) table.insert(resultStrList,w) end)
return resultStrList
end
local function get_client_ip_bylog()
local client_ip = "unknown"
local cdn = auto_config['cdn']

@ -66,7 +66,9 @@ def pSqliteDb(dbname='web_logs', site_name='unset', fn="logs"):
conn.execute("PRAGMA page_size = 4096", ())
conn.execute("PRAGMA journal_mode = wal", ())
conn.text_factory = lambda x: str(x, encoding="utf-8", errors='ignore')
conn.autoTextFactory()
# conn.text_factory = lambda x: str(x, encoding="utf-8", errors='ignore')
# conn.text_factory = lambda x: unicode(x, "utf-8", "ignore")
return conn

@ -61,7 +61,8 @@ def createBgTask():
import crontab_api
api = crontab_api.crontab_api()
cmd = "nice -n 10 python3 " + getPluginDir() + "/tool_task.py execute"
cmd = "cd " + mw.getServerDir() + "/mdserver-web && nice -n 10 python3 " + \
getPluginDir() + "/tool_task.py execute"
params = {
'name': name,
'type': 'day',

@ -6,7 +6,7 @@ server
root {$SERVER_PATH}/xhprof/xhprof_html;
#error_page 404 /404.html;
include enable-php-{$PHP_VER}.conf;
include {$SERVER_PATH}/web_conf/php/conf/enable-php-{$PHP_VER}.conf;
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{

@ -2031,6 +2031,50 @@ function pluginRollingLogs(_name, version, func, _args, line){
},1000);
},'json');
}
function pluginStandAloneLogs(_name, version, func, _args, line){
if ( typeof(version) == 'undefined' ){
version = '';
}
var func_name = 'error_log';
if ( typeof(func) != 'undefined' ){
func_name = func;
}
var file_line = 100;
if ( typeof(line) != 'undefined' ){
file_line = line;
}
layer.open({
type: 1,
title: _name + '日志',
area: '640px',
content:'<div class="change-default pd20" id="plugins_stand_alone_logs">\
<textarea readonly="readonly" style="margin: 0px;width: 100%;height: 360px;background-color: #333;color:#fff; padding:0 5px"></textarea>\
</div>'
});
$.post('/plugins/run', {name:_name, func:func_name, version:version,args:_args},function (data) {
var fileName = data.data;
$.post('/files/get_last_body', 'path=' + fileName+'&line='+file_line, function(rdata) {
if (!rdata.status){
return;
}
if(rdata.data == '') {
rdata.data = '当前没有日志!';
}
var ebody = '<textarea readonly="" style="margin: 0px;width: 100%;height: 360px;background-color: #333;color:#fff; padding:0 5px">'+rdata.data+'</textarea>';
$("#plugins_stand_alone_logs").html(ebody);
var ob = document.getElementById('plugins_stand_alone_logs');
ob.scrollTop = ob.scrollHeight;
},'json');
},'json');
}
/*** 其中功能,针对插件通过库使用 end ***/

@ -26,6 +26,7 @@
</div>
</div>
<!--
<div class="ss-text pull-left mr50">
<em class="btlink" style="color: #20a53a;" onclick="getPanelSSL()" title="点击自定义面板证书">面板SSL</em>
<div class='ssh-item'>
@ -33,6 +34,7 @@
<label class='btswitch-btn' for='panelSSL' onclick="setPanelSSL()"></label>
</div>
</div>
-->
</div>
</div>
<div class="setbox bgw mtb15">
@ -60,11 +62,14 @@
<input id="admin_path" name="admin_path" class="inputtxt bt-input-text disable" type="text" value="{{data['admin_path']}}"><span class="modify btn btn-xs btn-success" onclick="modifyAuthPath();">修改</span>
<span class="set-info c7">面板管理入口,设置后只能通过指定安全入口登录面板,如: /abc</span>
</p>
<!--
<p class="mtb15">
<span class="set-tit text-right">域名</span>
<input name="domain" class="inputtxt bt-input-text" type="text" value="">
<span class="set-info c7">为面板绑定一个访问域名;注意:一旦绑定域名,只能通过域名访问面板!</span>
</p>
-->
<p class="mtb15">
<span class="set-tit text-right" title="默认建站目录">默认建站目录</span>

@ -5,7 +5,7 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="renderer" content="webkit">
<title>mdserver-web|linux面板</title>
<title>{{data['title']}}</title>
<link rel="shortcut icon" href="/static/favicon.ico" type="image/x-icon" />
<link href="/static/bootstrap-3.3.5/css/bootstrap.min.css?v={{config.version}}" rel="stylesheet">
<link href="/static/css/site.css?v={{config.version}}" rel="stylesheet">

@ -115,7 +115,7 @@ pip3 install gevent-websocket==0.10.1
pip3 install flask-caching==1.10.1
pip3 install flask-session==0.3.2
pip3 install flask-socketio==5.2.0
# pip3 install mysqlclient
pip3 install mysqlclient
if [ ! -f /www/server/mdserver-web/bin/activate ];then
@ -133,5 +133,5 @@ pip3 install gevent-websocket==0.10.1
pip3 install flask-caching==1.10.1
pip3 install flask-session==0.3.2
pip3 install flask-socketio==5.2.0
# pip3 install mysqlclient
pip3 install mysqlclient

Loading…
Cancel
Save