diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index b4691d745..f98b21bb5 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -12,4 +12,7 @@ from .site import * from .site_types import * from .site_default import * from .php import * -from .logs import * \ No newline at end of file +from .logs import * +from .dir import * +from .redirect import * +from .ssl import * \ No newline at end of file diff --git a/web/admin/site/dir.py b/web/admin/site/dir.py new file mode 100644 index 000000000..19c153f06 --- /dev/null +++ b/web/admin/site/dir.py @@ -0,0 +1,83 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import utils.site as site +import core.mw as mw +import thisdb + +from .site import blueprint + +# 获取网站目录 +@blueprint.route('/get_dir_user_ini', endpoint='get_dir_user_ini',methods=['POST']) +@panel_login_required +def get_dir_user_ini(): + site_id = request.form.get('id', '') + return MwSites.instance().getDirUserIni(site_id) + +# 设置防跨站攻击 +@blueprint.route('/set_dir_user_ini', endpoint='set_dir_user_ini',methods=['POST']) +@panel_login_required +def set_dir_user_ini(): + path = request.form.get('path', '') + run_path = request.form.get('run_path', '') + return MwSites.instance().setDirUserIni(path,run_path) + +# 获取子目录绑定 +@blueprint.route('/get_dir_binding', endpoint='get_dir_binding',methods=['POST']) +@panel_login_required +def get_dir_binding(): + site_id = request.form.get('id', '') + return MwSites.instance().getDirBinding(site_id) + + +# 添加子目录绑定 +@blueprint.route('/add_dir_bind', endpoint='add_dir_bind',methods=['POST']) +@panel_login_required +def add_dir_bind(): + site_id = request.form.get('id', '') + domain = request.form.get('domain', '') + dir_name = request.form.get('dir_name', '') + return MwSites.instance().addDirBind(site_id,domain,dir_name) + + +# 获取目录绑定rewrite +@blueprint.route('/get_dir_bind_rewrite', endpoint='get_dir_bind_rewrite',methods=['POST']) +@panel_login_required +def get_dir_bind_rewrite(): + binding_id = request.form.get('id', '') + add = request.form.get('add', '') + return MwSites.instance().getDirBindingRewrite(binding_id,add) + + +# 获取目录绑定rewrite +@blueprint.route('/del_dir_bind', endpoint='del_dir_bind',methods=['POST']) +@panel_login_required +def del_dir_bind(): + binding_id = request.form.get('id', '') + return MwSites.instance().delDirBinding(binding_id) + + + + + + + + + diff --git a/web/admin/site/redirect.py b/web/admin/site/redirect.py new file mode 100644 index 000000000..c74983ed4 --- /dev/null +++ b/web/admin/site/redirect.py @@ -0,0 +1,58 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import utils.site as site +import core.mw as mw +import thisdb + +from .site import blueprint + +# 获取重定向列表 +@blueprint.route('/get_redirect', endpoint='get_redirect', methods=['POST']) +@panel_login_required +def get_redirect(): + site_name = request.form.get("siteName", '') + return MwSites.instance().getRedirect(site_name) + +# 设置重定向列表 +@blueprint.route('/set_redirect', endpoint='set_redirect', methods=['POST']) +@panel_login_required +def set_redirect(): + site_name = request.form.get("siteName", '') + site_from = request.form.get("from", '') + to = request.form.get("to", '') # redirect to + type = request.form.get("type", '') # path / domain + r_type = request.form.get("r_type", '') # redirect type + keep_path = request.form.get("keep_path", '') # keep path + return MwSites.instance().setRedirect(site_name, site_from, to, type, r_type, keep_path) + +# 设置重定向配置 +@blueprint.route('/get_redirect_conf', endpoint='get_redirect_conf', methods=['POST']) +@panel_login_required +def get_redirect_conf(): + site_name = request.form.get("siteName", '') + rid = request.form.get("id", '') + return MwSites.instance().getRedirectConf(site_name, rid) + + + + + + diff --git a/web/admin/site/site.py b/web/admin/site/site.py index c3e0972e6..6cbde59e0 100644 --- a/web/admin/site/site.py +++ b/web/admin/site/site.py @@ -212,21 +212,6 @@ def get_php_version(): tplname = request.form.get('tplname', '') return MwSites.instance().getRewriteTpl(tplname) -# 获取网站目录 -@blueprint.route('/get_dir_user_ini', endpoint='get_dir_user_ini',methods=['POST']) -@panel_login_required -def get_dir_user_ini(): - site_id = request.form.get('id', '') - return MwSites.instance().getDirUserIni(site_id) - -# 设置防跨站攻击 -@blueprint.route('/set_dir_user_ini', endpoint='set_dir_user_ini',methods=['POST']) -@panel_login_required -def set_dir_user_ini(): - path = request.form.get('path', '') - run_path = request.form.get('run_path', '') - return MwSites.instance().setDirUserIni(path,run_path) - # 网站日志开关 @blueprint.route('/logs_open', endpoint='logs_open',methods=['POST']) @panel_login_required @@ -252,8 +237,6 @@ def set_site_run_path(): return MwSites.instance().setSiteRunPath(site_id, run_path) - - # 设置网站 - 开启密码访问 @blueprint.route('/set_has_pwd', endpoint='set_has_pwd',methods=['POST']) @panel_login_required diff --git a/web/admin/site/ssl.py b/web/admin/site/ssl.py new file mode 100644 index 000000000..7c9b0ad82 --- /dev/null +++ b/web/admin/site/ssl.py @@ -0,0 +1,33 @@ +# coding:utf-8 + +# --------------------------------------------------------------------------------- +# MW-Linux面板 +# --------------------------------------------------------------------------------- +# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved. +# --------------------------------------------------------------------------------- +# Author: midoks +# --------------------------------------------------------------------------------- + +import os +import json + +from flask import Blueprint, render_template +from flask import request + +from admin.user_login_check import panel_login_required + +from utils.plugin import plugin as MwPlugin +from utils.site import sites as MwSites +import utils.site as site +import core.mw as mw +import thisdb + +from .site import blueprint + + + + + + + + diff --git a/web/core/db.py b/web/core/db.py index 0d63b222b..2cd72efa8 100755 --- a/web/core/db.py +++ b/web/core/db.py @@ -191,7 +191,8 @@ class Sql(): self.__close() return data except Exception as ex: - return "error: " + str(ex) + # return "error: " + str(ex) + return [] def inquiry(self, input_field=''): # 查询数据集 diff --git a/web/static/app/site.js b/web/static/app/site.js index 63ed78bb5..798e536d1 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -1347,13 +1347,13 @@ function showRewrite(rdata){ //添加子目录绑定 function addDirBinding(id){ var domain = $("input[name='domain']").val(); - var dirName = $("select[name='dirName']").val(); - if(domain == '' || dirName == '' || dirName == null){ + var dir_name = $("select[name='dirName']").val(); + if(domain == '' || dir_name == '' || dir_name == null){ layer.msg(lan.site.d_s_empty,{icon:2}); return; } - var data = 'id='+id+'&domain='+domain+'&dirName='+dirName; + var data = 'id='+id+'&domain='+domain+'&dir_name='+dir_name; $.post('/site/add_dir_bind',data,function(rdata){ dirBinding(id); layer.msg(rdata.msg,{icon:rdata.status?1:2}); @@ -1421,6 +1421,7 @@ function to301(siteName, type, obj){ $('.btn-colse-prosy').click(function() { layer.close(redirect_form); }); + $('.btn-submit-redirect').click(function() { var keep_path = $('[name="keep_path"]').prop('checked') ? 1 : 0; var r_type = $('[name="r_type"]').val(); @@ -1428,24 +1429,14 @@ function to301(siteName, type, obj){ var from = $('[name="from"]').val(); var to = $('[name="to"]').val(); - $.post('/site/set_redirect', { - siteName: siteName, - type: type, - r_type: r_type, - from: from, - to: to, - keep_path: keep_path - }, function(res) { - res = JSON.parse(res); - if (res.status) { + $.post('/site/set_redirect', {siteName: siteName,type: type,r_type: r_type,from: from,to: to,keep_path: keep_path}, function(data) { + if (data.status) { layer.close(redirect_form); - to301(siteName) + to301(siteName); } else { - layer.msg(res.msg, { - icon: 2 - }); + layer.msg(data.msg, {icon: 2}); } - }); + },'json'); }); }, 100); } @@ -1455,14 +1446,13 @@ function to301(siteName, type, obj){ siteName: siteName, id: obj, }, function(res) { - res = JSON.parse(res); if (res.status == true) { layer.msg('删除成功', {time: 1000,icon: 1}); to301(siteName); } else { layer.msg(res.msg, {time: 1000,icon: 2}); } - }); + },'json'); return } @@ -1471,7 +1461,6 @@ function to301(siteName, type, obj){ var data = {siteName: siteName,id: obj}; $.post('/site/get_redirect_conf', data, function(res) { layer.close(laoding); - res = JSON.parse(res); if (res.status == true) { var mBody = "
\ \ @@ -2326,7 +2315,7 @@ function opSSL(type, id, siteName, callback){ var key = ''; var csr = ''; var loadT = layer.msg('正在提交任务...',{icon:16,time:0,shade: [0.3, '#000']}); - $.post('site/get_ssl','site_name='+siteName,function(data){ + $.post('/site/get_ssl','site_name='+siteName,function(data){ layer.close(loadT); var rdata = data['data']; diff --git a/web/thisdb/binding.py b/web/thisdb/binding.py index ea968812d..689989530 100644 --- a/web/thisdb/binding.py +++ b/web/thisdb/binding.py @@ -14,6 +14,10 @@ import core.mw as mw __FIELD = 'id,pid,domain,port,path,add_time' +def getBindingCountByDomain(name): + # .debug(True) + return mw.M('binding').where("domain=?", (name,)).count() + def addBinding(pid, domain, port, path): now_time = mw.getDateFromNow() insert_data = { @@ -25,15 +29,17 @@ def addBinding(pid, domain, port, path): } return mw.M('binding').insert(insert_data) - def getBindingListBySiteId(site_id): - task_list = mw.M('binding').where('pid=?', (site_id,)).field(__FIELD).select() - return task_list + # .debug(True) + binding_list = mw.M('binding').field(__FIELD).where('pid=?', (site_id,)).select() + return binding_list +def getBindingById(site_id): + return mw.M('binding').where("id=?", (site_id,)).field(__FIELD).find() -def deleteBindingId(domain_id): - return mw.M('binding').where("id=?", (domain_id,)).delete() +def deleteBindingById(binding_id): + return mw.M('binding').where("id=?", (binding_id,)).delete() def deleteBindingBySiteId(site_id): return mw.M('binding').where("pid=?", (site_id,)).delete() diff --git a/web/thisdb/domain.py b/web/thisdb/domain.py index fb163a624..bdf3e47d2 100644 --- a/web/thisdb/domain.py +++ b/web/thisdb/domain.py @@ -12,6 +12,10 @@ import core.mw as mw __FIELD = 'id,pid,name,port,add_time' +def getDomainCountByName(name): + # .debug(True) + return mw.M('domain').where("name=?", (name,)).count() + def addDomain(pid, name, port): now_time = mw.getDateFromNow() insert_data = { @@ -25,7 +29,7 @@ def addDomain(pid, name, port): def getDomainByPid(pid): # .debug(True) return mw.M('domain').field(__FIELD).where("pid=?", (pid,)).select() - + def deleteDomainId(domain_id): return mw.M('domain').where("id=?", (domain_id,)).delete() diff --git a/web/utils/site.py b/web/utils/site.py index d6785d6aa..807b03993 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -124,8 +124,8 @@ class sites(object): def getProxyPath(self, siteName): return "{}/{}".format(self.proxyPath, siteName) - def getDirBindRewrite(self, siteName, dirname): - return self.rewritePath + '/' + siteName + '_' + dirname + '.conf' + def getDirBindRewrite(self, site_name, dir_name): + return self.rewritePath + '/' + site_name + '_' + dir_name + '.conf' def getIndexConf(self): return mw.getServerDir() + '/openresty/nginx/conf/nginx.conf' @@ -256,8 +256,7 @@ class sites(object): def nginxAddConf(self): source_tpl = mw.getPanelDir() + '/data/tpl/nginx.conf' - print(source_tpl) - vhost_file = self.vhostPath + '/' + self.siteName + '.conf' + vhost_file = self.getHostConf(self.siteName) content = mw.readFile(source_tpl) content = content.replace('{$PORT}', self.sitePort) @@ -463,6 +462,154 @@ class sites(object): self.addDirUserIni(site_path, run_path) return mw.returnData(True, '已打开防跨站设置!') + + def getDirBinding(self, site_id): + info = thisdb.getSitesById(site_id) + path = info['path'] + if not os.path.exists(path): + checks = ['/', '/usr', '/etc'] + if path in checks: + data = {} + data['dirs'] = [] + data['binding'] = [] + return mw.returnData(True, 'OK', data) + os.system('mkdir -p ' + path) + os.system('chmod 755 ' + path) + os.system('chown www:www ' + path) + siteName = info['name'] + mw.writeLog('网站管理', '站点[' + siteName + '],根目录[' + path + ']不存在,已重新创建!') + + dirnames = [] + for filename in os.listdir(path): + try: + filePath = path + '/' + filename + if os.path.islink(filePath): + continue + if os.path.isdir(filePath): + dirnames.append(filename) + except: + pass + + data = {} + data['dirs'] = dirnames + data['binding'] = thisdb.getBindingListBySiteId(site_id) + return mw.returnJson(True, 'OK', data) + + def addDirBind(self, site_id, domain, dir_name): + domain_split = domain.split(':') + domain = domain_split[0] + port = '80' + if len(domain_split) > 1: + port = domain_split[1] + if dir_name == '': + mw.returnData(False, '目录不能为空!') + + reg = r"^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$" + if not re.match(reg, domain): + return mw.returnData(False, '主域名格式不正确!') + + info = thisdb.getSitesById(site_id) + webdir = info['path'] + '/' + dir_name + + if thisdb.getBindingCountByDomain(domain): + return mw.returnData(False, '您添加的域名在子目录已存在!') + + if thisdb.getDomainCountByName(domain) > 0: + return mw.returnData(False, '您添加的域名已存在!') + + filename = self.getHostConf(info['name']) + conf = mw.readFile(filename) + if conf: + rep = r"enable-php-([0-9]{2,3})\.conf" + domain_split = re.search(rep, conf).groups() + version = domain_split[0] + + source_dirbind_tpl = mw.getPanelDir() + '/data/tpl/nginx_dirbind.conf' + content = mw.readFile(source_dirbind_tpl) + content = content.replace('{$PORT}', port) + content = content.replace('{$PHPVER}', version) + content = content.replace('{$DIRBIND}', domain) + content = content.replace('{$ROOT_DIR}', webdir) + content = content.replace('{$SERVER_MAIN}', info['name']) + content = content.replace('{$OR_REWRITE}', self.rewritePath) + content = content.replace('{$PHP_DIR}', self.setupPath + '/php') + content = content.replace('{$LOGPATH}', mw.getLogsDir()) + + conf += "\r\n" + content + mw.backFile(filename) + mw.writeFile(filename, conf) + conf = mw.readFile(filename) + + # 检查配置是否有误 + isError = mw.checkWebConfig() + if isError != True: + mw.restoreFile(filename) + msg = 'ERROR:
' + isError.replace("\n", '
') + '
' + return mw.returnData(False, msg) + + thisdb.addBinding(site_id,domain,port,dir_name) + msg = mw.getInfo('网站[{1}]子目录[{2}]绑定到[{3}]',(info['name'], dir_name, domain)) + mw.writeLog('网站管理', msg) + mw.restartWeb() + mw.removeBackFile(filename) + return mw.returnData(True, '添加成功!') + + # 取子目录Rewrite + def getDirBindingRewrite(self, binding_id, add): + binding_info = thisdb.getBindingById(binding_id) + info = thisdb.getSitesById(binding_info['pid']) + + filename = self.getDirBindRewrite(info['name'], binding_info['path']) + if add == '1': + mw.writeFile(filename, '') + file = self.getHostConf(info['name']) + conf = mw.readFile(file) + domain = binding_info['domain'] + rep = "\n#BINDING-" + domain + "-START(.|\n)+BINDING-" + domain + "-END" + tmp = re.search(rep, conf).group() + dirConf = tmp.replace('rewrite/' + info['name'] + '.conf;', 'rewrite/' + info['name'] + '_' + binding_info['path'] + '.conf;') + conf = conf.replace(tmp, dirConf) + mw.writeFile(file, conf) + data = {} + data['rewrite_dir'] = self.rewritePath + data['status'] = False + if os.path.exists(filename): + data['status'] = True + data['data'] = mw.readFile(filename) + data['rlist'] = [] + for ds in os.listdir(self.rewritePath): + if ds[0:1] == '.': + continue + if ds == 'list.txt': + continue + data['rlist'].append(ds[0:len(ds) - 5]) + data['filename'] = filename + return data + + def delDirBinding(self, binding_id): + + binding_info = thisdb.getBindingById(binding_id) + info = thisdb.getSitesById(binding_info['pid']) + + filename = self.getHostConf(info['name']) + conf = mw.readFile(filename) + if conf: + rep = r"\s*.+BINDING-" + binding_info['domain'] + "-START(.|\n)+BINDING-" + binding_info['domain'] + "-END" + conf = re.sub(rep, '', conf) + mw.writeFile(filename, conf) + + filename = self.getDirBindRewrite(info['name'], binding_info['path']) + if os.path.exists(filename): + os.remove(filename) + + msg = mw.getInfo('删除网站[{1}]子目录[{2}]绑定',(info['name'], binding_info['path'])) + mw.writeLog('网站管理', msg) + mw.restartWeb() + + thisdb.deleteBindingById(binding_id) + return mw.returnJson(True, '删除成功!') + + def logsOpen(self, site_id): info = thisdb.getSitesById(site_id) name = info['name'] @@ -782,6 +929,116 @@ class sites(object): mw.writeLog('网站管理', '网站[{1}]流量限制已关闭!', (siteName,)) return mw.returnData(True, '已关闭流量限制!') + + # 获取重定向配置 + def getRedirect(self, siteName): + redirect_file = self.getRedirectDataPath(siteName) + if not os.path.exists(redirect_file): + mw.execShell("mkdir {}/{}".format(self.redirectPath, siteName)) + return mw.returnData(True, "no exists!", {"result": [], "count": 0}) + + content = mw.readFile(redirect_file) + data = json.loads(content) + # 处理301信息 + return mw.returnData(True, "ok", {"result": data, "count": len(data)}) + + # 操作 重定向配置 + def operateRedirectConf(self, siteName, method='start'): + vhost_file = self.getHostConf(siteName) + content = mw.readFile(vhost_file) + + cnf_301 = '''#301-START + include %s/*.conf; + #301-END''' % (self.getRedirectPath( siteName)) + + cnf_301_source = '#301-START' + # print('operateRedirectConf', content.find('#301-END')) + if content.find('#301-END') != -1: + if method == 'stop': + rep = '#301-START(\n|.){1,500}#301-END' + content = re.sub(rep, '#301-START', content) + else: + if method == 'start': + content = re.sub(cnf_301_source, cnf_301, content) + + mw.writeFile(vhost_file, content) + + # get redirect status + def setRedirect(self, siteName, site_from, to, type, r_type, keep_path): + if siteName == '' or site_from == '' or to == '' or type == '' or r_type == '': + return mw.returnData(False, "必填项不能为空!") + + redirect_file = self.getRedirectDataPath(siteName) + content = mw.readFile(redirect_file) if os.path.exists(redirect_file) else "" + data = json.loads(content) if content != "" else [] + + _r_type = 0 if r_type == "301" else 1 + _type_code = 0 if type == "path" else 1 + _keep_path = 1 if keep_path == "1" else 0 + + # check if domain exists in site + if _type_code == 1: + pid = mw.M('domain').where("name=?", (_siteName,)).field('id,pid,name,port,addtime').select() + site_domain_lists = mw.M('domain').where("pid=?", (pid[0]['pid'],)).field('name').select() + found = False + for item in site_domain_lists: + if item['name'] == _from: + found = True + break + if found == False: + return mw.returnData(False, "域名不存在!") + + file_content = "" + # path + if _type_code == 0: + redirect_type = "permanent" if _r_type == 0 else "redirect" + if not site_from.startswith("/"): + site_from = "/{}".format(site_from) + if _keep_path == 1: + to = "{}$1".format(to) + site_from = "{}(.*)".format(site_from) + file_content = "rewrite ^{} {} {};".format(site_from, to, redirect_type) + # domain + else: + if _keep_path == 1: + _to = "{}$request_uri".format(_to) + + redirect_type = "301" if _type_code == 0 else "302" + _if = "if ($host ~ '^{}')".format(site_from) + _return = "return {} {}; ".format(redirect_type, to) + file_content = _if + "{\r\n " + _return + "\r\n}" + + _id = mw.md5("{}+{}".format(file_content, siteName)) + + # 防止规则重复 + for item in data: + if item["r_from"] == site_from: + return mw.returnData(False, "重复的规则!") + + rep = r"http(s)?\:\/\/([a-zA-Z0-9][-a-zA-Z0-9]{0,62}\.)+([a-zA-Z0-9][a-zA-Z0-9]{0,62})+.?" + if not re.match(rep, to): + return mw.returnData(False, "错误的目标地址") + + # write data json file + data.append({"r_from": site_from, "type": _type_code, "r_type": _type_code,"r_to": to, 'keep_path': _keep_path, 'id': _id}) + mw.writeFile(redirect_file, json.dumps(data)) + mw.writeFile("{}/{}.conf".format(self.getRedirectPath(siteName), _id), file_content) + + self.operateRedirectConf(siteName, 'start') + mw.restartWeb() + return mw.returnData(True, "设置成功") + + def getRedirectConf(self,siteName, rid): + if rid == '' or siteName == '': + return mw.returnData(False, "必填项不能为空!") + + path = self.getRedirectPath(siteName) + conf = "{}/{}.conf".format(path, rid) + data = mw.readFile(conf) + if data == False: + return mw.returnData(False, "获取失败!") + return mw.returnData(True, "ok", {"result": data}) + def setPhpVersion(self, siteName, version): # nginx file = self.getHostConf(siteName)