优化添加部署账户页面 修复部分已知问题
@ -43,8 +43,17 @@ class Cert extends BaseController
|
||||
|
||||
$list = [];
|
||||
foreach ($rows as $row) {
|
||||
$row['typename'] = $deploy == 1 ? DeployHelper::$deploy_config[$row['type']]['name'] : CertHelper::$cert_config[$row['type']]['name'];
|
||||
$row['icon'] = $deploy == 1 ? DeployHelper::$deploy_config[$row['type']]['icon'] : CertHelper::$cert_config[$row['type']]['icon'];
|
||||
if ($deploy == 1) {
|
||||
if (!empty($row['type']) && isset(DeployHelper::$deploy_config[$row['type']])) {
|
||||
$row['typename'] = DeployHelper::$deploy_config[$row['type']]['name'];
|
||||
$row['icon'] = DeployHelper::$deploy_config[$row['type']]['icon'];
|
||||
}
|
||||
} else {
|
||||
if (!empty($row['type']) && isset(CertHelper::$cert_config[$row['type']])) {
|
||||
$row['typename'] = CertHelper::$cert_config[$row['type']]['name'];
|
||||
$row['icon'] = CertHelper::$cert_config[$row['type']]['icon'];
|
||||
}
|
||||
}
|
||||
$list[] = $row;
|
||||
}
|
||||
|
||||
@ -202,6 +211,7 @@ class Cert extends BaseController
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$domain = $this->request->post('domain', null, 'trim');
|
||||
$id = input('post.id');
|
||||
$aid = input('post.aid', null, 'trim');
|
||||
$type = input('post.type', null, 'trim');
|
||||
$status = input('post.status', null, 'trim');
|
||||
$offset = input('post.offset/d');
|
||||
@ -214,6 +224,9 @@ class Cert extends BaseController
|
||||
$oids = Db::name('cert_domain')->where('domain', 'like', '%' . $domain . '%')->column('oid');
|
||||
$select->whereIn('A.id', $oids);
|
||||
}
|
||||
if (!empty($aid)) {
|
||||
$select->where('A.aid', $aid);
|
||||
}
|
||||
if (!empty($type)) {
|
||||
$select->where('B.type', $type);
|
||||
}
|
||||
@ -233,7 +246,7 @@ class Cert extends BaseController
|
||||
|
||||
$list = [];
|
||||
foreach ($rows as $row) {
|
||||
if (!empty($row['type'])) {
|
||||
if (!empty($row['type']) && isset(CertHelper::$cert_config[$row['type']])) {
|
||||
$row['typename'] = CertHelper::$cert_config[$row['type']]['name'];
|
||||
$row['icon'] = CertHelper::$cert_config[$row['type']]['icon'];
|
||||
} else {
|
||||
@ -599,6 +612,7 @@ class Cert extends BaseController
|
||||
|
||||
$accounts = [];
|
||||
foreach (Db::name('cert_account')->where('deploy', 0)->select() as $row) {
|
||||
if (empty($row['type']) || !isset(CertHelper::$cert_config[$row['type']])) continue;
|
||||
$accounts[$row['id']] = ['name' => $row['id'] . '_' . CertHelper::$cert_config[$row['type']]['name'], 'type' => $row['type']];
|
||||
if (!empty($row['remark'])) {
|
||||
$accounts[$row['id']]['name'] .= '(' . $row['remark'] . ')';
|
||||
@ -627,19 +641,23 @@ class Cert extends BaseController
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$domain = $this->request->post('domain', null, 'trim');
|
||||
$oid = input('post.oid');
|
||||
$aid = input('post.aid', null, 'trim');
|
||||
$type = input('post.type', null, 'trim');
|
||||
$status = input('post.status', null, 'trim');
|
||||
$remark = input('post.remark', null, 'trim');
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
|
||||
$select = Db::name('cert_deploy')->alias('A')->join('cert_account B', 'A.aid = B.id')->join('cert_order C', 'A.oid = C.id')->join('cert_account D', 'C.aid = D.id');
|
||||
$select = Db::name('cert_deploy')->alias('A')->leftJoin('cert_account B', 'A.aid = B.id')->leftJoin('cert_order C', 'A.oid = C.id')->leftJoin('cert_account D', 'C.aid = D.id');
|
||||
if (!empty($oid)) {
|
||||
$select->where('A.oid', $oid);
|
||||
} elseif (!empty($domain)) {
|
||||
$oids = Db::name('cert_domain')->where('domain', 'like', '%' . $domain . '%')->column('oid');
|
||||
$select->whereIn('oid', $oids);
|
||||
}
|
||||
if (!empty($aid)) {
|
||||
$select->where('A.aid', $aid);
|
||||
}
|
||||
if (!empty($type)) {
|
||||
$select->where('B.type', $type);
|
||||
}
|
||||
@ -654,9 +672,15 @@ class Cert extends BaseController
|
||||
|
||||
$list = [];
|
||||
foreach ($rows as $row) {
|
||||
$row['typename'] = DeployHelper::$deploy_config[$row['type']]['name'];
|
||||
$row['icon'] = DeployHelper::$deploy_config[$row['type']]['icon'];
|
||||
$row['certtypename'] = CertHelper::$cert_config[$row['certtype']]['name'];
|
||||
if (!empty($row['type']) && isset(DeployHelper::$deploy_config[$row['type']])) {
|
||||
$row['typename'] = DeployHelper::$deploy_config[$row['type']]['name'];
|
||||
$row['icon'] = DeployHelper::$deploy_config[$row['type']]['icon'];
|
||||
}
|
||||
if (!empty($row['certtype']) && isset(CertHelper::$cert_config[$row['certtype']])) {
|
||||
$row['certtypename'] = CertHelper::$cert_config[$row['certtype']]['name'];
|
||||
} else {
|
||||
$row['certtypename'] = '手动续期';
|
||||
}
|
||||
$row['domains'] = Db::name('cert_domain')->where('oid', $row['oid'])->order('sort', 'ASC')->column('domain');
|
||||
if ($row['error']) $row['error'] = htmlspecialchars(str_replace("'", "\\'", $row['error']));
|
||||
$list[] = $row;
|
||||
@ -782,6 +806,7 @@ class Cert extends BaseController
|
||||
|
||||
$accounts = [];
|
||||
foreach (Db::name('cert_account')->where('deploy', 1)->select() as $row) {
|
||||
if (empty($row['type']) || !isset(DeployHelper::$deploy_config[$row['type']])) continue;
|
||||
$accounts[$row['id']] = ['name' => $row['id'] . '_' . DeployHelper::$deploy_config[$row['type']]['name'], 'type' => $row['type']];
|
||||
if (!empty($row['remark'])) {
|
||||
$accounts[$row['id']]['name'] .= '(' . $row['remark'] . ')';
|
||||
@ -790,10 +815,15 @@ class Cert extends BaseController
|
||||
View::assign('accounts', $accounts);
|
||||
|
||||
$orders = [];
|
||||
foreach (Db::name('cert_order')->alias('A')->join('cert_account B', 'A.aid = B.id')->where('status', '<>', 4)->fieldRaw('A.id,A.aid,B.type,B.remark aremark')->order('id', 'desc')->select() as $row) {
|
||||
foreach (Db::name('cert_order')->alias('A')->leftJoin('cert_account B', 'A.aid = B.id')->where('status', '<>', 4)->fieldRaw('A.id,A.aid,B.type,B.remark aremark')->order('id', 'desc')->select() as $row) {
|
||||
$domains = Db::name('cert_domain')->where('oid', $row['id'])->order('sort', 'ASC')->column('domain');
|
||||
$domainstr = count($domains) > 2 ? implode('、', array_slice($domains, 0, 2)) . '等' . count($domains) . '个域名' : implode('、', $domains);
|
||||
$orders[$row['id']] = ['name' => $row['id'] . '_' . $domainstr . '(' . CertHelper::$cert_config[$row['type']]['name'] . ')'];
|
||||
if ($row['aid'] == 0) {
|
||||
$name = $row['id'] . '_' . $domainstr . '(手动续期)';
|
||||
} else {
|
||||
$name = $row['id'] . '_' . $domainstr . '(' . CertHelper::$cert_config[$row['type']]['name'] . ')';
|
||||
}
|
||||
$orders[$row['id']] = ['name' => $name];
|
||||
}
|
||||
View::assign('orders', $orders);
|
||||
|
||||
@ -821,7 +851,7 @@ class Cert extends BaseController
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
|
||||
$select = Db::name('cert_cname')->alias('A')->join('domain B', 'A.did = B.id');
|
||||
$select = Db::name('cert_cname')->alias('A')->leftJoin('domain B', 'A.did = B.id');
|
||||
if (!empty($kw)) {
|
||||
$select->whereLike('A.domain', '%' . $kw . '%');
|
||||
}
|
||||
|
||||
@ -177,7 +177,7 @@ location / {
|
||||
'tencent' => [
|
||||
'name' => '腾讯云免费SSL',
|
||||
'class' => 2,
|
||||
'icon' => 'tencent.ico',
|
||||
'icon' => 'tencent.png',
|
||||
'wildcard' => false,
|
||||
'max_domains' => 1,
|
||||
'cname' => false,
|
||||
@ -215,7 +215,7 @@ location / {
|
||||
'aliyun' => [
|
||||
'name' => '阿里云免费SSL',
|
||||
'class' => 2,
|
||||
'icon' => 'aliyun.ico',
|
||||
'icon' => 'aliyun.png',
|
||||
'wildcard' => false,
|
||||
'max_domains' => 1,
|
||||
'cname' => false,
|
||||
|
||||
@ -10,7 +10,8 @@ class DeployHelper
|
||||
'btpanel' => [
|
||||
'name' => '宝塔面板',
|
||||
'class' => 1,
|
||||
'icon' => 'bt.ico',
|
||||
'icon' => 'bt.png',
|
||||
'desc' => '支持部署到宝塔面板搭建的站点、Docker、邮局与面板本身',
|
||||
'note' => null,
|
||||
'inputs' => [
|
||||
'url' => [
|
||||
@ -63,6 +64,7 @@ class DeployHelper
|
||||
'name' => 'Kangle用户',
|
||||
'class' => 1,
|
||||
'icon' => 'host.png',
|
||||
'desc' => '支持虚拟主机与CDN站点',
|
||||
'note' => '以上登录信息为Easypanel用户面板的,非管理员面板。如选网站密码认证类型,则用户面板登录不能开启验证码。',
|
||||
'inputs' => [
|
||||
'url' => [
|
||||
@ -136,6 +138,7 @@ class DeployHelper
|
||||
'name' => 'Kangle管理员',
|
||||
'class' => 1,
|
||||
'icon' => 'host.png',
|
||||
'desc' => '支持虚拟主机与CDN站点',
|
||||
'note' => '以上登录地址需填写Easypanel管理员面板地址,非用户面板。',
|
||||
'inputs' => [
|
||||
'url' => [
|
||||
@ -202,6 +205,7 @@ class DeployHelper
|
||||
'name' => '雷池WAF',
|
||||
'class' => 1,
|
||||
'icon' => 'safeline.png',
|
||||
'desc' => '',
|
||||
'note' => null,
|
||||
'tasknote' => '系统会根据关联SSL证书的域名,自动更新对应证书',
|
||||
'inputs' => [
|
||||
@ -233,7 +237,8 @@ class DeployHelper
|
||||
'btwaf' => [
|
||||
'name' => '堡塔云WAF',
|
||||
'class' => 1,
|
||||
'icon' => 'bt.ico',
|
||||
'icon' => 'bt.png',
|
||||
'desc' => '',
|
||||
'note' => null,
|
||||
'tasknote' => '',
|
||||
'inputs' => [
|
||||
@ -273,6 +278,7 @@ class DeployHelper
|
||||
'name' => 'Cdnfly',
|
||||
'class' => 1,
|
||||
'icon' => 'waf.png',
|
||||
'desc' => '',
|
||||
'note' => '登录Cdnfly控制台->账户中心->API密钥,点击开启后获取',
|
||||
'inputs' => [
|
||||
'url' => [
|
||||
@ -318,6 +324,7 @@ class DeployHelper
|
||||
'name' => 'LeCDN',
|
||||
'class' => 1,
|
||||
'icon' => 'waf.png',
|
||||
'desc' => '',
|
||||
'note' => null,
|
||||
'inputs' => [
|
||||
'url' => [
|
||||
@ -363,6 +370,7 @@ class DeployHelper
|
||||
'name' => 'GoEdge',
|
||||
'class' => 1,
|
||||
'icon' => 'waf.png',
|
||||
'desc' => '',
|
||||
'note' => '需要先<a href="https://goedge.cloud/docs/API/Settings.md" target="_blank" rel="noreferrer">开启HTTP API端口</a>',
|
||||
'tasknote' => '系统会根据关联SSL证书的域名,自动更新对应证书',
|
||||
'inputs' => [
|
||||
@ -421,6 +429,7 @@ class DeployHelper
|
||||
'name' => '1Panel',
|
||||
'class' => 1,
|
||||
'icon' => 'opanel.png',
|
||||
'desc' => '更新面板证书管理内的SSL证书',
|
||||
'note' => null,
|
||||
'tasknote' => '系统会根据关联SSL证书的域名,自动更新对应证书',
|
||||
'inputs' => [
|
||||
@ -463,6 +472,7 @@ class DeployHelper
|
||||
'name' => 'MW面板',
|
||||
'class' => 1,
|
||||
'icon' => 'mwpanel.ico',
|
||||
'desc' => '',
|
||||
'note' => null,
|
||||
'tasknote' => '',
|
||||
'inputs' => [
|
||||
@ -520,6 +530,7 @@ class DeployHelper
|
||||
'name' => '耗子面板',
|
||||
'class' => 1,
|
||||
'icon' => 'ratpanel.ico',
|
||||
'desc' => '支持耗子面板 v2.5+ 版本使用',
|
||||
'note' => '支持耗子面板 v2.5+ 版本使用',
|
||||
'inputs' => [
|
||||
'url' => [
|
||||
@ -578,6 +589,7 @@ class DeployHelper
|
||||
'name' => '群晖面板',
|
||||
'class' => 1,
|
||||
'icon' => 'synology.png',
|
||||
'desc' => '支持群晖DSM 6.x/7.x版本',
|
||||
'note' => null,
|
||||
'tasknote' => '',
|
||||
'inputs' => [
|
||||
@ -633,6 +645,7 @@ class DeployHelper
|
||||
'name' => 'Proxmox VE',
|
||||
'class' => 1,
|
||||
'icon' => 'proxmox.ico',
|
||||
'desc' => '部署到PVE节点',
|
||||
'note' => '在“权限->API令牌”添加令牌,不要选特权分离',
|
||||
'tasknote' => '',
|
||||
'inputs' => [
|
||||
@ -677,8 +690,9 @@ class DeployHelper
|
||||
'aliyun' => [
|
||||
'name' => '阿里云',
|
||||
'class' => 2,
|
||||
'icon' => 'aliyun.ico',
|
||||
'note' => '支持部署到阿里云CDN、ESA、SLB、OSS、WAF等服务',
|
||||
'icon' => 'aliyun.png',
|
||||
'desc' => '支持部署到阿里云CDN、ESA、SLB、OSS、WAF、FC等服务',
|
||||
'note' => '支持部署到阿里云CDN、ESA、SLB、OSS、WAF、FC等服务',
|
||||
'tasknote' => '',
|
||||
'inputs' => [
|
||||
'AccessKeyId' => [
|
||||
@ -824,7 +838,8 @@ class DeployHelper
|
||||
'tencent' => [
|
||||
'name' => '腾讯云',
|
||||
'class' => 2,
|
||||
'icon' => 'tencent.ico',
|
||||
'icon' => 'tencent.png',
|
||||
'desc' => '支持部署到腾讯云CDN、EO、CLB、COS、TKE、SCF等服务',
|
||||
'note' => '支持部署到腾讯云CDN、EO、CLB、COS、TKE、SCF等服务',
|
||||
'tasknote' => '',
|
||||
'inputs' => [
|
||||
@ -959,6 +974,7 @@ class DeployHelper
|
||||
'name' => '华为云',
|
||||
'class' => 2,
|
||||
'icon' => 'huawei.ico',
|
||||
'desc' => '支持部署到华为云CDN、ELB、WAF等服务',
|
||||
'note' => '支持部署到华为云CDN、ELB、WAF等服务',
|
||||
'inputs' => [
|
||||
'AccessKeyId' => [
|
||||
@ -1031,6 +1047,7 @@ class DeployHelper
|
||||
'name' => 'UCloud',
|
||||
'class' => 2,
|
||||
'icon' => 'ucloud.ico',
|
||||
'desc' => '支持部署到UCDN',
|
||||
'note' => '支持部署到UCDN',
|
||||
'inputs' => [
|
||||
'PublicKey' => [
|
||||
@ -1060,6 +1077,7 @@ class DeployHelper
|
||||
'name' => '七牛云',
|
||||
'class' => 2,
|
||||
'icon' => 'qiniu.ico',
|
||||
'desc' => '支持部署到七牛云CDN、OSS',
|
||||
'note' => '支持部署到七牛云CDN、OSS',
|
||||
'inputs' => [
|
||||
'AccessKey' => [
|
||||
@ -1115,6 +1133,7 @@ class DeployHelper
|
||||
'name' => '多吉云',
|
||||
'class' => 2,
|
||||
'icon' => 'cloud.png',
|
||||
'desc' => '支持部署到多吉云融合CDN',
|
||||
'note' => '支持部署到多吉云融合CDN',
|
||||
'inputs' => [
|
||||
'AccessKey' => [
|
||||
@ -1152,6 +1171,8 @@ class DeployHelper
|
||||
'name' => '又拍云',
|
||||
'class' => 2,
|
||||
'icon' => 'upyun.ico',
|
||||
'desc' => '支持部署到又拍云CDN',
|
||||
'note' => '支持部署到又拍云CDN',
|
||||
'tasknote' => '系统会根据关联SSL证书的域名,进行证书的迁移操作',
|
||||
'inputs' => [
|
||||
'username' => [
|
||||
@ -1181,6 +1202,7 @@ class DeployHelper
|
||||
'name' => '百度云',
|
||||
'class' => 2,
|
||||
'icon' => 'baidu.ico',
|
||||
'desc' => '支持部署到百度云CDN',
|
||||
'note' => '支持部署到百度云CDN',
|
||||
'inputs' => [
|
||||
'AccessKeyId' => [
|
||||
@ -1218,6 +1240,7 @@ class DeployHelper
|
||||
'name' => '火山引擎',
|
||||
'class' => 2,
|
||||
'icon' => 'huoshan.ico',
|
||||
'desc' => '支持部署到火山引擎CDN',
|
||||
'note' => '支持部署到火山引擎CDN',
|
||||
'inputs' => [
|
||||
'AccessKeyId' => [
|
||||
@ -1285,6 +1308,7 @@ class DeployHelper
|
||||
'name' => '西部数码',
|
||||
'class' => 2,
|
||||
'icon' => 'west.ico',
|
||||
'desc' => '支持部署到西部数码虚拟主机',
|
||||
'note' => '支持部署到西部数码虚拟主机',
|
||||
'inputs' => [
|
||||
'username' => [
|
||||
@ -1322,6 +1346,7 @@ class DeployHelper
|
||||
'name' => '网宿科技',
|
||||
'class' => 2,
|
||||
'icon' => 'wangsu.ico',
|
||||
'desc' => '支持部署到网宿CDN',
|
||||
'note' => '适用产品:网页加速、下载分发、全站加速、点播分发、直播分发、上传加速、移动加速、上网加速、S-P2P、PCDN、应用性能管理、WEB应用防火墙、BotGuard爬虫管理、WSS、DMS、DDoS云清洗、应用加速、应用安全加速解决方案、IPv6一体化解决方案、电商安全加速解决方案、金融安全加速解决方案、政企安全加速解决方案、DDoS云清洗(非网站业务)、区块链安全加速解决方案、IPv6安全加速解决方案、CDN Pro。暂不支持AKSK鉴权。',
|
||||
'inputs' => [
|
||||
'username' => [
|
||||
@ -1390,6 +1415,7 @@ class DeployHelper
|
||||
'name' => '白山云',
|
||||
'class' => 2,
|
||||
'icon' => 'waf.png',
|
||||
'desc' => '替换白山云证书管理内的证书',
|
||||
'note' => null,
|
||||
'inputs' => [
|
||||
'account' => [
|
||||
@ -1429,7 +1455,8 @@ class DeployHelper
|
||||
'name' => '天翼云',
|
||||
'class' => 2,
|
||||
'icon' => 'ctyun.ico',
|
||||
'note' => '支持部署到天翼云CDN',
|
||||
'desc' => '支持部署到天翼云CDN、边缘加速',
|
||||
'note' => '支持部署到天翼云CDN、边缘加速',
|
||||
'inputs' => [
|
||||
'AccessKeyId' => [
|
||||
'name' => 'AccessKeyId',
|
||||
@ -1477,6 +1504,7 @@ class DeployHelper
|
||||
'name' => '括彩云',
|
||||
'class' => 2,
|
||||
'icon' => 'kuocai.jpg',
|
||||
'desc' => '替换括彩云证书管理内的证书',
|
||||
'note' => '支持括彩云及其代理商,填写控制台登录账号及密码',
|
||||
'inputs' => [
|
||||
'username' => [
|
||||
@ -1512,41 +1540,11 @@ class DeployHelper
|
||||
],
|
||||
],
|
||||
],
|
||||
'allwaf' => [
|
||||
'name' => 'AllWAF',
|
||||
'class' => 2,
|
||||
'icon' => 'waf.png',
|
||||
'note' => '在<a href="https://user.allwaf.cn/" target="_blank" rel="noreferrer">ALLWAF</a>访问控制页面创建AccessKey',
|
||||
'tasknote' => '系统会根据关联SSL证书的域名,自动更新对应证书',
|
||||
'inputs' => [
|
||||
'accessKeyId' => [
|
||||
'name' => 'AccessKey ID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'accessKey' => [
|
||||
'name' => 'AccessKey密钥',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'taskinputs' => [],
|
||||
],
|
||||
'aws' => [
|
||||
'name' => 'AWS',
|
||||
'class' => 2,
|
||||
'icon' => 'aws.ico',
|
||||
'icon' => 'aws.png',
|
||||
'desc' => '支持部署到Amazon CloudFront、AWS Certificate Manager',
|
||||
'note' => '支持部署到Amazon CloudFront、AWS Certificate Manager',
|
||||
'inputs' => [
|
||||
'AccessKeyId' => [
|
||||
@ -1603,6 +1601,7 @@ class DeployHelper
|
||||
'name' => 'Gcore',
|
||||
'class' => 2,
|
||||
'icon' => 'gcore.ico',
|
||||
'desc' => '替换Gcore CDN证书',
|
||||
'note' => '在 个人资料->API令牌 页面创建API令牌',
|
||||
'inputs' => [
|
||||
'account' => [
|
||||
@ -1647,6 +1646,7 @@ class DeployHelper
|
||||
'name' => 'Cachefly',
|
||||
'class' => 2,
|
||||
'icon' => 'cloud.png',
|
||||
'desc' => '替换Cachefly CDN证书',
|
||||
'note' => '在 API Tokens 页面生成 API Token',
|
||||
'inputs' => [
|
||||
'account' => [
|
||||
@ -1679,6 +1679,7 @@ class DeployHelper
|
||||
'name' => 'SSH服务器',
|
||||
'class' => 3,
|
||||
'icon' => 'server.png',
|
||||
'desc' => '可通过SSH连接到Linux/Windows服务器并部署证书',
|
||||
'note' => '可通过SSH连接到Linux/Windows服务器并部署证书,php需要安装ssh2扩展',
|
||||
'tasknote' => '请确保路径存在且有写入权限,路径一定要以/开头(Windows路径请使用/代替\,且需要在最开头加/)',
|
||||
'inputs' => [
|
||||
@ -1805,6 +1806,7 @@ class DeployHelper
|
||||
'name' => 'FTP服务器',
|
||||
'class' => 3,
|
||||
'icon' => 'server.png',
|
||||
'desc' => '可将证书上传到FTP服务器',
|
||||
'note' => '可将证书上传到FTP服务器,php需要安装ftp扩展',
|
||||
'tasknote' => '请确保路径存在且有写入权限',
|
||||
'inputs' => [
|
||||
@ -1888,6 +1890,7 @@ class DeployHelper
|
||||
'name' => '复制到本机',
|
||||
'class' => 3,
|
||||
'icon' => 'server2.png',
|
||||
'desc' => '将证书复制到本机指定路径',
|
||||
'note' => '将证书复制到本机指定路径',
|
||||
'tasknote' => '请确保php进程有对证书保存路径的写入权限,宝塔面板需关闭防跨站攻击,如果当前是Docker运行的,则需要做目录映射到宿主机。',
|
||||
'inputs' => [],
|
||||
|
||||
@ -1,146 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class allwaf implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url = 'https://api.allwaf.cn';
|
||||
private $accessKeyId;
|
||||
private $accessKey;
|
||||
private $usertype = 'user';
|
||||
private $proxy;
|
||||
private $accessToken;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->accessKeyId = $config['accessKeyId'];
|
||||
$this->accessKey = $config['accessKey'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->accessKeyId) || empty($this->accessKey)) throw new Exception('必填参数不能为空');
|
||||
$this->getAccessToken();
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$domains = $config['domainList'];
|
||||
if (empty($domains)) throw new Exception('没有设置要部署的域名');
|
||||
|
||||
$this->getAccessToken();
|
||||
|
||||
$params = [
|
||||
'domains' => $domains,
|
||||
'offset' => 0,
|
||||
'size' => 10,
|
||||
];
|
||||
try {
|
||||
$data = $this->request('/SSLCertService/listSSLCerts', $params);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取证书列表失败:' . $e->getMessage());
|
||||
}
|
||||
$list = json_decode(base64_decode($data['sslCertsJSON']), true);
|
||||
if (!$list || empty($list)) {
|
||||
throw new Exception('证书列表为空');
|
||||
}
|
||||
$this->log('获取证书列表成功(total=' . count($list) . ')');
|
||||
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
|
||||
if (!empty($list)) {
|
||||
foreach ($list as $row) {
|
||||
$params = [
|
||||
'sslCertId' => $row['id'],
|
||||
'isOn' => true,
|
||||
'name' => $row['name'],
|
||||
'description' => $row['description'],
|
||||
'serverName' => $row['serverName'],
|
||||
'isCA' => false,
|
||||
'certData' => base64_encode($fullchain),
|
||||
'keyData' => base64_encode($privatekey),
|
||||
'timeBeginAt' => $certInfo['validFrom_time_t'],
|
||||
'timeEndAt' => $certInfo['validTo_time_t'],
|
||||
'dnsNames' => $domains,
|
||||
'commonNames' => [$certInfo['issuer']['CN']],
|
||||
];
|
||||
$this->request('/SSLCertService/updateSSLCert', $params);
|
||||
$this->log('证书ID:' . $row['id'] . '更新成功!');
|
||||
}
|
||||
} else {
|
||||
$params = [
|
||||
'isOn' => true,
|
||||
'name' => $cert_name,
|
||||
'description' => $cert_name,
|
||||
'serverName' => $certInfo['subject']['CN'],
|
||||
'isCA' => false,
|
||||
'certData' => base64_encode($fullchain),
|
||||
'keyData' => base64_encode($privatekey),
|
||||
'timeBeginAt' => $certInfo['validFrom_time_t'],
|
||||
'timeEndAt' => $certInfo['validTo_time_t'],
|
||||
'dnsNames' => $domains,
|
||||
'commonNames' => [$certInfo['issuer']['CN']],
|
||||
];
|
||||
$result = $this->request('/SSLCertService/createSSLCert', $params);
|
||||
$this->log('证书ID:' . $result['sslCertId'] . '添加成功!');
|
||||
}
|
||||
}
|
||||
|
||||
private function getAccessToken()
|
||||
{
|
||||
$path = '/APIAccessTokenService/getAPIAccessToken';
|
||||
$params = [
|
||||
'type' => $this->usertype,
|
||||
'accessKeyId' => $this->accessKeyId,
|
||||
'accessKey' => $this->accessKey,
|
||||
];
|
||||
$result = $this->request($path, $params);
|
||||
if (isset($result['token'])) {
|
||||
$this->accessToken = $result['token'];
|
||||
} else {
|
||||
throw new Exception('登录成功,获取AccessToken失败');
|
||||
}
|
||||
}
|
||||
|
||||
private function request($path, $params = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = [];
|
||||
$body = null;
|
||||
if ($this->accessToken) {
|
||||
$headers[] = 'X-Cloud-Access-Token: ' . $this->accessToken;
|
||||
}
|
||||
if ($params) {
|
||||
$headers[] = 'Content-Type: application/json';
|
||||
$body = json_encode($params);
|
||||
}
|
||||
$response = curl_client($url, $body, null, null, $headers, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['code']) && $result['code'] == 200) {
|
||||
return isset($result['data']) ? $result['data'] : null;
|
||||
} elseif (isset($result['message'])) {
|
||||
throw new Exception($result['message']);
|
||||
} else {
|
||||
if (!empty($response['body'])) $this->log('Response:' . $response['body']);
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -89,6 +89,9 @@ class CertDeployService
|
||||
private function saveResult($status, $error = null, $retrytime = null)
|
||||
{
|
||||
$this->task['status'] = $status;
|
||||
if (mb_strlen($error) > 300) {
|
||||
$error = mb_strcut($error, 0, 300);
|
||||
}
|
||||
$update = ['status' => $status, 'error' => $error, 'retrytime' => $retrytime];
|
||||
if ($status == 1){
|
||||
$update['retry'] = 0;
|
||||
|
||||
@ -178,6 +178,9 @@ class CertOrderService
|
||||
private function saveResult($status, $error = null, $retrytime = null)
|
||||
{
|
||||
$this->order['status'] = $status;
|
||||
if (mb_strlen($error) > 300) {
|
||||
$error = mb_strcut($error, 0, 300);
|
||||
}
|
||||
$update = ['status' => $status, 'error' => $error, 'updatetime' => date('Y-m-d H:i:s'), 'retrytime' => $retrytime];
|
||||
$res = Db::name('cert_order')->where('id', $this->order['id'])->data($update);
|
||||
if ($status < 0 || $retrytime) {
|
||||
|
||||
@ -9,19 +9,104 @@
|
||||
color: #f56c6c;
|
||||
margin-right: 4px;
|
||||
}
|
||||
/* 账户类型卡片样式 */
|
||||
.account-type-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.account-type-category {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.account-type-card {
|
||||
width: calc(25% - 15px);
|
||||
min-width: 200px;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
background: #fff;
|
||||
height: 100px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.account-type-card:hover {
|
||||
border-color: #409EFF;
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
|
||||
}
|
||||
.account-type-card .icon {
|
||||
width: 30px;
|
||||
margin: 11px 8px;
|
||||
float: left;
|
||||
}
|
||||
.account-type-card .content {
|
||||
margin-left: 38px;
|
||||
}
|
||||
.account-type-card .title {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 3px;
|
||||
color: #333;
|
||||
}
|
||||
.account-type-card .desc {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
line-height: 1.4;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.account-type-card {
|
||||
width: calc(50% - 15px);
|
||||
}
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
.account-type-card {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div class="row" id="app">
|
||||
<div class="col-xs-12 center-block" style="float: none;">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><h3 class="panel-title"><a href="javascript:window.history.back()" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>{if $action=='edit'}编辑{else}添加{/if}{$title}</h3></div>
|
||||
<div class="panel-body">
|
||||
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="accountform">
|
||||
<!-- 账户类型选择视图 -->
|
||||
<div id="account-type-view" v-if="!selectedType">
|
||||
<div v-for="(category, classId) in groupedTypes" :key="classId">
|
||||
<div class="account-type-category">{{ category.label }}</div>
|
||||
<div class="account-type-container">
|
||||
<div class="account-type-card" v-for="type in category.types" :key="type.value" @click="selectType(type.value)">
|
||||
<img class="icon" :src="'/static/images/' + typeList[type.value].icon" :alt="type.label">
|
||||
<div class="content">
|
||||
<div class="title">{{ type.label }}</div>
|
||||
<div class="desc">{{ typeList[type.value].desc || ''}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表单视图 -->
|
||||
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="accountform" v-if="selectedType">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label no-padding-right" is-required>账户类型</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="type" v-model="set.type" class="form-control" required :disabled="action=='edit'">
|
||||
<optgroup v-for="item in typeOption" :label="item.label"><option v-for="item2 in item.children" :value="item2.value">{{item2.label}}</option></optgroup>
|
||||
</select>
|
||||
<div class="form-control-static">
|
||||
{{ typeList[set.type].name }}
|
||||
<a href="javascript:;" @click="selectedType = false" class="pull-right btn btn-default" v-if="action=='add'">重新选择</a>
|
||||
</div>
|
||||
<input type="hidden" name="type" v-model="set.type">
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(item,name) in inputs" v-show="isShow(item.show)">
|
||||
@ -104,6 +189,7 @@ new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
action: '{$action}',
|
||||
selectedType: false,
|
||||
set: {
|
||||
deploy: '{$deploy}',
|
||||
id: '',
|
||||
@ -140,16 +226,24 @@ new Vue({
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.typeOption = Object.keys(classList).map((key) => {
|
||||
var tempList = [];
|
||||
Object.keys(typeList).forEach((key2) => {
|
||||
if(typeList[key2].class == key){
|
||||
tempList.push({label: typeList[key2].name, value: key2})
|
||||
}
|
||||
computed: {
|
||||
groupedTypes() {
|
||||
return Object.keys(classList).map((key) => {
|
||||
var tempList = [];
|
||||
Object.keys(typeList).forEach((key2) => {
|
||||
if(typeList[key2].class == key){
|
||||
tempList.push({label: typeList[key2].name, value: key2})
|
||||
}
|
||||
})
|
||||
return {label: classList[key], types: tempList}
|
||||
})
|
||||
return {label: classList[key], children: tempList}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.typeOption = this.groupedTypes;
|
||||
if(this.action == 'edit') {
|
||||
this.selectedType = true;
|
||||
}
|
||||
if(this.action == 'edit'){
|
||||
Object.keys(info).forEach((key) => {
|
||||
this.set[key] = info[key]
|
||||
@ -181,6 +275,10 @@ new Vue({
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
selectType(type) {
|
||||
this.set.type = type;
|
||||
this.selectedType = true;
|
||||
},
|
||||
submit(){
|
||||
var that=this;
|
||||
Object.keys(this.config).forEach((key) => {
|
||||
@ -235,4 +333,4 @@ new Vue({
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{/block}
|
||||
{/block}
|
||||
|
||||
@ -68,7 +68,7 @@ $(document).ready(function(){
|
||||
field: '',
|
||||
title: '操作',
|
||||
formatter: function(value, row, index) {
|
||||
var html = '<a href="/cert/account/edit?deploy=0&id='+row.id+'" class="btn btn-info btn-xs">编辑</a> <a href="javascript:delItem('+row.id+')" class="btn btn-danger btn-xs">删除</a>';
|
||||
var html = '<a href="/cert/account/edit?deploy=0&id='+row.id+'" class="btn btn-info btn-xs">编辑</a> <a href="javascript:delItem('+row.id+')" class="btn btn-danger btn-xs">删除</a> <a href="/cert/certorder?aid='+row.id+'" class="btn btn-default btn-xs">订单</a>';
|
||||
return html;
|
||||
}
|
||||
},
|
||||
@ -79,12 +79,12 @@ function delItem(id){
|
||||
layer.confirm('确定要删除此账户吗?', {
|
||||
btn: ['确定','取消']
|
||||
}, function(){
|
||||
$.post('/cert/account/del?deploy=0', {id: id}, function(data){
|
||||
$.post('/cert/account/del', {id: id, deploy: 0}, function(data){
|
||||
if(data.code == 0) {
|
||||
layer.msg('删除成功', {icon: 1, time:800});
|
||||
$('#listTable').bootstrapTable('refresh');
|
||||
} else {
|
||||
layer.msg(data.msg, {icon: 2});
|
||||
layer.alert(data.msg, {icon: 2});
|
||||
}
|
||||
}, 'json');
|
||||
});
|
||||
|
||||
@ -16,6 +16,7 @@ pre.pre-log{height: 330px;overflow-y: auto;width: 100%;background-color: rgba(51
|
||||
|
||||
<form onsubmit="return searchSubmit()" method="GET" class="form-inline" id="searchToolbar">
|
||||
<input type="hidden" name="id" value="">
|
||||
<input type="hidden" name="aid" value="">
|
||||
<div class="form-group">
|
||||
<label>搜索</label>
|
||||
<div class="form-group">
|
||||
|
||||
@ -68,7 +68,7 @@ $(document).ready(function(){
|
||||
field: '',
|
||||
title: '操作',
|
||||
formatter: function(value, row, index) {
|
||||
var html = '<a href="/cert/account/edit?deploy=1&id='+row.id+'" class="btn btn-info btn-xs">编辑</a> <a href="javascript:delItem('+row.id+')" class="btn btn-danger btn-xs">删除</a>';
|
||||
var html = '<a href="/cert/account/edit?deploy=1&id='+row.id+'" class="btn btn-info btn-xs">编辑</a> <a href="javascript:delItem('+row.id+')" class="btn btn-danger btn-xs">删除</a> <a href="/cert/deploytask?aid='+row.id+'" class="btn btn-default btn-xs">任务</a>';
|
||||
return html;
|
||||
}
|
||||
},
|
||||
@ -79,12 +79,12 @@ function delItem(id){
|
||||
layer.confirm('确定要删除此账户吗?', {
|
||||
btn: ['确定','取消']
|
||||
}, function(){
|
||||
$.post('/cert/account/del?deploy=1', {id: id}, function(data){
|
||||
$.post('/cert/account/del', {id: id, deploy: 1}, function(data){
|
||||
if(data.code == 0) {
|
||||
layer.msg('删除成功', {icon: 1, time:800});
|
||||
$('#listTable').bootstrapTable('refresh');
|
||||
} else {
|
||||
layer.msg(data.msg, {icon: 2});
|
||||
layer.alert(data.msg, {icon: 2});
|
||||
}
|
||||
}, 'json');
|
||||
});
|
||||
|
||||
@ -12,6 +12,7 @@ pre.pre-log{height: 330px;overflow-y: auto;width: 100%;background-color: rgba(51
|
||||
<div class="panel-body">
|
||||
|
||||
<form onsubmit="return searchSubmit()" method="GET" class="form-inline" id="searchToolbar">
|
||||
<input type="hidden" name="aid" value="">
|
||||
<input type="hidden" name="oid" value="">
|
||||
<div class="form-group">
|
||||
<label>搜索</label>
|
||||
@ -80,6 +81,7 @@ $(document).ready(function(){
|
||||
field: 'typename',
|
||||
title: '自动部署账户',
|
||||
formatter: function(value, row, index) {
|
||||
if(!value) return '已被删除'
|
||||
return '<span title="'+row.aname+'" data-toggle="tooltip" data-placement="right"><img src="/static/images/'+row.icon+'" class="type-logo">'+(row.aremark?row.aremark:value+'('+row.aid+')')+'</span>';
|
||||
}
|
||||
},
|
||||
|
||||
BIN
public/static/images/aliyun.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
public/static/images/aws.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
BIN
public/static/images/bt.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 37 KiB |
BIN
public/static/images/maoyun.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 949 B |
BIN
public/static/images/tencent.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |