新增支持宝塔Win极速版,lecdn支持令牌认证

This commit is contained in:
net909 2025-08-25 17:18:35 +08:00
parent fd21a55d01
commit 79437aba60
4 changed files with 240 additions and 37 deletions

View File

@ -67,7 +67,7 @@ class Schedule extends BaseController
'switchdate' => input('post.switchdate', null, 'trim'),
'switchtime' => input('post.switchtime', null, 'trim'),
'value' => input('post.value', null, 'trim'),
'line' => input('post.value', null, 'trim'),
'line' => input('post.line', null, 'trim'),
'remark' => input('post.remark', null, 'trim'),
'recordinfo' => input('post.recordinfo', null, 'trim'),
'addtime' => time(),
@ -96,7 +96,7 @@ class Schedule extends BaseController
'switchdate' => input('post.switchdate', null, 'trim'),
'switchtime' => input('post.switchtime', null, 'trim'),
'value' => input('post.value', null, 'trim'),
'line' => input('post.value', null, 'trim'),
'line' => input('post.line', null, 'trim'),
'remark' => input('post.remark', null, 'trim'),
'recordinfo' => input('post.recordinfo', null, 'trim'),
];

View File

@ -11,7 +11,7 @@ class DeployHelper
'name' => '宝塔面板',
'class' => 1,
'icon' => 'bt.png',
'desc' => '支持部署到宝塔面板搭建的站点、Docker、邮局与面板本身',
'desc' => '支持部署到宝塔面板&aaPanel搭建的站点、Docker、邮局与面板本身',
'note' => null,
'inputs' => [
'url' => [
@ -27,6 +27,15 @@ class DeployHelper
'placeholder' => '宝塔面板设置->面板设置->API接口',
'required' => true,
],
'version' => [
'name' => '面板版本',
'type' => 'radio',
'options' => [
'0' => 'Linux面板+Win经典版',
'1' => 'Win极速版',
],
'value' => '0'
],
'proxy' => [
'name' => '使用代理服务器',
'type' => 'radio',
@ -54,10 +63,20 @@ class DeployHelper
'name' => '网站名称列表',
'type' => 'textarea',
'placeholder' => '填写要部署证书的网站名称,每行一个',
'note' => 'PHP项目和反代项目填写创建时绑定的第一个域名Java/Node/Go等其他项目填写项目名称邮局填写域名',
'note' => 'PHP项目和反代项目填写创建时绑定的第一个域名Java/Node/Go等其他项目填写项目名称邮局和IIS站点填写绑定的域名',
'show' => 'type==0||type==2||type==3',
'required' => true,
],
'is_iis' => [
'name' => '是否IIS站点',
'type' => 'radio',
'options' => [
'0' => '否',
'1' => '是',
],
'show' => 'type==0',
'value' => '0'
],
],
],
'kangle' => [
@ -360,17 +379,36 @@ class DeployHelper
'note' => '填写示例http://demo.xxxx.cn',
'required' => true,
],
'auth' => [
'name' => '认证方式',
'type' => 'radio',
'options' => [
'0' => '账号密码(旧版)',
'1' => 'API访问令牌',
],
'value' => '0',
'required' => true,
],
'api_key' => [
'name' => 'API访问令牌',
'type' => 'input',
'placeholder' => '',
'required' => true,
'show' => 'auth==1',
],
'email' => [
'name' => '邮箱地址',
'type' => 'input',
'placeholder' => '',
'required' => true,
'show' => 'auth==0',
],
'password' => [
'name' => '密码',
'type' => 'input',
'placeholder' => '',
'required' => true,
'show' => 'auth==0',
],
'proxy' => [
'name' => '使用代理服务器',

View File

@ -3,6 +3,7 @@
namespace app\lib\deploy;
use app\lib\DeployInterface;
use app\lib\CertHelper;
use Exception;
class btpanel implements DeployInterface
@ -10,12 +11,14 @@ class btpanel implements DeployInterface
private $logger;
private $url;
private $key;
private $version;
private $proxy;
public function __construct($config)
{
$this->url = rtrim($config['url'], '/');
$this->key = $config['key'];
$this->version = isset($config['version']) ? intval($config['version']) : 0;
$this->proxy = $config['proxy'] == 1;
}
@ -23,13 +26,24 @@ class btpanel implements DeployInterface
{
if (empty($this->url) || empty($this->key)) throw new Exception('请填写面板地址和接口密钥');
$path = '/config?action=get_config';
$response = $this->request($path, []);
$result = json_decode($response, true);
if (isset($result['status']) && ($result['status']==1 || isset($result['sites_path']))) {
return true;
if ($this->version == 1) {
$path = '/config/get_config';
$response = $this->request($path, []);
$result = json_decode($response, true);
if (isset($result['panel']['status']) && $result['panel']['status']) {
return true;
} else {
throw new Exception(isset($result['msg']) ? $result['msg'] : '面板地址无法连接');
}
} else {
throw new Exception(isset($result['msg']) ? $result['msg'] : '面板地址无法连接');
$path = '/config?action=get_config';
$response = $this->request($path, []);
$result = json_decode($response, true);
if (isset($result['status']) && ($result['status'] == 1 || isset($result['sites_path']))) {
return true;
} else {
throw new Exception(isset($result['msg']) ? $result['msg'] : '面板地址无法连接');
}
}
}
@ -40,6 +54,40 @@ class btpanel implements DeployInterface
$this->log("面板证书部署成功");
return;
}
$isIIS = $config['type'] == '0' && $this->version == 1 && isset($config['is_iis']) && $config['is_iis'] == '1';
if ($isIIS) {
$response = $this->request('/panel/get_config', []);
$result = json_decode($response, true);
if (isset($result['paths']['soft'])) {
if ($result['config']['webserver'] != 'iis') {
throw new Exception('当前安装的Web服务器不是IIS');
}
$panel_path = $result['paths']['soft'];
} else {
throw new Exception(isset($result['msg']) ? $result['msg'] : '面板地址无法连接');
}
$pfx_dir = $panel_path . '/temp/ssl/' . getMillisecond();
$pfx_path = $pfx_dir . '/cert.pfx';
$pfx_password = '123456';
$pfx = CertHelper::getPfx($fullchain, $privatekey, $pfx_password);
$data = [
['name' => 'path', 'contents' => $pfx_dir],
['name' => 'filename', 'contents' => 'cert.pfx'],
['name' => 'size', 'contents' => strlen($pfx)],
['name' => 'start', 'contents' => '0'],
['name' => 'blob', 'filename' => 'cert.pfx', 'contents' => $pfx],
['name' => 'force', 'contents' => 'true'],
];
$response = $this->request('/files/upload', $data, true);
$result = json_decode($response, true);
if (isset($result['status']) && $result['status']) {
} else {
throw new Exception(isset($result['msg']) ? $result['msg'] : '面板地址无法连接');
}
}
$sites = explode("\n", $config['sites']);
$success = 0;
$errmsg = null;
@ -64,6 +112,15 @@ class btpanel implements DeployInterface
$errmsg = $e->getMessage();
$this->log("邮局域名 {$siteName} 证书部署失败:" . $errmsg);
}
} elseif ($isIIS) {
try {
$this->deployIISSite($siteName, $pfx_path, $pfx_password);
$this->log("域名 {$siteName} 证书部署成功");
$success++;
} catch (Exception $e) {
$errmsg = $e->getMessage();
$this->log("域名 {$siteName} 证书部署失败:" . $errmsg);
}
} else {
try {
$this->deploySite($siteName, $fullchain, $privatekey);
@ -82,30 +139,113 @@ class btpanel implements DeployInterface
private function deployPanel($fullchain, $privatekey)
{
$path = '/config?action=SavePanelSSL';
$data = [
'privateKey' => $privatekey,
'certPem' => $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']);
if ($this->version == 1) {
$path = '/config/set_panel_ssl';
$data = [
'ssl_key' => $privatekey,
'ssl_pem' => $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 : '返回数据解析失败');
}
} else {
throw new Exception($response ? $response : '返回数据解析失败');
$path = '/config?action=SavePanelSSL';
$data = [
'privateKey' => $privatekey,
'certPem' => $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 : '返回数据解析失败');
}
}
}
private function deploySite($siteName, $fullchain, $privatekey)
{
$path = '/site?action=SetSSL';
if ($this->version == 1) {
$path = '/datalist/get_data_list';
$data = [
'table' => 'sites',
'search_type' => 'PHP',
'search' => $siteName,
'p' => 1,
'limit' => 10,
'type' => -1,
];
$response = $this->request($path, $data);
$result = json_decode($response, true);
if (isset($result['data'])) {
if (empty($result['data'])) throw new Exception("网站 {$siteName} 不存在");
$siteId = null;
foreach ($result['data'] as $item) {
if ($item['name'] == $siteName) {
$siteId = $item['id'];
break;
}
}
if (is_null($siteId)) throw new Exception("网站 {$siteName} 不存在");
$path = '/site/set_site_ssl';
$data = [
'siteid' => $siteId,
'status' => 'true',
'sslType' => '',
'cert' => $fullchain,
'key' => $privatekey,
];
$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 : '返回数据解析失败');
}
return true;
} elseif (isset($result['msg'])) {
throw new Exception($result['msg']);
} else {
throw new Exception($response ? $response : '返回数据解析失败');
}
} else {
$path = '/site?action=SetSSL';
$data = [
'type' => '0',
'siteName' => $siteName,
'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 : '返回数据解析失败');
}
}
}
private function deployIISSite($domain, $pfx_path, $password = '123456')
{
$path = '/site/set_site_domain_ssl';
$data = [
'type' => '0',
'siteName' => $siteName,
'key' => $privatekey,
'csr' => $fullchain,
'domain' => $domain,
'path' => $pfx_path,
'password' => $password,
];
$response = $this->request($path, $data);
$result = json_decode($response, true);
@ -169,17 +309,27 @@ class btpanel implements DeployInterface
}
}
private function request($path, $params)
private function request($path, $params, $file = false)
{
$url = $this->url . $path;
$now_time = time();
$post_data = [
'request_token' => md5($now_time . md5($this->key)),
'request_time' => $now_time
];
$post_data = array_merge($post_data, $params);
$response = http_request($url, $post_data, null, null, null, $this->proxy);
$headers = [];
if ($file) {
$post_data = [
['name' => 'request_token', 'contents' => md5($now_time . md5($this->key))],
['name' => 'request_time', 'contents' => $now_time],
];
$post_data = array_merge($post_data, $params);
$headers['Content-Type'] = 'multipart/form-data';
} else {
$post_data = [
'request_token' => md5($now_time . md5($this->key)),
'request_time' => $now_time
];
$post_data = array_merge($post_data, $params);
}
$response = http_request($url, $post_data, null, null, $headers, $this->proxy);
return $response['body'];
}
}

