mirror of
https://github.com/netcccyun/dnsmgr.git
synced 2026-03-09 06:57:21 +08:00
增加群辉面板部署,支持状态过滤
This commit is contained in:
parent
70f2e0d487
commit
12d8017df5
@ -203,6 +203,7 @@ class Cert extends BaseController
|
||||
$domain = $this->request->post('domain', null, 'trim');
|
||||
$id = input('post.id');
|
||||
$type = input('post.type', null, 'trim');
|
||||
$status = input('post.status', null, 'trim');
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
|
||||
@ -216,6 +217,17 @@ class Cert extends BaseController
|
||||
if (!empty($type)) {
|
||||
$select->where('B.type', $type);
|
||||
}
|
||||
if (!isNullOrEmpty($status)) {
|
||||
if ($status == '5') {
|
||||
$select->where('A.status', '<', 0);
|
||||
} elseif ($status == '6') {
|
||||
$select->where('A.expiretime', '<', date('Y-m-d H:i:s', time() + 86400 * 7))->where('A.expiretime', '>=', date('Y-m-d H:i:s'));
|
||||
} elseif ($status == '7') {
|
||||
$select->where('A.expiretime', '<', date('Y-m-d H:i:s'));
|
||||
} else {
|
||||
$select->where('A.status', $status);
|
||||
}
|
||||
}
|
||||
$total = $select->count();
|
||||
$rows = $select->fieldRaw('A.*,B.type,B.remark aremark')->order('id', 'desc')->limit($offset, $limit)->select();
|
||||
|
||||
@ -541,6 +553,7 @@ class Cert extends BaseController
|
||||
$domain = $this->request->post('domain', null, 'trim');
|
||||
$oid = input('post.oid');
|
||||
$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');
|
||||
@ -555,6 +568,9 @@ class Cert extends BaseController
|
||||
if (!empty($type)) {
|
||||
$select->where('B.type', $type);
|
||||
}
|
||||
if (!isNullOrEmpty($status)) {
|
||||
$select->where('A.status', $status);
|
||||
}
|
||||
if (!empty($remark)) {
|
||||
$select->where('A.remark', $remark);
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ class DeployHelper
|
||||
],
|
||||
],
|
||||
'kangle' => [
|
||||
'name' => 'Kangle',
|
||||
'name' => 'Kangle用户',
|
||||
'class' => 1,
|
||||
'icon' => 'host.png',
|
||||
'note' => '以上登录信息为Easypanel用户面板的,非管理员面板。如选网站密码认证类型,则用户面板登录不能开启验证码。',
|
||||
@ -131,6 +131,72 @@ class DeployHelper
|
||||
],
|
||||
],
|
||||
],
|
||||
'kangleadmin' => [
|
||||
'name' => 'Kangle管理员',
|
||||
'class' => 1,
|
||||
'icon' => 'host.png',
|
||||
'note' => '以上登录地址需填写Easypanel管理员面板地址,非用户面板。',
|
||||
'inputs' => [
|
||||
'url' => [
|
||||
'name' => '面板地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => 'Easypanel管理员面板地址',
|
||||
'note' => '填写规则如:http://192.168.1.100:3312 ,不要带其他后缀',
|
||||
'required' => true,
|
||||
],
|
||||
'path' => [
|
||||
'name' => '管理员面板路径',
|
||||
'type' => 'input',
|
||||
'placeholder' => '留空默认为/admin',
|
||||
],
|
||||
'username' => [
|
||||
'name' => '管理员用户名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'skey' => [
|
||||
'name' => '面板安全码',
|
||||
'type' => 'input',
|
||||
'placeholder' => '管理员面板->服务器设置->面板通信安全码',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'taskinputs' => [
|
||||
'name' => [
|
||||
'name' => '网站用户名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'type' => [
|
||||
'name' => '部署类型',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '网站SSL证书',
|
||||
'1' => '单域名SSL证书(仅CDN支持)',
|
||||
],
|
||||
'value' => '0',
|
||||
'required' => true,
|
||||
],
|
||||
'domains' => [
|
||||
'name' => 'CDN域名列表',
|
||||
'type' => 'textarea',
|
||||
'placeholder' => '填写要部署证书的域名,每行一个',
|
||||
'show' => 'type==1',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
'safeline' => [
|
||||
'name' => '雷池WAF',
|
||||
'class' => 1,
|
||||
@ -400,6 +466,61 @@ class DeployHelper
|
||||
],
|
||||
],
|
||||
],
|
||||
'synology' => [
|
||||
'name' => '群辉面板',
|
||||
'class' => 1,
|
||||
'icon' => 'synology.png',
|
||||
'note' => null,
|
||||
'tasknote' => '',
|
||||
'inputs' => [
|
||||
'url' => [
|
||||
'name' => '面板地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => '群辉面板地址',
|
||||
'note' => '填写规则如:http://192.168.1.100:5000 ,不要带其他后缀',
|
||||
'required' => true,
|
||||
],
|
||||
'username' => [
|
||||
'name' => '登录账号',
|
||||
'type' => 'input',
|
||||
'placeholder' => '必须是处于管理员用户组,不能开启双重认证',
|
||||
'required' => true,
|
||||
],
|
||||
'password' => [
|
||||
'name' => '登录密码',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'version' => [
|
||||
'name' => '群辉版本',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '7.x',
|
||||
'1' => '6.x',
|
||||
],
|
||||
'value' => '0',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'taskinputs' => [
|
||||
'desc' => [
|
||||
'name' => '群晖证书描述',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'note' => '根据证书描述匹配替换对应证书,留空则根据证书通用名匹配',
|
||||
],
|
||||
],
|
||||
],
|
||||
'aliyun' => [
|
||||
'name' => '阿里云',
|
||||
'class' => 2,
|
||||
|
||||
173
app/lib/deploy/kangleadmin.php
Normal file
173
app/lib/deploy/kangleadmin.php
Normal file
@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class kangleadmin implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $path;
|
||||
private $username;
|
||||
private $skey;
|
||||
private $proxy;
|
||||
private $cookie;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
if (empty($config['path'])) $config['path'] = '/admin';
|
||||
$this->path = rtrim($config['path'], '/');
|
||||
$this->username = $config['username'];
|
||||
$this->skey = $config['skey'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->username) || empty($this->skey)) throw new Exception('必填参数不能为空');
|
||||
$this->login();
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (empty($config['name'])) throw new Exception('网站用户名不能为空');
|
||||
$this->login();
|
||||
$this->log('登录成功 cookie:' . $this->cookie);
|
||||
$this->loginVhost($config['name']);
|
||||
|
||||
if ($config['type'] == '1' && !empty($config['domains'])) {
|
||||
$domains = explode("\n", $config['domains']);
|
||||
$success = 0;
|
||||
$errmsg = null;
|
||||
foreach ($domains as $domain) {
|
||||
$domain = trim($domain);
|
||||
if (empty($domain)) continue;
|
||||
try {
|
||||
$this->deployDomain($domain, $fullchain, $privatekey);
|
||||
$this->log("域名 {$domain} 证书部署成功");
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
$errmsg = $e->getMessage();
|
||||
$this->log("域名 {$domain} 证书部署失败:" . $errmsg);
|
||||
}
|
||||
}
|
||||
if ($success == 0) {
|
||||
throw new Exception($errmsg ? $errmsg : '要部署的域名不存在');
|
||||
}
|
||||
} else {
|
||||
$this->deployAccount($fullchain, $privatekey);
|
||||
$this->log("账号级SSL证书部署成功");
|
||||
}
|
||||
}
|
||||
|
||||
private function deployDomain($domain, $fullchain, $privatekey)
|
||||
{
|
||||
$path = '/vhost/?c=ssl&a=domainSsl';
|
||||
$post = [
|
||||
'domain' => $domain,
|
||||
'certificate' => $fullchain,
|
||||
'certificate_key' => $privatekey,
|
||||
];
|
||||
$response = curl_client($this->url . $path, http_build_query($post), null, $this->cookie, null, $this->proxy);
|
||||
if (strpos($response['body'], '成功')) {
|
||||
return true;
|
||||
} elseif (preg_match('/alert\(\'(.*?)\'\)/i', $response['body'], $match)) {
|
||||
throw new Exception(htmlspecialchars($match[1]));
|
||||
} elseif (strlen($response['body']) > 3 && strlen($response['body']) < 50) {
|
||||
throw new Exception(htmlspecialchars($response['body']));
|
||||
} else {
|
||||
throw new Exception('原因未知(httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
}
|
||||
|
||||
private function deployAccount($fullchain, $privatekey)
|
||||
{
|
||||
$path = '/vhost/?c=ssl&a=ssl';
|
||||
$post = [
|
||||
'certificate' => $fullchain,
|
||||
'certificate_key' => $privatekey,
|
||||
];
|
||||
$response = curl_client($this->url . $path, http_build_query($post), null, $this->cookie, null, $this->proxy);
|
||||
if (strpos($response['body'], '成功')) {
|
||||
return true;
|
||||
} elseif (preg_match('/alert\(\'(.*?)\'\)/i', $response['body'], $match)) {
|
||||
throw new Exception(htmlspecialchars($match[1]));
|
||||
} elseif (strlen($response['body']) > 3 && strlen($response['body']) < 50) {
|
||||
throw new Exception(htmlspecialchars($response['body']));
|
||||
} else {
|
||||
throw new Exception('原因未知(httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
}
|
||||
|
||||
private function login()
|
||||
{
|
||||
$url = $this->url . $this->path . '/index.php?c=sso&a=hello&url=' . urlencode($this->url . $this->path . '/index.php?');
|
||||
$response = curl_client($url, null, null, null, null, $this->proxy);
|
||||
if ($response['code'] == 302 && !empty($response['redirect_url'])) {
|
||||
$cookie = '';
|
||||
if (preg_match_all('/Set-Cookie: (.*);/iU', $response['header'], $matchs)) {
|
||||
foreach ($matchs[1] as $val) {
|
||||
$arr = explode('=', $val);
|
||||
if ($arr[1] == '' || $arr[1] == 'deleted') continue;
|
||||
$cookie .= $val . '; ';
|
||||
}
|
||||
$query = parse_url($response['redirect_url'], PHP_URL_QUERY);
|
||||
parse_str($query, $params);
|
||||
if (isset($params['r'])) {
|
||||
$sess_key = $params['r'];
|
||||
$this->login2($cookie, $sess_key);
|
||||
$this->cookie = $cookie;
|
||||
return true;
|
||||
} else {
|
||||
throw new Exception('获取SSO凭据失败,sess_key获取失败');
|
||||
}
|
||||
} else {
|
||||
throw new Exception('获取SSO凭据失败,获取cookie失败');
|
||||
}
|
||||
} elseif (strlen($response['body']) > 3 && strlen($response['body']) < 50) {
|
||||
throw new Exception('获取SSO凭据失败 (' . htmlspecialchars($response['body']) . ')');
|
||||
} else {
|
||||
throw new Exception('获取SSO凭据失败 (httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
}
|
||||
|
||||
private function login2($cookie, $sess_key)
|
||||
{
|
||||
$s = md5($sess_key . $this->username . $sess_key . $this->skey);
|
||||
$url = $this->url . $this->path . '/index.php?c=sso&a=login&name=' . $this->username . '&r=' . $sess_key . '&s=' . $s;
|
||||
$response = curl_client($url, null, null, $cookie, null, $this->proxy);
|
||||
if ($response['code'] == 302) {
|
||||
return true;
|
||||
} elseif (strlen($response['body']) > 3 && strlen($response['body']) < 50) {
|
||||
throw new Exception('SSO登录失败 (' . htmlspecialchars($response['body']) . ')');
|
||||
} else {
|
||||
throw new Exception('SSO登录失败 (httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
}
|
||||
|
||||
private function loginVhost($name)
|
||||
{
|
||||
$url = $this->url . $this->path . '/index.php?c=vhost&a=impLogin&name=' . $name;
|
||||
$response = curl_client($url, null, null, $this->cookie, null, $this->proxy);
|
||||
if ($response['code'] == 302) {
|
||||
curl_client($this->url . '/vhost/', null, null, $this->cookie, null, $this->proxy);
|
||||
} else {
|
||||
throw new Exception('用户面板登录失败 (httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
147
app/lib/deploy/synology.php
Normal file
147
app/lib/deploy/synology.php
Normal file
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class synology implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $username;
|
||||
private $password;
|
||||
private $version;
|
||||
private $token;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
$this->username = $config['username'];
|
||||
$this->password = $config['password'];
|
||||
$this->version = $config['version'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->username) || empty($this->password)) throw new Exception('必填内容不能为空');
|
||||
$this->login();
|
||||
}
|
||||
|
||||
private function login()
|
||||
{
|
||||
$url = $this->url . '/webapi/' . ($this->version == '1' ? 'auth.cgi' : 'entry.cgi');
|
||||
$params = [
|
||||
'api' => 'SYNO.API.Auth',
|
||||
'version' => 6,
|
||||
'method' => 'login',
|
||||
'account' => $this->username,
|
||||
'passwd' => $this->password,
|
||||
'format' => 'sid',
|
||||
'enable_syno_token' => 'yes',
|
||||
];
|
||||
$response = curl_client($url, http_build_query($params), null, null, null, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['success']) && $result['success']) {
|
||||
$this->token = $result['data'];
|
||||
} elseif(isset($result['error'])) {
|
||||
throw new Exception('登录失败:' . $result['error']);
|
||||
} else {
|
||||
throw new Exception('请求失败(httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
|
||||
$url = $this->url . '/webapi/entry.cgi';
|
||||
$params = [
|
||||
'api' => 'SYNO.Core.Certificate.CRT',
|
||||
'version' => 1,
|
||||
'method' => 'list',
|
||||
'_sid' => $this->token['sid'],
|
||||
'SynoToken' => $this->token['synotoken'],
|
||||
];
|
||||
$response = curl_client($url, http_build_query($params), null, null, null, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['success']) && $result['success']) {
|
||||
$this->log('获取证书列表成功');
|
||||
} elseif(isset($result['error'])) {
|
||||
throw new Exception('获取证书列表失败:' . $result['error']);
|
||||
} else {
|
||||
throw new Exception('获取证书列表失败(httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
|
||||
$id = null;
|
||||
foreach ($result['data']['certificates'] as $certificate) {
|
||||
if ($certificate['subject']['common_name'] == $certInfo['subject']['CN'] || $certificate['desc'] == $config['desc']) {
|
||||
$id = $certificate['id'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($id) {
|
||||
$this->import($fullchain, $privatekey, $config, $id);
|
||||
} else {
|
||||
$this->import($fullchain, $privatekey, $config);
|
||||
}
|
||||
}
|
||||
|
||||
private function import($fullchain, $privatekey, $config, $id = null)
|
||||
{
|
||||
$url = $this->url . '/webapi/entry.cgi';
|
||||
$params = [
|
||||
'api' => 'SYNO.Core.Certificate',
|
||||
'version' => 1,
|
||||
'method' => 'import',
|
||||
'_sid' => $this->token['sid'],
|
||||
'SynoToken' => $this->token['synotoken'],
|
||||
];
|
||||
$privatekey_file = tempnam(sys_get_temp_dir(), 'privatekey');
|
||||
file_put_contents($privatekey_file, $privatekey);
|
||||
$fullchain_file = tempnam(sys_get_temp_dir(), 'fullchain');
|
||||
file_put_contents($fullchain_file, $fullchain);
|
||||
$post = [
|
||||
'key' => new \CURLFile($privatekey_file),
|
||||
'cert' => new \CURLFile($fullchain_file),
|
||||
'id' => $id,
|
||||
'desc' => $config['desc'],
|
||||
];
|
||||
$response = curl_client($url . '?' . http_build_query($params), $post, null, null, null, $this->proxy);
|
||||
unlink($privatekey_file);
|
||||
unlink($fullchain_file);
|
||||
$result = json_decode($response['body'], true);
|
||||
if ($id) {
|
||||
if (isset($result['success']) && $result['success']) {
|
||||
$this->log('证书ID:'.$id.'更新成功!');
|
||||
} elseif(isset($result['error'])) {
|
||||
throw new Exception('证书ID:'.$id.'更新失败:' . $result['error']);
|
||||
} else {
|
||||
throw new Exception('证书ID:'.$id.'更新失败(httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
} else {
|
||||
if (isset($result['success']) && $result['success']) {
|
||||
$this->log('证书上传成功!');
|
||||
} elseif(isset($result['error'])) {
|
||||
throw new Exception('证书上传失败:' . $result['error']);
|
||||
} else {
|
||||
throw new Exception('证书上传失败(httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -27,6 +27,9 @@ pre.pre-log{height: 330px;overflow-y: auto;width: 100%;background-color: rgba(51
|
||||
<option value="{$k}">{$v}</option>
|
||||
{/foreach}</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<select name="status" class="form-control"><option value="">所有状态</option><option value="0">待提交</option><option value="1">待验证</option><option value="2">正在验证</option><option value="5">失败</option><option value="3">已签发</option><option value="4">已吊销</option><option value="6">即将过期</option><option value="7">已过期</option></select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
|
||||
<a href="javascript:searchClear()" class="btn btn-default" title="刷新订单列表"><i class="fa fa-refresh"></i> 刷新</a>
|
||||
<div class="btn-group">
|
||||
@ -137,6 +140,16 @@ $(document).ready(function(){
|
||||
} else if(value == 3) {
|
||||
return '<span class="label label-success">已签发</span>';
|
||||
} else if(value == 2) {
|
||||
if(row.retrytime != null){
|
||||
var now = new Date().getTime();
|
||||
var retry = new Date(row.retrytime).getTime();
|
||||
var diff = retry - now;
|
||||
if(diff > 0){
|
||||
var min = Math.floor(diff / 60000);
|
||||
var sec = Math.floor((diff - min * 60000) / 1000);
|
||||
return '<span title="'+min+'分'+sec+'秒后自动验证" data-toggle="tooltip" data-placement="top" class="label" style="background-color: #3e76fb;">正在验证</span>';
|
||||
}
|
||||
}
|
||||
return '<span class="label" style="background-color: #3e76fb;">正在验证</span>';
|
||||
} else if(value == 1) {
|
||||
if(row.retrytime != null){
|
||||
|
||||
@ -29,6 +29,9 @@ pre.pre-log{height: 330px;overflow-y: auto;width: 100%;background-color: rgba(51
|
||||
<option value="{$k}">{$v}</option>
|
||||
{/foreach}</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<select name="status" class="form-control"><option value="">所有状态</option><option value="0">待处理</option><option value="1">已完成</option><option value="-1">处理失败</option></select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
|
||||
<a href="javascript:searchClear()" class="btn btn-default" title="刷新任务列表"><i class="fa fa-refresh"></i> 刷新</a>
|
||||
<a href="/cert/deploy/add" class="btn btn-success"><i class="fa fa-plus"></i> 添加</a>
|
||||
|
||||
BIN
public/static/images/ctyun.ico
Normal file
BIN
public/static/images/ctyun.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
BIN
public/static/images/synology.png
Normal file
BIN
public/static/images/synology.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 KiB |
Loading…
Reference in New Issue
Block a user