mirror of
https://github.com/netcccyun/dnsmgr.git
synced 2026-02-21 07:17:22 +08:00
证书订单支持设置手动续期
This commit is contained in:
parent
0015015b7a
commit
236610d8fb
@ -66,7 +66,7 @@ class Cert extends BaseController
|
||||
if ($type == 'local') $name = '复制到本机';
|
||||
if (empty($name) || empty($config)) return json(['code' => -1, 'msg' => '必填参数不能为空']);
|
||||
if (Db::name('cert_account')->where('type', $type)->where('config', $config)->find()) {
|
||||
return json(['code' => -1, 'msg' => $title.'已存在']);
|
||||
return json(['code' => -1, 'msg' => $title . '已存在']);
|
||||
}
|
||||
Db::startTrans();
|
||||
$id = Db::name('cert_account')->insertGetId([
|
||||
@ -80,15 +80,15 @@ class Cert extends BaseController
|
||||
try {
|
||||
$this->checkAccount($id, $type, $deploy);
|
||||
Db::commit();
|
||||
return json(['code' => 0, 'msg' => '添加'.$title.'成功!']);
|
||||
} catch(Exception $e) {
|
||||
return json(['code' => 0, 'msg' => '添加' . $title . '成功!']);
|
||||
} catch (Exception $e) {
|
||||
Db::rollback();
|
||||
return json(['code' => -1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
} elseif ($action == 'edit') {
|
||||
$id = input('post.id/d');
|
||||
$row = Db::name('cert_account')->where('id', $id)->find();
|
||||
if (!$row) return json(['code' => -1, 'msg' => $title.'不存在']);
|
||||
if (!$row) return json(['code' => -1, 'msg' => $title . '不存在']);
|
||||
$type = input('post.type');
|
||||
$name = input('post.name', null, 'trim');
|
||||
$config = input('post.config', null, 'trim');
|
||||
@ -96,7 +96,7 @@ class Cert extends BaseController
|
||||
if ($type == 'local') $name = '复制到本机';
|
||||
if (empty($name) || empty($config)) return json(['code' => -1, 'msg' => '必填参数不能为空']);
|
||||
if (Db::name('cert_account')->where('type', $type)->where('config', $config)->where('id', '<>', $id)->find()) {
|
||||
return json(['code' => -1, 'msg' => $title.'已存在']);
|
||||
return json(['code' => -1, 'msg' => $title . '已存在']);
|
||||
}
|
||||
Db::startTrans();
|
||||
Db::name('cert_account')->where('id', $id)->update([
|
||||
@ -108,19 +108,19 @@ class Cert extends BaseController
|
||||
try {
|
||||
$this->checkAccount($id, $type, $deploy);
|
||||
Db::commit();
|
||||
return json(['code' => 0, 'msg' => '修改'.$title.'成功!']);
|
||||
} catch(Exception $e) {
|
||||
return json(['code' => 0, 'msg' => '修改' . $title . '成功!']);
|
||||
} catch (Exception $e) {
|
||||
Db::rollback();
|
||||
return json(['code' => -1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
} elseif ($action == 'del') {
|
||||
$id = input('post.id/d');
|
||||
if($deploy == 0){
|
||||
if ($deploy == 0) {
|
||||
$dcount = DB::name('cert_order')->where('aid', $id)->count();
|
||||
if ($dcount > 0) return json(['code' => -1, 'msg' => '该'.$title.'下存在证书订单,无法删除']);
|
||||
}else{
|
||||
if ($dcount > 0) return json(['code' => -1, 'msg' => '该' . $title . '下存在证书订单,无法删除']);
|
||||
} else {
|
||||
$dcount = DB::name('cert_deploy')->where('aid', $id)->count();
|
||||
if ($dcount > 0) return json(['code' => -1, 'msg' => '该'.$title.'下存在自动部署任务,无法删除']);
|
||||
if ($dcount > 0) return json(['code' => -1, 'msg' => '该' . $title . '下存在自动部署任务,无法删除']);
|
||||
}
|
||||
Db::name('cert_account')->where('id', $id)->delete();
|
||||
return json(['code' => 0]);
|
||||
@ -139,7 +139,7 @@ class Cert extends BaseController
|
||||
if ($action == 'edit') {
|
||||
$id = input('get.id/d');
|
||||
$account = Db::name('cert_account')->where('id', $id)->find();
|
||||
if (empty($account)) return $this->alert('error', $title.'不存在');
|
||||
if (empty($account)) return $this->alert('error', $title . '不存在');
|
||||
}
|
||||
|
||||
$typeList = $deploy == 1 ? DeployHelper::getList() : CertHelper::getList();
|
||||
@ -156,32 +156,32 @@ class Cert extends BaseController
|
||||
|
||||
private function checkAccount($id, $type, $deploy)
|
||||
{
|
||||
if($deploy == 0){
|
||||
if ($deploy == 0) {
|
||||
$mod = CertHelper::getModel($id);
|
||||
if($mod){
|
||||
try{
|
||||
if ($mod) {
|
||||
try {
|
||||
$ext = $mod->register();
|
||||
if(is_array($ext)){
|
||||
Db::name('cert_account')->where('id', $id)->update(['ext'=>json_encode($ext)]);
|
||||
if (is_array($ext)) {
|
||||
Db::name('cert_account')->where('id', $id)->update(['ext' => json_encode($ext)]);
|
||||
}
|
||||
return true;
|
||||
}catch(Exception $e){
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('验证SSL证书账户失败,' . $e->getMessage());
|
||||
}
|
||||
}else{
|
||||
throw new Exception('SSL证书申请模块'.$type.'不存在');
|
||||
} else {
|
||||
throw new Exception('SSL证书申请模块' . $type . '不存在');
|
||||
}
|
||||
}else{
|
||||
} else {
|
||||
$mod = DeployHelper::getModel($id);
|
||||
if($mod){
|
||||
try{
|
||||
if ($mod) {
|
||||
try {
|
||||
$mod->check();
|
||||
return true;
|
||||
}catch(Exception $e){
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('验证自动部署账户失败,' . $e->getMessage());
|
||||
}
|
||||
}else{
|
||||
throw new Exception('SSL证书申请模块'.$type.'不存在');
|
||||
} else {
|
||||
throw new Exception('SSL证书申请模块' . $type . '不存在');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -190,7 +190,7 @@ class Cert extends BaseController
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$types = [];
|
||||
foreach(CertHelper::$cert_config as $key=>$value){
|
||||
foreach (CertHelper::$cert_config as $key => $value) {
|
||||
$types[$key] = $value['name'];
|
||||
}
|
||||
View::assign('types', $types);
|
||||
@ -207,10 +207,10 @@ class Cert extends BaseController
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
|
||||
$select = Db::name('cert_order')->alias('A')->join('cert_account B', 'A.aid = B.id');
|
||||
$select = Db::name('cert_order')->alias('A')->leftJoin('cert_account B', 'A.aid = B.id');
|
||||
if (!empty($id)) {
|
||||
$select->where('A.id', $id);
|
||||
}elseif (!empty($domain)) {
|
||||
} elseif (!empty($domain)) {
|
||||
$oids = Db::name('cert_domain')->where('domain', 'like', '%' . $domain . '%')->column('oid');
|
||||
$select->whereIn('A.id', $oids);
|
||||
}
|
||||
@ -233,11 +233,15 @@ class Cert extends BaseController
|
||||
|
||||
$list = [];
|
||||
foreach ($rows as $row) {
|
||||
$row['typename'] = CertHelper::$cert_config[$row['type']]['name'];
|
||||
$row['icon'] = CertHelper::$cert_config[$row['type']]['icon'];
|
||||
$row['domains'] = Db::name('cert_domain')->where('oid', $row['id'])->order('sort','ASC')->column('domain');
|
||||
if (!empty($row['type'])) {
|
||||
$row['typename'] = CertHelper::$cert_config[$row['type']]['name'];
|
||||
$row['icon'] = CertHelper::$cert_config[$row['type']]['icon'];
|
||||
} else {
|
||||
$row['typename'] = null;
|
||||
}
|
||||
$row['domains'] = Db::name('cert_domain')->where('oid', $row['id'])->order('sort', 'ASC')->column('domain');
|
||||
$row['end_day'] = $row['expiretime'] ? ceil((strtotime($row['expiretime']) - time()) / 86400) : null;
|
||||
if($row['error']) $row['error'] = htmlspecialchars(str_replace("'", "\\'", $row['error']));
|
||||
if ($row['error']) $row['error'] = htmlspecialchars(str_replace("'", "\\'", $row['error']));
|
||||
$list[] = $row;
|
||||
}
|
||||
|
||||
@ -252,7 +256,7 @@ class Cert extends BaseController
|
||||
if (!$row) return json(['code' => -1, 'msg' => '证书订单不存在']);
|
||||
$pfx = CertHelper::getPfx($row['fullchain'], $row['privatekey']);
|
||||
$row['pfx'] = base64_encode($pfx);
|
||||
return json(['code' => 0, 'data' => ['id' => $row['id'], 'crt' => $row['fullchain'], 'key' => $row['privatekey'], 'pfx' => $row['pfx'], 'issuetime' => $row['issuetime'], 'expiretime' => $row['expiretime'], 'domains' => Db::name('cert_domain')->where('oid', $row['id'])->order('sort','ASC')->column('domain')]]);
|
||||
return json(['code' => 0, 'data' => ['id' => $row['id'], 'crt' => $row['fullchain'], 'key' => $row['privatekey'], 'pfx' => $row['pfx'], 'issuetime' => $row['issuetime'], 'expiretime' => $row['expiretime'], 'domains' => Db::name('cert_domain')->where('oid', $row['id'])->order('sort', 'ASC')->column('domain')]]);
|
||||
}
|
||||
|
||||
public function order_op()
|
||||
@ -268,32 +272,66 @@ class Cert extends BaseController
|
||||
$row['pfx'] = base64_encode($pfx);
|
||||
return json(['code' => 0, 'data' => $row]);
|
||||
} elseif ($action == 'add') {
|
||||
$domains = input('post.domains', [], 'trim');
|
||||
$order = [
|
||||
'aid' => input('post.aid/d'),
|
||||
'keytype' => input('post.keytype'),
|
||||
'keysize' => input('post.keysize'),
|
||||
'addtime' => date('Y-m-d H:i:s'),
|
||||
'issuer' => '',
|
||||
'status' => 0,
|
||||
'isauto' => 1,
|
||||
];
|
||||
$domains = array_map('trim', $domains);
|
||||
$domains = array_filter($domains, function ($v) {
|
||||
return !empty($v);
|
||||
});
|
||||
$domains = array_unique($domains);
|
||||
if (empty($domains)) return json(['code' => -1, 'msg' => '绑定域名不能为空']);
|
||||
if (empty($order['aid']) || empty($order['keytype']) || empty($order['keysize'])) return json(['code' => -1, 'msg' => '必填参数不能为空']);
|
||||
$aid = input('post.aid/d');
|
||||
|
||||
$res = $this->check_order($order, $domains);
|
||||
if (is_array($res)) return json($res);
|
||||
if ($aid == -1) {
|
||||
$fullchain = input('post.fullchain', null, 'trim');
|
||||
$privatekey = input('post.privatekey', null, 'trim');
|
||||
$certInfo = $this->parse_cert_key($fullchain, $privatekey);
|
||||
if ($certInfo['code'] == -1) return json($certInfo);
|
||||
$domains = $certInfo['domains'];
|
||||
|
||||
$order_ids = Db::name('cert_order')->where('issuetime', $certInfo['issuetime'])->column('id');
|
||||
if (!empty($order_ids)) {
|
||||
foreach ($order_ids as $order_id) {
|
||||
$domains2 = Db::name('cert_domain')->where('oid', $order_id)->column('domain');
|
||||
if (arrays_are_equal($domains2, $domains)) {
|
||||
return json(['code' => -1, 'msg' => '该证书已存在,无需重复添加']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$order = [
|
||||
'aid' => 0,
|
||||
'keytype' => $certInfo['keytype'],
|
||||
'keysize' => $certInfo['keysize'],
|
||||
'addtime' => date('Y-m-d H:i:s'),
|
||||
'updatetime' => date('Y-m-d H:i:s'),
|
||||
'issuetime' => $certInfo['issuetime'],
|
||||
'expiretime' => $certInfo['expiretime'],
|
||||
'issuer' => $certInfo['issuer'],
|
||||
'status' => 3,
|
||||
'isauto' => 1,
|
||||
'fullchain' => $fullchain,
|
||||
'privatekey' => $privatekey,
|
||||
];
|
||||
} else {
|
||||
$order = [
|
||||
'aid' => $aid,
|
||||
'keytype' => input('post.keytype'),
|
||||
'keysize' => input('post.keysize'),
|
||||
'addtime' => date('Y-m-d H:i:s'),
|
||||
'issuer' => '',
|
||||
'status' => 0,
|
||||
'isauto' => 1,
|
||||
];
|
||||
$domains = input('post.domains', [], 'trim');
|
||||
$domains = array_map('trim', $domains);
|
||||
$domains = array_filter($domains, function ($v) {
|
||||
return !empty($v);
|
||||
});
|
||||
$domains = array_unique($domains);
|
||||
if (empty($domains)) return json(['code' => -1, 'msg' => '绑定域名不能为空']);
|
||||
$res = $this->check_order($order, $domains);
|
||||
if (is_array($res)) return json($res);
|
||||
}
|
||||
if (empty($order['keytype']) || empty($order['keysize'])) return json(['code' => -1, 'msg' => '必填参数不能为空']);
|
||||
|
||||
Db::startTrans();
|
||||
$id = Db::name('cert_order')->insertGetId($order);
|
||||
$domainList = [];
|
||||
$i=1;
|
||||
foreach($domains as $domain){
|
||||
$i = 1;
|
||||
foreach ($domains as $domain) {
|
||||
$domainList[] = [
|
||||
'oid' => $id,
|
||||
'domain' => convertDomainToAscii($domain),
|
||||
@ -307,31 +345,53 @@ class Cert extends BaseController
|
||||
$id = input('post.id/d');
|
||||
$row = Db::name('cert_order')->where('id', $id)->find();
|
||||
if (!$row) return json(['code' => -1, 'msg' => '证书订单不存在']);
|
||||
|
||||
$domains = input('post.domains', [], 'trim');
|
||||
$order = [
|
||||
'aid' => input('post.aid/d'),
|
||||
'keytype' => input('post.keytype'),
|
||||
'keysize' => input('post.keysize'),
|
||||
'updatetime' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
$domains = array_map('trim', $domains);
|
||||
$domains = array_filter($domains, function ($v) {
|
||||
return !empty($v);
|
||||
});
|
||||
$domains = array_unique($domains);
|
||||
if (empty($domains)) return json(['code' => -1, 'msg' => '绑定域名不能为空']);
|
||||
if (empty($order['aid']) || empty($order['keytype']) || empty($order['keysize'])) return json(['code' => -1, 'msg' => '必填参数不能为空']);
|
||||
|
||||
$res = $this->check_order($order, $domains);
|
||||
if (is_array($res)) return json($res);
|
||||
$aid = input('post.aid/d');
|
||||
if ($aid == -1) {
|
||||
$fullchain = input('post.fullchain', null, 'trim');
|
||||
$privatekey = input('post.privatekey', null, 'trim');
|
||||
$certInfo = $this->parse_cert_key($fullchain, $privatekey);
|
||||
if ($certInfo['code'] == -1) return json($certInfo);
|
||||
$domains = $certInfo['domains'];
|
||||
|
||||
$order = [
|
||||
'aid' => 0,
|
||||
'keytype' => $certInfo['keytype'],
|
||||
'keysize' => $certInfo['keysize'],
|
||||
'updatetime' => date('Y-m-d H:i:s'),
|
||||
'issuetime' => $certInfo['issuetime'],
|
||||
'expiretime' => $certInfo['expiretime'],
|
||||
'issuer' => $certInfo['issuer'],
|
||||
'status' => 3,
|
||||
'issend' => 0,
|
||||
'fullchain' => $fullchain,
|
||||
'privatekey' => $privatekey,
|
||||
];
|
||||
} else {
|
||||
$domains = input('post.domains', [], 'trim');
|
||||
$order = [
|
||||
'aid' => $aid,
|
||||
'keytype' => input('post.keytype'),
|
||||
'keysize' => input('post.keysize'),
|
||||
'updatetime' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
$domains = array_map('trim', $domains);
|
||||
$domains = array_filter($domains, function ($v) {
|
||||
return !empty($v);
|
||||
});
|
||||
$domains = array_unique($domains);
|
||||
if (empty($domains)) return json(['code' => -1, 'msg' => '绑定域名不能为空']);
|
||||
$res = $this->check_order($order, $domains);
|
||||
if (is_array($res)) return json($res);
|
||||
}
|
||||
if (empty($order['keytype']) || empty($order['keysize'])) return json(['code' => -1, 'msg' => '必填参数不能为空']);
|
||||
|
||||
Db::startTrans();
|
||||
Db::name('cert_order')->where('id', $id)->update($order);
|
||||
Db::name('cert_domain')->where('oid', $id)->delete();
|
||||
$domainList = [];
|
||||
$i=1;
|
||||
foreach($domains as $domain){
|
||||
$i = 1;
|
||||
foreach ($domains as $domain) {
|
||||
$domainList[] = [
|
||||
'oid' => $id,
|
||||
'domain' => convertDomainToAscii($domain),
|
||||
@ -341,79 +401,13 @@ class Cert extends BaseController
|
||||
Db::name('cert_domain')->insertAll($domainList);
|
||||
Db::commit();
|
||||
return json(['code' => 0, 'msg' => '修改证书订单成功!']);
|
||||
} elseif ($action == 'import') {
|
||||
$fullchain = input('post.fullchain', null, 'trim');
|
||||
$privatekey = input('post.privatekey', null, 'trim');
|
||||
if (!openssl_x509_read($fullchain)) return json(['code' => -1, 'msg' => '证书内容填写错误']);
|
||||
if (!openssl_get_privatekey($privatekey)) return json(['code' => -1, 'msg' => '私钥内容填写错误']);
|
||||
if (!openssl_x509_check_private_key($fullchain, $privatekey)) return json(['code' => -1, 'msg' => 'SSL证书与私钥不匹配']);
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo || !isset($certInfo['extensions']['subjectAltName'])) return json(['code' => -1, 'msg' => '证书内容解析失败']);
|
||||
|
||||
$domains = [];
|
||||
$subjectAltName = explode(',', $certInfo['extensions']['subjectAltName']);
|
||||
foreach ($subjectAltName as $domain) {
|
||||
$domain = trim($domain);
|
||||
if (strpos($domain, 'DNS:') === 0) $domain = substr($domain, 4);
|
||||
if (!empty($domain)) {
|
||||
$domains[] = $domain;
|
||||
}
|
||||
}
|
||||
$domains = array_unique($domains);
|
||||
if (empty($domains)) return json(['code' => -1, 'msg' => '证书绑定域名不能为空']);
|
||||
$issuetime = date('Y-m-d H:i:s', $certInfo['validFrom_time_t']);
|
||||
$expiretime = date('Y-m-d H:i:s', $certInfo['validTo_time_t']);
|
||||
$issuer = $certInfo['issuer']['CN'];
|
||||
|
||||
$order_ids = Db::name('cert_order')->where('issuetime', $issuetime)->column('id');
|
||||
if (!empty($order_ids)) {
|
||||
foreach ($order_ids as $order_id) {
|
||||
$domains2 = Db::name('cert_domain')->where('oid', $order_id)->column('domain');
|
||||
if (arrays_are_equal($domains2, $domains)) {
|
||||
return json(['code' => -1, 'msg' => '该证书已存在,无需重复添加']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$order = [
|
||||
'aid' => input('post.aid/d'),
|
||||
'keytype' => input('post.keytype'),
|
||||
'keysize' => input('post.keysize'),
|
||||
'addtime' => date('Y-m-d H:i:s'),
|
||||
'updatetime' => date('Y-m-d H:i:s'),
|
||||
'issuetime' => $issuetime,
|
||||
'expiretime' => $expiretime,
|
||||
'issuer' => $issuer,
|
||||
'status' => 3,
|
||||
'fullchain' => $fullchain,
|
||||
'privatekey' => $privatekey,
|
||||
];
|
||||
if (empty($order['aid']) || empty($order['keytype']) || empty($order['keysize'])) return json(['code' => -1, 'msg' => '必填参数不能为空']);
|
||||
|
||||
$res = $this->check_order($order, $domains);
|
||||
if (is_array($res)) return json($res);
|
||||
|
||||
Db::startTrans();
|
||||
$id = Db::name('cert_order')->insertGetId($order);
|
||||
$domainList = [];
|
||||
$i = 1;
|
||||
foreach ($domains as $domain) {
|
||||
$domainList[] = [
|
||||
'oid' => $id,
|
||||
'domain' => $domain,
|
||||
'sort' => $i++,
|
||||
];
|
||||
}
|
||||
Db::name('cert_domain')->insertAll($domainList);
|
||||
Db::commit();
|
||||
return json(['code' => 0, 'msg' => '导入证书成功!']);
|
||||
} elseif ($action == 'del') {
|
||||
$id = input('post.id/d');
|
||||
$dcount = DB::name('cert_deploy')->where('oid', $id)->count();
|
||||
if ($dcount > 0) return json(['code' => -1, 'msg' => '该证书关联了自动部署任务,无法删除']);
|
||||
try{
|
||||
try {
|
||||
(new CertOrderService($id))->cancel();
|
||||
}catch(Exception $e){
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
Db::name('cert_order')->where('id', $id)->delete();
|
||||
Db::name('cert_domain')->where('oid', $id)->delete();
|
||||
@ -425,28 +419,28 @@ class Cert extends BaseController
|
||||
return json(['code' => 0]);
|
||||
} elseif ($action == 'reset') {
|
||||
$id = input('post.id/d');
|
||||
try{
|
||||
try {
|
||||
$service = new CertOrderService($id);
|
||||
$service->cancel();
|
||||
$service->reset();
|
||||
return json(['code' => 0]);
|
||||
}catch(Exception $e){
|
||||
} catch (Exception $e) {
|
||||
return json(['code' => -1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
} elseif ($action == 'revoke') {
|
||||
$id = input('post.id/d');
|
||||
try{
|
||||
try {
|
||||
$service = new CertOrderService($id);
|
||||
$service->revoke();
|
||||
return json(['code' => 0]);
|
||||
}catch(Exception $e){
|
||||
} catch (Exception $e) {
|
||||
return json(['code' => -1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
} elseif ($action == 'show_log') {
|
||||
$processid = input('post.processid');
|
||||
$file = app()->getRuntimePath().'log/'.$processid.'.log';
|
||||
if(!file_exists($file)) return json(['code' => -1, 'msg' => '日志文件不存在']);
|
||||
return json(['code' => 0, 'data' => file_get_contents($file), 'time'=>filemtime($file)]);
|
||||
$file = app()->getRuntimePath() . 'log/' . $processid . '.log';
|
||||
if (!file_exists($file)) return json(['code' => -1, 'msg' => '日志文件不存在']);
|
||||
return json(['code' => 0, 'data' => file_get_contents($file), 'time' => filemtime($file)]);
|
||||
} elseif ($action == 'operation') {
|
||||
$ids = input('post.ids');
|
||||
$success = 0;
|
||||
@ -489,24 +483,79 @@ class Cert extends BaseController
|
||||
$cname = CertHelper::$cert_config[$account['type']]['cname'];
|
||||
if (count($domains) > $max_domains) {
|
||||
if (!(count($domains) == 2 && $max_domains == 1 && ltrim($domains[0], 'www.') == ltrim($domains[1], 'www.'))) {
|
||||
return ['code' => -1, 'msg' => '域名数量不能超过'.$max_domains.'个'];
|
||||
return ['code' => -1, 'msg' => '域名数量不能超过' . $max_domains . '个'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach($domains as $domain){
|
||||
foreach ($domains as $domain) {
|
||||
if (!$wildcard && strpos($domain, '*') !== false) return ['code' => -1, 'msg' => '该证书账户类型不支持泛域名'];
|
||||
$mainDomain = getMainDomain($domain);
|
||||
$drow = Db::name('domain')->where('name', $mainDomain)->find();
|
||||
if (!$drow) {
|
||||
if (substr($domain, 0, 2) == '*.') $domain = substr($domain, 2);
|
||||
if (!$cname || !Db::name('cert_cname')->where('domain', $domain)->where('status', 1)->find()) {
|
||||
return ['code' => -1, 'msg' => '域名'.$domain.'未在本系统添加'];
|
||||
return ['code' => -1, 'msg' => '域名' . $domain . '未在本系统添加'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function parse_cert_key($fullchain, $privatekey)
|
||||
{
|
||||
if (!openssl_x509_read($fullchain)) return ['code' => -1, 'msg' => '证书内容填写错误'];
|
||||
if (!openssl_get_privatekey($privatekey)) return ['code' => -1, 'msg' => '私钥内容填写错误'];
|
||||
if (!openssl_x509_check_private_key($fullchain, $privatekey)) return ['code' => -1, 'msg' => 'SSL证书与私钥不匹配'];
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo || !isset($certInfo['extensions']['subjectAltName'])) return ['code' => -1, 'msg' => '证书内容解析失败'];
|
||||
|
||||
$pubKey = openssl_pkey_get_public($fullchain);
|
||||
if (!$pubKey) return ['code' => -1, 'msg' => '证书公钥解析失败'];
|
||||
$keyDetails = openssl_pkey_get_details($pubKey);
|
||||
$keytype = null;
|
||||
$keysize = 0;
|
||||
switch ($keyDetails['type']) {
|
||||
case OPENSSL_KEYTYPE_RSA:
|
||||
$keytype = 'RSA';
|
||||
$keysize = $keyDetails['bits'];
|
||||
break;
|
||||
case OPENSSL_KEYTYPE_EC:
|
||||
$keytype = 'ECC';
|
||||
$keysize = $keyDetails['bits'];
|
||||
break;
|
||||
case OPENSSL_KEYTYPE_DSA:
|
||||
$keytype = 'DSA';
|
||||
$keysize = $keyDetails['bits'];
|
||||
break;
|
||||
default:
|
||||
$keytype = 'Unknown';
|
||||
}
|
||||
|
||||
$domains = [];
|
||||
$subjectAltName = explode(',', $certInfo['extensions']['subjectAltName']);
|
||||
foreach ($subjectAltName as $domain) {
|
||||
$domain = trim($domain);
|
||||
if (strpos($domain, 'DNS:') === 0) $domain = substr($domain, 4);
|
||||
if (!empty($domain)) {
|
||||
$domains[] = $domain;
|
||||
}
|
||||
}
|
||||
$domains = array_unique($domains);
|
||||
if (empty($domains)) return ['code' => -1, 'msg' => '证书绑定域名不能为空'];
|
||||
$issuetime = date('Y-m-d H:i:s', $certInfo['validFrom_time_t']);
|
||||
$expiretime = date('Y-m-d H:i:s', $certInfo['validTo_time_t']);
|
||||
$issuer = $certInfo['issuer']['CN'];
|
||||
return [
|
||||
'code' => 0,
|
||||
'keytype' => $keytype,
|
||||
'keysize' => $keysize,
|
||||
'issuetime' => $issuetime,
|
||||
'expiretime' => $expiretime,
|
||||
'issuer' => $issuer,
|
||||
'domains' => $domains,
|
||||
];
|
||||
}
|
||||
|
||||
public function order_process()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
@ -518,18 +567,18 @@ class Cert extends BaseController
|
||||
}
|
||||
$id = input('post.id/d');
|
||||
$reset = input('post.reset/d', 0);
|
||||
try{
|
||||
try {
|
||||
$service = new CertOrderService($id);
|
||||
if($reset == 1){
|
||||
if ($reset == 1) {
|
||||
$service->reset();
|
||||
}
|
||||
$retcode = $service->process(true);
|
||||
if($retcode == 3){
|
||||
if ($retcode == 3) {
|
||||
return json(['code' => 0, 'msg' => '证书已签发成功!']);
|
||||
}elseif($retcode == 1){
|
||||
} elseif ($retcode == 1) {
|
||||
return json(['code' => 0, 'msg' => '添加DNS记录成功!请等待DNS生效后点击验证']);
|
||||
}
|
||||
}catch(Exception $e){
|
||||
} catch (Exception $e) {
|
||||
return json(['code' => -1, 'msg' => $e->getMessage(), 'trace' => $e->getTrace()]);
|
||||
}
|
||||
}
|
||||
@ -542,14 +591,15 @@ class Cert extends BaseController
|
||||
$order = null;
|
||||
if ($action == 'edit') {
|
||||
$id = input('get.id/d');
|
||||
$order = Db::name('cert_order')->where('id', $id)->fieldRaw('id,aid,keytype,keysize,status')->find();
|
||||
$order = Db::name('cert_order')->where('id', $id)->fieldRaw('id,aid,keytype,keysize,status,fullchain,privatekey')->find();
|
||||
if (empty($order)) return $this->alert('error', '证书订单不存在');
|
||||
$order['domains'] = Db::name('cert_domain')->where('oid', $order['id'])->order('sort','ASC')->column('domain');
|
||||
$order['domains'] = Db::name('cert_domain')->where('oid', $order['id'])->order('sort', 'ASC')->column('domain');
|
||||
if ($order['aid'] == 0) $order['aid'] = -1;
|
||||
}
|
||||
|
||||
$accounts = [];
|
||||
foreach (Db::name('cert_account')->where('deploy', 0)->select() as $row) {
|
||||
$accounts[$row['id']] = ['name'=>$row['id'].'_'.CertHelper::$cert_config[$row['type']]['name'], 'type'=>$row['type']];
|
||||
$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'] . ')';
|
||||
}
|
||||
@ -561,26 +611,11 @@ class Cert extends BaseController
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function order_import()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$accounts = [];
|
||||
foreach (Db::name('cert_account')->where('deploy', 0)->select() as $row) {
|
||||
$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'] . ')';
|
||||
}
|
||||
}
|
||||
View::assign('accounts', $accounts);
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
|
||||
public function deploytask()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$types = [];
|
||||
foreach(DeployHelper::$deploy_config as $key=>$value){
|
||||
foreach (DeployHelper::$deploy_config as $key => $value) {
|
||||
$types[$key] = $value['name'];
|
||||
}
|
||||
View::assign('types', $types);
|
||||
@ -622,8 +657,8 @@ class Cert extends BaseController
|
||||
$row['typename'] = DeployHelper::$deploy_config[$row['type']]['name'];
|
||||
$row['icon'] = DeployHelper::$deploy_config[$row['type']]['icon'];
|
||||
$row['certtypename'] = CertHelper::$cert_config[$row['certtype']]['name'];
|
||||
$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']));
|
||||
$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;
|
||||
}
|
||||
|
||||
@ -652,7 +687,7 @@ class Cert extends BaseController
|
||||
$id = input('post.id/d');
|
||||
$row = Db::name('cert_deploy')->where('id', $id)->find();
|
||||
if (!$row) return json(['code' => -1, 'msg' => '自动部署任务不存在']);
|
||||
|
||||
|
||||
$task = [
|
||||
'aid' => input('post.aid/d'),
|
||||
'oid' => input('post.oid/d'),
|
||||
@ -673,18 +708,18 @@ class Cert extends BaseController
|
||||
return json(['code' => 0]);
|
||||
} elseif ($action == 'reset') {
|
||||
$id = input('post.id/d');
|
||||
try{
|
||||
try {
|
||||
$service = new CertDeployService($id);
|
||||
$service->reset();
|
||||
return json(['code' => 0]);
|
||||
}catch(Exception $e){
|
||||
} catch (Exception $e) {
|
||||
return json(['code' => -1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
} elseif ($action == 'show_log') {
|
||||
$processid = input('post.processid');
|
||||
$file = app()->getRuntimePath().'log/'.$processid.'.log';
|
||||
if(!file_exists($file)) return json(['code' => -1, 'msg' => '日志文件不存在']);
|
||||
return json(['code' => 0, 'data' => file_get_contents($file), 'time'=>filemtime($file)]);
|
||||
$file = app()->getRuntimePath() . 'log/' . $processid . '.log';
|
||||
if (!file_exists($file)) return json(['code' => -1, 'msg' => '日志文件不存在']);
|
||||
return json(['code' => 0, 'data' => file_get_contents($file), 'time' => filemtime($file)]);
|
||||
} elseif ($action == 'operation') {
|
||||
$ids = input('post.ids');
|
||||
$success = 0;
|
||||
@ -721,14 +756,14 @@ class Cert extends BaseController
|
||||
}
|
||||
$id = input('post.id/d');
|
||||
$reset = input('post.reset/d', 0);
|
||||
try{
|
||||
try {
|
||||
$service = new CertDeployService($id);
|
||||
if($reset == 1){
|
||||
if ($reset == 1) {
|
||||
$service->reset();
|
||||
}
|
||||
$service->process(true);
|
||||
return json(['code' => 0, 'msg' => 'SSL证书部署任务执行成功!']);
|
||||
}catch(Exception $e){
|
||||
} catch (Exception $e) {
|
||||
return json(['code' => -1, 'msg' => $e->getMessage(), 'trace' => $e->getTrace()]);
|
||||
}
|
||||
}
|
||||
@ -747,7 +782,7 @@ class Cert extends BaseController
|
||||
|
||||
$accounts = [];
|
||||
foreach (Db::name('cert_account')->where('deploy', 1)->select() as $row) {
|
||||
$accounts[$row['id']] = ['name'=>$row['id'].'_'.DeployHelper::$deploy_config[$row['type']]['name'], 'type'=>$row['type']];
|
||||
$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'] . ')';
|
||||
}
|
||||
@ -756,9 +791,9 @@ class Cert extends BaseController
|
||||
|
||||
$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) {
|
||||
$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'].')'];
|
||||
$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'] . ')'];
|
||||
}
|
||||
View::assign('orders', $orders);
|
||||
|
||||
@ -829,7 +864,7 @@ class Cert extends BaseController
|
||||
if (empty($data['domain']) || empty($data['rr']) || empty($data['did'])) return json(['code' => -1, 'msg' => '必填参数不能为空']);
|
||||
if (!checkDomain($data['domain'])) return json(['code' => -1, 'msg' => '域名格式不正确']);
|
||||
if (Db::name('cert_cname')->where('domain', $data['domain'])->find()) {
|
||||
return json(['code' => -1, 'msg' => '域名'.$data['domain'].'已存在']);
|
||||
return json(['code' => -1, 'msg' => '域名' . $data['domain'] . '已存在']);
|
||||
}
|
||||
if (Db::name('cert_cname')->where('rr', $data['rr'])->where('did', $data['did'])->find()) {
|
||||
return json(['code' => -1, 'msg' => '已存在相同CNAME记录值']);
|
||||
@ -840,7 +875,7 @@ class Cert extends BaseController
|
||||
$id = input('post.id/d');
|
||||
$row = Db::name('cert_cname')->where('id', $id)->find();
|
||||
if (!$row) return json(['code' => -1, 'msg' => 'CMAME代理不存在']);
|
||||
|
||||
|
||||
$data = [
|
||||
'rr' => input('post.rr', null, 'trim'),
|
||||
'did' => input('post.did/d'),
|
||||
@ -867,13 +902,13 @@ class Cert extends BaseController
|
||||
$domain = '_acme-challenge.' . $row['domain'];
|
||||
$record = $row['rr'] . '.' . $row['cnamedomain'];
|
||||
$result = \app\utils\DnsQueryUtils::get_dns_records($domain, 'CNAME');
|
||||
if(!$result || !in_array($record, $result)){
|
||||
if (!$result || !in_array($record, $result)) {
|
||||
$result = \app\utils\DnsQueryUtils::query_dns_doh($domain, 'CNAME');
|
||||
if(!$result || !in_array($record, $result)){
|
||||
if (!$result || !in_array($record, $result)) {
|
||||
$status = 0;
|
||||
}
|
||||
}
|
||||
if($status != $row['status']){
|
||||
if ($status != $row['status']) {
|
||||
Db::name('cert_cname')->where('id', $id)->update(['status' => $status]);
|
||||
}
|
||||
return json(['code' => 0, 'status' => $status]);
|
||||
|
||||
@ -41,9 +41,10 @@ class DeployHelper
|
||||
'name' => '部署类型',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '宝塔面板站点的证书',
|
||||
'1' => '宝塔面板本身的证书',
|
||||
'2' => '宝塔邮局域名的证书',
|
||||
'0' => '网站的证书',
|
||||
'3' => 'Docker网站的证书',
|
||||
'2' => '邮局域名的证书',
|
||||
'1' => '面板本身的证书',
|
||||
],
|
||||
'value' => '0',
|
||||
'required' => true,
|
||||
@ -53,7 +54,7 @@ class DeployHelper
|
||||
'type' => 'textarea',
|
||||
'placeholder' => '填写要部署证书的网站名称,每行一个',
|
||||
'note' => 'PHP项目和反代项目填写创建时绑定的第一个域名,Java/Node/Go等其他项目填写项目名称,邮局填写域名',
|
||||
'show' => 'type==0||type==2',
|
||||
'show' => 'type==0||type==2||type==3',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
|
||||
@ -46,7 +46,16 @@ class btpanel implements DeployInterface
|
||||
foreach ($sites as $site) {
|
||||
$siteName = trim($site);
|
||||
if (empty($siteName)) continue;
|
||||
if ($config['type'] == '2') {
|
||||
if ($config['type'] == '3') {
|
||||
try {
|
||||
$this->deployDocker($siteName, $fullchain, $privatekey);
|
||||
$this->log("Docker域名 {$siteName} 证书部署成功");
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
$errmsg = $e->getMessage();
|
||||
$this->log("Docker域名 {$siteName} 证书部署失败:" . $errmsg);
|
||||
}
|
||||
} elseif ($config['type'] == '2') {
|
||||
try {
|
||||
$this->deployMailSys($siteName, $fullchain, $privatekey);
|
||||
$this->log("邮局域名 {$siteName} 证书部署成功");
|
||||
@ -129,6 +138,25 @@ class btpanel implements DeployInterface
|
||||
}
|
||||
}
|
||||
|
||||
private function deployDocker($domain, $fullchain, $privatekey)
|
||||
{
|
||||
$path = '/mod/docker/com/set_ssl';
|
||||
$data = [
|
||||
'site_name' => $domain,
|
||||
'key' => $privatekey,
|
||||
'csr' => $fullchain,
|
||||
];
|
||||
$response = $this->request($path, $data);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['status']) && $result['status']) {
|
||||
return true;
|
||||
} elseif (isset($result['msg'])) {
|
||||
throw new Exception($result['msg']);
|
||||
} else {
|
||||
throw new Exception($response ? $response : '返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
|
||||
@ -21,10 +21,14 @@ class CertTaskService
|
||||
private function execute_order()
|
||||
{
|
||||
$days = config_get('cert_renewdays', 7);
|
||||
$list = Db::name('cert_order')->field('id,status,issend')->whereRaw('status NOT IN (3,4) AND (retrytime IS NULL OR retrytime<NOW()) OR status=3 AND isauto=1 AND expiretime<:expiretime', ['expiretime' => date('Y-m-d H:i:s', time() + $days * 86400)])->select();
|
||||
$list = Db::name('cert_order')->field('id,aid,status,issend')->whereRaw('status NOT IN (3,4) AND (retrytime IS NULL OR retrytime<NOW()) OR status=3 AND isauto=1 AND expiretime<:expiretime', ['expiretime' => date('Y-m-d H:i:s', time() + $days * 86400)])->select();
|
||||
//print_r($list);exit;
|
||||
$failcount = 0;
|
||||
foreach ($list as $row) {
|
||||
if ($row['aid'] == 0) {
|
||||
if($row['issend'] == 0) MsgNotice::cert_order_send($row['id'], true);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$service = new CertOrderService($row['id']);
|
||||
if ($row['status'] == 3) {
|
||||
|
||||
@ -66,24 +66,33 @@ class MsgNotice
|
||||
{
|
||||
$row = Db::name('cert_order')->field('id,aid,issuetime,expiretime,issuer,status,error')->where('id', $id)->find();
|
||||
if (!$row) return;
|
||||
$type = Db::name('cert_account')->where('id', $row['aid'])->value('type');
|
||||
$domainList = Db::name('cert_domain')->where('oid', $id)->column('domain');
|
||||
if (empty($domainList)) return;
|
||||
if ($result) {
|
||||
if ($row['aid'] == 0) {
|
||||
if (count($domainList) > 1) {
|
||||
$mail_title = $domainList[0] . '等' . count($domainList) . '个域名SSL证书签发成功通知';
|
||||
$mail_title = $domainList[0] . '等' . count($domainList) . '个域名SSL证书即将到期提醒';
|
||||
} else {
|
||||
$mail_title = $domainList[0] . '域名SSL证书签发成功通知';
|
||||
$mail_title = $domainList[0] . '域名SSL证书即将到期提醒';
|
||||
}
|
||||
$mail_content = '尊敬的用户,您好:您的SSL证书已签发成功!<br/><b>证书账户:</b> '.CertHelper::$cert_config[$type]['name'].'('.$row['aid'].')<br/><b>证书域名:</b> '.implode('、', $domainList).'<br/><b>签发时间:</b> '.$row['issuetime'].'<br/><b>到期时间:</b> '.$row['expiretime'].'<br/><b>颁发机构:</b> '.$row['issuer'];
|
||||
$mail_content = '尊敬的用户,您好:您有一张SSL证书将在'.config_get('cert_renewdays', 7).'天后到期,该证书为手动续期证书,请及时续期!<br/><b>证书域名:</b> '.implode('、', $domainList).'<br/><b>签发时间:</b> '.$row['issuetime'].'<br/><b>到期时间:</b> '.$row['expiretime'].'<br/><b>颁发机构:</b> '.$row['issuer'];
|
||||
} else {
|
||||
$status_arr = [0 => '失败', -1 => '购买证书失败', -2 => '创建订单失败', -3 => '添加DNS失败', -4 => '验证DNS失败', -5 => '验证订单失败', -6 => '订单验证未通过', -7 => '签发证书失败'];
|
||||
if(count($domainList) > 1){
|
||||
$mail_title = $domainList[0].'等'.count($domainList).'个域名SSL证书'.$status_arr[$row['status']].'通知';
|
||||
}else{
|
||||
$mail_title = $domainList[0].'域名SSL证书'.$status_arr[$row['status']].'通知';
|
||||
$type = Db::name('cert_account')->where('id', $row['aid'])->value('type');
|
||||
if ($result) {
|
||||
if (count($domainList) > 1) {
|
||||
$mail_title = $domainList[0] . '等' . count($domainList) . '个域名SSL证书签发成功通知';
|
||||
} else {
|
||||
$mail_title = $domainList[0] . '域名SSL证书签发成功通知';
|
||||
}
|
||||
$mail_content = '尊敬的用户,您好:您的SSL证书已签发成功!<br/><b>证书账户:</b> '.CertHelper::$cert_config[$type]['name'].'('.$row['aid'].')<br/><b>证书域名:</b> '.implode('、', $domainList).'<br/><b>签发时间:</b> '.$row['issuetime'].'<br/><b>到期时间:</b> '.$row['expiretime'].'<br/><b>颁发机构:</b> '.$row['issuer'];
|
||||
} else {
|
||||
$status_arr = [0 => '失败', -1 => '购买证书失败', -2 => '创建订单失败', -3 => '添加DNS失败', -4 => '验证DNS失败', -5 => '验证订单失败', -6 => '订单验证未通过', -7 => '签发证书失败'];
|
||||
if(count($domainList) > 1){
|
||||
$mail_title = $domainList[0].'等'.count($domainList).'个域名SSL证书'.$status_arr[$row['status']].'通知';
|
||||
}else{
|
||||
$mail_title = $domainList[0].'域名SSL证书'.$status_arr[$row['status']].'通知';
|
||||
}
|
||||
$mail_content = '尊敬的用户,您好:您的SSL证书'.$status_arr[$row['status']].'!<br/><b>证书账户:</b> '.CertHelper::$cert_config[$type]['name'].'('.$row['aid'].')<br/><b>证书域名:</b> '.implode('、', $domainList).'<br/><b>失败时间:</b> '.date('Y-m-d H:i:s').'<br/><b>失败原因:</b> <font color="warning">'.$row['error'].'</font>';
|
||||
}
|
||||
$mail_content = '尊敬的用户,您好:您的SSL证书'.$status_arr[$row['status']].'!<br/><b>证书账户:</b> '.CertHelper::$cert_config[$type]['name'].'('.$row['aid'].')<br/><b>证书域名:</b> '.implode('、', $domainList).'<br/><b>失败时间:</b> '.date('Y-m-d H:i:s').'<br/><b>失败原因:</b> <font color="warning">'.$row['error'].'</font>';
|
||||
}
|
||||
$mail_content .= '<br/><font color="grey">'.self::$sitename.'</font><br/><font color="grey">'.date('Y-m-d H:i:s').'</font>';
|
||||
|
||||
|
||||
@ -34,12 +34,6 @@ pre.pre-log{height: 330px;overflow-y: auto;width: 100%;background-color: rgba(51
|
||||
<a href="javascript:searchClear()" class="btn btn-default" title="刷新订单列表"><i class="fa fa-refresh"></i> 刷新</a>
|
||||
<div class="btn-group">
|
||||
<a href="/cert/order/add" class="btn btn-success"><i class="fa fa-plus"></i> 添加</a>
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="/cert/order/import">导入已有证书</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">批量操作 <span class="caret"></span></button>
|
||||
@ -87,7 +81,10 @@ $(document).ready(function(){
|
||||
field: 'typename',
|
||||
title: '证书账户',
|
||||
formatter: function(value, row, index) {
|
||||
return '<span title="'+row.aremark+'" data-toggle="tooltip" data-placement="right"><img src="/static/images/'+row.icon+'" class="type-logo">'+value+'('+row.aid+')</span>';
|
||||
if(value){
|
||||
return '<span title="'+row.aremark+'" data-toggle="tooltip" data-placement="right"><img src="/static/images/'+row.icon+'" class="type-logo">'+value+'('+row.aid+')</span>';
|
||||
}
|
||||
return '手动续期';
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -208,7 +205,10 @@ $(document).ready(function(){
|
||||
}else if(row.status == 2) {
|
||||
html += '<a href="javascript:doOrder(\''+row.id+'\')" class="btn btn-success btn-xs"><i class="fa fa-check-circle"></i> 继续验证</a> ';
|
||||
}else if(row.status == 3) {
|
||||
html += '<a href="javascript:download(\''+row.id+'\')" class="btn btn-success btn-xs"><i class="fa fa-download"></i> 下载</a> <a href="javascript:renewOrder(\''+row.id+'\')" class="btn btn-warning btn-xs"><i class="fa fa-refresh"></i> 续签</a> ';
|
||||
html += '<a href="javascript:download(\''+row.id+'\')" class="btn btn-success btn-xs"><i class="fa fa-download"></i> 下载</a> ';
|
||||
if(row.aid > 0){
|
||||
html += '<a href="javascript:renewOrder(\''+row.id+'\')" class="btn btn-warning btn-xs"><i class="fa fa-refresh"></i> 续签</a> ';
|
||||
}
|
||||
}else if(row.status == 4) {
|
||||
html += '<a href="javascript:renewOrder(\''+row.id+'\')" class="btn btn-success btn-xs"><i class="fa fa-play-circle"></i> 重新申请</a> ';
|
||||
}else{
|
||||
@ -219,7 +219,9 @@ $(document).ready(function(){
|
||||
html += '<li><a href="javascript:showLog(\''+row.processid+'\')">查看日志</a></li>';
|
||||
if(row.status == 3){
|
||||
html += '<li><a href="/cert/deploytask?oid='+row.id+'">部署任务</a></li>';
|
||||
html += '<li><a href="javascript:revokeOrder(\''+row.id+'\')">吊销证书</a></li>';
|
||||
if(row.aid > 0){
|
||||
html += '<li><a href="javascript:revokeOrder(\''+row.id+'\')">吊销证书</a></li>';
|
||||
}
|
||||
}else if(row.status < 0){
|
||||
html += '<li><a href="javascript:resetOrder(\''+row.id+'\')">重置订单</a></li>';
|
||||
}else if(row.status == 1 || row.status == 2){
|
||||
|
||||
@ -22,9 +22,28 @@
|
||||
{foreach $accounts as $k=>$v}
|
||||
<option value="{$k}" data-type="{$v.type}">{$v.name}</option>
|
||||
{/foreach}
|
||||
<option value="-1" data-type="">手动续期</option>
|
||||
</select></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-group" v-show="set.aid==-1">
|
||||
<label class="col-sm-3 control-label no-padding-right" is-required>证书内容</label>
|
||||
<div class="col-sm-6">
|
||||
<div class="input-group">
|
||||
<textarea name="fullchain" v-model="set.fullchain" class="form-control" rows="5" placeholder="输入PEM格式证书链" required></textarea>
|
||||
<a class="btn btn-default input-group-addon" @click="upload('fullchain')" title="上传证书文件"><i class="fa fa-upload"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-show="set.aid==-1">
|
||||
<label class="col-sm-3 control-label no-padding-right" is-required>私钥内容</label>
|
||||
<div class="col-sm-6">
|
||||
<div class="input-group">
|
||||
<textarea name="privatekey" v-model="set.privatekey" class="form-control" rows="5" placeholder="输入PEM格式私钥" required></textarea>
|
||||
<a class="btn btn-default input-group-addon" @click="upload('privatekey')" title="上传私钥文件"><i class="fa fa-upload"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" v-show="set.aid!=-1">
|
||||
<label class="col-sm-3 control-label no-padding-right" is-required>签名算法</label>
|
||||
<div class="col-sm-6">
|
||||
<label class="radio-inline" v-for="item in keytypeList">
|
||||
@ -32,7 +51,7 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-group" v-show="set.aid!=-1">
|
||||
<label class="col-sm-3 control-label no-padding-right" is-required>密钥长度</label>
|
||||
<div class="col-sm-6">
|
||||
<label class="radio-inline" v-for="item in keysizeList">
|
||||
@ -41,7 +60,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="form-group" v-show="set.aid!=-1">
|
||||
<label class="col-sm-3 control-label no-padding-right" is-required>绑定域名</label>
|
||||
<div class="col-sm-6">
|
||||
<textarea name="domains" v-model="domains" class="form-control" rows="5" placeholder="请输入域名,一行一个" required></textarea>
|
||||
@ -51,7 +70,8 @@
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-3 col-sm-6"><button type="button" class="btn btn-primary" @click="submit">提交</button></div>
|
||||
</div>
|
||||
<div class="panel panel-default"><div class="panel-body"><p><b style="color:#39b603;"><i class="fa fa-info-circle fa-fw"></i></b>提示:添加或修改订单信息,点击提交后,不会立即执行签发,只能通过计划任务或列表手动点击来执行</p><p>证书签发之前确保该主域名下没有CAA类型记录,避免证书验证失败。</p></div></div>
|
||||
<div class="panel panel-default" v-show="set.aid!=-1"><div class="panel-body"><p><b style="color:#39b603;"><i class="fa fa-info-circle fa-fw"></i></b>提示:添加或修改订单信息,点击提交后,不会立即执行签发,只能通过计划任务或列表手动点击来执行</p><p>证书签发之前确保该主域名下没有CAA类型记录,避免证书验证失败。</p></div></div>
|
||||
<div class="panel panel-default" v-show="set.aid==-1"><div class="panel-body"><p><b style="color:#39b603;"><i class="fa fa-info-circle fa-fw"></i></b>提示:选择手动续期,到达设置的续期天数,只会发送消息通知。</p></div></div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@ -72,6 +92,8 @@ new Vue({
|
||||
set: {
|
||||
id: '',
|
||||
aid: '',
|
||||
fullchain: '',
|
||||
privatekey: '',
|
||||
keytype: '',
|
||||
keysize: '',
|
||||
domains: [],
|
||||
@ -150,6 +172,22 @@ new Vue({
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
},
|
||||
upload(name){
|
||||
//读取上传文件并填充到表单
|
||||
var file = document.createElement('input');
|
||||
file.type = 'file';
|
||||
file.accept = '.pem,.crt,.key';
|
||||
file.style.display = 'none';
|
||||
file.onchange = function(){
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e){
|
||||
this.set[name] = e.target.result;
|
||||
}.bind(this);
|
||||
reader.readAsText(file.files[0]);
|
||||
}.bind(this);
|
||||
document.body.appendChild(file);
|
||||
file.click();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,167 +0,0 @@
|
||||
{extend name="common/layout" /}
|
||||
{block name="title"}导入已有证书{/block}
|
||||
{block name="main"}
|
||||
<style>
|
||||
.tips{color: #f6a838; padding-left: 5px;}
|
||||
.control-label[is-required]:before {
|
||||
content: "*";
|
||||
color: #f56c6c;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.input-group-addon{padding: 6px 6px;}
|
||||
</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="/cert/certorder" class="btn btn-sm btn-default pull-right" style="margin-top:-6px"><i class="fa fa-reply fa-fw"></i> 返回</a>导入已有证书</h3></div>
|
||||
<div class="panel-body">
|
||||
<form onsubmit="return false" method="post" class="form-horizontal" role="form" id="taskform">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label no-padding-right" is-required>证书内容</label>
|
||||
<div class="col-sm-6">
|
||||
<div class="input-group">
|
||||
<textarea name="fullchain" v-model="set.fullchain" class="form-control" rows="5" placeholder="输入PEM格式证书链" required></textarea>
|
||||
<a class="btn btn-default input-group-addon" @click="upload('fullchain')" title="上传证书文件"><i class="fa fa-upload"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label no-padding-right" is-required>私钥内容</label>
|
||||
<div class="col-sm-6">
|
||||
<div class="input-group">
|
||||
<textarea name="privatekey" v-model="set.privatekey" class="form-control" rows="5" placeholder="输入PEM格式私钥" required></textarea>
|
||||
<a class="btn btn-default input-group-addon" @click="upload('privatekey')" title="上传私钥文件"><i class="fa fa-upload"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 col-xs-12 control-label no-padding-right" is-required>证书续期账户</label>
|
||||
<div class="col-sm-6"><select name="aid" v-model="set.aid" class="form-control" required>
|
||||
<option value="">--选择证书账户--</option>
|
||||
{foreach $accounts as $k=>$v}
|
||||
<option value="{$k}" data-type="{$v.type}">{$v.name}</option>
|
||||
{/foreach}
|
||||
</select></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label no-padding-right" is-required>签名算法</label>
|
||||
<div class="col-sm-6">
|
||||
<label class="radio-inline" v-for="item in keytypeList">
|
||||
<input type="radio" name="keytype" :value="item" v-model="set.keytype"> {{item}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label no-padding-right" is-required>密钥长度</label>
|
||||
<div class="col-sm-6">
|
||||
<label class="radio-inline" v-for="item in keysizeList">
|
||||
<input type="radio" name="keysize" :value="item.value" v-model="set.keysize"> {{item.label}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-3 col-sm-6"><button type="button" class="btn btn-primary" @click="submit">提交</button></div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{/block}
|
||||
{block name="script"}
|
||||
<script src="{$cdnpublic}vue/2.6.14/vue.min.js"></script>
|
||||
<script src="{$cdnpublic}layer/3.1.1/layer.js"></script>
|
||||
<script src="/static/js/bootstrapValidator.min.js"></script>
|
||||
<script>
|
||||
new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
type: '',
|
||||
set: {
|
||||
fullchain: '',
|
||||
privatekey: '',
|
||||
aid: '',
|
||||
keytype: '',
|
||||
keysize: '',
|
||||
},
|
||||
keytypeList: [
|
||||
'RSA',
|
||||
'ECC'
|
||||
],
|
||||
keysizeMap: [
|
||||
{label:'2048 bit',value:'2048',type:'RSA'},
|
||||
{label:'3072 bit',value:'3072',type:'RSA'},
|
||||
{label:'P-256',value:'256',type:'ECC'},
|
||||
{label:'P-384',value:'384',type:'ECC'},
|
||||
],
|
||||
keysizeList: [],
|
||||
},
|
||||
watch: {
|
||||
'set.aid': function(val){
|
||||
this.type = $('option:selected', 'select[name=aid]').data('type');
|
||||
},
|
||||
'set.keytype': function(val){
|
||||
this.keysizeList = this.keysizeMap.filter((item) => {
|
||||
return item.type == val;
|
||||
})
|
||||
if(!this.keysizeList.filter((item) => {return item.value == this.set.keysize}).length)
|
||||
this.set.keysize = this.keysizeList[0].value;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.set.keytype = 'RSA';
|
||||
$("#taskform").bootstrapValidator({
|
||||
live: 'submitted',
|
||||
});
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
},
|
||||
methods: {
|
||||
submit(){
|
||||
var that=this;
|
||||
$("#taskform").data("bootstrapValidator").validate();
|
||||
if(!$("#taskform").data("bootstrapValidator").isValid()){
|
||||
return false;
|
||||
}
|
||||
var ii = layer.load(2, {shade:[0.1,'#fff']});
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "",
|
||||
data: this.set,
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
layer.close(ii);
|
||||
if(data.code == 0){
|
||||
layer.alert(data.msg, {icon: 1}, function(){
|
||||
if(document.referrer.indexOf('/cert/certorder?') > 0)
|
||||
window.location.href = document.referrer;
|
||||
else
|
||||
window.location.href = '/cert/certorder';
|
||||
});
|
||||
}else{
|
||||
layer.alert(data.msg, {icon: 2});
|
||||
}
|
||||
},
|
||||
error: function(data){
|
||||
layer.close(ii);
|
||||
layer.msg('服务器错误');
|
||||
}
|
||||
});
|
||||
},
|
||||
upload(name){
|
||||
//读取上传文件并填充到表单
|
||||
var file = document.createElement('input');
|
||||
file.type = 'file';
|
||||
file.accept = '.pem,.crt,.key';
|
||||
file.style.display = 'none';
|
||||
file.onchange = function(){
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e){
|
||||
this.set[name] = e.target.result;
|
||||
}.bind(this);
|
||||
reader.readAsText(file.files[0]);
|
||||
}.bind(this);
|
||||
document.body.appendChild(file);
|
||||
file.click();
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{/block}
|
||||
@ -98,7 +98,6 @@ Route::group(function () {
|
||||
Route::post('/cert/order/data', 'cert/order_data');
|
||||
Route::post('/cert/order/process', 'cert/order_process');
|
||||
Route::post('/cert/order/:action', 'cert/order_op');
|
||||
Route::get('/cert/order/import', 'cert/order_import');
|
||||
Route::get('/cert/order/:action', 'cert/order_form');
|
||||
|
||||
Route::get('/cert/deploytask', 'cert/deploytask');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user