View File

@ -11,6 +11,8 @@ class lecdn implements DeployInterface
private $url;
private $email;
private $password;
private $auth;
private $apiKey;
private $proxy;
private $accessToken;
@ -19,13 +21,22 @@ class lecdn implements DeployInterface
$this->url = rtrim($config['url'], '/');
$this->email = $config['email'];
$this->password = $config['password'];
$this->auth = isset($config['auth']) ? intval($config['auth']) : 0;
if ($this->auth == 1) {
$this->apiKey = $config['api_key'];
}
$this->proxy = $config['proxy'] == 1;
}
public function check()
{
if (empty($this->url) || empty($this->email) || empty($this->password)) throw new Exception('账号和密码不能为空');
$this->login();
if ($this->auth == 1) {
if (empty($this->url) || empty($this->apiKey)) throw new Exception('API访问令牌不能为空');
$this->request('/prod-api/system/info');
} else {
if (empty($this->url) || empty($this->email) || empty($this->password)) throw new Exception('账号和密码不能为空');
$this->login();
}
}
public function deploy($fullchain, $privatekey, $config, &$info)
@ -33,7 +44,9 @@ class lecdn implements DeployInterface
$id = $config['id'];
if (empty($id)) throw new Exception('证书ID不能为空');
$this->login();
if ($this->auth == 0) {
$this->login();
}
try {
$data = $this->request('/prod-api/certificate/' . $id);
@ -77,6 +90,8 @@ class lecdn implements DeployInterface
$body = null;
if ($this->accessToken) {
$headers['Authorization'] = 'Bearer ' . $this->accessToken;
} elseif ($this->auth == 1 && $this->apiKey) {
$headers['Authorization'] = $this->apiKey;
}
if ($params) {
$headers['Content-Type'] = 'application/json;charset=UTF-8';