diff --git a/web/admin/setup/option.py b/web/admin/setup/option.py index 65c7ccf7c..93a02551a 100644 --- a/web/admin/setup/option.py +++ b/web/admin/setup/option.py @@ -20,6 +20,9 @@ def init_option(): thisdb.setOption('recycle_bin', 'open') thisdb.setOption('template', 'default') + # SSL邮件地址 + thisdb.setOption('ssl_email', '') + # 后台面板是否关闭 thisdb.setOption('admin_close', 'no') diff --git a/web/admin/site/__init__.py b/web/admin/site/__init__.py index f7b5c7397..31844ce93 100644 --- a/web/admin/site/__init__.py +++ b/web/admin/site/__init__.py @@ -11,9 +11,12 @@ from .site import * from .site_types import * from .site_default import * + from .php import * from .logs import * from .dir import * from .redirect import * from .proxy import * -from .ssl import * \ No newline at end of file + +from .ssl import * +from .ssl_acme import * \ No newline at end of file diff --git a/web/admin/site/ssl.py b/web/admin/site/ssl.py index 10dd57579..724df7b1e 100644 --- a/web/admin/site/ssl.py +++ b/web/admin/site/ssl.py @@ -39,6 +39,13 @@ def get_cert_list(): return MwSites.instance().getCertList() +# 获取证书配置 +@blueprint.route('/get_dnsapi', endpoint='get_dnsapi', methods=['GET','POST']) +@panel_login_required +def get_dnsapi(): + return MwSites.instance().getDnsapi() + + diff --git a/web/admin/site/ssl_acme.py b/web/admin/site/ssl_acme.py new file mode 100644 index 000000000..98aa599fc --- /dev/null +++ b/web/admin/site/ssl_acme.py @@ -0,0 +1,52 @@ +# 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 core.mw as mw +import thisdb + +from .site import blueprint + + +# 获取ACME日志 +@blueprint.route('/get_acme_logs', endpoint='get_acme_logs', methods=['POST']) +@panel_login_required +def get_acme_logs(): + log_file = mw.getPanelDir() + '/logs/acme.log' + if not os.path.exists(log_file): + mw.execShell('touch ' + log_file) + return mw.returnData(True, 'OK', log_file) + + +@blueprint.route('/create_acme', endpoint='create_acme', methods=['POST']) +@panel_login_required +def create_acme(): + site_name = request.form.get('siteName', '') + domains = request.form.get('domains', '') + force = request.form.get('force', '') + renew = request.form.get('renew', '') + email = request.form.get('email', '') + return MwSites.instance().createAcme(site_name, domains,force,renew,email) + + + + + + + diff --git a/web/static/app/site.js b/web/static/app/site.js index 798e536d1..c44577a6b 100755 --- a/web/static/app/site.js +++ b/web/static/app/site.js @@ -2037,16 +2037,222 @@ function renewSSL(type,id,siteName){ } function fileCheck(){ - + $('#dnsapi_option').css('display','none'); } function dnsCheck(){ + $('#dnsapi_option').css('display','block'); +} + +function renderDnsapiHtml(data){ + console.log(data); + layer.open({ + type: 1, + area: '500px', + title: '设置'+data['title']+'接口', + closeBtn: 1, + shift: 5, + shadeClose: true, + btn:["确定","取消"], + content: "
\ +
\ + DNSAPI类型\ +
\ + \ +
\ +
\ +
\ + CF_Key\ +
\ + \ +
\ + CF_Email\ +
\ + \ +
\ +
\ +
\ +
\ +
    \ +
  • 使用【"+data['title']+"】的API接口自动解析申请SSL
  • \ +
\ +
\ +
\ +
", + success:function(){ + + }, + yes:function(index) { + + } + }); } -//SSL -function opSSL(type, id, siteName, callback){ +function renderDnsapi(){ + $.post('/site/get_dnsapi', {}, function(data){ + var dnsapi_option = ''; + for (var i = 0; i < data.length; i++) { + dnsapi_option+=''; + } + $('#dnsapi_option select').html(dnsapi_option); + + $('#dnsapi_option select').on('change',function(){ + var val = $(this).val(); + var index = $('#dnsapi_option option:selected').attr('index'); + if (val == 'none'){ + $('#dnsapi_option button').css('display','none'); + } else { + $('#dnsapi_option button').css('display','inline-block'); + } + renderDnsapiHtml(data[index]); + }); + + $('#dnsapi_set').on('click', function(){ + var index = $('#dnsapi_option option:selected').attr('index'); + renderDnsapiHtml(data[index]); + }); + },'json'); +} + +function opSSLAcme(type, id, siteName, callback){ + var acme = '
\ +
\ +
\ + 验证方式\ +
\ + \ + \ + \ + \ +
\ +
\ + \ +
\ +
\ + \ + \ +
\ +
\ +
\ +
\ + 邮箱\ + \ +
\ +
\ + 域名\ +
    \ +
    \ +
    \ + \ +
    \ + \ + \ +
    '; + + $(".tab-con").html(acme); + + renderDnsapi(); + + $.post('/site/get_ssl','site_name='+siteName+'&ssl_type=acme', function(data){ + var rdata = data['data']; + if(rdata.csr == false){ + $.post('/site/get_site_domains','id='+id, function(rdata) { + var data = rdata['data']; + var opt=''; + for(var i=0;i'+data.domains[i].name + +''; + } + } + $("input[name='admin_email']").val(data.email); + $("#ymlist").html(opt); + $("#ymlist li input").click(function(e){ + e.stopPropagation(); + }) + $("#ymlist li").click(function(){ + var o = $(this).find("input"); + if(o.prop("checked")){ + o.prop("checked",false) + } + else{ + o.prop("checked",true); + } + }) + $(".letsApply").click(function(){ + var c = $("#ymlist input[type='checkbox']"); + var str = []; + var domains = ''; + for(var i=0; i\ +
    证书(PEM格式)
    \ + \ +
    \ + \ + \ +
    \ + \ +
      \ +
    • 已为您自动生成ACME免费证书
    • \ +
    • 由ACME免费申请证书,有效期3个月,支持多域名。默认会自动续签
    • \ +
    • 如需使用其他SSL,请切换其他证书后粘贴您的KEY以及PEM内容,然后保存即可。
    • \ +
    '; + $(".tab-con").html(acme); + + if (rdata['cert_data']){ + var issuer = rdata['cert_data']['issuer'].split(" "); + var domains = rdata['cert_data']['dns'].join("、"); + + var cert_data = "
    \ +
    证书品牌:"+issuer[0]+"
    \ +
    到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
    \ +
    \ +
    \ +
    认证域名:"+domains+"
    \ +
    "; + $(".ssl_state_info").html(cert_data); + $(".ssl_state_info").css('display','block'); + } + },'json'); +} + +function opSSLNow(type, id, siteName, callback){ var now = '
    \ \
    \ @@ -2061,10 +2267,66 @@ function opSSL(type, id, siteName, callback){
  • 粘贴您的*.key以及*.pem内容,然后保存即可。
  • \
  • 如果浏览器提示证书链不完整,请检查是否正确拼接PEM证书
  • PEM格式证书 = 域名证书.crt + 根证书(root_bundle).crt
  • \
  • 在未指定SSL默认站点时,未开启SSL的站点使用HTTPS会直接访问到已开启SSL的站点
  • \ - '; + '; - // \ - // \ + $(".tab-con").html(now); + 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){ + layer.close(loadT); + var rdata = data['data']; + + if (rdata['cert_data']){ + var issuer = rdata['cert_data']['issuer'].split(" "); + var domains = rdata['cert_data']['dns'].join("、"); + + var cert_data = "
    \ +
    证书品牌:"+issuer[0]+"
    \ +
    到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
    \ +
    \ +
    \ +
    认证域名:"+domains+"
    \ +
    强制HTTPS:\ + \ +
    \ +
    "; + $(".ssl_state_info").html(cert_data); + $(".ssl_state_info").css('display','block'); + } + + if(rdata.key == false){ + rdata.key = ''; + } else { + $(".ssl-btn").append(''); + } + + if(rdata.csr == false){ + rdata.csr = ''; + } + $("#key").val(rdata.key); + $("#csr").val(rdata.csr); + + $("#toHttps").attr('checked',rdata.httpTohttps); + if(rdata.status){ + $('.warning_info').css('display','none'); + + $(".ssl-btn").append(""); + $('#now_ssl').html('当前证书 - [已部署SSL]'); + } else{ + $('.warning_info').css('display','block'); + $('#now_ssl').html('当前证书 - [未部署SSL]'); + } + + + if (typeof (callback) != 'undefined'){ + callback(rdata); + } + },'json'); +} + +function opSSLLet(type, id, siteName, callback){ var lets = '
    \
    \
    \ @@ -2102,274 +2364,96 @@ function opSSL(type, id, siteName, callback){ \
    '; - // \ - // \ - var acme = '
    \ -
    \ -
    \ -
    \ - 验证方式\ -
    \ - \ - \ -
    \ -
    \ -
    \ -
    \ -
    \ - \ - \ -
    \ -
    \ -
    \ -
    \ - 管理员邮箱\ - \ -
    \ -
    \ - 域名\ -
      \ -
      \ -
      \ - \ -
      \ -
        \ -
      • 申请之前,请确保域名已解析,如未解析会导致审核失败
      • \ -
      • 由ACME免费申请证书,有效期3个月,支持多域名。默认会自动续签
      • \ -
      • 若您的站点使用了CDN或301重定向会导致续签失败
      • \ -
      • 在未指定SSL默认站点时,未开启SSL的站点使用HTTPS会直接访问到已开启SSL的站点
      \ - \ -
      '; - - - switch(type){ - case 'lets': - $(".tab-con").html(lets); - $.post('/site/get_ssl', 'site_name='+siteName+'&ssl_type=lets', function(data){ - var rdata = data['data']; - if(rdata.csr == false){ - $.post('/site/get_site_domains','id='+id, function(rdata) { - var data = rdata['data']; - var opt=''; - for(var i=0;i'+data.domains[i].name+'' - } + $(".tab-con").html(lets); + $.post('/site/get_ssl', 'site_name='+siteName+'&ssl_type=lets', function(data){ + var rdata = data['data']; + if(rdata.csr == false){ + $.post('/site/get_site_domains','id='+id, function(rdata) { + var data = rdata['data']; + var opt=''; + for(var i=0;i'+data.domains[i].name+'' + } + } + $("input[name='admin_email']").val(data.email); + $("#ymlist").html(opt); + $("#ymlist li input").click(function(e){ + e.stopPropagation(); + }) + $("#ymlist li").click(function(){ + var o = $(this).find("input"); + if(o.prop("checked")){ + o.prop("checked",false) + } + else{ + o.prop("checked",true); + } + }) + $(".letsApply").click(function(){ + var c = $("#ymlist input[type='checkbox']"); + var str = []; + var domains = ''; + for(var i=0; i
      \ -
      证书(PEM格式)
      \ -
      \ -
      \ - \ - \ - \ -
      \ -
      \ -
        \ -
      • 已为您自动生成Let\'s Encrypt免费证书
      • \ -
      • 由Let\'s Encrypt免费申请证书,有效期3个月,支持多域名。默认会自动续签
      • \ -
      • 如需使用其他SSL,请切换其他证书后粘贴您的KEY以及PEM内容,然后保存即可。
      • \ -
      '; - $(".tab-con").html(lets); - - if (rdata['cert_data']){ - var issuer = rdata['cert_data']['issuer'].split(" "); - var domains = rdata['cert_data']['dns'].join("、"); - - var cert_data = "
      \ -
      证书品牌:"+issuer[0]+"
      \ -
      到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
      \ -
      \ -
      \ -
      认证域名:"+domains+"
      \ -
      "; - $(".ssl_state_info").html(cert_data); - $(".ssl_state_info").css('display','block'); - } - },'json'); - break; - case 'acme': - $(".tab-con").html(acme); - $.post('/site/get_ssl', 'site_name='+siteName+'&ssl_type=acme', function(data){ - var rdata = data['data']; - if(rdata.csr == false){ - $.post('/site/get_site_domains','id='+id, function(rdata) { - var data = rdata['data']; - var opt=''; - for(var i=0;i'+data.domains[i].name - +''; - } - } - $("input[name='admin_email']").val(data.email); - $("#ymlist").html(opt); - $("#ymlist li input").click(function(e){ - e.stopPropagation(); - }) - $("#ymlist li").click(function(){ - var o = $(this).find("input"); - if(o.prop("checked")){ - o.prop("checked",false) - } - else{ - o.prop("checked",true); - } - }) - $(".letsApply").click(function(){ - var c = $("#ymlist input[type='checkbox']"); - var str = []; - var domains = ''; - for(var i=0; i
      \ -
      证书(PEM格式)
      \ - \ -
      \ - \ - \ -
      \ - \ -
        \ -
      • 已为您自动生成ACME免费证书
      • \ -
      • 由ACME免费申请证书,有效期3个月,支持多域名。默认会自动续签
      • \ -
      • 如需使用其他SSL,请切换其他证书后粘贴您的KEY以及PEM内容,然后保存即可。
      • \ -
      '; - $(".tab-con").html(acme); - - if (rdata['cert_data']){ - var issuer = rdata['cert_data']['issuer'].split(" "); - var domains = rdata['cert_data']['dns'].join("、"); - - var cert_data = "
      \ -
      证书品牌:"+issuer[0]+"
      \ -
      到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
      \ -
      \ -
      \ -
      认证域名:"+domains+"
      \ -
      "; - $(".ssl_state_info").html(cert_data); - $(".ssl_state_info").css('display','block'); - } - },'json'); - break; - case 'now': - $(".tab-con").html(now); - 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){ - layer.close(loadT); - var rdata = data['data']; - - if (rdata['cert_data']){ - var issuer = rdata['cert_data']['issuer'].split(" "); - var domains = rdata['cert_data']['dns'].join("、"); - - var cert_data = "
      \ -
      证书品牌:"+issuer[0]+"
      \ -
      到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
      \ -
      \ -
      \ -
      认证域名:"+domains+"
      \ -
      强制HTTPS:\ - \ -
      \ -
      "; - $(".ssl_state_info").html(cert_data); - $(".ssl_state_info").css('display','block'); - } - - if(rdata.key == false){ - rdata.key = ''; - } else { - $(".ssl-btn").append(''); - } - - if(rdata.csr == false){ - rdata.csr = ''; - } - $("#key").val(rdata.key); - $("#csr").val(rdata.csr); - - $("#toHttps").attr('checked',rdata.httpTohttps); - if(rdata.status){ - $('.warning_info').css('display','none'); - - $(".ssl-btn").append(""); - $('#now_ssl').html('当前证书 - [已部署SSL]'); - } else{ - $('.warning_info').css('display','block'); - $('#now_ssl').html('当前证书 - [未部署SSL]'); - } - + } + domains = JSON.stringify(str); + newSSL(siteName, id, domains); + }); if (typeof (callback) != 'undefined'){ callback(rdata); } },'json'); - break; - default: - layer.msg("错误类型", {icon:5}); - break; + return; + } + var lets = '
      \ + \ +
      \ +
      密钥(KEY)
      \ +
      证书(PEM格式)
      \ +
      \ +
      \ + \ + \ + \ +
      \ +
      \ +
        \ +
      • 已为您自动生成Let\'s Encrypt免费证书
      • \ +
      • 由Let\'s Encrypt免费申请证书,有效期3个月,支持多域名。默认会自动续签
      • \ +
      • 如需使用其他SSL,请切换其他证书后粘贴您的KEY以及PEM内容,然后保存即可。
      • \ +
      '; + $(".tab-con").html(lets); + + if (rdata['cert_data']){ + var issuer = rdata['cert_data']['issuer'].split(" "); + var domains = rdata['cert_data']['dns'].join("、"); + + var cert_data = "
      \ +
      证书品牌:"+issuer[0]+"
      \ +
      到期时间:剩余"+rdata['cert_data']['endtime']+"天到期
      \ +
      \ +
      \ +
      认证域名:"+domains+"
      \ +
      "; + $(".ssl_state_info").html(cert_data); + $(".ssl_state_info").css('display','block'); + } + },'json'); +} + +//SSL +function opSSL(type, id, siteName, callback){ + switch(type){ + case 'lets':opSSLLet(type, id, siteName, callback);break; + case 'acme':opSSLAcme(type, id, siteName, callback);break; + case 'now':opSSLNow(type, id, siteName, callback);break; + default:layer.msg("错误类型", {icon:5});break; } } diff --git a/web/thisdb/sites.py b/web/thisdb/sites.py index 8ea9f7986..487b54897 100644 --- a/web/thisdb/sites.py +++ b/web/thisdb/sites.py @@ -10,6 +10,8 @@ import core.mw as mw +from .option import getOption + __FIELD = 'id,name,path,status,ps,edate,type_id,add_time,update_time' def getSitesCount(): @@ -32,7 +34,7 @@ def getSitesDomainById(site_id): t['id'] = b['id'] domains.append(t) data['domains'] = domains - data['email'] = mw.M('users').getField('email') + data['email'] = getOption('ssl_email', default='') return data def addSites(name, path): diff --git a/web/utils/site.py b/web/utils/site.py index 3c860e4e4..dd865fe7c 100644 --- a/web/utils/site.py +++ b/web/utils/site.py @@ -137,6 +137,18 @@ class sites(object): return path[0:-1] return path + def getSitePath(self, siteName): + file = self.getHostConf(siteName) + if os.path.exists(file): + conf = mw.readFile(file) + rep = r'\s*root\s*(.+);' + find_cnf = re.search(rep, conf) + if not find_cnf: + return '' + path = find_cnf.groups()[0] + return path + return '' + def createRootDir(self, path): autoInit = False if not os.path.exists(path): @@ -1319,7 +1331,7 @@ location ^~ {from} {\n\ to_https = self.isToHttps(site_name) info = thisdb.getSitesByName(site_name) - domains = mw.M('domain').where("pid=?", (info['id'],)).field('name').select() + domains = mw.M('domain').field('name').where("pid=?", (info['id'],)).select() csr_path = path + '/fullchain.pem' # 生成证书路径 key_path = path + '/privkey.pem' # 密钥文件路径 @@ -1333,15 +1345,13 @@ location ^~ {from} {\n\ csr_path = acme_dir + '/fullchain.cer' # ACME生成证书路径 key_path = acme_dir + '/' + site_name + '.key' # ACME密钥文件路径 + key = '' if os.path.exists(key_path): key = mw.readFile(key_path) - else: - key = '' + csr = '' if os.path.exists(csr_path): csr = mw.readFile(csr_path) - else: - csr = '' cert_data = mw.getCertName(csr_path) data = { @@ -1472,6 +1482,198 @@ location ^~ {from} {\n\ thisdb.addSiteTypes(name) return mw.returnData(True, '添加成功!') + def getDnsapi(self): + dnsapi_data = thisdb.getOptionByJson('dnsapi', default={}) + dnsapi_option = [ + {"name":"none", "title":'手动解析', 'key':''}, + {"name":"dns_ali", "title":'Aliyun', 'key':'Ali_Key:Ali_Secret'}, + {"name":"dns_cf", "title":'cloudflare', 'key':'CF_Key:CF_Email'}, + {"name":"dns_dp", "title":'dnspod/国内', 'key':'DP_Id:DP_Key'}, + {"name":"dns_dpi", "title":'dnspod/国际', 'key':'DPI_Id:DPI_Key'}, + {"name":"dns_gd", "title":'GoDaddy', 'key':'GD_Key:GD_Secret'}, + {"name":"dns_pdns", "title":'PowerDNS', 'key':'PDNS_Url:PDNS_ServerId:PDNS_Token:PDNS_Ttl'}, + {"name":"dns_lua", "title":'LuaDNS', 'key':'LUA_Key:LUA_Email'}, + {"name":"dns_me", "title":'DNSMadeEasy', 'key':'ME_Key:ME_Secret'}, + {"name":"dns_aws", "title":'Amazon Route53', 'key':'AWS_ACCESS_KEY_ID:AWS_SECRET_ACCESS_KEY'}, + {"name":"dns_ispconfig", "title":'ISPConfig', 'key':'ISPC_User:ISPC_Password:ISPC_Api:ISPC_Api_Insecure'}, + {"name":"dns_ad", "title":'Alwaysdata', 'key':'AD_API_KEY'}, + {"name":"dns_linode_v4", "title":'Linode', 'key':'LINODE_V4_API_KEY'}, + {"name":"dns_freedns", "title":'FreeDNS', 'key':'FREEDNS_User:FREEDNS_Password'}, + {"name":"dns_cyon", "title":'cyon.ch', 'key':'CY_Username:CY_Password:CY_OTP_Secret'}, + {"name":"dns_gandi_livedns", "title":'LiveDNS', 'key':'GANDI_LIVEDNS_TOKEN'}, + {"name":"dns_knot", "title":'Knot', 'key':'KNOT_SERVER:KNOT_KEY'}, + {"name":"dns_dgon", "title":'DigitalOcean', 'key':'DO_API_KEY'}, + {"name":"dns_cloudns", "title":'ClouDNS.net', 'key':'CLOUDNS_SUB_AUTH_ID:CLOUDNS_AUTH_PASSWORD'}, + {"name":"dns_namesilo", "title":'Namesilo', 'key':'Namesilo_Key'}, + {"name":"dns_azure", "title":'Azure', 'key':'AZUREDNS_SUBSCRIPTIONID:AZUREDNS_TENANTID:AZUREDNS_APPID:AZUREDNS_CLIENTSECRET'}, + {"name":"dns_selectel", "title":'selectel.com', 'key':'SL_Key'}, + {"name":"dns_zonomi", "title":'zonomi.com', 'key':'ZM_Key'}, + {"name":"dns_kinghost", "title":'KingHost', 'key':'KINGHOST_Username:KINGHOST_Password'}, + {"name":"dns_zilore", "title":'Zilore', 'key':'Zilore_Key'}, + {"name":"dns_gcloud", "title":'Google Cloud DNS', 'key':'CLOUDSDK_ACTIVE_CONFIG_NAME'}, + {"name":"dns_mydnsjp", "title":'MyDNS.JP', 'key':'MYDNSJP_MasterID:MYDNSJP_Password'}, + {"name":"dns_doapi", "title":'do.de', 'key':'DO_LETOKEN'}, + {"name":"dns_online", "title":'Online', 'key':'ONLINE_API_KEY'}, + {"name":"dns_cn", "title":'Core-Networks', 'key':'CN_User:CN_Password'}, + {"name":"dns_ultra", "title":'UltraDNS', 'key':'ULTRA_USR:ULTRA_PWD'}, + {"name":"dns_hetzner", "title":'Hetzner', 'key':'HETZNER_Token'}, + {"name":"dns_ddnss", "title":'DDNSS.de', 'key':'DDNSS_Token'}, + ]; + + for i in range(len(dnsapi_option)): + dval = dnsapi_option[i]['key'] + dname = dnsapi_option[i]['name'] + if dname == 'none': + continue + + keys = dval.split(':') + data = {} + if dname in dnsapi_data: + dnsapi_option[i]['data'] = dnsapi_data[dname] + dnsapi_option[i]['title'] = dnsapi_option[i]['title'] + '[已配置]' + else: + t = {} + for field in keys: + t[field] = '' + dnsapi_option[i]['data'] = t + return dnsapi_option + + def createAcme(self, site_name, domains, force, renew, input_email): + domains = json.loads(domains) + + print(site_name,domains,input_email,renew,force) + email = thisdb.getOption('ssl_email', default='') + if input_email.strip() != '': + thisdb.setOption('ssl_email', input_email) + email = input_email + + if not len(domains): + return mw.returnData(False, '请选择域名') + + file = self.getHostConf(site_name) + if os.path.exists(file): + siteConf = mw.readFile(file) + if siteConf.find('301-END') != -1: + return mw.returnData(False, '检测到您的站点做了301重定向设置,请先关闭重定向!') + + # 检测存在反向代理 + data_path = self.getProxyDataPath(site_name) + data_content = mw.readFile(data_path) + if data_content != False: + try: + data = json.loads(data_content) + except: + pass + for proxy in data: + proxy_dir = "{}/{}".format(self.proxyPath, site_name) + proxy_dir_file = proxy_dir + '/' + proxy['id'] + '.conf' + if os.path.exists(proxy_dir_file): + return mw.returnData(False, '检测到您的站点做了反向代理设置,请先关闭反向代理!') + + siteInfo = thisdb.getSitesByName(site_name) + path = self.getSitePath(site_name) + if path == '': + return mw.returnData(False, '【'+site_name+'】配置文件,异常!') + + srcPath = siteInfo['path'] + + # 检测acme是否安装 + acme_dir = mw.getAcmeDir() + if not os.path.exists(acme_dir): + try: + mw.execShell("curl -sS curl https://get.acme.sh | sh") + except: + pass + if not os.path.exists(acme_dir): + return mw.returnData(False, '尝试自动安装ACME失败,请通过以下命令尝试手动安装

      安装命令: curl https://get.acme.sh | sh

      ') + + # 避免频繁执行 + checkAcmeRun = mw.execShell('ps -ef|grep acme.sh |grep -v grep') + if checkAcmeRun[0] != '': + return mw.returnData(False, '正在申请或更新SSL中...') + + if force == 'true': + force_bool = True + + if renew == 'true': + execStr = acme_dir + "/acme.sh --renew --yes-I-know-dns-manual-mode-enough-go-ahead-please" + else: + execStr = acme_dir + "/acme.sh --issue --force" + + # 确定主域名顺序 + domainsTmp = [] + if site_name in domains: + domainsTmp.append(site_name) + for domainTmp in domains: + if domainTmp == site_name: + continue + domainsTmp.append(domainTmp) + domains = domainsTmp + + domainCount = 0 + for domain in domains: + if mw.checkIp(domain): + continue + if domain.find('*.') != -1: + return mw.returnData(False, '泛域名不能使用【文件验证】的方式申请证书!') + execStr += ' -w ' + path + execStr += ' -d ' + domain + domainCount += 1 + if domainCount == 0: + return mw.returnData(False, '请选择域名(不包括IP地址与泛域名)!') + + log_file = mw.getPanelDir() + '/logs/acme.log' + mw.writeFile(log_file, "开始ACME申请...\n", "wb+") + cmd = 'export ACCOUNT_EMAIL=' + email + ' && ' + execStr + ' >> ' + log_file + # print(domains) + # print(cmd) + result = mw.execShell(cmd) + + src_path = mw.getAcmeDomainDir(domains[0]) + src_cert = src_path + '/fullchain.cer' + src_key = src_path + '/' + domains[0] + '.key' + src_cert.replace("\\*", "*") + + msg = '签发失败,您尝试申请证书的失败次数已达上限!

      1、检查域名是否绑定到对应站点

      \ +

      2、检查域名是否正确解析到本服务器,或解析还未完全生效

      \ +

      3、如果您的站点设置了反向代理,或使用了CDN,请先将其关闭

      \ +

      4、如果您的站点设置了301重定向,请先将其关闭

      \ +

      5、如果以上检查都确认没有问题,请尝试更换DNS服务商

      ' + if not os.path.exists(src_cert): + data = {} + data['err'] = result + data['out'] = result[0] + data['msg'] = msg + data['result'] = {} + if result[1].find('new-authz error:') != -1: + data['result'] = json.loads( + re.search("{.+}", result[1]).group()) + if data['result']['status'] == 429: + data['msg'] = msg + data['status'] = False + return mw.getJson(data) + + dst_path = self.sslDir + '/' + site_name + dst_cert = dst_path + "/fullchain.pem" # 生成证书路径 + dst_key = dst_path + "/privkey.pem" # 密钥文件路径 + + if not os.path.exists(dst_path): + mw.execShell("mkdir -p " + dst_path) + + mw.buildSoftLink(src_cert, dst_cert, True) + mw.buildSoftLink(src_key, dst_key, True) + mw.execShell('echo "acme" > "' + dst_path + '/README"') + + # 写入配置文件 + result = self.setSslConf(site_name) + if not result['status']: + return mw.getJson(result) + result['csr'] = mw.readFile(src_cert) + result['key'] = mw.readFile(src_key) + + mw.restartWeb() + return mw.returnData(True, '证书已更新!', result) + def getPhpVersion(self): phpVersions = ('00', '52', '53', '54', '55',