mirror of
https://github.com/netcccyun/dnsmgr.git
synced 2026-05-09 23:16:27 +02:00
Compare commits
144 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e00773c0a | ||
|
|
4bee80e06e | ||
|
|
7cb745acdf | ||
|
|
79437aba60 | ||
|
|
fd21a55d01 | ||
|
|
9f529e2528 | ||
|
|
9a70fd7116 | ||
|
|
3dd55cf007 | ||
|
|
6ff8cb9f45 | ||
|
|
98f185ee8e | ||
|
|
028688df20 | ||
|
|
e8c68375a9 | ||
|
|
6958530337 | ||
|
|
0863d02cc9 | ||
|
|
9032ea0405 | ||
|
|
e3749ecb6c | ||
|
|
e1e90c3c71 | ||
|
|
a171a5b9b0 | ||
|
|
f608b2fceb | ||
|
|
0837ac9be1 | ||
|
|
c31e0eaf41 | ||
|
|
987deda95d | ||
|
|
654151ce5b | ||
|
|
2fedee1e93 | ||
|
|
ba97ac3685 | ||
|
|
f2f1a0d01e | ||
|
|
933f98a909 | ||
|
|
3beb0ec637 | ||
|
|
7a92ad7094 | ||
|
|
5dc4dbfabd | ||
|
|
291f715e91 | ||
|
|
4f15d4d7a2 | ||
|
|
11e83860b7 | ||
|
|
479af4fe5f | ||
|
|
3c00e426cc | ||
|
|
814781c1d1 | ||
|
|
af2117faf0 | ||
|
|
9275256e36 | ||
|
|
d368a0190a | ||
|
|
684a4e59ce | ||
|
|
1bab48ad93 | ||
|
|
02718e58a9 | ||
|
|
8e563a89ff | ||
|
|
7f370a095d | ||
|
|
5aab54c79e | ||
|
|
ce9ae51aeb | ||
|
|
8b06cc5400 | ||
|
|
a71fd35f6f | ||
|
|
0d20d8ad46 | ||
|
|
ff676b7be1 | ||
|
|
8c7c568e5c | ||
|
|
a4698d67c6 | ||
|
|
5f0e973770 | ||
|
|
a99d0320df | ||
|
|
bb35ee3378 | ||
|
|
8193ade02d | ||
|
|
904c3ceb5b | ||
|
|
5add791ef1 | ||
|
|
3587db2b53 | ||
|
|
4a4cdd059c | ||
|
|
1021deb60d | ||
|
|
848610ffe0 | ||
|
|
767aec5ebc | ||
|
|
30912fdf75 | ||
|
|
5050af2f73 | ||
|
|
6418c3a2ee | ||
|
|
efd18676f3 | ||
|
|
5ba7c324af | ||
|
|
236610d8fb | ||
|
|
0015015b7a | ||
|
|
f776b9f47f | ||
|
|
0860624bd5 | ||
|
|
e328dc6808 | ||
|
|
e58d8f4af1 | ||
|
|
9d4260062c | ||
|
|
17c50e4ba1 | ||
|
|
55272fd51b | ||
|
|
a1e4476603 | ||
|
|
3734e98048 | ||
|
|
372018c03a | ||
|
|
9b037834ad | ||
|
|
bf05d51d08 | ||
|
|
ec89fd685b | ||
|
|
300686aa0a | ||
|
|
8a158ea0a5 | ||
|
|
8a41c1642a | ||
|
|
6e3350afbd | ||
|
|
b5f74368d2 | ||
|
|
49047db438 | ||
|
|
7c54d8af44 | ||
|
|
9b7a7c2d60 | ||
|
|
8e7adead48 | ||
|
|
1e747a8e9e | ||
|
|
a13fb38e66 | ||
|
|
3c6944a701 | ||
|
|
c141089c69 | ||
|
|
994bdc7fa3 | ||
|
|
842b2aa2d9 | ||
|
|
4850250f3c | ||
|
|
f8add88e3d | ||
|
|
dcc440c1f9 | ||
|
|
a4d3cdd612 | ||
|
|
a483476e6b | ||
|
|
be55d4b67d | ||
|
|
fa47ffb080 | ||
|
|
333aacaab9 | ||
|
|
0752f07f7d | ||
|
|
4310ccb770 | ||
|
|
fb8fe3526b | ||
|
|
cf36b4bd51 | ||
|
|
079a142b40 | ||
|
|
76e9adb405 | ||
|
|
1723761b94 | ||
|
|
9fb3764878 | ||
|
|
651132967f | ||
|
|
81a85fce45 | ||
|
|
4c62084bc6 | ||
|
|
33ba9a6ebd | ||
|
|
64d0585788 | ||
|
|
1354f63050 | ||
|
|
646fa54bfa | ||
|
|
2023fb9808 | ||
|
|
6a45222c1f | ||
|
|
44790639cd | ||
|
|
5b12a368fc | ||
|
|
36622e6642 | ||
|
|
12d8017df5 | ||
|
|
70f2e0d487 | ||
|
|
521275ee33 | ||
|
|
c06bf2d34c | ||
|
|
39dc789ac3 | ||
|
|
0877674efb | ||
|
|
fe9a50469d | ||
|
|
d9f8cc18eb | ||
|
|
48d5ad7569 | ||
|
|
8980910d47 | ||
|
|
2ed8a717db | ||
|
|
b4258dbc81 | ||
|
|
d1eb6267a2 | ||
|
|
2c81b36249 | ||
|
|
8d5a9bc083 | ||
|
|
31300d8a7b | ||
|
|
300f2a9b92 | ||
|
|
865275c065 |
15
.github/dependabot.yml
vendored
Normal file
15
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# Dependabot configuration.
|
||||
#
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/code-security/dependabot/working-with-dependabot/dependabot-options-reference
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "composer"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,4 +3,3 @@
|
||||
/vendor
|
||||
*.log
|
||||
.env
|
||||
/composer.lock
|
||||
|
||||
290
README.md
290
README.md
@@ -1,16 +1,164 @@
|
||||
## 聚合DNS管理系统
|
||||
# 彩虹聚合DNS管理系统
|
||||
|
||||
聚合DNS管理系统可以实现在一个网站内管理多个平台的域名解析,目前已支持的域名平台有:阿里云、腾讯云、华为云、百度云、西部数码、火山引擎、DNSLA、CloudFlare、Namesilo
|
||||
<div align="center">
|
||||
|
||||
### 功能特性
|
||||
[](https://github.com/netcccyun/dnsmgr/stargazers)
|
||||
[](https://github.com/netcccyun/dnsmgr/forks)
|
||||
[](https://hub.docker.com/r/netcccyun/dnsmgr)
|
||||
[](https://github.com/netcccyun/dnsmgr/releases)
|
||||
[](https://github.com/netcccyun/dnsmgr/commits/main)
|
||||
|
||||
- 多用户管理,可为每个用户可分配不同的域名解析权限
|
||||
- 提供API接口,可获取域名单独的登录链接,方便各种IDC系统对接
|
||||
- 容灾切换功能,支持ping、tcp、http(s)检测协议并自动暂停/修改域名解析,并支持邮件、微信公众号、TG群机器人通知
|
||||
- CF优选IP功能,支持获取最新的Cloudflare优选IP,并自动更新到解析记录
|
||||
- SSL证书申请与自动部署功能,支持从Let's Encrypt等渠道申请SSL证书,并自动部署到各种面板、云服务商、服务器等
|
||||
</div>
|
||||
|
||||
### 演示截图
|
||||
彩虹聚合DNS管理系统 是一款基于ThinkPHP开发的网站程序,可实现在单一网站内管理多个平台的域名解析,目前已支持的域名解析平台有:阿里云、腾讯云、华为云、百度云、西部数码、火山引擎、DNSLA、CloudFlare、Namesilo、PowerDNS
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 多用户管理,可为每个用户可分配不同的域名解析权限;
|
||||
- 提供API接口,可获取域名单独的登录链接,方便各种IDC系统对接;
|
||||
- 容灾切换功能,支持ping、tcp、http(s)检测协议并自动暂停/修改域名解析,并支持发送通知;
|
||||
- 定时切换功能,设置在指定时间/周期,自动修改/开启/暂停/删除域名解析;
|
||||
- CF优选IP功能,支持获取最新的Cloudflare优选IP,并自动更新到解析记录;
|
||||
- SSL证书申请与自动部署功能,支持从Let's Encrypt等渠道申请SSL证书,并自动部署到各种面板、云服务商、服务器等;
|
||||
- 支持邮件、微信公众号、Telegram、钉钉、飞书、企业微信等多种通知渠道。
|
||||
|
||||
## 部署方式
|
||||
|
||||
### 自部署
|
||||
|
||||
可以使用宝塔、Kangle等任意支持PHP-MySQL的环境部署
|
||||
|
||||
* 从[Release](https://github.com/netcccyun/dnsmgr/releases)页面下载安装包
|
||||
|
||||
* 运行环境要求PHP8.0+,MySQL5.6+
|
||||
|
||||
* 设置网站运行目录为`public`
|
||||
|
||||
* 设置伪静态为`ThinkPHP`
|
||||
|
||||
* 如果是下载的Source code包,还需Composer安装依赖(Release页面下载的安装包不需要)
|
||||
|
||||
```
|
||||
composer install --no-dev
|
||||
```
|
||||
|
||||
* 访问网站,会自动跳转到安装页面,根据提示安装完成
|
||||
|
||||
* 访问首页登录控制面板
|
||||
|
||||
* 后续更新方式:重新下载安装包上传覆盖即可
|
||||
|
||||
##### 伪静态规则
|
||||
|
||||
* Nginx
|
||||
|
||||
```
|
||||
location ~* (runtime|application)/ {
|
||||
return 403;
|
||||
}
|
||||
location / {
|
||||
if (!-e $request_filename) {
|
||||
rewrite ^(.*)$ /index.php?s=$1 last; break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* Apache
|
||||
|
||||
```
|
||||
<IfModule mod_rewrite.c>
|
||||
Options +FollowSymlinks -Multiviews
|
||||
RewriteEngine On
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
|
||||
</IfModule>
|
||||
```
|
||||
|
||||
### Docker 部署
|
||||
|
||||
首先需要安装Docker,然后执行以下命令拉取镜像并启动(启动后监听8081端口):
|
||||
|
||||
```
|
||||
docker run --name dnsmgr -dit -p 8081:80 -v /var/dnsmgr:/app/www netcccyun/dnsmgr
|
||||
```
|
||||
|
||||
访问并安装好后如果容灾切换未自动启动,重启容器即可:
|
||||
|
||||
```
|
||||
docker restart dnsmgr
|
||||
```
|
||||
|
||||
从国内镜像地址拉取:
|
||||
|
||||
```
|
||||
docker pull swr.cn-east-3.myhuaweicloud.com/netcccyun/dnsmgr:latest
|
||||
```
|
||||
|
||||
### docker-compose 部署
|
||||
|
||||
```
|
||||
services:
|
||||
dnsmgr-web:
|
||||
container_name: dnsmgr-web
|
||||
stdin_open: true
|
||||
tty: true
|
||||
ports:
|
||||
- 8081:80
|
||||
volumes:
|
||||
- ./web:/app/www
|
||||
image: netcccyun/dnsmgr
|
||||
depends_on:
|
||||
- dnsmgr-mysql
|
||||
networks:
|
||||
- dnsmgr-network
|
||||
|
||||
dnsmgr-mysql:
|
||||
container_name: dnsmgr-mysql
|
||||
restart: always
|
||||
ports:
|
||||
- 3306:3306
|
||||
volumes:
|
||||
- ./mysql/conf/my.cnf:/etc/mysql/my.cnf
|
||||
- ./mysql/logs:/logs
|
||||
- ./mysql/data:/var/lib/mysql
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=123456
|
||||
- TZ=Asia/Shanghai
|
||||
image: mysql:5.7
|
||||
networks:
|
||||
- dnsmgr-network
|
||||
|
||||
networks:
|
||||
dnsmgr-network:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
在运行之前请创建好目录
|
||||
|
||||
```
|
||||
mkdir -p ./web
|
||||
mkdir -p ./mysql/conf
|
||||
mkdir -p ./mysql/logs
|
||||
mkdir -p ./mysql/data
|
||||
|
||||
vim mysql/conf/my.cnf
|
||||
[mysqld]
|
||||
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
|
||||
```
|
||||
|
||||
登陆mysql容器创建数据库
|
||||
|
||||
```
|
||||
docker exec -it dnsmgr-mysql /bin/bash
|
||||
mysql -uroot -p123456
|
||||
create database dnsmgr;
|
||||
```
|
||||
|
||||
在install界面链接IP填写dnsmgr-mysql
|
||||
|
||||
## 演示截图
|
||||
|
||||
添加域名账户
|
||||
|
||||
@@ -44,129 +192,15 @@ SSL证书自动部署功能
|
||||
|
||||

|
||||
|
||||
### 部署方法
|
||||
## 支持与反馈
|
||||
|
||||
* 从[Release](https://github.com/netcccyun/dnsmgr/releases)页面下载安装包
|
||||
🌐 作者信息:消失的彩虹海(https://blog.cccyun.cn)
|
||||
|
||||
* 运行环境要求PHP7.4+,MySQL5.6+
|
||||
⭐ 如果您觉得本项目对您有帮助,欢迎给项目点个 Star
|
||||
|
||||
* 设置网站运行目录为`public`
|
||||
🤝 捐赠:
|
||||
|
||||
* 设置伪静态为`ThinkPHP`
|
||||
|
||||
* 如果是下载的Source code包,还需Composer安装依赖(Release页面下载的安装包不需要)
|
||||
|
||||
```
|
||||
composer install --no-dev
|
||||
```
|
||||
|
||||
* 访问网站,会自动跳转到安装页面,根据提示安装完成
|
||||
|
||||
* 访问首页登录控制面板
|
||||
|
||||
##### 伪静态规则
|
||||
|
||||
* Nginx
|
||||
|
||||
```
|
||||
location / {
|
||||
if (!-e $request_filename){
|
||||
rewrite ^(.*)$ /index.php?s=$1 last; break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* Apache
|
||||
|
||||
```
|
||||
<IfModule mod_rewrite.c>
|
||||
Options +FollowSymlinks -Multiviews
|
||||
RewriteEngine On
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
|
||||
</IfModule>
|
||||
```
|
||||
|
||||
### Docker部署方法
|
||||
|
||||
首先需要安装Docker,然后执行以下命令拉取镜像并启动(启动后监听8081端口):
|
||||
|
||||
```
|
||||
docker run --name dnsmgr -dit -p 8081:80 -v /var/dnsmgr:/app/www netcccyun/dnsmgr
|
||||
```
|
||||
|
||||
访问并安装好后如果容灾切换未自动启动,重启容器即可:
|
||||
|
||||
```
|
||||
docker restart dnsmgr
|
||||
```
|
||||
|
||||
### docker-compose部署方法
|
||||
|
||||
```
|
||||
version: '3'
|
||||
services:
|
||||
dnsmgr-web:
|
||||
container_name: dnsmgr-web
|
||||
stdin_open: true
|
||||
tty: true
|
||||
ports:
|
||||
- 8081:80
|
||||
volumes:
|
||||
- /volume1/docker/dnsmgr/web:/app/www
|
||||
image: netcccyun/dnsmgr
|
||||
depends_on:
|
||||
- dnsmgr-mysql
|
||||
networks:
|
||||
- dnsmgr-network
|
||||
|
||||
dnsmgr-mysql:
|
||||
container_name: dnsmgr-mysql
|
||||
restart: always
|
||||
ports:
|
||||
- 3306:3306
|
||||
volumes:
|
||||
- ./mysql/conf/my.cnf:/etc/mysql/my.cnf
|
||||
- ./mysql/logs:/logs
|
||||
- ./mysql/data:/var/lib/mysql
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=123456
|
||||
- TZ=Asia/Shanghai
|
||||
image: mysql:5.7
|
||||
networks:
|
||||
- dnsmgr-network
|
||||
|
||||
networks:
|
||||
dnsmgr-network:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
在运行之前请创建好目录
|
||||
```
|
||||
mkdir -p ./web
|
||||
mkdir -p ./mysql/conf
|
||||
mkdir -p ./mysql/logs
|
||||
mkdir -p ./mysql/data
|
||||
|
||||
vim mysql/conf/my.cnf
|
||||
[mysqld]
|
||||
sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
|
||||
```
|
||||
|
||||
登陆mysql容器创建数据库
|
||||
```
|
||||
docker exec -it dnsmgr-mysql /bin/bash
|
||||
mysql -uroot -p123456
|
||||
create database dnsmgr;
|
||||
```
|
||||
|
||||
在install界面链接IP填写dnsmgr-mysql
|
||||
|
||||
### 作者信息
|
||||
|
||||
消失的彩虹海(https://blog.cccyun.cn)
|
||||
<img height="240" src="https://wkphoto.bj.bcebos.com/d8f9d72a6059252db065f556249b033b5bb5b976.jpg">
|
||||
|
||||
### 其他推荐
|
||||
|
||||
|
||||
@@ -12,7 +12,10 @@ use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\facade\Db;
|
||||
use think\facade\Config;
|
||||
use app\service\OptimizeService;
|
||||
use app\service\CertTaskService;
|
||||
use app\service\ExpireNoticeService;
|
||||
use app\service\ScheduleService;
|
||||
|
||||
class Certtask extends Command
|
||||
{
|
||||
@@ -20,7 +23,7 @@ class Certtask extends Command
|
||||
{
|
||||
// 指令配置
|
||||
$this->setName('certtask')
|
||||
->setDescription('证书申请与部署任务');
|
||||
->setDescription('SSL证书续签与部署、域名到期提醒、定时切换解析、CF优选IP更新');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
@@ -28,6 +31,12 @@ class Certtask extends Command
|
||||
$res = Db::name('config')->cache('configs', 0)->column('value', 'key');
|
||||
Config::set($res, 'sys');
|
||||
|
||||
(new CertTaskService())->execute();
|
||||
(new ScheduleService())->execute();
|
||||
$res = (new OptimizeService())->execute();
|
||||
if (!$res) {
|
||||
(new CertTaskService())->execute();
|
||||
(new ExpireNoticeService())->task();
|
||||
}
|
||||
echo 'done'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\command;
|
||||
|
||||
use Exception;
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\facade\Db;
|
||||
use think\facade\Config;
|
||||
use app\service\OptimizeService;
|
||||
|
||||
class Opiptask extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
// 指令配置
|
||||
$this->setName('opiptask')
|
||||
->setDescription('CF优选IP任务');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$res = Db::name('config')->cache('configs', 0)->column('value', 'key');
|
||||
Config::set($res, 'sys');
|
||||
|
||||
(new OptimizeService())->execute();
|
||||
}
|
||||
}
|
||||
371
app/common.php
371
app/common.php
@@ -2,48 +2,53 @@
|
||||
// 应用公共文件
|
||||
use think\facade\Db;
|
||||
use think\facade\Request;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
|
||||
function get_curl($url, $post = 0, $referer = 0, $cookie = 0, $header = 0, $ua = 0, $nobody = 0, $addheader = 0)
|
||||
function get_curl($url, $post = 0, $referer = 0, $cookie = 0, $ua = 0, $nobody = 0, $addheader = [])
|
||||
{
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
$httpheader[] = "Accept: */*";
|
||||
$httpheader[] = "Accept-Encoding: gzip,deflate,sdch";
|
||||
$httpheader[] = "Accept-Language: zh-CN,zh;q=0.8";
|
||||
$httpheader[] = "Connection: close";
|
||||
if ($addheader) {
|
||||
$httpheader = array_merge($httpheader, $addheader);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
|
||||
if ($post) {
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
|
||||
}
|
||||
if ($header) {
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
$options = [
|
||||
'timeout' => 10,
|
||||
'verify' => false,
|
||||
'headers' => [
|
||||
'User-Agent' => $ua ?: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
|
||||
],
|
||||
'http_errors' => false // 不抛出异常
|
||||
];
|
||||
|
||||
$options['headers'] = array_merge($options['headers'], $addheader);
|
||||
if ($referer) {
|
||||
$options['headers']['Referer'] = $referer;
|
||||
}
|
||||
if ($cookie) {
|
||||
curl_setopt($ch, CURLOPT_COOKIE, $cookie);
|
||||
$options['headers']['Cookie'] = $cookie;
|
||||
}
|
||||
if ($referer) {
|
||||
curl_setopt($ch, CURLOPT_REFERER, $referer);
|
||||
|
||||
$method = 'GET';
|
||||
if ($post) {
|
||||
$method = 'POST';
|
||||
if (!isset($options['headers']['Content-Type'])) {
|
||||
$options['headers']['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
}
|
||||
if (is_array($post)) {
|
||||
$options['form_params'] = $post;
|
||||
} else {
|
||||
$options['body'] = $post;
|
||||
}
|
||||
}
|
||||
if ($ua) {
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, $ua);
|
||||
} else {
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Linux; U; Android 4.0.4; es-mx; HTC_One_X Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0");
|
||||
|
||||
try {
|
||||
$client = new Client();
|
||||
$response = $client->request($method, $url, $options);
|
||||
|
||||
if ($nobody) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $response->getBody()->getContents();
|
||||
} catch (GuzzleException $e) {
|
||||
return '';
|
||||
}
|
||||
if ($nobody) {
|
||||
curl_setopt($ch, CURLOPT_NOBODY, 1);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_ENCODING, "gzip");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
$ret = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return $ret;
|
||||
}
|
||||
|
||||
function real_ip($type = 0)
|
||||
@@ -166,6 +171,11 @@ function getSubstr($str, $leftStr, $rightStr)
|
||||
}
|
||||
}
|
||||
|
||||
function arrays_are_equal($array1, $array2)
|
||||
{
|
||||
return empty(array_diff($array1, $array2)) && empty(array_diff($array2, $array1));
|
||||
}
|
||||
|
||||
function checkRefererHost()
|
||||
{
|
||||
if (!Request::header('referer')) {
|
||||
@@ -289,6 +299,16 @@ function convert_second($s)
|
||||
function getMainDomain($host)
|
||||
{
|
||||
if (filter_var($host, FILTER_VALIDATE_IP)) return $host;
|
||||
$domains = config('temp.domains');
|
||||
if (!$domains) {
|
||||
$domains = Db::name('domain')->column('name');
|
||||
config(['domains'=>$domains], 'temp');
|
||||
}
|
||||
foreach ($domains as $domain) {
|
||||
if (str_ends_with($host, $domain)) {
|
||||
return $domain;
|
||||
}
|
||||
}
|
||||
$domain_root = file_get_contents(app()->getBasePath() . 'data' . DIRECTORY_SEPARATOR . 'domain_root.txt');
|
||||
$domain_root = explode("\r\n", $domain_root);
|
||||
$data = explode('.', $host);
|
||||
@@ -303,45 +323,41 @@ function getMainDomain($host)
|
||||
|
||||
function check_proxy($url, $proxy_server, $proxy_port, $type, $proxy_user, $proxy_pwd)
|
||||
{
|
||||
$ch = curl_init($url);
|
||||
if ($type == 'https') {
|
||||
$proxy_type = CURLPROXY_HTTPS;
|
||||
} elseif ($type == 'sock4') {
|
||||
$proxy_type = CURLPROXY_SOCKS4;
|
||||
} elseif ($type == 'sock5') {
|
||||
$proxy_type = CURLPROXY_SOCKS5;
|
||||
} else {
|
||||
$proxy_type = CURLPROXY_HTTP;
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
|
||||
curl_setopt($ch, CURLOPT_PROXY, $proxy_server);
|
||||
curl_setopt($ch, CURLOPT_PROXYPORT, intval($proxy_port));
|
||||
match ($type) {
|
||||
'https' => $proxy_string = 'https://',
|
||||
'sock4' => $proxy_string = 'socks4://',
|
||||
'sock5' => $proxy_string = 'socks5://',
|
||||
'sock5h' => $proxy_string = 'socks5h://',
|
||||
default => $proxy_string = 'http://',
|
||||
};
|
||||
|
||||
if (!empty($proxy_user) && !empty($proxy_pwd)) {
|
||||
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxy_user . ':' . $proxy_pwd);
|
||||
$proxy_string .= $proxy_user . ':' . $proxy_pwd . '@';
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_PROXYTYPE, $proxy_type);
|
||||
$httpheader[] = "Accept: */*";
|
||||
$httpheader[] = "Accept-Language: zh-CN,zh;q=0.8";
|
||||
$httpheader[] = "Connection: close";
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36');
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 3);
|
||||
curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
if ($errno) {
|
||||
$errmsg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception($errmsg);
|
||||
}
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
if ($httpCode >= 200 && $httpCode < 400) {
|
||||
return true;
|
||||
} else {
|
||||
throw new Exception('HTTP状态码异常:' . $httpCode);
|
||||
|
||||
$proxy_string .= $proxy_server . ':' . intval($proxy_port);
|
||||
$options = [
|
||||
'proxy' => $proxy_string,
|
||||
'timeout' => 3,
|
||||
'connect_timeout' => 3,
|
||||
'verify' => false,
|
||||
'headers' => [
|
||||
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36'
|
||||
]
|
||||
];
|
||||
|
||||
try {
|
||||
$client = new Client();
|
||||
$response = $client->request('GET', $url, $options);
|
||||
$httpCode = $response->getStatusCode();
|
||||
|
||||
if ($httpCode >= 200 && $httpCode < 400) {
|
||||
return true;
|
||||
} else {
|
||||
throw new Exception('HTTP状态码异常:' . $httpCode);
|
||||
}
|
||||
} catch (GuzzleException $e) {
|
||||
throw new Exception(guzzle_error($e));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,54 +392,155 @@ function clearDirectory($dir): bool
|
||||
return true;
|
||||
}
|
||||
|
||||
function curl_client($url, $data = null, $referer = null, $cookie = null, $headers = null, $proxy = false, $method = null, $timeout = 5)
|
||||
/**
|
||||
* 发送 HTTP 请求
|
||||
*
|
||||
* @param string $url 请求URL
|
||||
* @param mixed $data 请求数据,可以是字符串或数组,数组将自动根据请求方法及传入的 Content-Type 头序列化
|
||||
* @param string|null $referer 请求的 Referer 头
|
||||
* @param array|null $cookie 请求的 Cookie 头
|
||||
* @param array|null $headers 其他自定义请求头
|
||||
* @param bool $proxy 是否使用代理
|
||||
* @param string|null $method 请求方法,默认为 GET 或 POST(如果存在请求数据)
|
||||
* @param int $timeout 请求超时时间,默认为 10 秒
|
||||
* @return array 包含 HTTP 状态码、重定向 URL、响应头和响应体的数组
|
||||
* @throws Exception
|
||||
*/
|
||||
function http_request($url, $data = null, $referer = null, $cookie = null, $headers = null, $proxy = false, $method = null, $timeout = 10): array
|
||||
{
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
$httpheader[] = "Accept: */*";
|
||||
$httpheader[] = "Accept-Language: zh-CN,zh;q=0.8";
|
||||
$httpheader[] = "Connection: close";
|
||||
if ($headers) {
|
||||
$httpheader = array_merge($httpheader, $headers);
|
||||
$options = [
|
||||
'timeout' => $timeout,
|
||||
'connect_timeout' => $timeout,
|
||||
'allow_redirects' => false,
|
||||
'verify' => false,
|
||||
'headers' => [
|
||||
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36',
|
||||
],
|
||||
'http_errors' => false // 不抛出异常
|
||||
];
|
||||
|
||||
// 默认请求方法
|
||||
if (!$method) {
|
||||
$method = $data ? 'POST' : 'GET';
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36");
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
if ($data) {
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
||||
// 处理头部
|
||||
if (is_array($headers)) {
|
||||
$options['headers'] = array_merge($options['headers'], $headers);
|
||||
}
|
||||
// 处理Cookie
|
||||
if ($cookie) {
|
||||
curl_setopt($ch, CURLOPT_COOKIE, $cookie);
|
||||
$options['headers']['Cookie'] = $cookie;
|
||||
}
|
||||
// 处理Referer
|
||||
if ($referer) {
|
||||
curl_setopt($ch, CURLOPT_REFERER, $referer);
|
||||
$options['headers']['Referer'] = $referer;
|
||||
}
|
||||
if ($method) {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
// 处理数据
|
||||
if ($data) {
|
||||
if ($method !== 'GET') {
|
||||
if (is_string($data)) {
|
||||
$options['body'] = $data;
|
||||
if (!isset($options['headers']['Content-Type'])) {
|
||||
if (json_validate($data)) {
|
||||
// json
|
||||
$options['headers']['Content-Type'] = 'application/json';
|
||||
} elseif (str_contains($data, '=') || str_contains($data, '&')) {
|
||||
// 表单
|
||||
$options['headers']['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
}
|
||||
}
|
||||
} else if (is_array($data) || is_object($data)) {
|
||||
if (!isset($options['headers']['Content-Type'])) {
|
||||
// 默认为表单
|
||||
$options['headers']['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
}
|
||||
if ($options['headers']['Content-Type'] == 'application/x-www-form-urlencoded') {
|
||||
// 表单
|
||||
$options['form_params'] = $data;
|
||||
} else if ($options['headers']['Content-Type'] == 'multipart/form-data') {
|
||||
// 表单文件
|
||||
$options['multipart'] = $data;
|
||||
unset($options['headers']['Content-Type']); // 由GuzzleHttp重新生成Content-Type头部
|
||||
} else if ($options['headers']['Content-Type'] == 'application/json') {
|
||||
// json
|
||||
$options['json'] = $data;
|
||||
} else {
|
||||
// 其他
|
||||
$options['body'] = http_build_query($data);
|
||||
}
|
||||
} else {
|
||||
$options['body'] = $data;
|
||||
}
|
||||
} else {
|
||||
// 兼容已经存在查询字符串的情况
|
||||
if (!str_contains($url, '?')) {
|
||||
$options['query'] = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理代理
|
||||
if ($proxy) {
|
||||
curl_set_proxy($ch);
|
||||
$proxy_server = config_get('proxy_server');
|
||||
$proxy_port = intval(config_get('proxy_port'));
|
||||
$proxy_userpwd = config_get('proxy_user').':'.config_get('proxy_pwd');
|
||||
$proxy_type = config_get('proxy_type');
|
||||
|
||||
if (empty($proxy_server) || empty($proxy_port)) {
|
||||
throw new Exception('代理服务器或端口未配置');
|
||||
}
|
||||
|
||||
match ($proxy_type) {
|
||||
'https' => $proxy_string = 'https://',
|
||||
'sock4' => $proxy_string = 'socks4://',
|
||||
'sock5' => $proxy_string = 'socks5://',
|
||||
'sock5h' => $proxy_string = 'socks5h://',
|
||||
default => $proxy_string = 'http://',
|
||||
};
|
||||
|
||||
if ($proxy_userpwd != ':') {
|
||||
$proxy_string .= $proxy_userpwd . '@';
|
||||
}
|
||||
|
||||
$proxy_string .= $proxy_server . ':' . $proxy_port;
|
||||
$options['proxy'] = $proxy_string;
|
||||
}
|
||||
|
||||
$ret = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
if ($errno) {
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . curl_error($ch));
|
||||
try {
|
||||
$client = new Client();
|
||||
$response = $client->request($method, $url, $options);
|
||||
$code = $response->getStatusCode();
|
||||
|
||||
// 取重定向URL
|
||||
$redirect_url = '';
|
||||
if ($code >= 300 && $code < 400) {
|
||||
$redirect_url = $response->getHeaderLine('Location');
|
||||
}
|
||||
|
||||
return [
|
||||
'code' => $code,
|
||||
'redirect_url' => $redirect_url,
|
||||
'headers' => $response->getHeaders(),
|
||||
'body' => $response->getBody()->getContents()
|
||||
];
|
||||
} catch (GuzzleException $e) {
|
||||
throw new Exception('请求失败: ' . guzzle_error($e));
|
||||
}
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
||||
$redirect_url = curl_getinfo($ch, CURLINFO_REDIRECT_URL);
|
||||
curl_close($ch);
|
||||
$header = substr($ret, 0, $headerSize);
|
||||
$body = substr($ret, $headerSize);
|
||||
return ['code' => $httpCode, 'redirect_url' => $redirect_url, 'header' => $header, 'body' => $body];
|
||||
}
|
||||
|
||||
function guzzle_error($e)
|
||||
{
|
||||
$errmsg = $e->getMessage();
|
||||
if (preg_match('/^cURL error \d+: /', $errmsg)) {
|
||||
$errmsg = preg_replace('/^cURL error \d+: /', '', $errmsg);
|
||||
}
|
||||
$pos = strpos($errmsg, ' (see https://curl.haxx.se/libcurl/c/libcurl-errors.html)');
|
||||
if ($pos !== false) {
|
||||
$errmsg = substr($errmsg, 0, $pos);
|
||||
}
|
||||
if (strlen($errmsg) > 100) {
|
||||
$errmsg = substr($errmsg, 0, 97) . '...';
|
||||
}
|
||||
return $errmsg;
|
||||
}
|
||||
|
||||
function curl_set_proxy(&$ch)
|
||||
@@ -441,6 +558,8 @@ function curl_set_proxy(&$ch)
|
||||
$proxy_type = CURLPROXY_SOCKS4;
|
||||
} elseif ($proxy_type == 'sock5') {
|
||||
$proxy_type = CURLPROXY_SOCKS5;
|
||||
} elseif ($proxy_type == 'sock5h') {
|
||||
$proxy_type = CURLPROXY_SOCKS5_HOSTNAME;
|
||||
} else {
|
||||
$proxy_type = CURLPROXY_HTTP;
|
||||
}
|
||||
@@ -451,4 +570,38 @@ function curl_set_proxy(&$ch)
|
||||
curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxy_userpwd);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_PROXYTYPE, $proxy_type);
|
||||
}
|
||||
}
|
||||
|
||||
function convertDomainToAscii($domain) {
|
||||
if (preg_match('/[\x{4e00}-\x{9fa5}]/u', $domain)) {
|
||||
return idn_to_ascii($domain);
|
||||
} else {
|
||||
return $domain;
|
||||
}
|
||||
}
|
||||
function convertDomainToUtf8($domain) {
|
||||
if (preg_match('/^xn--/', $domain)) {
|
||||
return idn_to_utf8($domain);
|
||||
} else {
|
||||
return $domain;
|
||||
}
|
||||
}
|
||||
|
||||
function getDomainDate($domain)
|
||||
{
|
||||
try {
|
||||
$whois = \Iodev\Whois\Factory::get()->createWhois();
|
||||
$info = $whois->loadDomainInfo($domain);
|
||||
if ($info) {
|
||||
if ($info->expirationDate > 0) {
|
||||
return [$info->creationDate > 0 ? date('Y-m-d H:i:s', $info->creationDate) : null, date('Y-m-d H:i:s', $info->expirationDate)];
|
||||
} else {
|
||||
throw new Exception('域名到期时间未知');
|
||||
}
|
||||
} else {
|
||||
throw new Exception('域名信息未找到');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('查询域名whois失败: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class Auth extends BaseController
|
||||
if (empty($username) || empty($password)) {
|
||||
return json(['code' => -1, 'msg' => '用户名或密码不能为空']);
|
||||
}
|
||||
if (!captcha_check($code)) {
|
||||
if (config_get('vcode', '1') == '1' && !captcha_check($code)) {
|
||||
return json(['code' => -1, 'msg' => '验证码错误', 'vcode' => 1]);
|
||||
}
|
||||
if (file_exists($login_limit_file)) {
|
||||
@@ -53,7 +53,9 @@ class Auth extends BaseController
|
||||
} else {
|
||||
if ($user) {
|
||||
Db::name('log')->insert(['uid' => $user['id'], 'action' => '登录失败', 'data' => 'IP:' . $this->clientip, 'addtime' => date("Y-m-d H:i:s")]);
|
||||
if (isset($user['totp_open']) && $user['totp_open'] == 1 && !empty($user['totp_secret'])) $login_limit_count = 10;
|
||||
if (isset($user['totp_open']) && $user['totp_open'] == 1 && !empty($user['totp_secret'])) {
|
||||
return json(['code' => -1, 'msg' => '用户名或密码错误', 'vcode' => 1]);
|
||||
}
|
||||
}
|
||||
if (!file_exists($login_limit_file)) {
|
||||
$login_limit = ['count' => 0, 'time' => 0];
|
||||
|
||||
@@ -36,15 +36,24 @@ class Cert extends BaseController
|
||||
|
||||
$select = Db::name('cert_account')->where('deploy', $deploy);
|
||||
if (!empty($kw)) {
|
||||
$select->whereLike('name|remark', '%' . $kw . '%');
|
||||
$select->whereLike('name|remark', '%' . $kw . '%')->whereOr('id', $kw);
|
||||
}
|
||||
$total = $select->count();
|
||||
$rows = $select->order('id', 'desc')->limit($offset, $limit)->select();
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
@@ -66,7 +75,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 +89,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 +105,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 +117,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 +148,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 +165,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 +199,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);
|
||||
@@ -202,36 +211,67 @@ 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');
|
||||
$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);
|
||||
}
|
||||
if (!empty($aid)) {
|
||||
$select->where('A.aid', $aid);
|
||||
}
|
||||
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();
|
||||
|
||||
$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']) && isset(CertHelper::$cert_config[$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;
|
||||
}
|
||||
|
||||
return json(['total' => $total, 'rows' => $list]);
|
||||
}
|
||||
|
||||
public function order_info()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$id = input('post.id/d');
|
||||
$row = Db::name('cert_order')->where('id', $id)->find();
|
||||
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')]]);
|
||||
}
|
||||
|
||||
public function order_op()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
@@ -245,34 +285,69 @@ 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,
|
||||
];
|
||||
$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' => $domain,
|
||||
'domain' => convertDomainToAscii($domain),
|
||||
'sort' => $i++,
|
||||
];
|
||||
}
|
||||
@@ -283,34 +358,56 @@ 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' => $domain,
|
||||
'domain' => convertDomainToAscii($domain),
|
||||
'sort' => $i++,
|
||||
];
|
||||
}
|
||||
@@ -321,9 +418,9 @@ class Cert extends BaseController
|
||||
$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();
|
||||
@@ -335,28 +432,57 @@ 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;
|
||||
foreach ($ids as $id) {
|
||||
if (input('post.act') == 'delete') {
|
||||
$dcount = DB::name('cert_deploy')->where('oid', $id)->count();
|
||||
if ($dcount > 0) continue;
|
||||
try {
|
||||
(new CertOrderService($id))->cancel();
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
Db::name('cert_order')->where('id', $id)->delete();
|
||||
Db::name('cert_domain')->where('oid', $id)->delete();
|
||||
$success++;
|
||||
} elseif (input('post.act') == 'reset') {
|
||||
try {
|
||||
$service = new CertOrderService($id);
|
||||
$service->cancel();
|
||||
$service->reset();
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
} elseif (input('post.act') == 'open' || input('post.act') == 'close') {
|
||||
$isauto = input('post.act') == 'open' ? 1 : 0;
|
||||
Db::name('cert_order')->where('id', $id)->update(['isauto' => $isauto]);
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
return json(['code' => 0, 'msg' => '成功操作' . $success . '个证书订单']);
|
||||
}
|
||||
return json(['code' => -3]);
|
||||
}
|
||||
@@ -368,22 +494,81 @@ class Cert extends BaseController
|
||||
$max_domains = CertHelper::$cert_config[$account['type']]['max_domains'];
|
||||
$wildcard = CertHelper::$cert_config[$account['type']]['wildcard'];
|
||||
$cname = CertHelper::$cert_config[$account['type']]['cname'];
|
||||
if (count($domains) > $max_domains) return ['code' => -1, 'msg' => '域名数量不能超过'.$max_domains.'个'];
|
||||
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 . '个'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach($domains as $domain){
|
||||
if(!$wildcard && strpos($domain, '*') !== false) return ['code' => -1, 'msg' => '该证书账户类型不支持泛域名'];
|
||||
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', '无权限');
|
||||
@@ -395,19 +580,19 @@ 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){
|
||||
return json(['code' => -1, 'msg' => $e->getMessage()]);
|
||||
} catch (Exception $e) {
|
||||
return json(['code' => -1, 'msg' => $e->getMessage(), 'trace' => $e->getTrace()]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,14 +604,16 @@ 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']];
|
||||
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'] . ')';
|
||||
}
|
||||
@@ -438,12 +625,11 @@ class Cert extends BaseController
|
||||
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);
|
||||
@@ -455,21 +641,29 @@ 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);
|
||||
}
|
||||
if (!isNullOrEmpty($status)) {
|
||||
$select->where('A.status', $status);
|
||||
}
|
||||
if (!empty($remark)) {
|
||||
$select->where('A.remark', $remark);
|
||||
}
|
||||
@@ -478,11 +672,17 @@ 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'];
|
||||
$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']));
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -511,7 +711,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'),
|
||||
@@ -532,18 +732,48 @@ 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;
|
||||
$certid = 0;
|
||||
if (input('post.action') == 'cert') {
|
||||
$certid = input('post.certid/d');
|
||||
$cert = Db::name('cert_order')->where('id', $certid)->find();
|
||||
if (!$cert) return json(['code' => -1, 'msg' => '证书订单不存在']);
|
||||
}
|
||||
foreach ($ids as $id) {
|
||||
if (input('post.act') == 'delete') {
|
||||
Db::name('cert_deploy')->where('id', $id)->delete();
|
||||
$success++;
|
||||
} elseif (input('post.act') == 'reset') {
|
||||
try {
|
||||
$service = new CertDeployService($id);
|
||||
$service->reset();
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
} elseif (input('post.act') == 'open' || input('post.act') == 'close') {
|
||||
$active = input('post.act') == 'open' ? 1 : 0;
|
||||
Db::name('cert_deploy')->where('id', $id)->update(['active' => $active]);
|
||||
$success++;
|
||||
} elseif (input('post.act') == 'cert') {
|
||||
Db::name('cert_deploy')->where('id', $id)->update(['oid' => $certid]);
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
return json(['code' => 0, 'msg' => '成功操作' . $success . '个任务']);
|
||||
}
|
||||
return json(['code' => -3]);
|
||||
}
|
||||
@@ -559,15 +789,15 @@ 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){
|
||||
return json(['code' => -1, 'msg' => $e->getMessage()]);
|
||||
} catch (Exception $e) {
|
||||
return json(['code' => -1, 'msg' => $e->getMessage(), 'trace' => $e->getTrace()]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -585,7 +815,8 @@ 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']];
|
||||
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'] . ')';
|
||||
}
|
||||
@@ -593,10 +824,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) {
|
||||
$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'].')'];
|
||||
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);
|
||||
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);
|
||||
|
||||
@@ -624,7 +860,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 . '%');
|
||||
}
|
||||
@@ -667,7 +903,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记录值']);
|
||||
@@ -678,7 +914,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'),
|
||||
@@ -705,13 +941,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]);
|
||||
@@ -721,17 +957,6 @@ class Cert extends BaseController
|
||||
public function certset()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
if ($this->request->isPost()) {
|
||||
$params = input('post.');
|
||||
foreach ($params as $key => $value) {
|
||||
if (empty($key)) {
|
||||
continue;
|
||||
}
|
||||
config_set($key, $value);
|
||||
Cache::delete('configs');
|
||||
}
|
||||
return json(['code' => 0, 'msg' => 'succ']);
|
||||
}
|
||||
return View::fetch();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ class Dmonitor extends BaseController
|
||||
{
|
||||
if (!checkPermission(2)) return json(['total' => 0, 'rows' => []]);
|
||||
$type = input('post.type/d', 1);
|
||||
$status = input('post.status', null);
|
||||
$kw = input('post.kw', null, 'trim');
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
@@ -57,104 +58,132 @@ class Dmonitor extends BaseController
|
||||
$select->whereLike('remark', '%' . $kw . '%');
|
||||
}
|
||||
}
|
||||
if (!isNullOrEmpty($status)) {
|
||||
$select->where('status', intval($status));
|
||||
}
|
||||
$total = $select->count();
|
||||
$list = $select->order('A.id', 'desc')->limit($offset, $limit)->field('A.*,B.name domain')->select()->toArray();
|
||||
|
||||
foreach ($list as &$row) {
|
||||
$row['checktimestr'] = date('Y-m-d H:i:s', $row['checktime']);
|
||||
$row['addtimestr'] = date('Y-m-d H:i:s', $row['addtime']);
|
||||
$row['checktimestr'] = $row['checktime'] > 0 ? date('Y-m-d H:i:s', $row['checktime']) : '未运行';
|
||||
}
|
||||
|
||||
return json(['total' => $total, 'rows' => $list]);
|
||||
}
|
||||
|
||||
public function task_op()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$action = input('param.action');
|
||||
if ($action == 'add') {
|
||||
$task = [
|
||||
'did' => input('post.did/d'),
|
||||
'rr' => input('post.rr', null, 'trim'),
|
||||
'recordid' => input('post.recordid', null, 'trim'),
|
||||
'type' => input('post.type/d'),
|
||||
'main_value' => input('post.main_value', null, 'trim'),
|
||||
'backup_value' => input('post.backup_value', null, 'trim'),
|
||||
'checktype' => input('post.checktype/d'),
|
||||
'checkurl' => input('post.checkurl', null, 'trim'),
|
||||
'tcpport' => !empty(input('post.tcpport')) ? input('post.tcpport/d') : null,
|
||||
'frequency' => input('post.frequency/d'),
|
||||
'cycle' => input('post.cycle/d'),
|
||||
'timeout' => input('post.timeout/d'),
|
||||
'proxy' => input('post.proxy/d'),
|
||||
'cdn' => input('post.cdn') == 'true' || input('post.cdn') == '1' ? 1 : 0,
|
||||
'remark' => input('post.remark', null, 'trim'),
|
||||
'recordinfo' => input('post.recordinfo', null, 'trim'),
|
||||
'addtime' => time(),
|
||||
'active' => 1
|
||||
];
|
||||
|
||||
if (empty($task['did']) || empty($task['rr']) || empty($task['recordid']) || empty($task['main_value']) || empty($task['frequency']) || empty($task['cycle'])) {
|
||||
return json(['code' => -1, 'msg' => '必填项不能为空']);
|
||||
}
|
||||
if ($task['checktype'] > 0 && $task['timeout'] > $task['frequency']) {
|
||||
return json(['code' => -1, 'msg' => '为保障容灾切换任务正常运行,最大超时时间不能大于检测间隔']);
|
||||
}
|
||||
if ($task['type'] == 2 && $task['backup_value'] == $task['main_value']) {
|
||||
return json(['code' => -1, 'msg' => '主备地址不能相同']);
|
||||
}
|
||||
if (Db::name('dmtask')->where('recordid', $task['recordid'])->find()) {
|
||||
return json(['code' => -1, 'msg' => '当前容灾切换策略已存在']);
|
||||
}
|
||||
Db::name('dmtask')->insert($task);
|
||||
return json(['code' => 0, 'msg' => '添加成功']);
|
||||
} elseif ($action == 'edit') {
|
||||
$id = input('post.id/d');
|
||||
$task = [
|
||||
'did' => input('post.did/d'),
|
||||
'rr' => input('post.rr', null, 'trim'),
|
||||
'recordid' => input('post.recordid', null, 'trim'),
|
||||
'type' => input('post.type/d'),
|
||||
'main_value' => input('post.main_value', null, 'trim'),
|
||||
'backup_value' => input('post.backup_value', null, 'trim'),
|
||||
'checktype' => input('post.checktype/d'),
|
||||
'checkurl' => input('post.checkurl', null, 'trim'),
|
||||
'tcpport' => !empty(input('post.tcpport')) ? input('post.tcpport/d') : null,
|
||||
'frequency' => input('post.frequency/d'),
|
||||
'cycle' => input('post.cycle/d'),
|
||||
'timeout' => input('post.timeout/d'),
|
||||
'proxy' => input('post.proxy/d'),
|
||||
'cdn' => input('post.cdn') == 'true' || input('post.cdn') == '1' ? 1 : 0,
|
||||
'remark' => input('post.remark', null, 'trim'),
|
||||
'recordinfo' => input('post.recordinfo', null, 'trim'),
|
||||
];
|
||||
|
||||
if (empty($task['did']) || empty($task['rr']) || empty($task['recordid']) || empty($task['main_value']) || empty($task['frequency']) || empty($task['cycle'])) {
|
||||
return json(['code' => -1, 'msg' => '必填项不能为空']);
|
||||
}
|
||||
if ($task['checktype'] > 0 && $task['timeout'] > $task['frequency']) {
|
||||
return json(['code' => -1, 'msg' => '为保障容灾切换任务正常运行,最大超时时间不能大于检测间隔']);
|
||||
}
|
||||
if ($task['type'] == 2 && $task['backup_value'] == $task['main_value']) {
|
||||
return json(['code' => -1, 'msg' => '主备地址不能相同']);
|
||||
}
|
||||
if (Db::name('dmtask')->where('recordid', $task['recordid'])->where('id', '<>', $id)->find()) {
|
||||
return json(['code' => -1, 'msg' => '当前容灾切换策略已存在']);
|
||||
}
|
||||
Db::name('dmtask')->where('id', $id)->update($task);
|
||||
return json(['code' => 0, 'msg' => '修改成功']);
|
||||
} elseif ($action == 'setactive') {
|
||||
$id = input('post.id/d');
|
||||
$active = input('post.active/d');
|
||||
Db::name('dmtask')->where('id', $id)->update(['active' => $active]);
|
||||
return json(['code' => 0, 'msg' => '设置成功']);
|
||||
} elseif ($action == 'del') {
|
||||
$id = input('post.id/d');
|
||||
Db::name('dmtask')->where('id', $id)->delete();
|
||||
Db::name('dmlog')->where('taskid', $id)->delete();
|
||||
return json(['code' => 0, 'msg' => '删除成功']);
|
||||
} elseif ($action == 'operation') {
|
||||
$ids = input('post.ids');
|
||||
$success = 0;
|
||||
foreach ($ids as $id) {
|
||||
if (input('post.act') == 'delete') {
|
||||
Db::name('dmtask')->where('id', $id)->delete();
|
||||
Db::name('dmlog')->where('taskid', $id)->delete();
|
||||
$success++;
|
||||
} elseif (input('post.act') == 'retry') {
|
||||
Db::name('dmtask')->where('id', $id)->update(['checknexttime' => time()]);
|
||||
$success++;
|
||||
} elseif (input('post.act') == 'open' || input('post.act') == 'close') {
|
||||
$isauto = input('post.act') == 'open' ? 1 : 0;
|
||||
Db::name('dmtask')->where('id', $id)->update(['active' => $isauto]);
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
return json(['code' => 0, 'msg' => '成功操作' . $success . '个容灾切换策略']);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '参数错误']);
|
||||
}
|
||||
}
|
||||
|
||||
public function taskform()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$action = input('param.action');
|
||||
if ($this->request->isPost()) {
|
||||
if ($action == 'add') {
|
||||
$task = [
|
||||
'did' => input('post.did/d'),
|
||||
'rr' => input('post.rr', null, 'trim'),
|
||||
'recordid' => input('post.recordid', null, 'trim'),
|
||||
'type' => input('post.type/d'),
|
||||
'main_value' => input('post.main_value', null, 'trim'),
|
||||
'backup_value' => input('post.backup_value', null, 'trim'),
|
||||
'checktype' => input('post.checktype/d'),
|
||||
'checkurl' => input('post.checkurl', null, 'trim'),
|
||||
'tcpport' => !empty(input('post.tcpport')) ? input('post.tcpport/d') : null,
|
||||
'frequency' => input('post.frequency/d'),
|
||||
'cycle' => input('post.cycle/d'),
|
||||
'timeout' => input('post.timeout/d'),
|
||||
'proxy' => input('post.proxy/d'),
|
||||
'remark' => input('post.remark', null, 'trim'),
|
||||
'recordinfo' => input('post.recordinfo', null, 'trim'),
|
||||
'addtime' => time(),
|
||||
'active' => 1
|
||||
];
|
||||
|
||||
if (empty($task['did']) || empty($task['rr']) || empty($task['recordid']) || empty($task['main_value']) || empty($task['frequency']) || empty($task['cycle'])) {
|
||||
return json(['code' => -1, 'msg' => '必填项不能为空']);
|
||||
}
|
||||
if ($task['checktype'] > 0 && $task['timeout'] > $task['frequency']) {
|
||||
return json(['code' => -1, 'msg' => '为保障容灾切换任务正常运行,最大超时时间不能大于检测间隔']);
|
||||
}
|
||||
if ($task['type'] == 2 && $task['backup_value'] == $task['main_value']) {
|
||||
return json(['code' => -1, 'msg' => '主备地址不能相同']);
|
||||
}
|
||||
if (Db::name('dmtask')->where('recordid', $task['recordid'])->find()) {
|
||||
return json(['code' => -1, 'msg' => '当前容灾切换策略已存在']);
|
||||
}
|
||||
Db::name('dmtask')->insert($task);
|
||||
return json(['code' => 0, 'msg' => '添加成功']);
|
||||
} elseif ($action == 'edit') {
|
||||
$id = input('post.id/d');
|
||||
$task = [
|
||||
'did' => input('post.did/d'),
|
||||
'rr' => input('post.rr', null, 'trim'),
|
||||
'recordid' => input('post.recordid', null, 'trim'),
|
||||
'type' => input('post.type/d'),
|
||||
'main_value' => input('post.main_value', null, 'trim'),
|
||||
'backup_value' => input('post.backup_value', null, 'trim'),
|
||||
'checktype' => input('post.checktype/d'),
|
||||
'checkurl' => input('post.checkurl', null, 'trim'),
|
||||
'tcpport' => !empty(input('post.tcpport')) ? input('post.tcpport/d') : null,
|
||||
'frequency' => input('post.frequency/d'),
|
||||
'cycle' => input('post.cycle/d'),
|
||||
'timeout' => input('post.timeout/d'),
|
||||
'proxy' => input('post.proxy/d'),
|
||||
'remark' => input('post.remark', null, 'trim'),
|
||||
'recordinfo' => input('post.recordinfo', null, 'trim'),
|
||||
];
|
||||
|
||||
if (empty($task['did']) || empty($task['rr']) || empty($task['recordid']) || empty($task['main_value']) || empty($task['frequency']) || empty($task['cycle'])) {
|
||||
return json(['code' => -1, 'msg' => '必填项不能为空']);
|
||||
}
|
||||
if ($task['checktype'] > 0 && $task['timeout'] > $task['frequency']) {
|
||||
return json(['code' => -1, 'msg' => '为保障容灾切换任务正常运行,最大超时时间不能大于检测间隔']);
|
||||
}
|
||||
if ($task['type'] == 2 && $task['backup_value'] == $task['main_value']) {
|
||||
return json(['code' => -1, 'msg' => '主备地址不能相同']);
|
||||
}
|
||||
if (Db::name('dmtask')->where('recordid', $task['recordid'])->where('id', '<>', $id)->find()) {
|
||||
return json(['code' => -1, 'msg' => '当前容灾切换策略已存在']);
|
||||
}
|
||||
Db::name('dmtask')->where('id', $id)->update($task);
|
||||
return json(['code' => 0, 'msg' => '修改成功']);
|
||||
} elseif ($action == 'setactive') {
|
||||
$id = input('post.id/d');
|
||||
$active = input('post.active/d');
|
||||
Db::name('dmtask')->where('id', $id)->update(['active' => $active]);
|
||||
return json(['code' => 0, 'msg' => '设置成功']);
|
||||
} elseif ($action == 'del') {
|
||||
$id = input('post.id/d');
|
||||
Db::name('dmtask')->where('id', $id)->delete();
|
||||
Db::name('dmlog')->where('taskid', $id)->delete();
|
||||
return json(['code' => 0, 'msg' => '删除成功']);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '参数错误']);
|
||||
}
|
||||
}
|
||||
$task = null;
|
||||
if ($action == 'edit') {
|
||||
$id = input('get.id/d');
|
||||
@@ -163,8 +192,9 @@ class Dmonitor extends BaseController
|
||||
}
|
||||
|
||||
$domains = [];
|
||||
foreach (Db::name('domain')->select() as $row) {
|
||||
$domains[$row['id']] = $row['name'];
|
||||
$domainList = Db::name('domain')->alias('A')->join('account B', 'A.aid = B.id')->field('A.id,A.name,B.type')->select();
|
||||
foreach ($domainList as $row) {
|
||||
$domains[] = ['id'=>$row['id'], 'name'=>$row['name'], 'type'=>$row['type']];
|
||||
}
|
||||
View::assign('domains', $domains);
|
||||
|
||||
@@ -215,20 +245,6 @@ class Dmonitor extends BaseController
|
||||
return json(['total' => $total, 'rows' => $list]);
|
||||
}
|
||||
|
||||
public function noticeset()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$params = input('post.');
|
||||
foreach ($params as $key => $value) {
|
||||
if (empty($key)) {
|
||||
continue;
|
||||
}
|
||||
config_set($key, $value);
|
||||
Cache::delete('configs');
|
||||
}
|
||||
return json(['code' => 0, 'msg' => 'succ']);
|
||||
}
|
||||
|
||||
public function clean()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
|
||||
@@ -5,8 +5,9 @@ namespace app\controller;
|
||||
use app\BaseController;
|
||||
use think\facade\Db;
|
||||
use think\facade\View;
|
||||
use think\facade\Request;
|
||||
use think\facade\Cache;
|
||||
use app\lib\DnsHelper;
|
||||
use app\service\ExpireNoticeService;
|
||||
use Exception;
|
||||
|
||||
class Domain extends BaseController
|
||||
@@ -141,13 +142,14 @@ class Domain extends BaseController
|
||||
$accounts = [];
|
||||
$types = [];
|
||||
foreach ($list as $row) {
|
||||
$accounts[$row['id']] = $row['id'] . '_' . DnsHelper::$dns_config[$row['type']]['name'];
|
||||
$name = $row['id'] . '_' . DnsHelper::$dns_config[$row['type']]['name'];
|
||||
if (!array_key_exists($row['type'], $types)) {
|
||||
$types[$row['type']] = DnsHelper::$dns_config[$row['type']]['name'];
|
||||
}
|
||||
if (!empty($row['remark'])) {
|
||||
$accounts[$row['id']] .= '(' . $row['remark'] . ')';
|
||||
$name .= '(' . $row['remark'] . ')';
|
||||
}
|
||||
$accounts[] = ['id' => $row['id'], 'name' => $name, 'type' => DnsHelper::$dns_config[$row['type']]['name'], 'add' => DnsHelper::$dns_config[$row['type']]['add']];
|
||||
}
|
||||
View::assign('accounts', $accounts);
|
||||
View::assign('types', $types);
|
||||
@@ -179,6 +181,7 @@ class Domain extends BaseController
|
||||
if (!checkPermission(1)) return json(['total' => 0, 'rows' => []]);
|
||||
$kw = input('post.kw', null, 'trim');
|
||||
$type = input('post.type', null, 'trim');
|
||||
$status = input('post.status', null, 'trim');
|
||||
$offset = input('post.offset/d', 0);
|
||||
$limit = input('post.limit/d', 10);
|
||||
|
||||
@@ -192,6 +195,13 @@ class Domain extends BaseController
|
||||
if (request()->user['level'] == 1) {
|
||||
$select->where('is_hide', 0)->where('A.name', 'in', request()->user['permission']);
|
||||
}
|
||||
if (!isNullOrEmpty($status)) {
|
||||
if ($status == '2') {
|
||||
$select->where('A.expiretime', '<=', date('Y-m-d H:i:s'));
|
||||
} elseif ($status == '1') {
|
||||
$select->where('A.expiretime', '<=', date('Y-m-d H:i:s', time() + 86400 * 30))->where('A.expiretime', '>', date('Y-m-d H:i:s'));
|
||||
}
|
||||
}
|
||||
$total = $select->count();
|
||||
$rows = $select->fieldRaw('A.*,B.type,B.remark aremark')->order('A.id', 'desc')->limit($offset, $limit)->select();
|
||||
|
||||
@@ -216,13 +226,21 @@ class Domain extends BaseController
|
||||
} elseif ($act == 'add') {
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$aid = input('post.aid/d');
|
||||
$method = input('post.method/d', 0);
|
||||
$name = input('post.name', null, 'trim');
|
||||
$thirdid = input('post.thirdid', null, 'trim');
|
||||
$recordcount = input('post.recordcount/d', 0);
|
||||
if (empty($name) || empty($thirdid)) return json(['code' => -1, 'msg' => '参数不能为空']);
|
||||
if ($method == 1 && empty($name) || $method == 0 && (empty($name) || empty($thirdid))) return json(['code' => -1, 'msg' => '参数不能为空']);
|
||||
if (Db::name('domain')->where('aid', $aid)->where('name', $name)->find()) {
|
||||
return json(['code' => -1, 'msg' => '域名已存在']);
|
||||
}
|
||||
if ($method == 1) {
|
||||
$dns = DnsHelper::getModel($aid);
|
||||
$result = $dns->addDomain($name);
|
||||
if (!$result) return json(['code' => -1, 'msg' => '添加域名失败,' . $dns->getError()]);
|
||||
$name = $result['name'];
|
||||
$thirdid = $result['id'];
|
||||
}
|
||||
Db::name('domain')->insert([
|
||||
'aid' => $aid,
|
||||
'name' => $name,
|
||||
@@ -240,10 +258,15 @@ class Domain extends BaseController
|
||||
if (!$row) return json(['code' => -1, 'msg' => '域名不存在']);
|
||||
$is_hide = input('post.is_hide/d');
|
||||
$is_sso = input('post.is_sso/d');
|
||||
$is_notice = input('post.is_notice/d');
|
||||
$expiretime = input('post.expiretime', null, 'trim');
|
||||
$remark = input('post.remark', null, 'trim');
|
||||
if (empty($remark)) $remark = null;
|
||||
Db::name('domain')->where('id', $id)->update([
|
||||
'is_hide' => $is_hide,
|
||||
'is_sso' => $is_sso,
|
||||
'is_notice' => $is_notice,
|
||||
'expiretime' => $expiretime ? $expiretime : null,
|
||||
'remark' => $remark,
|
||||
]);
|
||||
return json(['code' => 0, 'msg' => '修改域名配置成功!']);
|
||||
@@ -253,6 +276,7 @@ class Domain extends BaseController
|
||||
Db::name('domain')->where('id', $id)->delete();
|
||||
Db::name('dmtask')->where('did', $id)->delete();
|
||||
Db::name('optimizeip')->where('did', $id)->delete();
|
||||
Db::name('sctask')->where('did', $id)->delete();
|
||||
return json(['code' => 0]);
|
||||
} elseif ($act == 'batchadd') {
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
@@ -273,6 +297,30 @@ class Domain extends BaseController
|
||||
}
|
||||
Db::name('domain')->insertAll($data);
|
||||
return json(['code' => 0, 'msg' => '成功添加' . count($data) . '个域名!']);
|
||||
} elseif ($act == 'batchedit') {
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$ids = input('post.ids');
|
||||
if (empty($ids)) return json(['code' => -1, 'msg' => '参数不能为空']);
|
||||
$remark = input('post.remark', null, 'trim');
|
||||
if (empty($remark)) $remark = null;
|
||||
$count = Db::name('domain')->where('id', 'in', $ids)->update(['remark' => $remark]);
|
||||
return json(['code' => 0, 'msg' => '成功修改' . $count . '个域名!']);
|
||||
} elseif ($act == 'batchsetnotice') {
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$ids = input('post.ids');
|
||||
$is_notice = input('post.is_notice/d', 0);
|
||||
if (empty($ids)) return json(['code' => -1, 'msg' => '参数不能为空']);
|
||||
$count = Db::name('domain')->where('id', 'in', $ids)->update(['is_notice' => $is_notice]);
|
||||
return json(['code' => 0, 'msg' => '成功修改' . $count . '个域名!']);
|
||||
} elseif ($act == 'batchdel') {
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$ids = input('post.ids');
|
||||
if (empty($ids)) return json(['code' => -1, 'msg' => '参数不能为空']);
|
||||
Db::name('domain')->where('id', 'in', $ids)->delete();
|
||||
Db::name('dmtask')->where('did', 'in', $ids)->delete();
|
||||
Db::name('optimizeip')->where('did', 'in', $ids)->delete();
|
||||
Db::name('sctask')->where('did', 'in', $ids)->delete();
|
||||
return json(['code' => 0, 'msg' => '成功删除' . count($ids) . '个域名!']);
|
||||
}
|
||||
return json(['code' => -3]);
|
||||
}
|
||||
@@ -384,8 +432,8 @@ class Domain extends BaseController
|
||||
$type = input('post.type', null, 'trim');
|
||||
$line = input('post.line', null, 'trim');
|
||||
$status = input('post.status', null, 'trim');
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
$offset = input('post.offset/d', 0);
|
||||
$limit = input('post.limit/d', 10);
|
||||
if ($limit == 0) {
|
||||
$page = 1;
|
||||
} else {
|
||||
@@ -413,7 +461,7 @@ class Domain extends BaseController
|
||||
}
|
||||
|
||||
$dnstype = Db::name('account')->where('id', $drow['aid'])->value('type');
|
||||
if ($dnstype == 'baidu' || $dnstype == 'namesilo') {
|
||||
if (DnsHelper::$dns_config[$dnstype]['page']) {
|
||||
return json($domainRecords['list']);
|
||||
}
|
||||
|
||||
@@ -422,7 +470,6 @@ class Domain extends BaseController
|
||||
|
||||
public function record_list()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$id = input('post.id/d');
|
||||
$rr = input('post.rr', null, 'trim');
|
||||
|
||||
@@ -438,11 +485,14 @@ class Domain extends BaseController
|
||||
|
||||
list($recordLine, $minTTL) = $this->get_line_and_ttl($drow);
|
||||
|
||||
$list = [];
|
||||
foreach ($domainRecords['list'] as &$row) {
|
||||
if ($rr == '@' && ($row['Type'] == 'NS' || $row['Type'] == 'SOA')) continue;
|
||||
$row['LineName'] = isset($recordLine[$row['Line']]) ? $recordLine[$row['Line']]['name'] : $row['Line'];
|
||||
$list[] = $row;
|
||||
}
|
||||
|
||||
return json(['code' => 0, 'data' => $domainRecords['list']]);
|
||||
return json(['code' => 0, 'data' => $list]);
|
||||
}
|
||||
|
||||
public function record_add()
|
||||
@@ -470,7 +520,7 @@ class Domain extends BaseController
|
||||
$dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
|
||||
$recordid = $dns->addDomainRecord($name, $type, $value, $line, $ttl, $mx, $weight, $remark);
|
||||
if ($recordid) {
|
||||
$this->add_log($drow['name'], '添加解析', $type . '记录 ' . $name . ' ' . $value . ' (线路:' . $line . ' TTL:' . $ttl . ')');
|
||||
$this->add_log($drow['name'], '添加解析', $name.' ['.$type.'] '.$value.' (线路:'.$line.' TTL:'.$ttl.')');
|
||||
return json(['code' => 0, 'msg' => '添加解析记录成功!']);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '添加解析记录失败,' . $dns->getError()]);
|
||||
@@ -496,6 +546,8 @@ class Domain extends BaseController
|
||||
$mx = input('post.mx/d', 1);
|
||||
$remark = input('post.remark', null, 'trim');
|
||||
|
||||
$recordinfo = input('post.recordinfo', null, 'trim');
|
||||
|
||||
if (empty($recordid) || empty($name) || empty($type) || empty($value)) {
|
||||
return json(['code' => -1, 'msg' => '参数不能为空']);
|
||||
}
|
||||
@@ -503,7 +555,17 @@ class Domain extends BaseController
|
||||
$dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
|
||||
$recordid = $dns->updateDomainRecord($recordid, $name, $type, $value, $line, $ttl, $mx, $weight, $remark);
|
||||
if ($recordid) {
|
||||
$this->add_log($drow['name'], '修改解析', $type . '记录 ' . $name . ' ' . $value . ' (线路:' . $line . ' TTL:' . $ttl . ')');
|
||||
if ($recordinfo) {
|
||||
$recordinfo = json_decode($recordinfo, true);
|
||||
if (is_array($recordinfo['Value'])) $recordinfo['Value'] = implode(',', $recordinfo['Value']);
|
||||
if ($recordinfo['Name'] != $name || $recordinfo['Type'] != $type || $recordinfo['Value'] != $value) {
|
||||
$this->add_log($drow['name'], '修改解析', $recordinfo['Name'].' ['.$recordinfo['Type'].'] '.$recordinfo['Value'].' → '.$name.' ['.$type.'] '.$value.' (线路:'.$line.' TTL:'.$ttl.')');
|
||||
} elseif($recordinfo['Line'] != $line || $recordinfo['TTL'] != $ttl) {
|
||||
$this->add_log($drow['name'], '修改解析', $name.' ['.$type.'] '.$value.' (线路:'.$line.' TTL:'.$ttl.')');
|
||||
}
|
||||
} else {
|
||||
$this->add_log($drow['name'], '修改解析', $name.' ['.$type.'] '.$value.' (线路:'.$line.' TTL:'.$ttl.')');
|
||||
}
|
||||
return json(['code' => 0, 'msg' => '修改解析记录成功!']);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '修改解析记录失败,' . $dns->getError()]);
|
||||
@@ -520,6 +582,7 @@ class Domain extends BaseController
|
||||
if (!checkPermission(0, $drow['name'])) return $this->alert('error', '无权限');
|
||||
|
||||
$recordid = input('post.recordid', null, 'trim');
|
||||
$recordinfo = input('post.recordinfo', null, 'trim');
|
||||
|
||||
if (empty($recordid)) {
|
||||
return json(['code' => -1, 'msg' => '参数不能为空']);
|
||||
@@ -527,7 +590,13 @@ class Domain extends BaseController
|
||||
|
||||
$dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
|
||||
if ($dns->deleteDomainRecord($recordid)) {
|
||||
$this->add_log($drow['name'], '删除解析', '记录ID:' . $recordid);
|
||||
if ($recordinfo) {
|
||||
$recordinfo = json_decode($recordinfo, true);
|
||||
if (is_array($recordinfo['Value'])) $recordinfo['Value'] = implode(',', $recordinfo['Value']);
|
||||
$this->add_log($drow['name'], '删除解析', $recordinfo['Name'].' ['.$recordinfo['Type'].'] '.$recordinfo['Value'].' (线路:'.$recordinfo['Line'].' TTL:'.$recordinfo['TTL'].')');
|
||||
} else {
|
||||
$this->add_log($drow['name'], '删除解析', '记录ID:'.$recordid);
|
||||
}
|
||||
return json(['code' => 0, 'msg' => '删除解析记录成功!']);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '删除解析记录失败,' . $dns->getError()]);
|
||||
@@ -545,6 +614,7 @@ class Domain extends BaseController
|
||||
|
||||
$recordid = input('post.recordid', null, 'trim');
|
||||
$status = input('post.status', null, 'trim');
|
||||
$recordinfo = input('post.recordinfo', null, 'trim');
|
||||
|
||||
if (empty($recordid)) {
|
||||
return json(['code' => -1, 'msg' => '参数不能为空']);
|
||||
@@ -553,7 +623,13 @@ class Domain extends BaseController
|
||||
$dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
|
||||
if ($dns->setDomainRecordStatus($recordid, $status)) {
|
||||
$action = $status == '1' ? '启用解析' : '暂停解析';
|
||||
$this->add_log($drow['name'], $action, '记录ID:' . $recordid);
|
||||
if ($recordinfo) {
|
||||
$recordinfo = json_decode($recordinfo, true);
|
||||
if (is_array($recordinfo['Value'])) $recordinfo['Value'] = implode(',', $recordinfo['Value']);
|
||||
$this->add_log($drow['name'], $action, $recordinfo['Name'].' ['.$recordinfo['Type'].'] '.$recordinfo['Value'].' (线路:'.$recordinfo['Line'].' TTL:'.$recordinfo['TTL'].')');
|
||||
} else {
|
||||
$this->add_log($drow['name'], $action, '记录ID:'.$recordid);
|
||||
}
|
||||
return json(['code' => 0, 'msg' => '操作成功!']);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '操作失败,' . $dns->getError()]);
|
||||
@@ -594,10 +670,11 @@ class Domain extends BaseController
|
||||
}
|
||||
if (!checkPermission(0, $drow['name'])) return $this->alert('error', '无权限');
|
||||
|
||||
$recordids = input('post.recordids', null, 'trim');
|
||||
$action = input('post.action', null, 'trim');
|
||||
$recordinfo = input('post.recordinfo', null, 'trim');
|
||||
$recordinfo = json_decode($recordinfo, true);
|
||||
|
||||
if (empty($recordids) || empty($action)) {
|
||||
if (empty($recordinfo) || empty($action)) {
|
||||
return json(['code' => -1, 'msg' => '参数不能为空']);
|
||||
}
|
||||
|
||||
@@ -605,25 +682,28 @@ class Domain extends BaseController
|
||||
$fail = 0;
|
||||
$dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
|
||||
if ($action == 'open') {
|
||||
foreach ($recordids as $recordid) {
|
||||
if ($dns->setDomainRecordStatus($recordid, '1')) {
|
||||
$this->add_log($drow['name'], '启用解析', '记录ID:' . $recordid);
|
||||
foreach ($recordinfo as $record) {
|
||||
if ($dns->setDomainRecordStatus($record['RecordId'], '1')) {
|
||||
if (is_array($record['Value'])) $record['Value'] = implode(',', $record['Value']);
|
||||
$this->add_log($drow['name'], '启用解析', $record['Name'].' ['.$record['Type'].'] '.$record['Value'].' (线路:'.$record['Line'].' TTL:'.$record['TTL'].')');
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
$msg = '成功启用' . $success . '条解析记录';
|
||||
} else if ($action == 'pause') {
|
||||
foreach ($recordids as $recordid) {
|
||||
if ($dns->setDomainRecordStatus($recordid, '0')) {
|
||||
$this->add_log($drow['name'], '暂停解析', '记录ID:' . $recordid);
|
||||
foreach ($recordinfo as $record) {
|
||||
if ($dns->setDomainRecordStatus($record['RecordId'], '0')) {
|
||||
if (is_array($record['Value'])) $record['Value'] = implode(',', $record['Value']);
|
||||
$this->add_log($drow['name'], '暂停解析', $record['Name'].' ['.$record['Type'].'] '.$record['Value'].' (线路:'.$record['Line'].' TTL:'.$record['TTL'].')');
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
$msg = '成功暂停' . $success . '条解析记录';
|
||||
} else if ($action == 'delete') {
|
||||
foreach ($recordids as $recordid) {
|
||||
if ($dns->deleteDomainRecord($recordid)) {
|
||||
$this->add_log($drow['name'], '删除解析', '记录ID:' . $recordid);
|
||||
foreach ($recordinfo as $record) {
|
||||
if ($dns->deleteDomainRecord($record['RecordId'])) {
|
||||
if (is_array($record['Value'])) $record['Value'] = implode(',', $record['Value']);
|
||||
$this->add_log($drow['name'], '删除解析', $record['Name'].' ['.$record['Type'].'] '.$record['Value'].' (线路:'.$record['Line'].' TTL:'.$record['TTL'].')');
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
@@ -631,8 +711,8 @@ class Domain extends BaseController
|
||||
} else if ($action == 'remark') {
|
||||
$remark = input('post.remark', null, 'trim');
|
||||
if (empty($remark)) $remark = null;
|
||||
foreach ($recordids as $recordid) {
|
||||
if ($dns->updateDomainRecordRemark($recordid, $remark)) {
|
||||
foreach ($recordinfo as $record) {
|
||||
if ($dns->updateDomainRecordRemark($record['RecordId'], $remark)) {
|
||||
$success++;
|
||||
} else {
|
||||
$fail++;
|
||||
@@ -669,9 +749,10 @@ class Domain extends BaseController
|
||||
$success = 0;
|
||||
$fail = 0;
|
||||
foreach ($recordinfo as $record) {
|
||||
$recordid = $dns->updateDomainRecord($record['recordid'], $record['name'], $type, $value, $record['line'], $record['ttl'], $record['mx'], $record['weight'], $record['remark']);
|
||||
$recordid = $dns->updateDomainRecord($record['RecordId'], $record['Name'], $type, $value, $record['Line'], $record['TTL'], $record['MX'], $record['Weight'], $record['Remark']);
|
||||
if ($recordid) {
|
||||
$this->add_log($drow['name'], '修改解析', $type . '记录 ' . $record['name'] . ' ' . $value . ' (线路:' . $record['line'] . ' TTL:' . $record['ttl'] . ')');
|
||||
if (is_array($record['Value'])) $record['Value'] = implode(',', $record['Value']);
|
||||
$this->add_log($drow['name'], '修改解析', $record['Name'].' ['.$record['Type'].'] '.$record['Value'].' → '.$record['Name'].' ['.$type.'] '.$value.' (线路:'.$record['Line'].' TTL:'.$record['TTL'].')');
|
||||
$success++;
|
||||
} else {
|
||||
$fail++;
|
||||
@@ -690,9 +771,10 @@ class Domain extends BaseController
|
||||
$success = 0;
|
||||
$fail = 0;
|
||||
foreach ($recordinfo as $record) {
|
||||
$recordid = $dns->updateDomainRecord($record['recordid'], $record['name'], $record['type'], $record['value'], $line, $record['ttl'], $record['mx'], $record['weight'], $record['remark']);
|
||||
$recordid = $dns->updateDomainRecord($record['RecordId'], $record['Name'], $record['Type'], $record['Value'], $line, $record['TTL'], $record['MX'], $record['Weight'], $record['Remark']);
|
||||
if ($recordid) {
|
||||
$this->add_log($drow['name'], '修改解析', $record['type'] . '记录 ' . $record['name'] . ' ' . $record['value'] . ' (线路:' . $line . ' TTL:' . $record['ttl'] . ')');
|
||||
if (is_array($record['Value'])) $record['Value'] = implode(',', $record['Value']);
|
||||
$this->add_log($drow['name'], '修改解析', $record['Name'].' ['.$record['Type'].'] '.$record['Value'].' (线路:'.$line.' TTL:'.$record['TTL'].')');
|
||||
$success++;
|
||||
} else {
|
||||
$fail++;
|
||||
@@ -723,6 +805,12 @@ class Domain extends BaseController
|
||||
if (empty($record) || empty($recordlist)) {
|
||||
return json(['code' => -1, 'msg' => '参数不能为空']);
|
||||
}
|
||||
if (is_null($line)) {
|
||||
$line = DnsHelper::$line_name[$dnstype]['DEF'];
|
||||
if ($dnstype == 'cloudflare' && input('post.proxy/d', 0) == 1) {
|
||||
$line = '1';
|
||||
}
|
||||
}
|
||||
|
||||
$dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
|
||||
|
||||
@@ -735,13 +823,19 @@ class Domain extends BaseController
|
||||
$thistype = empty($type) ? getDnsType($arr[1]) : $type;
|
||||
$recordid = $dns->addDomainRecord($arr[0], $thistype, $arr[1], $line, $ttl, $mx);
|
||||
if ($recordid) {
|
||||
$this->add_log($drow['name'], '添加解析', $thistype . '记录 ' . $arr[0] . ' ' . $arr[1] . ' (线路:' . $line . ' TTL:' . $ttl . ')');
|
||||
$this->add_log($drow['name'], '添加解析', $arr[0].' ['.$thistype.'] '.$arr[1].' (线路:'.$line.' TTL:'.$ttl.')');
|
||||
$success++;
|
||||
} else {
|
||||
$fail++;
|
||||
}
|
||||
}
|
||||
return json(['code' => 0, 'msg' => '批量添加解析,成功' . $success . '条,失败' . $fail . '条']);
|
||||
if ($success > 0) {
|
||||
return json(['code' => 0, 'msg' => '批量添加解析,成功' . $success . '条,失败' . $fail . '条']);
|
||||
} elseif($fail > 0) {
|
||||
return json(['code' => -1, 'msg' => '批量添加解析失败,' . $dns->getError()]);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '批量添加解析失败,没有可添加的记录']);
|
||||
}
|
||||
}
|
||||
|
||||
list($recordLine, $minTTL) = $this->get_line_and_ttl($drow);
|
||||
@@ -762,6 +856,89 @@ class Domain extends BaseController
|
||||
return view('batchadd');
|
||||
}
|
||||
|
||||
public function record_batch_add2()
|
||||
{
|
||||
return view('batchadd2');
|
||||
}
|
||||
|
||||
public function record_batch_edit2()
|
||||
{
|
||||
if (request()->isAjax()) {
|
||||
$id = input('post.id/d');
|
||||
$drow = Db::name('domain')->where('id', $id)->find();
|
||||
if (!$drow) {
|
||||
return json(['code' => -1, 'msg' => '域名不存在']);
|
||||
}
|
||||
$dnstype = Db::name('account')->where('id', $drow['aid'])->value('type');
|
||||
if (!checkPermission(0, $drow['name'])) return json(['code' => -1, 'msg' => '无权限']);
|
||||
|
||||
$name = input('post.name', null, 'trim');
|
||||
$type = input('post.type', null, 'trim');
|
||||
$value = input('post.value', null, 'trim');
|
||||
$ttl = input('post.ttl/d', 0);
|
||||
$mx = input('post.mx/d', 0);
|
||||
|
||||
if (empty($name) || empty($type) || empty($value)) {
|
||||
return json(['code' => -1, 'msg' => '必填参数不能为空']);
|
||||
}
|
||||
$line = DnsHelper::$line_name[$dnstype]['DEF'];
|
||||
|
||||
$dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
|
||||
$domainRecords = $dns->getSubDomainRecords($name, 1, 100);
|
||||
if (!$domainRecords) return json(['code' => -1, 'msg' => '获取记录列表失败,' . $dns->getError()]);
|
||||
if (empty($domainRecords['list'])) return json(['code' => -1, 'msg' => '没有可修改的记录']);
|
||||
|
||||
if ($type == 'A' || $type == 'AAAA' || $type == 'CNAME') {
|
||||
$list2 = array_filter($domainRecords['list'], function ($item) use ($type) {
|
||||
return $item['Type'] == $type;
|
||||
});
|
||||
if (!empty($list2)) {
|
||||
$list = $list2;
|
||||
} else {
|
||||
$list = array_filter($domainRecords['list'], function ($item) {
|
||||
return $item['Type'] == 'A' || $item['Type'] == 'AAAA' || $item['Type'] == 'CNAME';
|
||||
});
|
||||
}
|
||||
} else {
|
||||
$list = array_filter($domainRecords['list'], function ($item) use ($type) {
|
||||
return $item['Type'] == $type;
|
||||
});
|
||||
}
|
||||
if (empty($list)) return json(['code' => -1, 'msg' => '没有可修改的'.$type.'记录']);
|
||||
|
||||
$list2 = array_filter($domainRecords['list'], function ($item) use ($line) {
|
||||
return $item['Line'] == $line;
|
||||
});
|
||||
if (!empty($list2)) $list = $list2;
|
||||
|
||||
$success = 0;
|
||||
$fail = 0;
|
||||
foreach ($list as $record) {
|
||||
if ($name == '@' && ($record['Type'] == 'NS' || $record['Type'] == 'SOA')) continue;
|
||||
|
||||
if ($ttl > 0) $record['TTL'] = $ttl;
|
||||
if ($mx > 0) $record['MX'] = $mx;
|
||||
$recordid = $dns->updateDomainRecord($record['RecordId'], $record['Name'], $type, $value, $record['Line'], $record['TTL'], $record['MX'], $record['Weight'], $record['Remark']);
|
||||
if ($recordid) {
|
||||
if (is_array($record['Value'])) $record['Value'] = implode(',', $record['Value']);
|
||||
$this->add_log($drow['name'], '修改解析', $record['Name'].' ['.$record['Type'].'] '.$record['Value'].' → '.$record['Name'].' ['.$type.'] '.$value.' (线路:'.$record['Line'].' TTL:'.$record['TTL'].')');
|
||||
$success++;
|
||||
} else {
|
||||
$fail++;
|
||||
}
|
||||
}
|
||||
if ($success > 0) {
|
||||
return json(['code' => 0, 'msg' => '成功修改' . $success . '条解析记录']);
|
||||
} elseif($fail > 0) {
|
||||
return json(['code' => -1, 'msg' => $dns->getError()]);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '没有可修改的记录']);
|
||||
}
|
||||
}
|
||||
|
||||
return view('batchedit');
|
||||
}
|
||||
|
||||
public function record_log()
|
||||
{
|
||||
$id = input('param.id/d');
|
||||
@@ -788,6 +965,135 @@ class Domain extends BaseController
|
||||
|
||||
private function add_log($domain, $action, $data)
|
||||
{
|
||||
if (strlen($data) > 500) $data = substr($data, 0, 500);
|
||||
Db::name('log')->insert(['uid' => request()->user['id'], 'domain' => $domain, 'action' => $action, 'data' => $data, 'addtime' => date("Y-m-d H:i:s")]);
|
||||
}
|
||||
|
||||
|
||||
public function weight()
|
||||
{
|
||||
$id = input('param.id/d');
|
||||
$drow = Db::name('domain')->where('id', $id)->find();
|
||||
if (!$drow) {
|
||||
return $this->alert('error', '域名不存在');
|
||||
}
|
||||
if (!checkPermission(0, $drow['name'])) return $this->alert('error', '无权限');
|
||||
if (request()->isAjax()) {
|
||||
$act = input('param.act');
|
||||
if ($act == 'status') {
|
||||
$subdomain = input('post.subdomain', null, 'trim');
|
||||
$status = input('post.status', null, 'trim');
|
||||
$type = input('post.type', null, 'trim');
|
||||
$line = input('post.line', null, 'trim');
|
||||
$dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
|
||||
if ($dns->setWeightStatus($subdomain, $status, $type, $line)) {
|
||||
return json(['code' => 0, 'msg' => '操作成功']);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '操作失败,' . $dns->getError()]);
|
||||
}
|
||||
} elseif ($act == 'update') {
|
||||
$subdomain = input('post.subdomain', null, 'trim');
|
||||
$status = input('post.status', '0', 'trim');
|
||||
$type = input('post.type', null, 'trim');
|
||||
$line = input('post.line', null, 'trim');
|
||||
$weight = input('post.weight');
|
||||
if (empty($subdomain) || empty($type) || empty($line) || $status == '1' && empty($weight)) {
|
||||
return json(['code' => -1, 'msg' => '参数不能为空']);
|
||||
}
|
||||
$dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
|
||||
if ($type == 'CNAME' || $dns->setWeightStatus($subdomain, $status, $type, $line)) {
|
||||
if ($status == '1') {
|
||||
$success = 0;
|
||||
foreach($weight as $recordid => $weight) {
|
||||
if ($dns->updateRecordWeight($recordid, $weight)) {
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
if ($success > 0) {
|
||||
return json(['code' => 0, 'msg' => '成功修改' . $success . '条解析记录权重']);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '修改权重失败,' . $dns->getError()]);
|
||||
}
|
||||
}
|
||||
return json(['code' => 0, 'msg' => '修改成功']);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '修改失败,' . $dns->getError()]);
|
||||
}
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '参数错误']);
|
||||
}
|
||||
}
|
||||
|
||||
$dnstype = Db::name('account')->where('id', $drow['aid'])->value('type');
|
||||
if ($dnstype != 'aliyun') {
|
||||
return $this->alert('error', '仅支持阿里云解析的域名');
|
||||
}
|
||||
list($recordLine, $minTTL) = $this->get_line_and_ttl($drow);
|
||||
|
||||
$recordLineArr = [];
|
||||
foreach ($recordLine as $key => $item) {
|
||||
$recordLineArr[] = ['id' => strval($key), 'name' => $item['name'], 'parent' => $item['parent']];
|
||||
}
|
||||
|
||||
$dnsconfig = DnsHelper::$dns_config[$dnstype];
|
||||
$dnsconfig['type'] = $dnstype;
|
||||
|
||||
View::assign('domainId', $id);
|
||||
View::assign('domainName', $drow['name']);
|
||||
View::assign('recordLine', $recordLineArr);
|
||||
View::assign('dnsconfig', $dnsconfig);
|
||||
return view();
|
||||
}
|
||||
|
||||
public function weight_data()
|
||||
{
|
||||
$id = input('param.id/d');
|
||||
$keyword = input('post.keyword', null, 'trim');
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
if ($limit == 0) {
|
||||
$page = 1;
|
||||
} else {
|
||||
$page = $offset / $limit + 1;
|
||||
}
|
||||
|
||||
$drow = Db::name('domain')->where('id', $id)->find();
|
||||
if (!$drow) {
|
||||
return json(['total' => 0, 'rows' => []]);
|
||||
}
|
||||
if (!checkPermission(0, $drow['name'])) return json(['total' => 0, 'rows' => []]);
|
||||
|
||||
$dns = DnsHelper::getModel($drow['aid'], $drow['name'], $drow['thirdid']);
|
||||
$domainRecords = $dns->getWeightSubDomains($page, $limit, $keyword);
|
||||
return json(['total' => $domainRecords['total'], 'rows' => $domainRecords['list']]);
|
||||
}
|
||||
|
||||
public function expire_notice()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
if ($this->request->isPost()) {
|
||||
$params = input('post.');
|
||||
foreach ($params as $key => $value) {
|
||||
if (empty($key)) {
|
||||
continue;
|
||||
}
|
||||
config_set($key, $value);
|
||||
Cache::delete('configs');
|
||||
}
|
||||
return json(['code' => 0, 'msg' => 'succ']);
|
||||
}
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function update_date()
|
||||
{
|
||||
$id = input('param.id/d');
|
||||
$drow = Db::name('domain')->where('id', $id)->find();
|
||||
if (!$drow) {
|
||||
return json(['code' => -1, 'msg' => '域名不存在']);
|
||||
}
|
||||
if (!checkPermission(0, $drow['name'])) return json(['code' => -1, 'msg' => '无权限']);
|
||||
$result = (new ExpireNoticeService())->updateDomainDate($id, $drow['name']);
|
||||
return json($result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@ use Exception;
|
||||
use think\facade\Db;
|
||||
use think\facade\View;
|
||||
use think\facade\Cache;
|
||||
use app\lib\DnsHelper;
|
||||
use app\utils\MsgNotice;
|
||||
|
||||
class Index extends BaseController
|
||||
{
|
||||
@@ -19,16 +17,36 @@ class Index extends BaseController
|
||||
}
|
||||
if ($this->request->isAjax()) {
|
||||
if (input('post.do') == 'stat') {
|
||||
$stat = ['domains' => 0, 'users' => 0, 'records' => 0, 'types' => count(DnsHelper::$dns_config)];
|
||||
$stat = [];
|
||||
if ($this->request->user['level'] == 2) {
|
||||
$stat['domains'] = Db::name('domain')->count();
|
||||
$stat['users'] = Db::name('user')->count();
|
||||
$stat['records'] = Db::name('domain')->sum('recordcount');
|
||||
} else {
|
||||
$stat['domains'] = Db::name('domain')->where('name', 'in', $this->request->user['permission'])->count();
|
||||
$stat['users'] = 1;
|
||||
$stat['records'] = Db::name('domain')->where('name', 'in', $this->request->user['permission'])->sum('recordcount');
|
||||
}
|
||||
$stat['tasks'] = Db::name('dmtask')->count();
|
||||
$stat['certs'] = Db::name('cert_order')->count();
|
||||
$stat['deploys'] = Db::name('cert_deploy')->count();
|
||||
|
||||
$run_time = config_get('run_time', null, true);
|
||||
$run_state = $run_time ? (time() - strtotime($run_time) > 10 ? 0 : 1) : 0;
|
||||
$stat['dmonitor_state'] = $run_state;
|
||||
$stat['dmonitor_active'] = Db::name('dmtask')->where('active', 1)->count();
|
||||
$stat['dmonitor_status_0'] = Db::name('dmtask')->where('status', 0)->count();
|
||||
$stat['dmonitor_status_1'] = Db::name('dmtask')->where('status', 1)->count();
|
||||
|
||||
$stat['optimizeip_active'] = Db::name('optimizeip')->where('active', 1)->count();
|
||||
$stat['optimizeip_status_1'] = Db::name('optimizeip')->where('status', 1)->count();
|
||||
$stat['optimizeip_status_2'] = Db::name('optimizeip')->where('status', 2)->count();
|
||||
|
||||
$stat['certorder_status_3'] = Db::name('cert_order')->where('status', 3)->count();
|
||||
$stat['certorder_status_5'] = Db::name('cert_order')->where('status', '<', 0)->count();
|
||||
$stat['certorder_status_6'] = Db::name('cert_order')->where('expiretime', '<', date('Y-m-d H:i:s', time() + 86400 * 7))->where('expiretime', '>=', date('Y-m-d H:i:s'))->count();
|
||||
$stat['certorder_status_7'] = Db::name('cert_order')->where('expiretime', '<', date('Y-m-d H:i:s'))->count();
|
||||
|
||||
$stat['certdeploy_status_0'] = Db::name('cert_deploy')->where('status', 0)->count();
|
||||
$stat['certdeploy_status_1'] = Db::name('cert_deploy')->where('status', 1)->count();
|
||||
$stat['certdeploy_status_2'] = Db::name('cert_deploy')->where('status', -1)->count();
|
||||
|
||||
return json($stat);
|
||||
}
|
||||
return json(['code' => -3]);
|
||||
@@ -43,7 +61,7 @@ class Index extends BaseController
|
||||
$tmp = 'version()';
|
||||
$mysqlVersion = Db::query("select version()")[0][$tmp];
|
||||
$info = [
|
||||
'framework_version' => app()::VERSION,
|
||||
'framework_version' => app()->version(),
|
||||
'php_version' => PHP_VERSION,
|
||||
'mysql_version' => $mysqlVersion,
|
||||
'software' => $_SERVER['SERVER_SOFTWARE'],
|
||||
|
||||
@@ -20,6 +20,9 @@ class Optimizeip extends BaseController
|
||||
if (empty($key)) {
|
||||
continue;
|
||||
}
|
||||
if ($key == 'optimize_ip_min' && intval($value) < 10) {
|
||||
return json(['code' => -1, 'msg' => '自动更新时间间隔不能小于10分钟']);
|
||||
}
|
||||
config_set($key, $value);
|
||||
Cache::delete('configs');
|
||||
}
|
||||
@@ -39,6 +42,7 @@ class Optimizeip extends BaseController
|
||||
if (!checkPermission(2)) return json(['total' => 0, 'rows' => []]);
|
||||
$type = input('post.type/d', 1);
|
||||
$kw = input('post.kw', null, 'trim');
|
||||
$status = input('post.status', null);
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
|
||||
@@ -50,6 +54,9 @@ class Optimizeip extends BaseController
|
||||
$select->whereLike('remark', '%' . $kw . '%');
|
||||
}
|
||||
}
|
||||
if (!isNullOrEmpty($status)) {
|
||||
$select->where('status', intval($status));
|
||||
}
|
||||
$total = $select->count();
|
||||
$list = $select->order('A.id', 'desc')->limit($offset, $limit)->field('A.*,B.name domain')->select();
|
||||
|
||||
|
||||
165
app/controller/Schedule.php
Normal file
165
app/controller/Schedule.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\BaseController;
|
||||
use think\facade\Db;
|
||||
use think\facade\View;
|
||||
use think\facade\Cache;
|
||||
use app\service\ScheduleService;
|
||||
|
||||
class Schedule extends BaseController
|
||||
{
|
||||
public function stask()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function stask_data()
|
||||
{
|
||||
if (!checkPermission(2)) return json(['total' => 0, 'rows' => []]);
|
||||
$type = input('post.type/d', 1);
|
||||
$kw = input('post.kw', null, 'trim');
|
||||
$stype = input('post.stype', null);
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
|
||||
$select = Db::name('sctask')->alias('A')->join('domain B', 'A.did = B.id');
|
||||
if (!empty($kw)) {
|
||||
if ($type == 1) {
|
||||
$select->whereLike('rr|B.name', '%' . $kw . '%');
|
||||
} elseif ($type == 2) {
|
||||
$select->where('recordid', $kw);
|
||||
} elseif ($type == 3) {
|
||||
$select->where('value', $kw);
|
||||
} elseif ($type == 4) {
|
||||
$select->whereLike('remark', '%' . $kw . '%');
|
||||
}
|
||||
}
|
||||
if (!isNullOrEmpty($stype)) {
|
||||
$select->where('type', $stype);
|
||||
}
|
||||
$total = $select->count();
|
||||
$list = $select->order('A.id', 'desc')->limit($offset, $limit)->field('A.*,B.name domain')->select()->toArray();
|
||||
|
||||
foreach ($list as &$row) {
|
||||
$row['addtimestr'] = date('Y-m-d H:i:s', $row['addtime']);
|
||||
$row['updatetimestr'] = $row['updatetime'] > 0 ? date('Y-m-d H:i:s', $row['updatetime']) : '未运行';
|
||||
$row['nexttimestr'] = $row['nexttime'] > 0 ? date('Y-m-d H:i:s', $row['nexttime']) : '无';
|
||||
}
|
||||
|
||||
return json(['total' => $total, 'rows' => $list]);
|
||||
}
|
||||
|
||||
public function stask_op()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$action = input('param.action');
|
||||
if ($action == 'add') {
|
||||
$task = [
|
||||
'did' => input('post.did/d'),
|
||||
'rr' => input('post.rr', null, 'trim'),
|
||||
'recordid' => input('post.recordid', null, 'trim'),
|
||||
'type' => input('post.type/d'),
|
||||
'cycle' => input('post.cycle/d'),
|
||||
'switchtype' => input('post.switchtype/d'),
|
||||
'switchdate' => input('post.switchdate', null, 'trim'),
|
||||
'switchtime' => input('post.switchtime', null, 'trim'),
|
||||
'value' => 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(),
|
||||
'active' => 1
|
||||
];
|
||||
|
||||
if (empty($task['did']) || empty($task['rr']) || empty($task['recordid'])) {
|
||||
return json(['code' => -1, 'msg' => '必填项不能为空']);
|
||||
}
|
||||
if (Db::name('sctask')->where('recordid', $task['recordid'])->where('switchtype', $task['switchtype'])->where('switchtime', $task['switchtime'])->find()) {
|
||||
return json(['code' => -1, 'msg' => '当前定时切换策略已存在']);
|
||||
}
|
||||
$id = Db::name('sctask')->insertGetId($task);
|
||||
$row = Db::name('sctask')->where('id', $id)->find();
|
||||
(new ScheduleService())->update_nexttime($row);
|
||||
return json(['code' => 0, 'msg' => '添加成功']);
|
||||
} elseif ($action == 'edit') {
|
||||
$id = input('post.id/d');
|
||||
$task = [
|
||||
'did' => input('post.did/d'),
|
||||
'rr' => input('post.rr', null, 'trim'),
|
||||
'recordid' => input('post.recordid', null, 'trim'),
|
||||
'type' => input('post.type/d'),
|
||||
'cycle' => input('post.cycle/d'),
|
||||
'switchtype' => input('post.switchtype/d'),
|
||||
'switchdate' => input('post.switchdate', null, 'trim'),
|
||||
'switchtime' => input('post.switchtime', null, 'trim'),
|
||||
'value' => input('post.value', null, 'trim'),
|
||||
'line' => input('post.line', null, 'trim'),
|
||||
'remark' => input('post.remark', null, 'trim'),
|
||||
'recordinfo' => input('post.recordinfo', null, 'trim'),
|
||||
];
|
||||
|
||||
if (empty($task['did']) || empty($task['rr']) || empty($task['recordid'])) {
|
||||
return json(['code' => -1, 'msg' => '必填项不能为空']);
|
||||
}
|
||||
if (Db::name('sctask')->where('recordid', $task['recordid'])->where('switchtype', $task['switchtype'])->where('switchtime', $task['switchtime'])->where('id', '<>', $id)->find()) {
|
||||
return json(['code' => -1, 'msg' => '当前定时切换策略已存在']);
|
||||
}
|
||||
Db::name('sctask')->where('id', $id)->update($task);
|
||||
$row = Db::name('sctask')->where('id', $id)->find();
|
||||
(new ScheduleService())->update_nexttime($row);
|
||||
return json(['code' => 0, 'msg' => '修改成功']);
|
||||
} elseif ($action == 'setactive') {
|
||||
$id = input('post.id/d');
|
||||
$active = input('post.active/d');
|
||||
Db::name('sctask')->where('id', $id)->update(['active' => $active]);
|
||||
return json(['code' => 0, 'msg' => '设置成功']);
|
||||
} elseif ($action == 'del') {
|
||||
$id = input('post.id/d');
|
||||
Db::name('sctask')->where('id', $id)->delete();
|
||||
return json(['code' => 0, 'msg' => '删除成功']);
|
||||
} elseif ($action == 'operation') {
|
||||
$ids = input('post.ids');
|
||||
$success = 0;
|
||||
foreach ($ids as $id) {
|
||||
if (input('post.act') == 'delete') {
|
||||
Db::name('sctask')->where('id', $id)->delete();
|
||||
$success++;
|
||||
} elseif (input('post.act') == 'open' || input('post.act') == 'close') {
|
||||
$isauto = input('post.act') == 'open' ? 1 : 0;
|
||||
Db::name('sctask')->where('id', $id)->update(['active' => $isauto]);
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
return json(['code' => 0, 'msg' => '成功操作' . $success . '个定时切换策略']);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '参数错误']);
|
||||
}
|
||||
}
|
||||
|
||||
public function staskform()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$action = input('param.action');
|
||||
$task = null;
|
||||
if ($action == 'edit') {
|
||||
$id = input('get.id/d');
|
||||
$task = Db::name('sctask')->where('id', $id)->find();
|
||||
if (empty($task)) return $this->alert('error', '切换策略不存在');
|
||||
}
|
||||
|
||||
$domains = [];
|
||||
$domainList = Db::name('domain')->alias('A')->join('account B', 'A.aid = B.id')->field('A.id,A.name,B.type')->select();
|
||||
foreach ($domainList as $row) {
|
||||
$domains[] = ['id'=>$row['id'], 'name'=>$row['name'], 'type'=>$row['type']];
|
||||
}
|
||||
View::assign('domains', $domains);
|
||||
|
||||
View::assign('info', $task);
|
||||
View::assign('action', $action);
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,44 +7,46 @@ use Exception;
|
||||
use think\facade\Db;
|
||||
use think\facade\View;
|
||||
use think\facade\Cache;
|
||||
use app\service\OptimizeService;
|
||||
use app\service\CertTaskService;
|
||||
use app\service\ExpireNoticeService;
|
||||
use app\service\ScheduleService;
|
||||
|
||||
class System extends BaseController
|
||||
{
|
||||
public function set()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$params = input('post.');
|
||||
if (isset($params['mail_type']) && isset($params['mail_name2']) && $params['mail_type'] > 0) {
|
||||
$params['mail_name'] = $params['mail_name2'];
|
||||
unset($params['mail_name2']);
|
||||
}
|
||||
foreach ($params as $key => $value) {
|
||||
if (empty($key)) {
|
||||
continue;
|
||||
}
|
||||
config_set($key, $value);
|
||||
}
|
||||
Cache::delete('configs');
|
||||
return json(['code' => 0, 'msg' => 'succ']);
|
||||
}
|
||||
|
||||
public function loginset()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function noticeset()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
if ($this->request->isPost()) {
|
||||
$params = input('post.');
|
||||
if (isset($params['mail_type']) && isset($params['mail_name2']) && $params['mail_type'] > 0) {
|
||||
$params['mail_name'] = $params['mail_name2'];
|
||||
unset($params['mail_name2']);
|
||||
}
|
||||
foreach ($params as $key => $value) {
|
||||
if (empty($key)) {
|
||||
continue;
|
||||
}
|
||||
config_set($key, $value);
|
||||
Cache::delete('configs');
|
||||
}
|
||||
return json(['code' => 0, 'msg' => 'succ']);
|
||||
}
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function proxyset()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
if ($this->request->isPost()) {
|
||||
$params = input('post.');
|
||||
foreach ($params as $key => $value) {
|
||||
if (empty($key)) {
|
||||
continue;
|
||||
}
|
||||
config_set($key, $value);
|
||||
Cache::delete('configs');
|
||||
}
|
||||
return json(['code' => 0, 'msg' => 'succ']);
|
||||
}
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
@@ -109,4 +111,39 @@ class System extends BaseController
|
||||
}
|
||||
return json(['code' => 0]);
|
||||
}
|
||||
|
||||
public function cronset()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
if (config_get('cron_key') === null) {
|
||||
config_set('cron_key', random(10));
|
||||
Cache::delete('configs');
|
||||
}
|
||||
View::assign('is_user_www', isset($_SERVER['USER']) && $_SERVER['USER'] == 'www');
|
||||
View::assign('siteurl', request()->root(true));
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function cron()
|
||||
{
|
||||
if (function_exists("set_time_limit")) {
|
||||
@set_time_limit(0);
|
||||
}
|
||||
if (function_exists("ignore_user_abort")) {
|
||||
@ignore_user_abort(true);
|
||||
}
|
||||
if (isset($_SERVER['HTTP_USER_AGENT']) && str_contains($_SERVER['HTTP_USER_AGENT'], 'Baiduspider')) exit;
|
||||
$key = input('get.key', '');
|
||||
$cron_key = config_get('cron_key');
|
||||
if (config_get('cron_type', '0') != '1' || empty($cron_key)) exit('未开启当前方式');
|
||||
if ($key != $cron_key) exit('访问密钥错误');
|
||||
|
||||
(new ScheduleService())->execute();
|
||||
$res = (new OptimizeService())->execute();
|
||||
if (!$res) {
|
||||
(new CertTaskService())->execute();
|
||||
(new ExpireNoticeService())->task();
|
||||
}
|
||||
echo 'success!';
|
||||
}
|
||||
}
|
||||
@@ -171,7 +171,7 @@ class User extends BaseController
|
||||
$select->where('domain', $this->request->user['name']);
|
||||
} elseif ($this->request->user['level'] == 1) {
|
||||
$select->where('uid', $this->request->user['id']);
|
||||
} elseif (!empty($uid)) {
|
||||
} elseif (!isNullOrEmpty($uid)) {
|
||||
$select->where('uid', $uid);
|
||||
}
|
||||
if (!empty($kw)) {
|
||||
|
||||
@@ -288,6 +288,7 @@ uk.com
|
||||
us.com
|
||||
uy.com
|
||||
za.com
|
||||
it.com
|
||||
co.cr
|
||||
ed.cr
|
||||
fi.cr
|
||||
@@ -1341,6 +1342,7 @@ zagan.pl
|
||||
zarow.pl
|
||||
zgora.pl
|
||||
zgorzelec.pl
|
||||
co.pl
|
||||
co.pn
|
||||
net.pn
|
||||
org.pn
|
||||
@@ -1925,4 +1927,18 @@ edu.kg
|
||||
edu.cn
|
||||
eu.org
|
||||
us.kg
|
||||
ggff.net
|
||||
xx.kg
|
||||
qzz.io
|
||||
dpdns.org
|
||||
ggff.net
|
||||
ac.ru
|
||||
edu.ru
|
||||
com.ru
|
||||
msk.ru
|
||||
net.ru
|
||||
nov.ru
|
||||
org.ru
|
||||
pp.ru
|
||||
spb.ru
|
||||
uk.co
|
||||
gov.scot
|
||||
@@ -1,367 +1,415 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib;
|
||||
|
||||
use think\facade\Db;
|
||||
|
||||
class CertHelper
|
||||
{
|
||||
public static $cert_config = [
|
||||
'letsencrypt' => [
|
||||
'name' => 'Let\'s Encrypt',
|
||||
'class' => 1,
|
||||
'icon' => 'letsencrypt.ico',
|
||||
'wildcard' => true,
|
||||
'max_domains' => 100,
|
||||
'cname' => true,
|
||||
'note' => null,
|
||||
'inputs' => [
|
||||
'email' => [
|
||||
'name' => '邮箱地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => '用于注册Let\'s Encrypt账号',
|
||||
'required' => true,
|
||||
],
|
||||
'mode' => [
|
||||
'name' => '环境选择',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'live' => '正式环境',
|
||||
'staging' => '测试环境',
|
||||
],
|
||||
'value' => 'live'
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
]
|
||||
],
|
||||
'zerossl' => [
|
||||
'name' => 'ZeroSSL',
|
||||
'class' => 1,
|
||||
'icon' => 'zerossl.ico',
|
||||
'wildcard' => true,
|
||||
'max_domains' => 100,
|
||||
'cname' => true,
|
||||
'note' => '<a href="https://app.zerossl.com/developer" target="_blank" rel="noreferrer">ZeroSSL密钥生成地址</a>',
|
||||
'inputs' => [
|
||||
'email' => [
|
||||
'name' => '邮箱地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => 'EAB申请邮箱',
|
||||
'required' => true,
|
||||
],
|
||||
'kid' => [
|
||||
'name' => 'EAB KID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'key' => [
|
||||
'name' => 'EAB HMAC Key',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
]
|
||||
],
|
||||
'google' => [
|
||||
'name' => 'Google SSL',
|
||||
'class' => 1,
|
||||
'icon' => 'google.ico',
|
||||
'wildcard' => true,
|
||||
'max_domains' => 100,
|
||||
'cname' => true,
|
||||
'note' => '<a href="https://cloud.google.com/certificate-manager/docs/public-ca-tutorial" target="_blank" rel="noreferrer">查看Google SSL账户配置说明</a>',
|
||||
'inputs' => [
|
||||
'email' => [
|
||||
'name' => '邮箱地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => 'EAB申请邮箱',
|
||||
'required' => true,
|
||||
],
|
||||
'kid' => [
|
||||
'name' => 'keyId',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'key' => [
|
||||
'name' => 'b64MacKey',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'mode' => [
|
||||
'name' => '环境选择',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'live' => '正式环境',
|
||||
'staging' => '测试环境',
|
||||
],
|
||||
'value' => 'live'
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
]
|
||||
],
|
||||
'tencent' => [
|
||||
'name' => '腾讯云免费SSL',
|
||||
'class' => 2,
|
||||
'icon' => 'tencent.ico',
|
||||
'wildcard' => false,
|
||||
'max_domains' => 1,
|
||||
'cname' => false,
|
||||
'note' => '一个账号有50张免费证书额度,证书到期或吊销可释放额度。<a href="https://cloud.tencent.com/document/product/400/89868" target="_blank" rel="noreferrer">腾讯云免费SSL简介与额度说明</a>',
|
||||
'inputs' => [
|
||||
'SecretId' => [
|
||||
'name' => 'SecretId',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'SecretKey' => [
|
||||
'name' => 'SecretKey',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'email' => [
|
||||
'name' => '邮箱地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => '申请证书时填写的邮箱',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
]
|
||||
],
|
||||
'aliyun' => [
|
||||
'name' => '阿里云免费SSL',
|
||||
'class' => 2,
|
||||
'icon' => 'aliyun.ico',
|
||||
'wildcard' => false,
|
||||
'max_domains' => 1,
|
||||
'cname' => false,
|
||||
'note' => '每个自然年有20张免费证书额度,证书到期或吊销不释放额度。需要先进入阿里云控制台-<a href="https://yundun.console.aliyun.com/?p=cas#/certExtend/free/cn-hangzhou" target="_blank" rel="noreferrer">数字证书管理服务</a>,购买个人测试证书资源包。',
|
||||
'inputs' => [
|
||||
'AccessKeyId' => [
|
||||
'name' => 'AccessKeyId',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'AccessKeySecret' => [
|
||||
'name' => 'AccessKeySecret',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'username' => [
|
||||
'name' => '姓名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '申请联系人的姓名',
|
||||
'required' => true,
|
||||
],
|
||||
'phone' => [
|
||||
'name' => '手机号码',
|
||||
'type' => 'input',
|
||||
'placeholder' => '申请联系人的手机号码',
|
||||
'required' => true,
|
||||
],
|
||||
'email' => [
|
||||
'name' => '邮箱地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => '申请联系人的邮箱地址',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
]
|
||||
],
|
||||
'ucloud' => [
|
||||
'name' => 'UCloud免费SSL',
|
||||
'class' => 2,
|
||||
'icon' => 'ucloud.ico',
|
||||
'wildcard' => false,
|
||||
'max_domains' => 1,
|
||||
'cname' => false,
|
||||
'note' => '一个账号有40张免费证书额度,证书到期或吊销可释放额度。',
|
||||
'inputs' => [
|
||||
'PublicKey' => [
|
||||
'name' => '公钥',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'PrivateKey' => [
|
||||
'name' => '私钥',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'username' => [
|
||||
'name' => '姓名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '申请联系人的姓名',
|
||||
'required' => true,
|
||||
],
|
||||
'phone' => [
|
||||
'name' => '手机号码',
|
||||
'type' => 'input',
|
||||
'placeholder' => '申请联系人的手机号码',
|
||||
'required' => true,
|
||||
],
|
||||
'email' => [
|
||||
'name' => '邮箱地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => '申请联系人的邮箱地址',
|
||||
'required' => true,
|
||||
],
|
||||
]
|
||||
],
|
||||
'customacme' => [
|
||||
'name' => '自定义ACME',
|
||||
'class' => 1,
|
||||
'icon' => 'ssl.ico',
|
||||
'wildcard' => true,
|
||||
'max_domains' => 100,
|
||||
'cname' => true,
|
||||
'note' => null,
|
||||
'inputs' => [
|
||||
'directory' => [
|
||||
'name' => 'ACME地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => 'ACME Directory 地址',
|
||||
'required' => true,
|
||||
],
|
||||
'email' => [
|
||||
'name' => '邮箱地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => '证书申请邮箱',
|
||||
'required' => true,
|
||||
],
|
||||
'kid' => [
|
||||
'name' => 'EAB KID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '留空则不使用EAB认证',
|
||||
],
|
||||
'key' => [
|
||||
'name' => 'EAB HMAC Key',
|
||||
'type' => 'input',
|
||||
'placeholder' => '留空则不使用EAB认证',
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
public static $class_config = [
|
||||
1 => '基于ACME的SSL证书',
|
||||
2 => '云服务商的SSL证书',
|
||||
];
|
||||
|
||||
public static function getList()
|
||||
{
|
||||
return self::$cert_config;
|
||||
}
|
||||
|
||||
private static function getConfig($aid)
|
||||
{
|
||||
$account = Db::name('cert_account')->where('id', $aid)->find();
|
||||
if (!$account) return false;
|
||||
return $account;
|
||||
}
|
||||
|
||||
public static function getInputs($type, $config = null)
|
||||
{
|
||||
$config = $config ? json_decode($config, true) : [];
|
||||
$inputs = self::$cert_config[$type]['inputs'];
|
||||
foreach ($inputs as &$input) {
|
||||
if (isset($config[$input['name']])) {
|
||||
$input['value'] = $config[$input['name']];
|
||||
}
|
||||
}
|
||||
return $inputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CertInterface|bool
|
||||
*/
|
||||
public static function getModel($aid)
|
||||
{
|
||||
$account = self::getConfig($aid);
|
||||
if (!$account) return false;
|
||||
$type = $account['type'];
|
||||
$class = "\\app\\lib\\cert\\{$type}";
|
||||
if (class_exists($class)) {
|
||||
$config = json_decode($account['config'], true);
|
||||
$ext = $account['ext'] ? json_decode($account['ext'], true) : null;
|
||||
$model = new $class($config, $ext);
|
||||
return $model;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CertInterface|bool
|
||||
*/
|
||||
public static function getModel2($type, $config, $ext = null)
|
||||
{
|
||||
$class = "\\app\\lib\\cert\\{$type}";
|
||||
if (class_exists($class)) {
|
||||
$model = new $class($config, $ext);
|
||||
return $model;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function getPfx($fullchain, $privatekey, $pwd = '123456'){
|
||||
openssl_pkcs12_export($fullchain, $pfx, $privatekey, $pwd);
|
||||
return $pfx;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib;
|
||||
|
||||
use think\facade\Db;
|
||||
|
||||
class CertHelper
|
||||
{
|
||||
public static $cert_config = [
|
||||
'letsencrypt' => [
|
||||
'name' => 'Let\'s Encrypt',
|
||||
'class' => 1,
|
||||
'icon' => 'letsencrypt.ico',
|
||||
'wildcard' => true,
|
||||
'max_domains' => 100,
|
||||
'cname' => true,
|
||||
'note' => null,
|
||||
'inputs' => [
|
||||
'email' => [
|
||||
'name' => '邮箱地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => '用于注册Let\'s Encrypt账号',
|
||||
'required' => true,
|
||||
],
|
||||
'mode' => [
|
||||
'name' => '环境选择',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'live' => '正式环境',
|
||||
'staging' => '测试环境',
|
||||
],
|
||||
'value' => 'live'
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
]
|
||||
],
|
||||
'zerossl' => [
|
||||
'name' => 'ZeroSSL',
|
||||
'class' => 1,
|
||||
'icon' => 'zerossl.ico',
|
||||
'wildcard' => true,
|
||||
'max_domains' => 100,
|
||||
'cname' => true,
|
||||
'note' => '<a href="https://app.zerossl.com/developer" target="_blank" rel="noreferrer">ZeroSSL密钥手动获取</a>',
|
||||
'inputs' => [
|
||||
'email' => [
|
||||
'name' => '邮箱地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => 'EAB申请邮箱',
|
||||
'required' => true,
|
||||
],
|
||||
'eabMode' => [
|
||||
'name' => 'EAB获取方式',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'auto' => '自动获取',
|
||||
'manual' => '手动输入',
|
||||
],
|
||||
'value' => 'manual'
|
||||
],
|
||||
'kid' => [
|
||||
'name' => 'EAB KID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
'show' => 'eabMode==\'manual\'',
|
||||
],
|
||||
'key' => [
|
||||
'name' => 'EAB HMAC Key',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
'show' => 'eabMode==\'manual\'',
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
]
|
||||
],
|
||||
'google' => [
|
||||
'name' => 'Google SSL',
|
||||
'class' => 1,
|
||||
'icon' => 'google.ico',
|
||||
'wildcard' => true,
|
||||
'max_domains' => 100,
|
||||
'cname' => true,
|
||||
'note' => 'EAB支持通过第三方接口<a href="https://panel.haozi.net" target="_blank" rel="noreferrer">(耗子面板提供)</a>自动获取(不支持测试环境)或手动输入,<a href="https://cloud.google.com/certificate-manager/docs/public-ca-tutorial" target="_blank" rel="noreferrer">查看Google SSL账户手动配置说明</a>',
|
||||
'inputs' => [
|
||||
'email' => [
|
||||
'name' => '邮箱地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => 'EAB申请邮箱',
|
||||
'required' => true,
|
||||
],
|
||||
'eabMode' => [
|
||||
'name' => 'EAB获取方式',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'auto' => '自动获取',
|
||||
'manual' => '手动输入',
|
||||
],
|
||||
'value' => 'manual'
|
||||
],
|
||||
'kid' => [
|
||||
'name' => 'keyId',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
'show' => 'eabMode==\'manual\'',
|
||||
],
|
||||
'key' => [
|
||||
'name' => 'b64MacKey',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
'show' => 'eabMode==\'manual\'',
|
||||
],
|
||||
'mode' => [
|
||||
'name' => '环境选择',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'live' => '正式环境',
|
||||
'staging' => '测试环境',
|
||||
],
|
||||
'value' => 'live'
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
'2' => '是(反向代理)'
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
'proxy_url' => [
|
||||
'name' => '反向代理地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => 'https://gts.rat.dev',
|
||||
'required' => true,
|
||||
'show' => 'proxy==2',
|
||||
'note' => '反向代理配置参考:
|
||||
<pre>resolver 8.8.8.8 ipv6=off valid=300s;
|
||||
resolver_timeout 10s;
|
||||
|
||||
location / {
|
||||
set $empty "";
|
||||
proxy_pass https://dv.acme-v02.api.pki.goog$empty;
|
||||
proxy_set_header Accept-Encoding "";
|
||||
proxy_ssl_session_reuse off;
|
||||
proxy_ssl_server_name on;
|
||||
proxy_ssl_protocols TLSv1.2 TLSv1.3;
|
||||
proxy_http_version 1.1;
|
||||
|
||||
sub_filter_once off;
|
||||
sub_filter_types *;
|
||||
sub_filter \'dv.acme-v02.api.pki.goog\' \'gts.rat.dev\'; # 替换自己的域名
|
||||
}</pre>',
|
||||
],
|
||||
]
|
||||
],
|
||||
'tencent' => [
|
||||
'name' => '腾讯云免费SSL',
|
||||
'class' => 2,
|
||||
'icon' => 'tencent.png',
|
||||
'wildcard' => false,
|
||||
'max_domains' => 1,
|
||||
'cname' => false,
|
||||
'note' => '一个账号有50张免费证书额度,证书到期或吊销可释放额度。<a href="https://cloud.tencent.com/document/product/400/89868" target="_blank" rel="noreferrer">腾讯云免费SSL简介与额度说明</a>',
|
||||
'inputs' => [
|
||||
'SecretId' => [
|
||||
'name' => 'SecretId',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'SecretKey' => [
|
||||
'name' => 'SecretKey',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'email' => [
|
||||
'name' => '邮箱地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => '申请证书时填写的邮箱',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
]
|
||||
],
|
||||
'aliyun' => [
|
||||
'name' => '阿里云免费SSL',
|
||||
'class' => 2,
|
||||
'icon' => 'aliyun.png',
|
||||
'wildcard' => false,
|
||||
'max_domains' => 1,
|
||||
'cname' => false,
|
||||
'note' => '每个自然年有20张免费证书额度,证书到期或吊销不释放额度。需要先进入阿里云控制台-<a href="https://yundun.console.aliyun.com/?p=cas#/certExtend/free/cn-hangzhou" target="_blank" rel="noreferrer">数字证书管理服务</a>,购买个人测试证书资源包。',
|
||||
'inputs' => [
|
||||
'AccessKeyId' => [
|
||||
'name' => 'AccessKeyId',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'AccessKeySecret' => [
|
||||
'name' => 'AccessKeySecret',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'username' => [
|
||||
'name' => '姓名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '申请联系人的姓名',
|
||||
'required' => true,
|
||||
],
|
||||
'phone' => [
|
||||
'name' => '手机号码',
|
||||
'type' => 'input',
|
||||
'placeholder' => '申请联系人的手机号码',
|
||||
'required' => true,
|
||||
],
|
||||
'email' => [
|
||||
'name' => '邮箱地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => '申请联系人的邮箱地址',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
]
|
||||
],
|
||||
'ucloud' => [
|
||||
'name' => 'UCloud免费SSL',
|
||||
'class' => 2,
|
||||
'icon' => 'ucloud.ico',
|
||||
'wildcard' => false,
|
||||
'max_domains' => 1,
|
||||
'cname' => false,
|
||||
'note' => '一个账号有40张免费证书额度,证书到期或吊销可释放额度。',
|
||||
'inputs' => [
|
||||
'PublicKey' => [
|
||||
'name' => '公钥',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'PrivateKey' => [
|
||||
'name' => '私钥',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'username' => [
|
||||
'name' => '姓名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '申请联系人的姓名',
|
||||
'required' => true,
|
||||
],
|
||||
'phone' => [
|
||||
'name' => '手机号码',
|
||||
'type' => 'input',
|
||||
'placeholder' => '申请联系人的手机号码',
|
||||
'required' => true,
|
||||
],
|
||||
'email' => [
|
||||
'name' => '邮箱地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => '申请联系人的邮箱地址',
|
||||
'required' => true,
|
||||
],
|
||||
]
|
||||
],
|
||||
'customacme' => [
|
||||
'name' => '自定义ACME',
|
||||
'class' => 1,
|
||||
'icon' => 'ssl.ico',
|
||||
'wildcard' => true,
|
||||
'max_domains' => 100,
|
||||
'cname' => true,
|
||||
'note' => null,
|
||||
'inputs' => [
|
||||
'directory' => [
|
||||
'name' => 'ACME地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => 'ACME Directory 地址',
|
||||
'required' => true,
|
||||
],
|
||||
'email' => [
|
||||
'name' => '邮箱地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => '证书申请邮箱',
|
||||
'required' => true,
|
||||
],
|
||||
'kid' => [
|
||||
'name' => 'EAB KID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '留空则不使用EAB认证',
|
||||
],
|
||||
'key' => [
|
||||
'name' => 'EAB HMAC Key',
|
||||
'type' => 'input',
|
||||
'placeholder' => '留空则不使用EAB认证',
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
public static $class_config = [
|
||||
1 => '基于ACME的SSL证书',
|
||||
2 => '云服务商的SSL证书',
|
||||
];
|
||||
|
||||
public static function getList()
|
||||
{
|
||||
return self::$cert_config;
|
||||
}
|
||||
|
||||
private static function getConfig($aid)
|
||||
{
|
||||
$account = Db::name('cert_account')->where('id', $aid)->find();
|
||||
if (!$account) return false;
|
||||
return $account;
|
||||
}
|
||||
|
||||
public static function getInputs($type, $config = null)
|
||||
{
|
||||
$config = $config ? json_decode($config, true) : [];
|
||||
$inputs = self::$cert_config[$type]['inputs'];
|
||||
foreach ($inputs as &$input) {
|
||||
if (isset($config[$input['name']])) {
|
||||
$input['value'] = $config[$input['name']];
|
||||
}
|
||||
}
|
||||
return $inputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CertInterface|bool
|
||||
*/
|
||||
public static function getModel($aid)
|
||||
{
|
||||
$account = self::getConfig($aid);
|
||||
if (!$account) return false;
|
||||
$type = $account['type'];
|
||||
$class = "\\app\\lib\\cert\\{$type}";
|
||||
if (class_exists($class)) {
|
||||
$config = json_decode($account['config'], true);
|
||||
$ext = $account['ext'] ? json_decode($account['ext'], true) : null;
|
||||
$model = new $class($config, $ext);
|
||||
return $model;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CertInterface|bool
|
||||
*/
|
||||
public static function getModel2($type, $config, $ext = null)
|
||||
{
|
||||
$class = "\\app\\lib\\cert\\{$type}";
|
||||
if (class_exists($class)) {
|
||||
$model = new $class($config, $ext);
|
||||
return $model;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static function getPfx($fullchain, $privatekey, $pwd = '123456')
|
||||
{
|
||||
openssl_pkcs12_export($fullchain, $pfx, $privatekey, $pwd);
|
||||
return $pfx;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,8 @@ class DnsHelper
|
||||
'redirect' => true, //是否支持域名转发
|
||||
'log' => true, //是否支持查看日志
|
||||
'weight' => false, //是否支持权重
|
||||
'page' => false, //是否客户端分页
|
||||
'add' => true, //是否支持添加域名
|
||||
],
|
||||
'dnspod' => [
|
||||
'name' => '腾讯云',
|
||||
@@ -30,6 +32,8 @@ class DnsHelper
|
||||
'redirect' => true,
|
||||
'log' => true,
|
||||
'weight' => true,
|
||||
'page' => false,
|
||||
'add' => true,
|
||||
],
|
||||
'huawei' => [
|
||||
'name' => '华为云',
|
||||
@@ -42,6 +46,8 @@ class DnsHelper
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => true,
|
||||
'page' => false,
|
||||
'add' => true,
|
||||
],
|
||||
'baidu' => [
|
||||
'name' => '百度云',
|
||||
@@ -54,6 +60,8 @@ class DnsHelper
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => false,
|
||||
'page' => true,
|
||||
'add' => true,
|
||||
],
|
||||
'west' => [
|
||||
'name' => '西部数码',
|
||||
@@ -66,6 +74,8 @@ class DnsHelper
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => false,
|
||||
'page' => false,
|
||||
'add' => false,
|
||||
],
|
||||
'huoshan' => [
|
||||
'name' => '火山引擎',
|
||||
@@ -78,6 +88,22 @@ class DnsHelper
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => true,
|
||||
'page' => false,
|
||||
'add' => true,
|
||||
],
|
||||
'jdcloud' => [
|
||||
'name' => '京东云',
|
||||
'config' => [
|
||||
'ak' => 'AccessKeyId',
|
||||
'sk' => 'AccessKeySecret',
|
||||
],
|
||||
'remark' => 0,
|
||||
'status' => true,
|
||||
'redirect' => true,
|
||||
'log' => false,
|
||||
'weight' => true,
|
||||
'page' => false,
|
||||
'add' => true,
|
||||
],
|
||||
'dnsla' => [
|
||||
'name' => 'DNSLA',
|
||||
@@ -90,6 +116,8 @@ class DnsHelper
|
||||
'redirect' => true,
|
||||
'log' => false,
|
||||
'weight' => true,
|
||||
'page' => false,
|
||||
'add' => true,
|
||||
],
|
||||
'cloudflare' => [
|
||||
'name' => 'Cloudflare',
|
||||
@@ -98,10 +126,12 @@ class DnsHelper
|
||||
'sk' => 'API密钥/令牌',
|
||||
],
|
||||
'remark' => 2,
|
||||
'status' => false,
|
||||
'status' => true,
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => false,
|
||||
'page' => false,
|
||||
'add' => true,
|
||||
],
|
||||
'namesilo' => [
|
||||
'name' => 'NameSilo',
|
||||
@@ -114,6 +144,23 @@ class DnsHelper
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => false,
|
||||
'page' => true,
|
||||
'add' => false,
|
||||
],
|
||||
'powerdns' => [
|
||||
'name' => 'PowerDNS',
|
||||
'config' => [
|
||||
'ak' => 'IP地址',
|
||||
'sk' => '端口',
|
||||
'ext' => 'API KEY',
|
||||
],
|
||||
'remark' => 2,
|
||||
'status' => true,
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => false,
|
||||
'page' => true,
|
||||
'add' => true,
|
||||
],
|
||||
];
|
||||
|
||||
@@ -125,7 +172,10 @@ class DnsHelper
|
||||
'dnsla' => ['DEF' => '', 'CT' => '84613316902921216', 'CU' => '84613316923892736', 'CM' => '84613316953252864', 'AB' => ''],
|
||||
'huoshan' => ['DEF' => 'default', 'CT' => 'telecom', 'CU' => 'unicom', 'CM' => 'mobile', 'AB' => 'oversea'],
|
||||
'baidu' => ['DEF' => 'default', 'CT' => 'ct', 'CU' => 'cnc', 'CM' => 'cmnet', 'AB' => ''],
|
||||
'jdcloud' => ['DEF' => '-1', 'CT' => '1', 'CU' => '2', 'CM' => '3', 'AB' => '4'],
|
||||
'cloudflare' => ['DEF' => '0'],
|
||||
'namesilo' => ['DEF' => 'default'],
|
||||
'powerdns' => ['DEF' => 'default'],
|
||||
];
|
||||
|
||||
public static function getList()
|
||||
|
||||
@@ -31,4 +31,6 @@ interface DnsInterface
|
||||
function getRecordLine();
|
||||
|
||||
function getMinTTL();
|
||||
|
||||
function addDomain($Domain);
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@ class NewDbManager extends \think\Db
|
||||
/**
|
||||
* 创建数据库连接实例
|
||||
* @access protected
|
||||
* @param string|null $name 连接标识
|
||||
* @param string|array|null $name 连接标识
|
||||
* @param bool $force 强制重新连接
|
||||
* @return ConnectionInterface
|
||||
*/
|
||||
protected function instance(string $name = null, bool $force = false): ConnectionInterface
|
||||
protected function instance(string|array $name = null, bool $force = false): ConnectionInterface
|
||||
{
|
||||
if (empty($name)) {
|
||||
$name = $this->getConfig('default', 'mysql');
|
||||
|
||||
@@ -25,7 +25,7 @@ class ACMECert extends ACMEv2
|
||||
$protected = array(
|
||||
'alg' => 'HS256',
|
||||
'kid' => $eab_kid,
|
||||
'url' => $this->resources['newAccount']
|
||||
'url' => $this->unproxiedURL($this->resources['newAccount'])
|
||||
);
|
||||
$payload = $this->jwk_header['jwk'];
|
||||
|
||||
@@ -227,7 +227,7 @@ class ACMECert extends ACMEv2
|
||||
|
||||
public function authOrder($order)
|
||||
{
|
||||
if ($order['status'] != 'ready' && empty($order['challenges'])) {
|
||||
if ($order['status'] != 'pending' && $order['status'] != 'ready' && empty($order['challenges'])) {
|
||||
throw new Exception('No challenges available');
|
||||
}
|
||||
|
||||
|
||||
@@ -8,13 +8,22 @@ class ACMEv2
|
||||
{ // Communication with Let's Encrypt via ACME v2 protocol
|
||||
|
||||
protected
|
||||
$ch = null, $logger = true, $bits, $sha_bits, $directory, $resources, $jwk_header, $kid_header, $account_key, $thumbprint, $nonce = null, $proxy;
|
||||
$ch = null, $logger = true, $bits, $sha_bits, $directory, $resources, $jwk_header, $kid_header, $account_key, $thumbprint, $nonce = null, $proxy, $proxy_config = null;
|
||||
private $delay_until = null;
|
||||
|
||||
public function __construct($directory, $proxy = false)
|
||||
{
|
||||
/**
|
||||
* @param $directory string ACME directory URL
|
||||
* @param $proxy int 代理模式,0为不使用代理,1为使用系统代理,2为使用反向代理
|
||||
* @param null $proxy_config array 反向代理配置,proxy参数为2时必填
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct($directory, $proxy = 0, $proxy_config = null)
|
||||
{
|
||||
$this->directory = $directory;
|
||||
$this->proxy = $proxy;
|
||||
if ($proxy == 2) {
|
||||
$this->proxy_config = $proxy_config;
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
@@ -190,7 +199,8 @@ class ACMEv2
|
||||
}
|
||||
|
||||
if (!$this->kid_header['kid'] && $type === 'newAccount') {
|
||||
$this->kid_header['kid'] = $ret['headers']['location'];
|
||||
// 反向替换反向代理配置,防止破坏签名
|
||||
$this->kid_header['kid'] = $this->unproxiedURL($ret['headers']['location']);
|
||||
$this->log('AccountID: ' . $this->kid_header['kid']);
|
||||
}
|
||||
|
||||
@@ -218,7 +228,8 @@ class ACMEv2
|
||||
throw new Exception('Resource "' . $type . '" not available.');
|
||||
}
|
||||
|
||||
$protected['url'] = $this->resources[$type];
|
||||
// 反向替换反向代理配置,防止破坏签名
|
||||
$protected['url'] = $this->unproxiedURL($this->resources[$type]);
|
||||
|
||||
$protected64 = $this->base64url(json_encode($protected, JSON_UNESCAPED_SLASHES));
|
||||
$payload64 = $this->base64url(is_string($payload) ? $payload : json_encode($payload, JSON_UNESCAPED_SLASHES));
|
||||
@@ -285,6 +296,9 @@ class ACMEv2
|
||||
$this->delay_until = null;
|
||||
}
|
||||
|
||||
// 替换反向代理配置
|
||||
$url = $this->proxiedURL($url);
|
||||
|
||||
$method = $data === false ? 'HEAD' : ($data === null ? 'GET' : 'POST');
|
||||
$user_agent = 'ACMECert v3.4.0 (+https://github.com/skoerfgen/ACMECert)';
|
||||
$header = ($data === null || $data === false) ? array() : array('Content-Type: application/jose+json');
|
||||
@@ -307,7 +321,7 @@ class ACMEv2
|
||||
}
|
||||
));
|
||||
|
||||
if ($this->proxy) {
|
||||
if ($this->proxy == 1) {
|
||||
curl_set_proxy($this->ch);
|
||||
}
|
||||
|
||||
@@ -406,4 +420,30 @@ class ACMEv2
|
||||
}, isset($error['subproblems']) ? $error['subproblems'] : array())
|
||||
);
|
||||
}
|
||||
|
||||
// 替换反向代理配置
|
||||
protected function proxiedURL($url)
|
||||
{
|
||||
if ($this->proxy == 2) {
|
||||
return str_replace(
|
||||
$this->proxy_config['origin'],
|
||||
$this->proxy_config['proxy'],
|
||||
$url
|
||||
);
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
// 反向替换反向代理配置
|
||||
protected function unproxiedURL($url)
|
||||
{
|
||||
if ($this->proxy == 2) {
|
||||
return str_replace(
|
||||
$this->proxy_config['proxy'],
|
||||
$this->proxy_config['origin'],
|
||||
$url
|
||||
);
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ class aliyun implements CertInterface
|
||||
$dnsList = [];
|
||||
if ($data['Type'] == 'domain_verify') {
|
||||
$mainDomain = getMainDomain($domain);
|
||||
$name = str_replace('.' . $mainDomain, '', $data['RecordDomain']);
|
||||
$name = substr($data['RecordDomain'], 0, -(strlen($mainDomain) + 1));
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => $data['RecordType'], 'value' => $data['RecordValue']];
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ class customacme implements CertInterface
|
||||
public function __construct($config, $ext = null)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->ac = new ACMECert($config['directory'], $config['proxy'] == 1);
|
||||
$this->ac = new ACMECert($config['directory'], (int)$config['proxy']);
|
||||
if ($ext) {
|
||||
$this->ext = $ext;
|
||||
$this->ac->loadAccountKey($ext['key']);
|
||||
@@ -64,7 +64,7 @@ class customacme implements CertInterface
|
||||
if (!empty($order['challenges'])) {
|
||||
foreach ($order['challenges'] as $opts) {
|
||||
$mainDomain = getMainDomain($opts['domain']);
|
||||
$name = str_replace('.' . $mainDomain, '', $opts['key']);
|
||||
$name = substr($opts['key'], 0, -(strlen($mainDomain) + 1));
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => 'TXT', 'value' => $opts['value']];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,118 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\cert;
|
||||
|
||||
use app\lib\CertInterface;
|
||||
use app\lib\acme\ACMECert;
|
||||
use Exception;
|
||||
|
||||
class google implements CertInterface
|
||||
{
|
||||
private $directories = array(
|
||||
'live' => 'https://dv.acme-v02.api.pki.goog/directory',
|
||||
'staging' => 'https://dv.acme-v02.test-api.pki.goog/directory'
|
||||
);
|
||||
private $ac;
|
||||
private $config;
|
||||
private $ext;
|
||||
|
||||
public function __construct($config, $ext = null)
|
||||
{
|
||||
$this->config = $config;
|
||||
if (empty($config['mode'])) $config['mode'] = 'live';
|
||||
$this->ac = new ACMECert($this->directories[$config['mode']], $config['proxy']==1);
|
||||
if ($ext) {
|
||||
$this->ext = $ext;
|
||||
$this->ac->loadAccountKey($ext['key']);
|
||||
$this->ac->setAccount($ext['kid']);
|
||||
}
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
if (empty($this->config['email'])) throw new Exception('邮件地址不能为空');
|
||||
if (empty($this->config['kid']) || empty($this->config['key'])) throw new Exception('必填参数不能为空');
|
||||
|
||||
if (!empty($this->ext['key'])) {
|
||||
$kid = $this->ac->registerEAB(true, $this->config['kid'], $this->config['key'], $this->config['email']);
|
||||
return ['kid' => $kid, 'key' => $this->ext['key']];
|
||||
}
|
||||
|
||||
$key = $this->ac->generateRSAKey(2048);
|
||||
$this->ac->loadAccountKey($key);
|
||||
$kid = $this->ac->registerEAB(true, $this->config['kid'], $this->config['key'], $this->config['email']);
|
||||
return ['kid' => $kid, 'key' => $key];
|
||||
}
|
||||
|
||||
public function buyCert($domainList, &$order)
|
||||
{
|
||||
}
|
||||
|
||||
public function createOrder($domainList, &$order, $keytype, $keysize)
|
||||
{
|
||||
$domain_config = [];
|
||||
foreach ($domainList as $domain) {
|
||||
if (empty($domain)) continue;
|
||||
$domain_config[$domain] = ['challenge' => 'dns-01'];
|
||||
}
|
||||
if (empty($domain_config)) throw new Exception('域名列表不能为空');
|
||||
|
||||
$order = $this->ac->createOrder($domain_config);
|
||||
|
||||
$dnsList = [];
|
||||
if (!empty($order['challenges'])) {
|
||||
foreach ($order['challenges'] as $opts) {
|
||||
$mainDomain = getMainDomain($opts['domain']);
|
||||
$name = str_replace('.' . $mainDomain, '', $opts['key']);
|
||||
/*if (!array_key_exists($mainDomain, $dnsList)) {
|
||||
$dnsList[$mainDomain][] = ['name' => '@', 'type' => 'CAA', 'value' => '0 issue "pki.goog"'];
|
||||
}*/
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => 'TXT', 'value' => $opts['value']];
|
||||
}
|
||||
}
|
||||
|
||||
return $dnsList;
|
||||
}
|
||||
|
||||
public function authOrder($domainList, $order)
|
||||
{
|
||||
$this->ac->authOrder($order);
|
||||
}
|
||||
|
||||
public function getAuthStatus($domainList, $order)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function finalizeOrder($domainList, $order, $keytype, $keysize)
|
||||
{
|
||||
if (empty($domainList)) throw new Exception('域名列表不能为空');
|
||||
|
||||
if ($keytype == 'ECC') {
|
||||
if (empty($keysize)) $keysize = '384';
|
||||
$private_key = $this->ac->generateECKey($keysize);
|
||||
} else {
|
||||
if (empty($keysize)) $keysize = '2048';
|
||||
$private_key = $this->ac->generateRSAKey($keysize);
|
||||
}
|
||||
$fullchain = $this->ac->finalizeOrder($domainList, $order, $private_key);
|
||||
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
return ['private_key' => $private_key, 'fullchain' => $fullchain, 'issuer' => $certInfo['issuer']['CN'], 'subject' => $certInfo['subject']['CN'], 'validFrom' => $certInfo['validFrom_time_t'], 'validTo' => $certInfo['validTo_time_t']];
|
||||
}
|
||||
|
||||
public function revoke($order, $pem)
|
||||
{
|
||||
$this->ac->revoke($pem);
|
||||
}
|
||||
|
||||
public function cancel($order)
|
||||
{
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->ac->setLogger($func);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\cert;
|
||||
|
||||
use app\lib\CertInterface;
|
||||
use app\lib\acme\ACMECert;
|
||||
use Exception;
|
||||
|
||||
class google implements CertInterface
|
||||
{
|
||||
private $directories = array(
|
||||
'live' => 'https://dv.acme-v02.api.pki.goog',
|
||||
'staging' => 'https://dv.acme-v02.test-api.pki.goog'
|
||||
);
|
||||
private $ac;
|
||||
private $config;
|
||||
private $ext;
|
||||
|
||||
public function __construct($config, $ext = null)
|
||||
{
|
||||
$this->config = $config;
|
||||
if (empty($config['mode'])) $config['mode'] = 'live';
|
||||
if (empty($config['proxy_url'])) $config['proxy_url'] = '';
|
||||
$this->ac = new ACMECert($this->directories[$config['mode']] . '/directory', (int)$config['proxy'], [
|
||||
'origin' => $this->directories[$config['mode']],
|
||||
'proxy' => rtrim($config['proxy_url'], '/'),
|
||||
]);
|
||||
if ($ext) {
|
||||
$this->ext = $ext;
|
||||
$this->ac->loadAccountKey($ext['key']);
|
||||
$this->ac->setAccount($ext['kid']);
|
||||
}
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
if (empty($this->config['email'])) throw new Exception('邮件地址不能为空');
|
||||
|
||||
if (isset($this->config['eabMode']) && $this->config['eabMode'] == 'auto') {
|
||||
$eab = $this->getEAB();
|
||||
} else {
|
||||
$eab = ['kid' => $this->config['kid'], 'key' => $this->config['key']];
|
||||
}
|
||||
|
||||
if (!empty($this->ext['key'])) {
|
||||
$kid = $this->ac->registerEAB(true, $eab['kid'], $eab['key'], $this->config['email']);
|
||||
return ['kid' => $kid, 'key' => $this->ext['key']];
|
||||
}
|
||||
|
||||
$key = $this->ac->generateRSAKey(2048);
|
||||
$this->ac->loadAccountKey($key);
|
||||
$kid = $this->ac->registerEAB(true, $eab['kid'], $eab['key'], $this->config['email']);
|
||||
return ['kid' => $kid, 'key' => $key];
|
||||
}
|
||||
|
||||
public function buyCert($domainList, &$order)
|
||||
{
|
||||
}
|
||||
|
||||
public function createOrder($domainList, &$order, $keytype, $keysize)
|
||||
{
|
||||
$domain_config = [];
|
||||
foreach ($domainList as $domain) {
|
||||
if (empty($domain)) continue;
|
||||
$domain_config[$domain] = ['challenge' => 'dns-01'];
|
||||
}
|
||||
if (empty($domain_config)) throw new Exception('域名列表不能为空');
|
||||
|
||||
$order = $this->ac->createOrder($domain_config);
|
||||
|
||||
$dnsList = [];
|
||||
if (!empty($order['challenges'])) {
|
||||
foreach ($order['challenges'] as $opts) {
|
||||
$mainDomain = getMainDomain($opts['domain']);
|
||||
$name = substr($opts['key'], 0, -(strlen($mainDomain) + 1));
|
||||
/*if (!array_key_exists($mainDomain, $dnsList)) {
|
||||
$dnsList[$mainDomain][] = ['name' => '@', 'type' => 'CAA', 'value' => '0 issue "pki.goog"'];
|
||||
}*/
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => 'TXT', 'value' => $opts['value']];
|
||||
}
|
||||
}
|
||||
|
||||
return $dnsList;
|
||||
}
|
||||
|
||||
public function authOrder($domainList, $order)
|
||||
{
|
||||
$this->ac->authOrder($order);
|
||||
}
|
||||
|
||||
public function getAuthStatus($domainList, $order)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function finalizeOrder($domainList, $order, $keytype, $keysize)
|
||||
{
|
||||
if (empty($domainList)) throw new Exception('域名列表不能为空');
|
||||
|
||||
if ($keytype == 'ECC') {
|
||||
if (empty($keysize)) $keysize = '384';
|
||||
$private_key = $this->ac->generateECKey($keysize);
|
||||
} else {
|
||||
if (empty($keysize)) $keysize = '2048';
|
||||
$private_key = $this->ac->generateRSAKey($keysize);
|
||||
}
|
||||
$fullchain = $this->ac->finalizeOrder($domainList, $order, $private_key);
|
||||
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
return ['private_key' => $private_key, 'fullchain' => $fullchain, 'issuer' => $certInfo['issuer']['CN'], 'subject' => $certInfo['subject']['CN'], 'validFrom' => $certInfo['validFrom_time_t'], 'validTo' => $certInfo['validTo_time_t']];
|
||||
}
|
||||
|
||||
public function revoke($order, $pem)
|
||||
{
|
||||
$this->ac->revoke($pem);
|
||||
}
|
||||
|
||||
public function cancel($order)
|
||||
{
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->ac->setLogger($func);
|
||||
}
|
||||
|
||||
private function getEAB()
|
||||
{
|
||||
$api = "https://gts.rat.dev/eab";
|
||||
$response = http_request($api, null, null, null, null, $this->config['proxy'] == 1, 'GET', 10);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (!isset($result['msg'])) {
|
||||
throw new Exception('解析返回数据失败:' . $response['body']);
|
||||
} elseif ($result['msg'] != 'success') {
|
||||
throw new Exception('获取EAB失败:' . $result['msg']);
|
||||
} elseif (empty($result['data']['key_id']) || empty($result['data']['mac_key'])) {
|
||||
throw new Exception('获取EAB失败:返回数据不完整');
|
||||
}
|
||||
return ['kid' => $result['data']['key_id'], 'key' => $result['data']['mac_key']];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ class huoshan implements CertInterface
|
||||
$type = $data['validation_type'] == 'dns_cname' ? 'CNAME' : 'TXT';
|
||||
foreach ($data['domains_to_be_validated'] as $opts) {
|
||||
$mainDomain = getMainDomain($domain);
|
||||
$name = str_replace('.' . $mainDomain, '', $opts['validation_domain']);
|
||||
$name = substr($opts['validation_domain'], 0, -(strlen($mainDomain) + 1));
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => $type, 'value' => $opts['value']];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class letsencrypt implements CertInterface
|
||||
{
|
||||
$this->config = $config;
|
||||
if (empty($config['mode'])) $config['mode'] = 'live';
|
||||
$this->ac = new ACMECert($this->directories[$config['mode']], $config['proxy'] == 1);
|
||||
$this->ac = new ACMECert($this->directories[$config['mode']], (int)$config['proxy']);
|
||||
if ($ext) {
|
||||
$this->ext = $ext;
|
||||
$this->ac->loadAccountKey($ext['key']);
|
||||
@@ -60,7 +60,7 @@ class letsencrypt implements CertInterface
|
||||
if (!empty($order['challenges'])) {
|
||||
foreach ($order['challenges'] as $opts) {
|
||||
$mainDomain = getMainDomain($opts['domain']);
|
||||
$name = str_replace('.' . $mainDomain, '', $opts['key']);
|
||||
$name = substr($opts['key'], 0, -(strlen($mainDomain) + 1));
|
||||
/*if (!array_key_exists($mainDomain, $dnsList)) {
|
||||
$dnsList[$mainDomain][] = ['name' => '@', 'type' => 'CAA', 'value' => '0 issue "letsencrypt.org"'];
|
||||
}*/
|
||||
|
||||
@@ -15,14 +15,15 @@ class tencent implements CertInterface
|
||||
private $service = "ssl";
|
||||
private $version = "2019-12-05";
|
||||
private $logger;
|
||||
private $proxy;
|
||||
private TencentCloud $client;
|
||||
|
||||
public function __construct($config, $ext = null)
|
||||
{
|
||||
$this->SecretId = $config['SecretId'];
|
||||
$this->SecretKey = $config['SecretKey'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new TencentCloud($this->SecretId, $this->SecretKey, $this->endpoint, $this->service, $this->version, null, $proxy);
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new TencentCloud($this->SecretId, $this->SecretKey, $this->endpoint, $this->service, $this->version, null, $this->proxy);
|
||||
$this->email = $config['email'];
|
||||
}
|
||||
|
||||
@@ -59,8 +60,9 @@ class tencent implements CertInterface
|
||||
$dnsList = [];
|
||||
if (!empty($data['DvAuthDetail']['DvAuths'])) {
|
||||
foreach ($data['DvAuthDetail']['DvAuths'] as $opts) {
|
||||
$mainDomain = $opts['DvAuthDomain'];
|
||||
$dnsList[$mainDomain][] = ['name' => $opts['DvAuthSubDomain'], 'type' => $opts['DvAuthVerifyType'] ?? 'CNAME', 'value' => $opts['DvAuthValue']];
|
||||
$mainDomain = getMainDomain($opts['DvAuthKey']);
|
||||
$name = substr($opts['DvAuthKey'], 0, -(strlen($mainDomain) + 1));
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => $opts['DvAuthVerifyType'] ?? 'CNAME', 'value' => $opts['DvAuthValue']];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,13 +98,21 @@ class tencent implements CertInterface
|
||||
|
||||
public function finalizeOrder($domainList, $order, $keytype, $keysize)
|
||||
{
|
||||
$param = [
|
||||
'CertificateIds' => [$order['CertificateId']],
|
||||
'SwitchStatus' => 1,
|
||||
];
|
||||
$this->request('ModifyCertificatesExpiringNotificationSwitch', $param);
|
||||
|
||||
if (!is_dir(app()->getRuntimePath() . 'cert')) mkdir(app()->getRuntimePath() . 'cert');
|
||||
$param = [
|
||||
'CertificateId' => $order['CertificateId'],
|
||||
'ServiceType' => 'nginx',
|
||||
];
|
||||
$data = $this->request('DescribeDownloadCertificateUrl', $param);
|
||||
$file_data = get_curl($data['DownloadCertificateUrl']);
|
||||
$file_data = http_request($data['DownloadCertificateUrl'], null, null, null, null, $this->proxy);
|
||||
$file_data = $file_data['body'] ?? null;
|
||||
if (empty($file_data)) throw new Exception('下载证书失败');
|
||||
$file_path = app()->getRuntimePath() . 'cert/' . $data['DownloadFilename'];
|
||||
$file_name = substr($data['DownloadFilename'], 0, -4);
|
||||
file_put_contents($file_path, $file_data);
|
||||
@@ -149,9 +159,9 @@ class tencent implements CertInterface
|
||||
if (!empty($data['RevokeDomainValidateAuths'])) {
|
||||
$dnsList = [];
|
||||
foreach ($data['RevokeDomainValidateAuths'] as $opts) {
|
||||
$mainDomain = getMainDomain($opts['DomainValidateAuthDomain']);
|
||||
$name = str_replace('.' . $mainDomain, '', $opts['DomainValidateAuthKey']);
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => 'CNAME', 'value' => $opts['DomainValidateAuthValue']];
|
||||
$mainDomain = getMainDomain($opts['DomainValidateAuthKey']);
|
||||
$name = substr($opts['DomainValidateAuthKey'], 0, -(strlen($mainDomain) + 1));
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => 'TXT', 'value' => $opts['DomainValidateAuthValue']];
|
||||
}
|
||||
\app\utils\CertDnsUtils::addDns($dnsList, function ($txt) {
|
||||
$this->log($txt);
|
||||
|
||||
@@ -80,7 +80,8 @@ class ucloud implements CertInterface
|
||||
if (!empty($data['Auths'])) {
|
||||
foreach ($data['Auths'] as $auth) {
|
||||
$mainDomain = getMainDomain($auth['Domain']);
|
||||
$dnsList[$mainDomain][] = ['name' => $auth['AuthRecord'], 'type' => $auth['AuthType'] == 'DNS_TXT' ? 'TXT' : 'CNAME', 'value' => $auth['AuthValue']];
|
||||
$name = substr($auth['AuthKey'], 0, -(strlen($mainDomain) + 1));
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => $auth['AuthType'] == 'DNS_TXT' ? 'TXT' : 'CNAME', 'value' => $auth['AuthValue']];
|
||||
}
|
||||
}
|
||||
return $dnsList;
|
||||
|
||||
@@ -16,7 +16,7 @@ class zerossl implements CertInterface
|
||||
public function __construct($config, $ext = null)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->ac = new ACMECert($this->directory, $config['proxy'] == 1);
|
||||
$this->ac = new ACMECert($this->directory, (int)$config['proxy']);
|
||||
if ($ext) {
|
||||
$this->ext = $ext;
|
||||
$this->ac->loadAccountKey($ext['key']);
|
||||
@@ -27,20 +27,27 @@ class zerossl implements CertInterface
|
||||
public function register()
|
||||
{
|
||||
if (empty($this->config['email'])) throw new Exception('邮件地址不能为空');
|
||||
if (empty($this->config['kid']) || empty($this->config['key'])) throw new Exception('必填参数不能为空');
|
||||
|
||||
if (isset($this->config['eabMode']) && $this->config['eabMode'] == 'auto') {
|
||||
$eab = $this->getEAB($this->config['email']);
|
||||
} else {
|
||||
$eab = ['kid' => $this->config['kid'], 'key' => $this->config['key']];
|
||||
}
|
||||
|
||||
if (!empty($this->ext['key'])) {
|
||||
$kid = $this->ac->registerEAB(true, $this->config['kid'], $this->config['key'], $this->config['email']);
|
||||
$kid = $this->ac->registerEAB(true, $eab['kid'], $eab['key'], $this->config['email']);
|
||||
return ['kid' => $kid, 'key' => $this->ext['key']];
|
||||
}
|
||||
|
||||
$key = $this->ac->generateRSAKey(2048);
|
||||
$this->ac->loadAccountKey($key);
|
||||
$kid = $this->ac->registerEAB(true, $this->config['kid'], $this->config['key'], $this->config['email']);
|
||||
$kid = $this->ac->registerEAB(true, $eab['kid'], $eab['key'], $this->config['email']);
|
||||
return ['kid' => $kid, 'key' => $key];
|
||||
}
|
||||
|
||||
public function buyCert($domainList, &$order) {}
|
||||
public function buyCert($domainList, &$order)
|
||||
{
|
||||
}
|
||||
|
||||
public function createOrder($domainList, &$order, $keytype, $keysize)
|
||||
{
|
||||
@@ -57,7 +64,7 @@ class zerossl implements CertInterface
|
||||
if (!empty($order['challenges'])) {
|
||||
foreach ($order['challenges'] as $opts) {
|
||||
$mainDomain = getMainDomain($opts['domain']);
|
||||
$name = str_replace('.' . $mainDomain, '', $opts['key']);
|
||||
$name = substr($opts['key'], 0, -(strlen($mainDomain) + 1));
|
||||
/*if (!array_key_exists($mainDomain, $dnsList)) {
|
||||
$dnsList[$mainDomain][] = ['name' => '@', 'type' => 'CAA', 'value' => '0 issue "sectigo.com"'];
|
||||
}*/
|
||||
@@ -101,10 +108,27 @@ class zerossl implements CertInterface
|
||||
$this->ac->revoke($pem);
|
||||
}
|
||||
|
||||
public function cancel($order) {}
|
||||
public function cancel($order)
|
||||
{
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->ac->setLogger($func);
|
||||
}
|
||||
|
||||
private function getEAB($email)
|
||||
{
|
||||
$api = "https://api.zerossl.com/acme/eab-credentials-email";
|
||||
$response = http_request($api, http_build_query(['email' => $email]), null, null, null, $this->config['proxy'] == 1);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (!isset($result['success'])) {
|
||||
throw new Exception('获取EAB失败:' . $response['body']);
|
||||
} elseif (!$result['success'] && isset($result['error'])) {
|
||||
throw new Exception('获取EAB失败:' . $result['error']['code'] . ' - ' . $result['error']['type']);
|
||||
} elseif (!isset($result['eab_kid']) || !isset($result['eab_hmac_key'])) {
|
||||
throw new Exception('获取EAB失败:返回数据不完整');
|
||||
}
|
||||
return ['kid' => $result['eab_kid'], 'key' => $result['eab_hmac_key']];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,6 +144,8 @@ class AWS
|
||||
}
|
||||
|
||||
$path = '/' . $this->version . $path;
|
||||
$body = '';
|
||||
$query = [];
|
||||
if ($method == 'GET' || $method == 'DELETE') {
|
||||
$query = $params;
|
||||
} else {
|
||||
@@ -181,7 +183,7 @@ class AWS
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = $method;
|
||||
$canonicalUri = $path;
|
||||
$canonicalUri = $this->getCanonicalURI($path);
|
||||
$canonicalQueryString = $this->getCanonicalQueryString($query);
|
||||
[$canonicalHeaders, $signedHeaders] = $this->getCanonicalHeaders($headers);
|
||||
$hashedRequestPayload = hash("sha256", $body);
|
||||
@@ -221,6 +223,17 @@ class AWS
|
||||
$replace = ['%20', '%2A', '~'];
|
||||
return str_replace($search, $replace, urlencode($str));
|
||||
}
|
||||
|
||||
private function getCanonicalURI($path)
|
||||
{
|
||||
if (empty($path)) return '/';
|
||||
$pattens = explode('/', $path);
|
||||
$pattens = array_map(function ($item) {
|
||||
return $this->escape($item);
|
||||
}, $pattens);
|
||||
$canonicalURI = implode('/', $pattens);
|
||||
return $canonicalURI;
|
||||
}
|
||||
|
||||
private function getCanonicalQueryString($parameters)
|
||||
{
|
||||
@@ -272,8 +285,9 @@ class AWS
|
||||
$response = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
if ($errno) {
|
||||
$errmsg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . curl_error($ch));
|
||||
throw new Exception('Curl error: ' . $errmsg);
|
||||
}
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if ($etag) {
|
||||
@@ -315,16 +329,31 @@ class AWS
|
||||
return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
|
||||
}
|
||||
|
||||
private function array2xml($array, $xml = null)
|
||||
private function array2xml($array, $xml = null, $parentTagName = 'root')
|
||||
{
|
||||
if ($xml === null) {
|
||||
$xml = new \SimpleXMLElement('<root/>');
|
||||
}
|
||||
|
||||
foreach ($array as $key => $value) {
|
||||
// 确定当前标签名:如果是数字键名,使用父级标签名,否则使用当前键名
|
||||
$tagName = is_numeric($key) ? $parentTagName : $key;
|
||||
|
||||
if (is_array($value)) {
|
||||
$subNode = $xml->addChild($key);
|
||||
$this->array2xml($value, $subNode);
|
||||
// 检查数组的第一个子节点的键是否为0
|
||||
$firstKey = array_key_first($value);
|
||||
$isFirstKeyZero = ($firstKey === 0 || $firstKey === '0');
|
||||
|
||||
if ($isFirstKeyZero) {
|
||||
// 如果第一个子节点的键是0,则不生成当前节点标签,直接递归子节点
|
||||
$this->array2xml($value, $xml, $tagName);
|
||||
|
||||
} else {
|
||||
// 否则生成当前节点标签,并递归子节点
|
||||
$subNode = $xml->addChild($tagName);
|
||||
$this->array2xml($value, $subNode, $tagName);
|
||||
}
|
||||
|
||||
} else {
|
||||
$xml->addChild($key, $value);
|
||||
}
|
||||
|
||||
@@ -62,8 +62,9 @@ class Aliyun
|
||||
$errno = curl_errno($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if ($errno) {
|
||||
$errmsg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . curl_error($ch));
|
||||
throw new Exception('Curl error: ' . $errmsg);
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
|
||||
@@ -34,13 +34,15 @@ class AliyunNew
|
||||
public function request($method, $action, $path = '/', $params = null)
|
||||
{
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) { return $a !== null;});
|
||||
$params = array_filter($params, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
}
|
||||
|
||||
if($method == 'GET' || $method == 'DELETE'){
|
||||
if ($method == 'GET' || $method == 'DELETE') {
|
||||
$query = $params;
|
||||
$body = '';
|
||||
}else{
|
||||
} else {
|
||||
$query = [];
|
||||
$body = !empty($params) ? json_encode($params) : '';
|
||||
}
|
||||
@@ -59,13 +61,13 @@ class AliyunNew
|
||||
$authorization = $this->generateSign($method, $path, $query, $headers, $body);
|
||||
$headers['Authorization'] = $authorization;
|
||||
|
||||
$url = 'https://'.$this->Endpoint.$path;
|
||||
$url = 'https://' . $this->Endpoint . $path;
|
||||
if (!empty($query)) {
|
||||
$url .= '?'.http_build_query($query);
|
||||
$url .= '?' . http_build_query($query);
|
||||
}
|
||||
$header = [];
|
||||
foreach ($headers as $key => $value) {
|
||||
$header[] = $key.': '.$value;
|
||||
$header[] = $key . ': ' . $value;
|
||||
}
|
||||
return $this->curl($method, $url, $body, $header);
|
||||
}
|
||||
@@ -76,21 +78,21 @@ class AliyunNew
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = $method;
|
||||
$canonicalUri = $path;
|
||||
$canonicalUri = $this->getCanonicalURI($path);
|
||||
$canonicalQueryString = $this->getCanonicalQueryString($query);
|
||||
[$canonicalHeaders, $signedHeaders] = $this->getCanonicalHeaders($headers);
|
||||
$hashedRequestPayload = hash("sha256", $body);
|
||||
$canonicalRequest = $httpRequestMethod."\n"
|
||||
.$canonicalUri."\n"
|
||||
.$canonicalQueryString."\n"
|
||||
.$canonicalHeaders."\n"
|
||||
.$signedHeaders."\n"
|
||||
.$hashedRequestPayload;
|
||||
$canonicalRequest = $httpRequestMethod . "\n"
|
||||
. $canonicalUri . "\n"
|
||||
. $canonicalQueryString . "\n"
|
||||
. $canonicalHeaders . "\n"
|
||||
. $signedHeaders . "\n"
|
||||
. $hashedRequestPayload;
|
||||
|
||||
// step 2: build string to sign
|
||||
$hashedCanonicalRequest = hash("sha256", $canonicalRequest);
|
||||
$stringToSign = $algorithm."\n"
|
||||
.$hashedCanonicalRequest;
|
||||
$stringToSign = $algorithm . "\n"
|
||||
. $hashedCanonicalRequest;
|
||||
|
||||
// step 3: sign string
|
||||
$signature = hash_hmac("sha256", $stringToSign, $this->AccessKeySecret);
|
||||
@@ -108,13 +110,24 @@ class AliyunNew
|
||||
return str_replace($search, $replace, urlencode($str));
|
||||
}
|
||||
|
||||
private function getCanonicalURI($path)
|
||||
{
|
||||
if (empty($path)) return '/';
|
||||
$pattens = explode('/', $path);
|
||||
$pattens = array_map(function ($item) {
|
||||
return $this->escape($item);
|
||||
}, $pattens);
|
||||
$canonicalURI = implode('/', $pattens);
|
||||
return $canonicalURI;
|
||||
}
|
||||
|
||||
private function getCanonicalQueryString($parameters)
|
||||
{
|
||||
if (empty($parameters)) return '';
|
||||
ksort($parameters);
|
||||
$canonicalQueryString = '';
|
||||
foreach ($parameters as $key => $value) {
|
||||
$canonicalQueryString .= '&' . $this->escape($key). '=' . $this->escape($value);
|
||||
$canonicalQueryString .= '&' . $this->escape($key) . '=' . $this->escape($value);
|
||||
}
|
||||
return substr($canonicalQueryString, 1);
|
||||
}
|
||||
@@ -155,8 +168,9 @@ class AliyunNew
|
||||
$response = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
if ($errno) {
|
||||
$errmsg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . curl_error($ch));
|
||||
throw new Exception('Curl error: ' . $errmsg);
|
||||
}
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
@@ -165,10 +179,10 @@ class AliyunNew
|
||||
if ($httpCode == 200) {
|
||||
return $arr;
|
||||
} elseif ($arr) {
|
||||
if(strpos($arr['Message'], '.') > 0) $arr['Message'] = substr($arr['Message'], 0, strpos($arr['Message'], '.')+1);
|
||||
if (strpos($arr['Message'], '.') > 0) $arr['Message'] = substr($arr['Message'], 0, strpos($arr['Message'], '.') + 1);
|
||||
throw new Exception($arr['Message']);
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,8 +119,9 @@ class AliyunOSS
|
||||
$errno = curl_errno($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if ($errno) {
|
||||
$errmsg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . curl_error($ch));
|
||||
throw new Exception('Curl error: ' . $errmsg);
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
@@ -158,8 +158,9 @@ class BaiduCloud
|
||||
$errno = curl_errno($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if ($errno) {
|
||||
$errmsg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . curl_error($ch));
|
||||
throw new Exception('Curl error: ' . $errmsg);
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
|
||||
164
app/lib/client/Ctyun.php
Normal file
164
app/lib/client/Ctyun.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 天翼云
|
||||
*/
|
||||
class Ctyun
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $SecretAccessKey;
|
||||
private $endpoint;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($AccessKeyId, $SecretAccessKey, $endpoint, $proxy = false)
|
||||
{
|
||||
$this->AccessKeyId = $AccessKeyId;
|
||||
$this->SecretAccessKey = $SecretAccessKey;
|
||||
$this->endpoint = $endpoint;
|
||||
$this->proxy = $proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method 请求方法
|
||||
* @param string $path 请求路径
|
||||
* @param array|null $query 请求参数
|
||||
* @param array|null $params 请求体
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function request($method, $path, $query = null, $params = null)
|
||||
{
|
||||
if (!empty($query)) {
|
||||
$query = array_filter($query, function ($a) { return $a !== null;});
|
||||
}
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) { return $a !== null;});
|
||||
}
|
||||
|
||||
$time = time();
|
||||
$date = date("Ymd\THis\Z", $time);
|
||||
$body = !empty($params) ? json_encode($params) : '';
|
||||
$headers = [
|
||||
'Host' => $this->endpoint,
|
||||
'Eop-date' => $date,
|
||||
'ctyun-eop-request-id' => getSid(),
|
||||
];
|
||||
if ($body) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
$authorization = $this->generateSign($query, $headers, $body, $date);
|
||||
$headers['Eop-Authorization'] = $authorization;
|
||||
|
||||
$url = 'https://' . $this->endpoint . $path;
|
||||
if (!empty($query)) {
|
||||
$url .= '?' . http_build_query($query);
|
||||
}
|
||||
$header = [];
|
||||
foreach ($headers as $key => $value) {
|
||||
$header[] = $key . ': ' . $value;
|
||||
}
|
||||
return $this->curl($method, $url, $body, $header);
|
||||
}
|
||||
|
||||
private function generateSign($query, $headers, $body, $date)
|
||||
{
|
||||
// step 1: build canonical request string
|
||||
$canonicalQueryString = $this->getCanonicalQueryString($query);
|
||||
[$canonicalHeaders, $signedHeaders] = $this->getCanonicalHeaders($headers);
|
||||
$hashedRequestPayload = hash("sha256", $body);
|
||||
|
||||
// step 2: build string to sign
|
||||
$stringToSign = $canonicalHeaders . "\n"
|
||||
. $canonicalQueryString . "\n"
|
||||
. $hashedRequestPayload;
|
||||
|
||||
// step 3: sign string
|
||||
$ktime = hash_hmac("sha256", $date, $this->SecretAccessKey, true);
|
||||
$kAk = hash_hmac("sha256", $this->AccessKeyId, $ktime, true);
|
||||
$kdate = hash_hmac("sha256", substr($date, 0, 8), $kAk, true);
|
||||
$signature = hash_hmac("sha256", $stringToSign, $kdate, true);
|
||||
$signature = base64_encode($signature);
|
||||
|
||||
// step 4: build authorization
|
||||
$authorization = $this->AccessKeyId . " Headers=" . $signedHeaders . " Signature=" . $signature;
|
||||
|
||||
return $authorization;
|
||||
}
|
||||
|
||||
private function escape($str)
|
||||
{
|
||||
$search = ['+', '*', '%7E'];
|
||||
$replace = ['%20', '%2A', '~'];
|
||||
return str_replace($search, $replace, urlencode($str));
|
||||
}
|
||||
|
||||
private function getCanonicalQueryString($parameters)
|
||||
{
|
||||
if (empty($parameters)) return '';
|
||||
ksort($parameters);
|
||||
$canonicalQueryString = '';
|
||||
foreach ($parameters as $key => $value) {
|
||||
$canonicalQueryString .= '&' . $this->escape($key) . '=' . $this->escape($value);
|
||||
}
|
||||
return substr($canonicalQueryString, 1);
|
||||
}
|
||||
|
||||
private function getCanonicalHeaders($oldheaders)
|
||||
{
|
||||
$headers = array();
|
||||
foreach ($oldheaders as $key => $value) {
|
||||
$headers[strtolower($key)] = trim($value);
|
||||
}
|
||||
ksort($headers);
|
||||
|
||||
$canonicalHeaders = '';
|
||||
$signedHeaders = '';
|
||||
foreach ($headers as $key => $value) {
|
||||
$canonicalHeaders .= $key . ':' . $value . "\n";
|
||||
$signedHeaders .= $key . ';';
|
||||
}
|
||||
$signedHeaders = substr($signedHeaders, 0, -1);
|
||||
return [$canonicalHeaders, $signedHeaders];
|
||||
}
|
||||
|
||||
private function curl($method, $url, $body, $header)
|
||||
{
|
||||
$ch = curl_init($url);
|
||||
if ($this->proxy) {
|
||||
curl_set_proxy($ch);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
if (!empty($body)) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
}
|
||||
$response = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
if ($errno) {
|
||||
$errmsg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . $errmsg);
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
$arr = json_decode($response, true);
|
||||
if (isset($arr['statusCode']) && $arr['statusCode'] == 100000) {
|
||||
return isset($arr['returnObj']) ? $arr['returnObj'] : true;
|
||||
} elseif (isset($arr['errorMessage'])) {
|
||||
throw new Exception($arr['errorMessage']);
|
||||
} elseif (isset($arr['message'])) {
|
||||
throw new Exception($arr['message']);
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,8 +70,7 @@ class HuaweiCloud
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = $method;
|
||||
$canonicalUri = $path;
|
||||
if (substr($canonicalUri, -1) != "/") $canonicalUri .= "/";
|
||||
$canonicalUri = $this->getCanonicalURI($path);
|
||||
$canonicalQueryString = $this->getCanonicalQueryString($query);
|
||||
[$canonicalHeaders, $signedHeaders] = $this->getCanonicalHeaders($headers);
|
||||
$hashedRequestPayload = hash("sha256", $body);
|
||||
@@ -105,6 +104,18 @@ class HuaweiCloud
|
||||
return str_replace($search, $replace, urlencode($str));
|
||||
}
|
||||
|
||||
private function getCanonicalURI($path)
|
||||
{
|
||||
if (empty($path)) return '/';
|
||||
$pattens = explode('/', $path);
|
||||
$pattens = array_map(function ($item) {
|
||||
return $this->escape($item);
|
||||
}, $pattens);
|
||||
$canonicalURI = implode('/', $pattens);
|
||||
if (substr($canonicalURI, -1) != '/') $canonicalURI .= '/';
|
||||
return $canonicalURI;
|
||||
}
|
||||
|
||||
private function getCanonicalQueryString($parameters)
|
||||
{
|
||||
if (empty($parameters)) return '';
|
||||
@@ -152,8 +163,9 @@ class HuaweiCloud
|
||||
$response = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
if ($errno) {
|
||||
$errmsg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . curl_error($ch));
|
||||
throw new Exception('Curl error: ' . $errmsg);
|
||||
}
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
191
app/lib/client/Jdcloud.php
Normal file
191
app/lib/client/Jdcloud.php
Normal file
@@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 京东云
|
||||
*/
|
||||
class Jdcloud
|
||||
{
|
||||
private static $algorithm = 'JDCLOUD2-HMAC-SHA256';
|
||||
private $AccessKeyId;
|
||||
private $AccessKeySecret;
|
||||
private $endpoint;
|
||||
private $service;
|
||||
private $region;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($AccessKeyId, $AccessKeySecret, $endpoint, $service, $region, $proxy = false)
|
||||
{
|
||||
$this->AccessKeyId = $AccessKeyId;
|
||||
$this->AccessKeySecret = $AccessKeySecret;
|
||||
$this->endpoint = $endpoint;
|
||||
$this->service = $service;
|
||||
$this->region = $region;
|
||||
$this->proxy = $proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method 请求方法
|
||||
* @param string $path 请求路径
|
||||
* @param array $params 请求参数
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function request($method, $path, $params = [])
|
||||
{
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
}
|
||||
|
||||
if ($method == 'GET' || $method == 'DELETE') {
|
||||
$query = $params;
|
||||
$body = '';
|
||||
} else {
|
||||
$query = [];
|
||||
$body = !empty($params) ? json_encode($params) : '';
|
||||
}
|
||||
|
||||
$date = gmdate("Ymd\THis\Z");
|
||||
$headers = [
|
||||
'Host' => $this->endpoint,
|
||||
'x-jdcloud-algorithm' => self::$algorithm,
|
||||
'x-jdcloud-date' => $date,
|
||||
'x-jdcloud-nonce' => uniqid('php', true),
|
||||
];
|
||||
if ($body) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
$authorization = $this->generateSign($method, $path, $query, $headers, $body, $date);
|
||||
$headers['authorization'] = $authorization;
|
||||
|
||||
$url = 'https://' . $this->endpoint . $path;
|
||||
if (!empty($query)) {
|
||||
$url .= '?' . http_build_query($query);
|
||||
}
|
||||
$header = [];
|
||||
foreach ($headers as $key => $value) {
|
||||
$header[] = $key . ': ' . $value;
|
||||
}
|
||||
return $this->curl($method, $url, $body, $header);
|
||||
}
|
||||
|
||||
private function generateSign($method, $path, $query, $headers, $body, $date)
|
||||
{
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = $method;
|
||||
$canonicalUri = $path;
|
||||
$canonicalQueryString = $this->getCanonicalQueryString($query);
|
||||
[$canonicalHeaders, $signedHeaders] = $this->getCanonicalHeaders($headers);
|
||||
$hashedRequestPayload = hash("sha256", $body);
|
||||
$canonicalRequest = $httpRequestMethod . "\n"
|
||||
. $canonicalUri . "\n"
|
||||
. $canonicalQueryString . "\n"
|
||||
. $canonicalHeaders . "\n"
|
||||
. $signedHeaders . "\n"
|
||||
. $hashedRequestPayload;
|
||||
|
||||
// step 2: build string to sign
|
||||
$shortDate = substr($date, 0, 8);
|
||||
$credentialScope = $shortDate . '/' . $this->region . '/' . $this->service . '/jdcloud2_request';
|
||||
$hashedCanonicalRequest = hash("sha256", $canonicalRequest);
|
||||
$stringToSign = self::$algorithm . "\n"
|
||||
. $date . "\n"
|
||||
. $credentialScope . "\n"
|
||||
. $hashedCanonicalRequest;
|
||||
|
||||
// step 3: sign string
|
||||
$kDate = hash_hmac("sha256", $shortDate, 'JDCLOUD2' . $this->AccessKeySecret, true);
|
||||
$kRegion = hash_hmac("sha256", $this->region, $kDate, true);
|
||||
$kService = hash_hmac("sha256", $this->service, $kRegion, true);
|
||||
$kSigning = hash_hmac("sha256", "jdcloud2_request", $kService, true);
|
||||
$signature = hash_hmac("sha256", $stringToSign, $kSigning);
|
||||
|
||||
// step 4: build authorization
|
||||
$credential = $this->AccessKeyId . '/' . $credentialScope;
|
||||
$authorization = self::$algorithm . ' Credential=' . $credential . ", SignedHeaders=" . $signedHeaders . ", Signature=" . $signature;
|
||||
|
||||
return $authorization;
|
||||
}
|
||||
|
||||
private function escape($str)
|
||||
{
|
||||
$search = ['+', '*', '%7E'];
|
||||
$replace = ['%20', '%2A', '~'];
|
||||
return str_replace($search, $replace, urlencode($str));
|
||||
}
|
||||
|
||||
private function getCanonicalQueryString($parameters)
|
||||
{
|
||||
if (empty($parameters)) return '';
|
||||
ksort($parameters);
|
||||
$canonicalQueryString = '';
|
||||
foreach ($parameters as $key => $value) {
|
||||
$canonicalQueryString .= '&' . $this->escape($key) . '=' . $this->escape($value);
|
||||
}
|
||||
return substr($canonicalQueryString, 1);
|
||||
}
|
||||
|
||||
private function getCanonicalHeaders($oldheaders)
|
||||
{
|
||||
$headers = array();
|
||||
foreach ($oldheaders as $key => $value) {
|
||||
$headers[strtolower($key)] = trim($value);
|
||||
}
|
||||
ksort($headers);
|
||||
|
||||
$canonicalHeaders = '';
|
||||
$signedHeaders = '';
|
||||
foreach ($headers as $key => $value) {
|
||||
$canonicalHeaders .= $key . ':' . $value . "\n";
|
||||
$signedHeaders .= $key . ';';
|
||||
}
|
||||
$signedHeaders = substr($signedHeaders, 0, -1);
|
||||
return [$canonicalHeaders, $signedHeaders];
|
||||
}
|
||||
|
||||
private function curl($method, $url, $body, $header)
|
||||
{
|
||||
$ch = curl_init($url);
|
||||
if ($this->proxy) {
|
||||
curl_set_proxy($ch);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
if (!empty($body)) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
}
|
||||
$response = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
if ($errno) {
|
||||
$errmsg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . $errmsg);
|
||||
}
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
$arr = json_decode($response, true);
|
||||
if ($httpCode == 200) {
|
||||
if (isset($arr['result'])) {
|
||||
return $arr['result'];
|
||||
}
|
||||
return $arr;
|
||||
} else {
|
||||
if (isset($arr['error']['message'])) {
|
||||
throw new Exception($arr['error']['message']);
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败(http_code=' . $httpCode . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,39 @@ class Qiniu
|
||||
return $this->curl($method, $url, $body, $header);
|
||||
}
|
||||
|
||||
public function pili_request($method, $path, $query = null, $params = null)
|
||||
{
|
||||
$this->ApiUrl = 'https://pili.qiniuapi.com';
|
||||
$url = $this->ApiUrl . $path;
|
||||
$query_str = null;
|
||||
$body = null;
|
||||
if (!empty($query)) {
|
||||
$query = array_filter($query, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
$query_str = http_build_query($query);
|
||||
$url .= '?' . $query_str;
|
||||
}
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
$body = json_encode($params);
|
||||
}
|
||||
|
||||
$sign_str = $method . ' ' . $path . ($query_str ? '?' . $query_str : '') . "\nHost: pili.qiniuapi.com" . ($body ? "\nContent-Type: application/json" : '') . "\n\n" . $body;
|
||||
$hmac = hash_hmac('sha1', $sign_str, $this->SecretKey, true);
|
||||
$sign = $this->AccessKey . ':' . $this->base64_urlSafeEncode($hmac);
|
||||
|
||||
$header = [
|
||||
'Authorization: Qiniu ' . $sign,
|
||||
];
|
||||
if ($body) {
|
||||
$header[] = 'Content-Type: application/json';
|
||||
}
|
||||
return $this->curl($method, $url, $body, $header);
|
||||
}
|
||||
|
||||
private function base64_urlSafeEncode($data)
|
||||
{
|
||||
$find = array('+', '/');
|
||||
@@ -88,14 +121,15 @@ class Qiniu
|
||||
$errno = curl_errno($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if ($errno) {
|
||||
$errmsg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . curl_error($ch));
|
||||
throw new Exception('Curl error: ' . $errmsg);
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode == 200) {
|
||||
$arr = json_decode($response, true);
|
||||
if($arr) return $arr;
|
||||
if ($arr) return $arr;
|
||||
return true;
|
||||
} else {
|
||||
$arr = json_decode($response, true);
|
||||
|
||||
@@ -113,8 +113,9 @@ class TencentCloud
|
||||
$response = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
if ($errno) {
|
||||
$errmsg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . curl_error($ch));
|
||||
throw new Exception('Curl error: ' . $errmsg);
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class Ucloud
|
||||
$param = array_merge($param, $params);
|
||||
$param['Signature'] = $this->ucloudSignature($param);
|
||||
$ua = sprintf("PHP/%s PHP-SDK/%s", phpversion(), self::VERSION);
|
||||
$response = get_curl($this->ApiUrl, json_encode($param), 0, 0, 0, $ua, 0, ['Content-Type: application/json']);
|
||||
$response = get_curl($this->ApiUrl, json_encode($param), 0, 0, $ua, 0, ['Content-Type' => 'application/json']);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['RetCode']) && $result['RetCode'] == 0) {
|
||||
return $result;
|
||||
|
||||
@@ -38,7 +38,9 @@ class Volcengine
|
||||
public function request($method, $action, $params = [], $querys = [])
|
||||
{
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) { return $a !== null;});
|
||||
$params = array_filter($params, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
}
|
||||
|
||||
$query = [
|
||||
@@ -78,9 +80,51 @@ class Volcengine
|
||||
return $this->curl($method, $url, $body, $header);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method 请求方法
|
||||
* @param string $action 方法名称
|
||||
* @param array $params 请求参数
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function tos_request($method, $params = [], $query = [])
|
||||
{
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
}
|
||||
|
||||
$body = '';
|
||||
if ($method != 'GET') {
|
||||
$body = !empty($params) ? json_encode($params) : '';
|
||||
}
|
||||
|
||||
$time = time();
|
||||
$headers = [
|
||||
'Host' => $this->endpoint,
|
||||
'X-Tos-Date' => gmdate("Ymd\THis\Z", $time),
|
||||
'X-Tos-Content-Sha256' => hash("sha256", $body),
|
||||
];
|
||||
if ($body) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
$path = '/';
|
||||
|
||||
$authorization = $this->generateSign($method, $path, $query, $headers, $body, $time);
|
||||
$headers['Authorization'] = $authorization;
|
||||
|
||||
$url = 'https://' . $this->endpoint . $path . '?' . http_build_query($query);
|
||||
$header = [];
|
||||
foreach ($headers as $key => $value) {
|
||||
$header[] = $key . ': ' . $value;
|
||||
}
|
||||
return $this->curl($method, $url, $body, $header);
|
||||
}
|
||||
|
||||
private function generateSign($method, $path, $query, $headers, $body, $time)
|
||||
{
|
||||
$algorithm = "HMAC-SHA256";
|
||||
$algorithm = $this->service == 'tos' ? "TOS4-HMAC-SHA256" : "HMAC-SHA256";
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = $method;
|
||||
@@ -174,24 +218,35 @@ class Volcengine
|
||||
$response = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
if ($errno) {
|
||||
$errmsg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . curl_error($ch));
|
||||
throw new Exception('Curl error: ' . $errmsg);
|
||||
}
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
$arr = json_decode($response, true);
|
||||
if ($arr) {
|
||||
if ($httpCode == 200) {
|
||||
if (isset($arr['ResponseMetadata']['Error']['MessageCN'])) {
|
||||
throw new Exception($arr['ResponseMetadata']['Error']['MessageCN']);
|
||||
} elseif (isset($arr['ResponseMetadata']['Error']['Message'])) {
|
||||
throw new Exception($arr['ResponseMetadata']['Error']['Message']);
|
||||
} elseif (isset($arr['Result'])) {
|
||||
return $arr['Result'];
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
if (isset($arr['ResponseMetadata']['Error']['MessageCN'])) {
|
||||
throw new Exception($arr['ResponseMetadata']['Error']['MessageCN']);
|
||||
} elseif (isset($arr['ResponseMetadata']['Error']['Message'])) {
|
||||
throw new Exception($arr['ResponseMetadata']['Error']['Message']);
|
||||
} elseif (isset($arr['Message'])) {
|
||||
throw new Exception($arr['Message']);
|
||||
} elseif (isset($arr['message'])) {
|
||||
throw new Exception($arr['message']);
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败(http_code=' . $httpCode . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,14 +42,14 @@ class aliyun implements DeployInterface
|
||||
} elseif ($config['product'] == 'fc2') {
|
||||
$this->deploy_fc2($fullchain, $privatekey, $config);
|
||||
} else {
|
||||
[$cert_id, $cert_name] = $this->get_cert_id($fullchain, $privatekey);
|
||||
[$cert_id, $cert_name] = $this->get_cert_id($fullchain, $privatekey, $config);
|
||||
if (!$cert_id) throw new Exception('证书ID获取失败');
|
||||
if ($config['product'] == 'cdn') {
|
||||
$this->deploy_cdn($cert_id, $cert_name, $config);
|
||||
} elseif ($config['product'] == 'dcdn') {
|
||||
$this->deploy_dcdn($cert_id, $cert_name, $config);
|
||||
} elseif ($config['product'] == 'esa') {
|
||||
$this->deploy_esa($cert_id, $config);
|
||||
$this->deploy_esa($cert_id, $cert_name, $config);
|
||||
} elseif ($config['product'] == 'oss') {
|
||||
$this->deploy_oss($cert_id, $config);
|
||||
} elseif ($config['product'] == 'waf') {
|
||||
@@ -74,18 +74,24 @@ class aliyun implements DeployInterface
|
||||
}
|
||||
}
|
||||
|
||||
private function get_cert_id($fullchain, $privatekey)
|
||||
private function get_cert_id($fullchain, $privatekey, $config)
|
||||
{
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
$serial_no = strtolower($certInfo['serialNumberHex']);
|
||||
|
||||
$client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, 'cas.aliyuncs.com', '2020-04-07', $this->proxy);
|
||||
if ($config['region'] == 'ap-southeast-1') {
|
||||
$endpoint = 'cas.ap-southeast-1.aliyuncs.com';
|
||||
} else {
|
||||
$endpoint = 'cas.aliyuncs.com';
|
||||
}
|
||||
|
||||
$client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, $endpoint, '2020-04-07', $this->proxy);
|
||||
$param = [
|
||||
'Action' => 'ListUserCertificateOrder',
|
||||
'Keyword' => $certInfo['subject']['CN'],
|
||||
'OrderType' => 'UPLOAD',
|
||||
'OrderType' => 'CERT',
|
||||
];
|
||||
try {
|
||||
$data = $client->request($param);
|
||||
@@ -95,7 +101,7 @@ class aliyun implements DeployInterface
|
||||
$cert_id = null;
|
||||
if ($data['TotalCount'] > 0 && !empty($data['CertificateOrderList'])) {
|
||||
foreach ($data['CertificateOrderList'] as $cert) {
|
||||
if (strtolower($cert['SerialNo']) == $serial_no) {
|
||||
if (strtolower($cert['SerialNo']) == $serial_no || strpos(strtolower($cert['SerialNo']), $serial_no) !== false) {
|
||||
$cert_id = $cert['CertificateId'];
|
||||
$cert_name = $cert['Name'];
|
||||
break;
|
||||
@@ -157,12 +163,18 @@ class aliyun implements DeployInterface
|
||||
$this->log('DCDN域名 ' . $domain . ' 部署证书成功!');
|
||||
}
|
||||
|
||||
private function deploy_esa($cas_id, $config)
|
||||
private function deploy_esa($cas_id, $cert_name, $config)
|
||||
{
|
||||
$sitename = $config['esa_sitename'];
|
||||
if (empty($sitename)) throw new Exception('ESA站点名称不能为空');
|
||||
|
||||
$client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, 'esa.cn-hangzhou.aliyuncs.com', '2024-09-10');
|
||||
if ($config['region'] == 'ap-southeast-1') {
|
||||
$endpoint = 'esa.ap-southeast-1.aliyuncs.com';
|
||||
} else {
|
||||
$endpoint = 'esa.cn-hangzhou.aliyuncs.com';
|
||||
}
|
||||
|
||||
$client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, $endpoint, '2024-09-10');
|
||||
$param = [
|
||||
'Action' => 'ListSites',
|
||||
'SiteName' => $sitename,
|
||||
@@ -188,22 +200,26 @@ class aliyun implements DeployInterface
|
||||
}
|
||||
$this->log('ESA站点 ' . $sitename . ' 查询到' . $data['TotalCount'] . '个SSL证书');
|
||||
|
||||
$cert_id = null;
|
||||
$cert_name = null;
|
||||
foreach ($data['Result'] as $cert) {
|
||||
$domains = explode(',', $cert['SAN']);
|
||||
$flag = true;
|
||||
foreach ($domains as $domain) {
|
||||
if (!in_array($domain, $config['domainList'])) {
|
||||
$flag = false;
|
||||
$exist_cert_id = null;
|
||||
$exist_cert_name = null;
|
||||
$exist_cert_casid = null;
|
||||
if ($data['TotalCount'] > 0) {
|
||||
foreach ($data['Result'] as $cert) {
|
||||
$domains = explode(',', $cert['SAN']);
|
||||
$flag = true;
|
||||
foreach ($domains as $domain) {
|
||||
if (!in_array($domain, $config['domainList'])) {
|
||||
$flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($flag) {
|
||||
$exist_cert_id = $cert['Id'];
|
||||
$exist_cert_name = $cert['Name'];
|
||||
$exist_cert_casid = isset($cert['CasId']) ? $cert['CasId'] : null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($flag) {
|
||||
$cert_id = $cert['Id'];
|
||||
$cert_name = $cert['CommonName'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$param = [
|
||||
@@ -211,16 +227,25 @@ class aliyun implements DeployInterface
|
||||
'SiteId' => $site_id,
|
||||
'Type' => 'cas',
|
||||
'CasId' => $cas_id,
|
||||
'Name' => $cert_name,
|
||||
'Region' => $config['region'],
|
||||
];
|
||||
if ($cert_id) {
|
||||
$param['Update'] = 'true';
|
||||
$param['Id'] = $cert_id;
|
||||
|
||||
if ($exist_cert_id) {
|
||||
$param['Id'] = $exist_cert_id;
|
||||
|
||||
if ($exist_cert_casid == $cas_id) {
|
||||
$this->log('ESA站点 ' . $sitename . ' 证书已配置,无需重复操作');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$client->request($param);
|
||||
if ($cert_id) {
|
||||
$this->log('ESA站点 ' . $sitename . ' 域名 ' . $cert_name . ' 更新证书成功!');
|
||||
|
||||
if ($exist_cert_name) {
|
||||
$this->log('ESA站点 ' . $sitename . ' 证书 ' . $exist_cert_name . ' 更新成功');
|
||||
} else {
|
||||
$this->log('ESA站点 ' . $sitename . ' 添加证书成功!');
|
||||
$this->log('ESA站点 ' . $sitename . ' 证书添加成功!');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,7 +255,7 @@ class aliyun implements DeployInterface
|
||||
if (empty($config['oss_endpoint'])) throw new Exception('OSS Endpoint不能为空');
|
||||
if (empty($config['oss_bucket'])) throw new Exception('OSS Bucket不能为空');
|
||||
$client = new AliyunOSS($this->AccessKeyId, $this->AccessKeySecret, $config['oss_endpoint']);
|
||||
$client->addBucketCnameCert($config['oss_bucket'], $config['domain'], $cert_id);
|
||||
$client->addBucketCnameCert($config['oss_bucket'], $config['domain'], $cert_id . '-cn-hangzhou');
|
||||
$this->log('OSS域名 ' . $config['domain'] . ' 部署证书成功!');
|
||||
}
|
||||
|
||||
@@ -239,6 +264,12 @@ class aliyun implements DeployInterface
|
||||
$domain = $config['domain'];
|
||||
if (empty($domain)) throw new Exception('WAF绑定域名不能为空');
|
||||
|
||||
if ($config['region'] == 'ap-southeast-1') {
|
||||
$cert_id .= '-ap-southeast-1';
|
||||
} else {
|
||||
$cert_id .= '-cn-hangzhou';
|
||||
}
|
||||
|
||||
$endpoint = 'wafopenapi.' . $config['region'] . '.aliyuncs.com';
|
||||
|
||||
$client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, $endpoint, '2021-10-01', $this->proxy);
|
||||
@@ -267,17 +298,19 @@ class aliyun implements DeployInterface
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('查询CNAME接入详情失败:' . $e->getMessage());
|
||||
}
|
||||
if (!isset($data['Listen'])) {
|
||||
throw new Exception('没有找到' . $domain . '监听器');
|
||||
}
|
||||
|
||||
if (isset($data['Listen']['CertId'])) {
|
||||
$old_cert_id = $data['Listen']['CertId'];
|
||||
if (strpos($old_cert_id, '-')) $old_cert_id = substr($old_cert_id, 0, strpos($old_cert_id, '-'));
|
||||
if (!empty($old_cert_id) && $old_cert_id == $cert_id) {
|
||||
$this->log('WAF域名 ' . $domain . ' 证书已配置,无需重复操作');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$data['Listen']['CertId'] = $cert_id . '-cn-hangzhou';
|
||||
$data['Listen']['CertId'] = $cert_id;
|
||||
if (empty($data['Listen']['HttpsPorts'])) $data['Listen']['HttpsPorts'] = [443];
|
||||
$data['Redirect']['Backends'] = $data['Redirect']['AllBackends'];
|
||||
$param = [
|
||||
@@ -535,36 +568,65 @@ class aliyun implements DeployInterface
|
||||
$this->log('找到已添加的服务器证书 ServerCertificateId=' . $ServerCertificateId);
|
||||
}
|
||||
|
||||
$param = [
|
||||
'Action' => 'DescribeLoadBalancerHTTPSListenerAttribute',
|
||||
'RegionId' => $config['regionid'],
|
||||
'LoadBalancerId' => $config['clb_id'],
|
||||
'ListenerPort' => $config['clb_port'],
|
||||
];
|
||||
try {
|
||||
$data = $client->request($param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('HTTPS监听配置查询失败:' . $e->getMessage());
|
||||
}
|
||||
$deploy_type = isset($config['deploy_type']) ? intval($config['deploy_type']) : 0;
|
||||
if ($deploy_type == 1) {
|
||||
if (empty($config['clb_domain'])) throw new Exception('扩展域名不能为空');
|
||||
$domains = explode(',', $config['clb_domain']);
|
||||
$param = [
|
||||
'Action' => 'DescribeDomainExtensions',
|
||||
'RegionId' => $config['regionid'],
|
||||
'LoadBalancerId' => $config['clb_id'],
|
||||
'ListenerPort' => $config['clb_port'],
|
||||
];
|
||||
try {
|
||||
$data = $client->request($param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('扩展域名列表查询失败:' . $e->getMessage());
|
||||
}
|
||||
foreach ($data['DomainExtensions']['DomainExtension'] as $item) {
|
||||
if (in_array($item['Domain'], $domains)) {
|
||||
if ($ServerCertificateId == $item['ServerCertificateId']) {
|
||||
$this->log('负载均衡HTTPS扩展域名 ' . $item['Domain'] . ' 证书已配置');
|
||||
} else {
|
||||
$param = [
|
||||
'Action' => 'SetDomainExtensionAttribute',
|
||||
'RegionId' => $config['regionid'],
|
||||
'DomainExtensionId' => $item['DomainExtensionId'],
|
||||
'ServerCertificateId' => $ServerCertificateId,
|
||||
];
|
||||
$client->request($param);
|
||||
$this->log('负载均衡HTTPS扩展域名 ' . $item['Domain'] . ' 证书更新成功');
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$param = [
|
||||
'Action' => 'DescribeLoadBalancerHTTPSListenerAttribute',
|
||||
'RegionId' => $config['regionid'],
|
||||
'LoadBalancerId' => $config['clb_id'],
|
||||
'ListenerPort' => $config['clb_port'],
|
||||
];
|
||||
try {
|
||||
$data = $client->request($param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('HTTPS监听配置查询失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
if ($data['ServerCertificateId'] == $ServerCertificateId) {
|
||||
$this->log('负载均衡HTTPS监听已配置该证书,无需重复操作');
|
||||
return;
|
||||
}
|
||||
if ($data['ServerCertificateId'] == $ServerCertificateId) {
|
||||
$this->log('负载均衡HTTPS监听已配置该证书,无需重复操作');
|
||||
return;
|
||||
}
|
||||
|
||||
$param = [
|
||||
'Action' => 'SetLoadBalancerHTTPSListenerAttribute',
|
||||
'RegionId' => $config['regionid'],
|
||||
'LoadBalancerId' => $config['clb_id'],
|
||||
'ListenerPort' => $config['clb_port'],
|
||||
];
|
||||
$keys = ['Bandwidth', 'XForwardedFor', 'Scheduler', 'StickySession', 'StickySessionType', 'CookieTimeout', 'Cookie', 'HealthCheck', 'HealthCheckMethod', 'HealthCheckDomain', 'HealthCheckURI', 'HealthyThreshold', 'UnhealthyThreshold', 'HealthCheckTimeout', 'HealthCheckInterval', 'HealthCheckConnectPort', 'HealthCheckHttpCode', 'ServerCertificateId', 'CACertificateId', 'VServerGroup', 'VServerGroupId', 'XForwardedFor_SLBIP', 'XForwardedFor_SLBID', 'XForwardedFor_proto', 'Gzip', 'AclId', 'AclType', 'AclStatus', 'IdleTimeout', 'RequestTimeout', 'EnableHttp2', 'TLSCipherPolicy', 'Description', 'XForwardedFor_SLBPORT', 'XForwardedFor_ClientSrcPort'];
|
||||
foreach ($keys as $key) {
|
||||
if (isset($data[$key])) $param[$key] = $data[$key];
|
||||
$param = [
|
||||
'Action' => 'SetLoadBalancerHTTPSListenerAttribute',
|
||||
'RegionId' => $config['regionid'],
|
||||
'LoadBalancerId' => $config['clb_id'],
|
||||
'ListenerPort' => $config['clb_port'],
|
||||
'ServerCertificateId' => $ServerCertificateId,
|
||||
];
|
||||
$client->request($param);
|
||||
$this->log('负载均衡HTTPS监听证书配置成功!');
|
||||
}
|
||||
$param['ServerCertificateId'] = $ServerCertificateId;
|
||||
$client->request($param);
|
||||
$this->log('负载均衡HTTPS监听证书配置成功!');
|
||||
}
|
||||
|
||||
private function deploy_alb($cert_id, $config)
|
||||
@@ -573,33 +635,44 @@ class aliyun implements DeployInterface
|
||||
|
||||
$endpoint = 'alb.' . $config['regionid'] . '.aliyuncs.com';
|
||||
$client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, $endpoint, '2020-06-16', $this->proxy);
|
||||
$cert_id = $cert_id . '-cn-hangzhou';
|
||||
$deploy_type = isset($config['deploy_type']) ? intval($config['deploy_type']) : 0;
|
||||
|
||||
$param = [
|
||||
'Action' => 'ListListenerCertificates',
|
||||
'MaxResults' => 100,
|
||||
'ListenerId' => $config['alb_listener_id'],
|
||||
'CertificateType' => 'Server',
|
||||
];
|
||||
try {
|
||||
$data = $client->request($param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取监听证书列表失败:' . $e->getMessage());
|
||||
}
|
||||
foreach ($data['Certificates'] as $cert) {
|
||||
if (strpos($cert['CertificateId'], '-')) $cert['CertificateId'] = substr($cert['CertificateId'], 0, strpos($cert['CertificateId'], '-'));
|
||||
if ($cert['CertificateId'] == $cert_id) {
|
||||
$this->log('负载均衡监听证书已添加,无需重复操作');
|
||||
return;
|
||||
if ($deploy_type == 1) {
|
||||
$param = [
|
||||
'Action' => 'ListListenerCertificates',
|
||||
'MaxResults' => 100,
|
||||
'ListenerId' => $config['alb_listener_id'],
|
||||
'CertificateType' => 'Server',
|
||||
];
|
||||
try {
|
||||
$data = $client->request($param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取监听证书列表失败:' . $e->getMessage());
|
||||
}
|
||||
foreach ($data['Certificates'] as $cert) {
|
||||
if ($cert['CertificateId'] == $cert_id) {
|
||||
$this->log('负载均衡监听扩展证书已添加,无需重复操作');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$param = [
|
||||
'Action' => 'AssociateAdditionalCertificatesWithListener',
|
||||
'ListenerId' => $config['alb_listener_id'],
|
||||
'Certificates.1.CertificateId' => $cert_id . '-cn-hangzhou',
|
||||
];
|
||||
$client->request($param);
|
||||
$this->log('应用型负载均衡监听证书添加成功!');
|
||||
$param = [
|
||||
'Action' => 'AssociateAdditionalCertificatesWithListener',
|
||||
'ListenerId' => $config['alb_listener_id'],
|
||||
'Certificates.1.CertificateId' => $cert_id,
|
||||
];
|
||||
$client->request($param);
|
||||
$this->log('应用型负载均衡监听扩展证书添加成功!');
|
||||
} else {
|
||||
$param = [
|
||||
'Action' => 'UpdateListenerAttribute',
|
||||
'ListenerId' => $config['alb_listener_id'],
|
||||
'Certificates.1.CertificateId' => $cert_id,
|
||||
];
|
||||
$client->request($param);
|
||||
$this->log('应用型负载均衡监听默认证书更新成功!');
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_nlb($cert_id, $config)
|
||||
@@ -608,33 +681,44 @@ class aliyun implements DeployInterface
|
||||
|
||||
$endpoint = 'nlb.' . $config['regionid'] . '.aliyuncs.com';
|
||||
$client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, $endpoint, '2022-04-30', $this->proxy);
|
||||
$cert_id = $cert_id . '-cn-hangzhou';
|
||||
$deploy_type = isset($config['deploy_type']) ? intval($config['deploy_type']) : 0;
|
||||
|
||||
$param = [
|
||||
'Action' => 'ListListenerCertificates',
|
||||
'MaxResults' => 50,
|
||||
'ListenerId' => $config['nlb_listener_id'],
|
||||
'CertificateType' => 'Server',
|
||||
];
|
||||
try {
|
||||
$data = $client->request($param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取监听证书列表失败:' . $e->getMessage());
|
||||
}
|
||||
foreach ($data['Certificates'] as $cert) {
|
||||
if (strpos($cert['CertificateId'], '-')) $cert['CertificateId'] = substr($cert['CertificateId'], 0, strpos($cert['CertificateId'], '-'));
|
||||
if ($cert['CertificateId'] == $cert_id) {
|
||||
$this->log('负载均衡监听证书已添加,无需重复操作');
|
||||
return;
|
||||
if ($deploy_type == 1) {
|
||||
$param = [
|
||||
'Action' => 'ListListenerCertificates',
|
||||
'MaxResults' => 50,
|
||||
'ListenerId' => $config['nlb_listener_id'],
|
||||
'CertificateType' => 'Server',
|
||||
];
|
||||
try {
|
||||
$data = $client->request($param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取监听证书列表失败:' . $e->getMessage());
|
||||
}
|
||||
foreach ($data['Certificates'] as $cert) {
|
||||
if ($cert['CertificateId'] == $cert_id) {
|
||||
$this->log('负载均衡监听扩展证书已添加,无需重复操作');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$param = [
|
||||
'Action' => 'AssociateAdditionalCertificatesWithListener',
|
||||
'ListenerId' => $config['nlb_listener_id'],
|
||||
'AdditionalCertificateIds.1' => $cert_id . '-cn-hangzhou',
|
||||
];
|
||||
$client->request($param);
|
||||
$this->log('网络型负载均衡监听证书添加成功!');
|
||||
$param = [
|
||||
'Action' => 'AssociateAdditionalCertificatesWithListener',
|
||||
'ListenerId' => $config['nlb_listener_id'],
|
||||
'AdditionalCertificateIds.1' => $cert_id,
|
||||
];
|
||||
$client->request($param);
|
||||
$this->log('网络型负载均衡监听扩展证书添加成功!');
|
||||
} else {
|
||||
$param = [
|
||||
'Action' => 'UpdateListenerAttribute',
|
||||
'ListenerId' => $config['nlb_listener_id'],
|
||||
'CertificateIds.1' => $cert_id,
|
||||
];
|
||||
$client->request($param);
|
||||
$this->log('网络型负载均衡监听默认证书更新成功!');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
|
||||
@@ -1,127 +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);
|
||||
|
||||
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'] . '更新成功!');
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,22 +29,24 @@ class aws implements DeployInterface
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if ($config['product'] == 'acm') {
|
||||
if (empty($config['acm_arn'])) throw new Exception('ACM ARN不能为空');
|
||||
$this->get_cert_id($fullchain, $privatekey, $config['acm_arn'], true);
|
||||
} else {
|
||||
$this->deploy_cloudfront($fullchain, $privatekey, $config, $info);
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_cloudfront($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (empty($config['distribution_id'])) throw new Exception('分配ID不能为空');
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
$config['cert_name'] = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
|
||||
if (isset($info['cert_id']) && isset($info['cert_name']) && $info['cert_name'] == $config['cert_name']) {
|
||||
$cert_id = $info['cert_id'];
|
||||
$this->log('证书已上传:' . $cert_id);
|
||||
} else {
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey);
|
||||
$this->log('证书上传成功:' . $cert_id);
|
||||
$info['cert_id'] = $cert_id;
|
||||
$info['cert_name'] = $config['cert_name'];
|
||||
usleep(500000);
|
||||
}
|
||||
$cert_id = isset($info['cert_id']) ? $info['cert_id'] : null;
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey, $cert_id);
|
||||
usleep(500000);
|
||||
|
||||
$client = new AWSClient($this->AccessKeyId, $this->SecretAccessKey, 'cloudfront.amazonaws.com', 'cloudfront', '2020-05-31', 'us-east-1', $this->proxy);
|
||||
try {
|
||||
@@ -54,20 +56,71 @@ class aws implements DeployInterface
|
||||
}
|
||||
|
||||
$data['ViewerCertificate']['ACMCertificateArn'] = $cert_id;
|
||||
$data['ViewerCertificate']['CloudFrontDefaultCertificate'] = false;
|
||||
$xml = new \SimpleXMLElement('<?xml version="1.0" encoding="utf-8"?><DistributionConfig></DistributionConfig>');
|
||||
$data['ViewerCertificate']['CloudFrontDefaultCertificate'] = 'false';
|
||||
unset($data['ViewerCertificate']['Certificate']);
|
||||
unset($data['ViewerCertificate']['CertificateSource']);
|
||||
|
||||
$xml = new \SimpleXMLElement('<DistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2020-05-31/"></DistributionConfig>');
|
||||
$client->requestXmlN('PUT', '/distribution/' . $config['distribution_id'] . '/config', $data, $xml);
|
||||
$this->log('分配ID: ' . $config['distribution_id'] . ' 证书部署成功!');
|
||||
}
|
||||
|
||||
private function get_cert_id($fullchain, $privatekey)
|
||||
private function get_cert_id($fullchain, $privatekey, $cert_id = null, $acm = false)
|
||||
{
|
||||
$cert = explode('-----END CERTIFICATE-----', $fullchain)[0] . '-----END CERTIFICATE-----';
|
||||
if ($acm === true && $cert_id == null) {
|
||||
throw new Exception('ACM ARN不能为空');
|
||||
}
|
||||
|
||||
$certificates = explode('-----END CERTIFICATE-----', $fullchain);
|
||||
$cert = $certificates[0] . '-----END CERTIFICATE-----';
|
||||
|
||||
$client = new AWSClient($this->AccessKeyId, $this->SecretAccessKey, 'acm.us-east-1.amazonaws.com', 'acm', '', 'us-east-1', $this->proxy);
|
||||
|
||||
if (!empty($cert_id)) {
|
||||
try {
|
||||
$data = $client->request('POST', 'CertificateManager.GetCertificate', [
|
||||
'CertificateArn' => $cert_id
|
||||
]);
|
||||
// 如果成功获取证书信息,说明证书存在,直接返回cert_id
|
||||
if (isset($data['Certificate']) && trim($data['Certificate']) == trim($cert)) {
|
||||
$this->log('证书已是最新,ACM ARN:' . $cert_id);
|
||||
return $cert_id;
|
||||
} else {
|
||||
$this->log('证书已过期或被删除,准备更新或者重新上传');
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
if ($acm === true) {
|
||||
throw new Exception('获取证书信息失败,请检查ACM ARN是否正确:' . $e->getMessage());
|
||||
}
|
||||
$this->log('证书已被删除:' . $cert_id. ',准备重新上传');
|
||||
}
|
||||
}
|
||||
|
||||
$certificateChain = '';
|
||||
if (count($certificates) > 1) {
|
||||
// 从第二个证书开始,重新拼接中间证书链
|
||||
for ($i = 1; $i < count($certificates); $i++) {
|
||||
if (trim($certificates[$i]) !== '') { // 忽略空字符串(可能由末尾分割产生)
|
||||
$certificateChain .= $certificates[$i] . '-----END CERTIFICATE-----';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$param = [
|
||||
'Certificate' => base64_encode($cert),
|
||||
'PrivateKey' => base64_encode($privatekey),
|
||||
];
|
||||
|
||||
// 如果有中间证书链,则添加到参数中
|
||||
if (!empty($certificateChain)) {
|
||||
$param['CertificateChain'] = base64_encode($certificateChain);
|
||||
}
|
||||
|
||||
// 如果是ACM,则添加ARN参数,用于更新证书
|
||||
if ($acm === true) {
|
||||
$param['CertificateArn'] = $cert_id;
|
||||
}
|
||||
|
||||
$client = new AWSClient($this->AccessKeyId, $this->SecretAccessKey, 'acm.us-east-1.amazonaws.com', 'acm', '', 'us-east-1', $this->proxy);
|
||||
try {
|
||||
$data = $client->request('POST', 'CertificateManager.ImportCertificate', $param);
|
||||
@@ -75,6 +128,11 @@ class aws implements DeployInterface
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$this->log('证书上传成功:' . $cert_id);
|
||||
|
||||
$info['cert_id'] = $cert_id;
|
||||
|
||||
return $cert_id;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,23 @@ class baidu implements DeployInterface
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (!isset($config['product']) || $config['product'] == 'cdn') {
|
||||
$this->deploy_cdn($fullchain, $privatekey, $config, $info);
|
||||
} else {
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey);
|
||||
$info['cert_id'] = $cert_id;
|
||||
if ($config['product'] == 'blb') {
|
||||
$this->deploy_blb($cert_id, $config);
|
||||
} elseif ($config['product'] == 'appblb') {
|
||||
$this->deploy_appblb($cert_id, $config);
|
||||
} else {
|
||||
throw new Exception('不支持的产品类型');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function deploy_cdn($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
@@ -36,16 +53,6 @@ class baidu implements DeployInterface
|
||||
$config['cert_name'] = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
|
||||
$client = new BaiduCloud($this->AccessKeyId, $this->SecretAccessKey, 'cdn.baidubce.com', $this->proxy);
|
||||
try {
|
||||
$data = $client->request('GET', '/v2/' . $config['domain'] . '/certificates');
|
||||
if (isset($data['certName']) && $data['certName'] == $config['cert_name']) {
|
||||
$this->log('CDN域名 ' . $config['domain'] . ' 证书已存在,无需重复部署');
|
||||
return;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->log($e->getMessage());
|
||||
}
|
||||
|
||||
$param = [
|
||||
'httpsEnable' => 'ON',
|
||||
'certificate' => [
|
||||
@@ -54,9 +61,89 @@ class baidu implements DeployInterface
|
||||
'certPrivateData' => $privatekey,
|
||||
],
|
||||
];
|
||||
$data = $client->request('PUT', '/v2/' . $config['domain'] . '/certificates', null, $param);
|
||||
$info['cert_id'] = $data['certId'];
|
||||
$this->log('CDN域名 ' . $config['domain'] . ' 证书部署成功!');
|
||||
foreach (explode(',', $config['domain']) as $domain) {
|
||||
if (empty($domain)) continue;
|
||||
try {
|
||||
$data = $client->request('GET', '/v2/' . $domain . '/certificates');
|
||||
if (isset($data['certName']) && $data['certName'] == $config['cert_name']) {
|
||||
$this->log('CDN域名 ' . $domain . ' 证书已存在,无需重复部署');
|
||||
return;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->log($e->getMessage());
|
||||
}
|
||||
|
||||
$data = $client->request('PUT', '/v2/' . $domain . '/certificates', null, $param);
|
||||
$info['cert_id'] = $data['certId'];
|
||||
$this->log('CDN域名 ' . $domain . ' 证书部署成功!');
|
||||
}
|
||||
}
|
||||
|
||||
public function deploy_blb($cert_id, $config)
|
||||
{
|
||||
if (empty($config['blb_id'])) throw new Exception('负载均衡实例ID不能为空');
|
||||
if (empty($config['blb_port'])) throw new Exception('HTTPS监听端口不能为空');
|
||||
$client = new BaiduCloud($this->AccessKeyId, $this->SecretAccessKey, 'blb.' . $config['region'] . '.baidubce.com', $this->proxy);
|
||||
$query = [
|
||||
'listenerPort' => $config['blb_port'],
|
||||
];
|
||||
$param = [
|
||||
'certIds' => [$cert_id],
|
||||
];
|
||||
$client->request('PUT', '/v1/blb/' . $config['blb_id'] . '/HTTPSlistener', $query, $param);
|
||||
$this->log('普通型BLB ' . $config['blb_id'] . ' 部署证书成功!');
|
||||
}
|
||||
|
||||
public function deploy_appblb($cert_id, $config)
|
||||
{
|
||||
if (empty($config['blb_id'])) throw new Exception('负载均衡实例ID不能为空');
|
||||
if (empty($config['blb_port'])) throw new Exception('HTTPS监听端口不能为空');
|
||||
$client = new BaiduCloud($this->AccessKeyId, $this->SecretAccessKey, 'blb.' . $config['region'] . '.baidubce.com', $this->proxy);
|
||||
$query = [
|
||||
'listenerPort' => $config['blb_port'],
|
||||
];
|
||||
$param = [
|
||||
'certIds' => [$cert_id],
|
||||
];
|
||||
$client->request('PUT', '/v1/appblb/' . $config['blb_id'] . '/HTTPSlistener', $query, $param);
|
||||
$this->log('应用型BLB ' . $config['blb_id'] . ' 部署证书成功!');
|
||||
}
|
||||
|
||||
private function get_cert_id($fullchain, $privatekey)
|
||||
{
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
|
||||
$client = new BaiduCloud($this->AccessKeyId, $this->SecretAccessKey, 'certificate.baidubce.com', $this->proxy);
|
||||
$query = [
|
||||
'certName' => $cert_name,
|
||||
];
|
||||
try {
|
||||
$data = $client->request('GET', '/v1/certificate', $query);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('查找证书失败:' . $e->getMessage());
|
||||
}
|
||||
foreach ($data['certs'] as $row) {
|
||||
if ($row['certName'] == $cert_name) {
|
||||
$this->log('证书已存在 CertId=' . $row['certId']);
|
||||
return $row['certId'];
|
||||
}
|
||||
}
|
||||
|
||||
$param = [
|
||||
'certName' => $cert_name,
|
||||
'certServerData' => $fullchain,
|
||||
'certPrivateData' => $privatekey,
|
||||
];
|
||||
try {
|
||||
$data = $client->request('POST', '/v1/certificate', null, $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
$cert_id = $data['certId'];
|
||||
$this->log('上传证书成功 CertId=' . $cert_id);
|
||||
return $cert_id;
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
|
||||
@@ -56,10 +56,10 @@ class baishan implements DeployInterface
|
||||
$headers = [];
|
||||
$body = null;
|
||||
if ($params) {
|
||||
$headers[] = 'Content-Type: application/json';
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
$body = json_encode($params);
|
||||
}
|
||||
$response = curl_client($url, $body, null, null, $headers, $this->proxy);
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['code']) && $result['code'] == 0) {
|
||||
return $result;
|
||||
|
||||
@@ -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) {
|
||||
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,19 +54,82 @@ 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;
|
||||
foreach ($sites as $site) {
|
||||
$siteName = trim($site);
|
||||
if (empty($siteName)) continue;
|
||||
try {
|
||||
$this->deploySite($siteName, $fullchain, $privatekey);
|
||||
$this->log("网站 {$siteName} 证书部署成功");
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
$errmsg = $e->getMessage();
|
||||
$this->log("网站 {$siteName} 证书部署失败:" . $errmsg);
|
||||
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} 证书部署成功");
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
$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);
|
||||
$this->log("网站 {$siteName} 证书部署成功");
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
$errmsg = $e->getMessage();
|
||||
$this->log("网站 {$siteName} 证书部署失败:" . $errmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($success == 0) {
|
||||
@@ -62,10 +139,113 @@ class btpanel implements DeployInterface
|
||||
|
||||
private function deployPanel($fullchain, $privatekey)
|
||||
{
|
||||
$path = '/config?action=SavePanelSSL';
|
||||
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 {
|
||||
$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)
|
||||
{
|
||||
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 = [
|
||||
'privateKey' => $privatekey,
|
||||
'certPem' => $fullchain,
|
||||
'domain' => $domain,
|
||||
'path' => $pfx_path,
|
||||
'password' => $password,
|
||||
];
|
||||
$response = $this->request($path, $data);
|
||||
$result = json_decode($response, true);
|
||||
@@ -78,12 +258,31 @@ class btpanel implements DeployInterface
|
||||
}
|
||||
}
|
||||
|
||||
private function deploySite($siteName, $fullchain, $privatekey)
|
||||
private function deployMailSys($domain, $fullchain, $privatekey)
|
||||
{
|
||||
$path = '/site?action=SetSSL';
|
||||
$path = '/plugin?action=a&name=mail_sys&s=set_mail_certificate_multiple';
|
||||
$data = [
|
||||
'type' => '0',
|
||||
'siteName' => $siteName,
|
||||
'domain' => $domain,
|
||||
'key' => $privatekey,
|
||||
'csr' => $fullchain,
|
||||
'act' => 'add',
|
||||
];
|
||||
$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 deployDocker($domain, $fullchain, $privatekey)
|
||||
{
|
||||
$path = '/mod/docker/com/set_ssl';
|
||||
$data = [
|
||||
'site_name' => $domain,
|
||||
'key' => $privatekey,
|
||||
'csr' => $fullchain,
|
||||
];
|
||||
@@ -110,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 = curl_client($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'];
|
||||
}
|
||||
}
|
||||
|
||||
134
app/lib/deploy/btwaf.php
Normal file
134
app/lib/deploy/btwaf.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class btwaf implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $key;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
$this->key = $config['key'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->key)) throw new Exception('请填写面板地址和接口密钥');
|
||||
|
||||
$path = '/api/user/latest_version';
|
||||
$response = $this->request($path, []);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['code']) && $result['code'] == 0) {
|
||||
return true;
|
||||
} else {
|
||||
throw new Exception(isset($result['res']) ? $result['res'] : '面板地址无法连接');
|
||||
}
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$sites = explode("\n", $config['sites']);
|
||||
$success = 0;
|
||||
$errmsg = null;
|
||||
foreach ($sites as $site) {
|
||||
$siteName = trim($site);
|
||||
if (empty($siteName)) continue;
|
||||
try {
|
||||
$this->deploySite($siteName, $fullchain, $privatekey);
|
||||
$this->log("网站 {$siteName} 证书部署成功");
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
$errmsg = $e->getMessage();
|
||||
$this->log("网站 {$siteName} 证书部署失败:" . $errmsg);
|
||||
}
|
||||
}
|
||||
if ($success == 0) {
|
||||
throw new Exception($errmsg ? $errmsg : '要部署的网站不存在');
|
||||
}
|
||||
}
|
||||
|
||||
private function deploySite($siteName, $fullchain, $privatekey)
|
||||
{
|
||||
$site_id = null;
|
||||
$listen_ssl_port = ['443'];
|
||||
$path = '/api/wafmastersite/get_site_list';
|
||||
$data = ['p' => 1, 'p_size' => 10, 'site_name' => $siteName];
|
||||
$response = $this->request($path, $data);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['code']) && $result['code'] == 0) {
|
||||
foreach ($result['res']['list'] as $site) {
|
||||
if ($site['site_name'] == $siteName) {
|
||||
$site_id = $site['site_id'];
|
||||
if (isset($site['server']['listen_ssl_port']) && !empty($site['server']['listen_ssl_port'])) {
|
||||
$listen_ssl_port = $site['server']['listen_ssl_port'];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$site_id) {
|
||||
throw new Exception("网站名称不存在");
|
||||
}
|
||||
} elseif (isset($result['res'])) {
|
||||
throw new Exception($result['res']);
|
||||
} else {
|
||||
throw new Exception($response ? $response : '返回数据解析失败');
|
||||
}
|
||||
$path = '/api/wafmastersite/modify_site';
|
||||
$data = [
|
||||
'types' => 'openCert',
|
||||
'site_id' => $site_id,
|
||||
'server' => [
|
||||
'listen_ssl_port' => $listen_ssl_port,
|
||||
'ssl' => [
|
||||
'is_ssl' => 1,
|
||||
'private_key' => $privatekey,
|
||||
'full_chain' => $fullchain,
|
||||
],
|
||||
]
|
||||
];
|
||||
$response = $this->request($path, $data);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['code']) && $result['code'] == 0) {
|
||||
return true;
|
||||
} elseif (isset($result['res'])) {
|
||||
throw new Exception($result['res']);
|
||||
} else {
|
||||
throw new Exception($response ? $response : '返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
private function request($path, $params)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
|
||||
$now_time = time();
|
||||
$headers = [
|
||||
'waf_request_time' => $now_time,
|
||||
'waf_request_token' => md5($now_time . md5($this->key)),
|
||||
'Content-Type' => 'application/json',
|
||||
];
|
||||
$post = $params ? json_encode($params) : null;
|
||||
$response = http_request($url, $post, null, null, $headers, $this->proxy, 'POST');
|
||||
return $response['body'];
|
||||
}
|
||||
}
|
||||
@@ -37,13 +37,13 @@ class cachefly implements DeployInterface
|
||||
private function request($path, $params = null, $method = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = ['x-cf-authorization: Bearer ' . $this->apikey];
|
||||
$headers = ['x-cf-authorization' => 'Bearer ' . $this->apikey];
|
||||
$body = null;
|
||||
if ($params) {
|
||||
$headers[] = 'Content-Type: application/json';
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
$body = json_encode($params);
|
||||
}
|
||||
$response = curl_client($url, $body, null, null, $headers, $this->proxy, $method);
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy, $method);
|
||||
$result = json_decode($response['body'], true);
|
||||
if ($response['code'] >= 200 && $response['code'] < 300) {
|
||||
return $result;
|
||||
|
||||
@@ -11,6 +11,9 @@ class cdnfly implements DeployInterface
|
||||
private $url;
|
||||
private $api_key;
|
||||
private $api_secret;
|
||||
private $auth = 0;
|
||||
private $username;
|
||||
private $password;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
@@ -18,13 +21,23 @@ class cdnfly implements DeployInterface
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
$this->api_key = $config['api_key'];
|
||||
$this->api_secret = $config['api_secret'];
|
||||
$this->auth = isset($config['auth']) ? $config['auth'] : 0;
|
||||
if ($this->auth == 1) {
|
||||
$this->username = $config['username'];
|
||||
$this->password = $config['password'];
|
||||
}
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->api_key) || empty($this->api_secret)) throw new Exception('必填参数不能为空');
|
||||
$this->request('/v1/user');
|
||||
if ($this->auth == 1) {
|
||||
if (empty($this->url) || empty($this->username) || empty($this->password)) throw new Exception('必填参数不能为空');
|
||||
$this->login();
|
||||
} else {
|
||||
if (empty($this->url) || empty($this->api_key) || empty($this->api_secret)) throw new Exception('必填参数不能为空');
|
||||
$this->request('/v1/user');
|
||||
}
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
@@ -37,20 +50,56 @@ class cdnfly implements DeployInterface
|
||||
'cert' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
];
|
||||
$this->request('/v1/certs/' . $id, $params, 'PUT');
|
||||
if ($this->auth == 1) {
|
||||
$access_token = $this->login();
|
||||
$url = $this->url . '/v1/certs/' . $id;
|
||||
$body = json_encode($params);
|
||||
$headers = [
|
||||
'Access-Token' => $access_token,
|
||||
];
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy, 'PUT');
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['code']) && $result['code'] == 0) {
|
||||
} elseif (isset($result['msg'])) {
|
||||
throw new Exception('证书ID:' . $id . '更新失败,' . $result['msg']);
|
||||
} else {
|
||||
throw new Exception('证书ID:' . $id . '更新失败,返回数据解析失败');
|
||||
}
|
||||
} else {
|
||||
$this->request('/v1/certs/' . $id, $params, 'PUT');
|
||||
}
|
||||
$this->log("证书ID:{$id}更新成功!");
|
||||
}
|
||||
|
||||
public function login()
|
||||
{
|
||||
$url = $this->url . '/v1/login';
|
||||
$params = [
|
||||
'account' => $this->username,
|
||||
'password' => $this->password,
|
||||
];
|
||||
$body = json_encode($params);
|
||||
$response = http_request($url, $body, null, null, null, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['code']) && $result['code'] == 0) {
|
||||
return $result['data']['access_token'];
|
||||
} elseif (isset($result['msg'])) {
|
||||
throw new Exception($result['msg']);
|
||||
} else {
|
||||
throw new Exception('登录失败,返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
private function request($path, $params = null, $method = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = ['api-key: ' . $this->api_key, 'api-secret: ' . $this->api_secret];
|
||||
$headers = ['api-key' => $this->api_key, 'api-secret' => $this->api_secret];
|
||||
$body = null;
|
||||
if ($params) {
|
||||
$headers[] = 'Content-Type: application/json';
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
$body = json_encode($params);
|
||||
}
|
||||
$response = curl_client($url, $body, null, null, $headers, $this->proxy, $method);
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy, $method);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['code']) && $result['code'] == 0) {
|
||||
return isset($result['data']) ? $result['data'] : null;
|
||||
|
||||
184
app/lib/deploy/ctyun.php
Normal file
184
app/lib/deploy/ctyun.php
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\client\Ctyun as CtyunClient;
|
||||
use Exception;
|
||||
|
||||
class ctyun implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $AccessKeyId;
|
||||
private $SecretAccessKey;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKeyId = $config['AccessKeyId'];
|
||||
$this->SecretAccessKey = $config['SecretAccessKey'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->AccessKeyId) || empty($this->SecretAccessKey)) throw new Exception('必填参数不能为空');
|
||||
$client = new CtyunClient($this->AccessKeyId, $this->SecretAccessKey, 'ctcdn-global.ctapi.ctyun.cn', $this->proxy);
|
||||
$client->request('GET', '/v1/cert/query-cert-list');
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
$config['cert_name'] = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
if ($config['product'] == 'cdn') {
|
||||
$this->deploy_cdn($fullchain, $privatekey, $config);
|
||||
} elseif ($config['product'] == 'icdn') {
|
||||
$this->deploy_icdn($fullchain, $privatekey, $config);
|
||||
} elseif ($config['product'] == 'accessone') {
|
||||
$this->deploy_accessone($fullchain, $privatekey, $config);
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_cdn($fullchain, $privatekey, $config)
|
||||
{
|
||||
$client = new CtyunClient($this->AccessKeyId, $this->SecretAccessKey, 'ctcdn-global.ctapi.ctyun.cn', $this->proxy);
|
||||
$param = [
|
||||
'name' => $config['cert_name'],
|
||||
'key' => $privatekey,
|
||||
'certs' => $fullchain,
|
||||
];
|
||||
try {
|
||||
$client->request('POST', '/v1/cert/creat-cert', null, $param);
|
||||
} catch (Exception $e) {
|
||||
if (strpos($e->getMessage(), '已存在重名的证书') !== false) {
|
||||
$this->log('已存在重名的证书 cert_name=' . $config['cert_name']);
|
||||
} else {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
$this->log('上传证书成功 cert_name=' . $config['cert_name']);
|
||||
|
||||
$param = [
|
||||
'domain' => $config['domain'],
|
||||
'https_status' => 'on',
|
||||
'cert_name' => $config['cert_name'],
|
||||
];
|
||||
try {
|
||||
$client->request('POST', '/v1/domain/update-domain', null, $param);
|
||||
} catch (Exception $e) {
|
||||
if (strpos($e->getMessage(), '请求已提交,请勿重复操作!') === false) {
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$this->log('CDN域名 ' . $config['domain'] . ' 部署证书成功!');
|
||||
}
|
||||
|
||||
private function deploy_icdn($fullchain, $privatekey, $config)
|
||||
{
|
||||
$client = new CtyunClient($this->AccessKeyId, $this->SecretAccessKey, 'icdn-global.ctapi.ctyun.cn', $this->proxy);
|
||||
$param = [
|
||||
'name' => $config['cert_name'],
|
||||
'key' => $privatekey,
|
||||
'certs' => $fullchain,
|
||||
];
|
||||
try {
|
||||
$client->request('POST', '/v1/cert/creat-cert', null, $param);
|
||||
} catch (Exception $e) {
|
||||
if (strpos($e->getMessage(), '已存在重名的证书') !== false) {
|
||||
$this->log('已存在重名的证书 cert_name=' . $config['cert_name']);
|
||||
} else {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
$this->log('上传证书成功 cert_name=' . $config['cert_name']);
|
||||
|
||||
$param = [
|
||||
'domain' => $config['domain'],
|
||||
'https_status' => 'on',
|
||||
'cert_name' => $config['cert_name'],
|
||||
];
|
||||
try {
|
||||
$client->request('POST', '/v1/domain/update-domain', null, $param);
|
||||
} catch (Exception $e) {
|
||||
if (strpos($e->getMessage(), '请求已提交,请勿重复操作!') === false) {
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$this->log('CDN域名 ' . $config['domain'] . ' 部署证书成功!');
|
||||
}
|
||||
|
||||
private function deploy_accessone($fullchain, $privatekey, $config)
|
||||
{
|
||||
$client = new CtyunClient($this->AccessKeyId, $this->SecretAccessKey, 'accessone-global.ctapi.ctyun.cn', $this->proxy);
|
||||
$param = [
|
||||
'name' => $config['cert_name'],
|
||||
'key' => $privatekey,
|
||||
'certs' => $fullchain,
|
||||
];
|
||||
try {
|
||||
$client->request('POST', '/ctapi/v1/accessone/cert/create', null, $param);
|
||||
} catch (Exception $e) {
|
||||
if (strpos($e->getMessage(), '已存在重名的证书') !== false) {
|
||||
$this->log('已存在重名的证书 cert_name=' . $config['cert_name']);
|
||||
} else {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
$this->log('上传证书成功 cert_name=' . $config['cert_name']);
|
||||
|
||||
$param = [
|
||||
'domain' => $config['domain'],
|
||||
'product_code' => '020',
|
||||
];
|
||||
try {
|
||||
$result = $client->request('POST', '/ctapi/v1/accessone/domain/config', null, $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('查询域名配置失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
if ($result['https_status'] == 'on' && $result['cert_name'] == $config['cert_name']) {
|
||||
$this->log('边缘安全加速域名 ' . $config['domain'] . ' 证书已部署,无需重复操作!');
|
||||
return;
|
||||
}
|
||||
|
||||
$result['https_status'] = 'on';
|
||||
$result['cert_name'] = $config['cert_name'];
|
||||
$exclude_keys = ['status', 'area_scope', 'cname', 'insert_date', 'status_date', 'record_status', 'record_num', 'customer_name', 'outlink_replace_filter', 'website_ipv6_access_mark', 'websocket_speed', 'dynamic_config', 'dynamic_ability'];
|
||||
foreach ($result as $key => $value) {
|
||||
if (in_array($key, $exclude_keys) || is_array($value) && empty($value)) {
|
||||
unset($result[$key]);
|
||||
}
|
||||
}
|
||||
if (isset($result['origin'])) {
|
||||
foreach ($result['origin'] as &$origin) {
|
||||
$origin['weight'] = strval($origin['weight']);
|
||||
}
|
||||
}
|
||||
try {
|
||||
$client->request('POST', '/ctapi/v1/accessone/domain/modify_config', null, $result);
|
||||
} catch (Exception $e) {
|
||||
if (strpos($e->getMessage(), '请求已提交,请勿重复操作!') === false) {
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$this->log('边缘安全加速域名 ' . $config['domain'] . ' 部署证书成功!');
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,8 +28,8 @@ class doge implements DeployInterface
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$domain = $config['domain'];
|
||||
if (empty($domain)) throw new Exception('绑定的域名不能为空');
|
||||
$domains = $config['domain'];
|
||||
if (empty($domains)) throw new Exception('绑定的域名不能为空');
|
||||
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
@@ -37,13 +37,15 @@ class doge implements DeployInterface
|
||||
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey, $cert_name);
|
||||
|
||||
$param = [
|
||||
'id' => $cert_id,
|
||||
'domain' => $domain,
|
||||
];
|
||||
$this->request('/cdn/cert/bind.json', $param);
|
||||
|
||||
$this->log('CDN域名 ' . $domain . ' 绑定证书成功!');
|
||||
foreach (explode(',', $domains) as $domain) {
|
||||
if (empty($domain)) continue;
|
||||
$param = [
|
||||
'id' => $cert_id,
|
||||
'domain' => $domain,
|
||||
];
|
||||
$this->request('/cdn/cert/bind.json', $param);
|
||||
$this->log('CDN域名 ' . $domain . ' 绑定证书成功!');
|
||||
}
|
||||
$info['cert_id'] = $cert_id;
|
||||
}
|
||||
|
||||
@@ -94,13 +96,13 @@ class doge implements DeployInterface
|
||||
$signStr = $path . "\n" . $body;
|
||||
$sign = hash_hmac('sha1', $signStr, $this->SecretKey);
|
||||
$authorization = "TOKEN " . $this->AccessKey . ":" . $sign;
|
||||
$headers = ['Authorization: ' . $authorization];
|
||||
if($body && $json) $headers[] = 'Content-Type: application/json';
|
||||
$headers = ['Authorization' => $authorization];
|
||||
if($body && $json) $headers['Content-Type'] = 'application/json';
|
||||
$url = 'https://api.dogecloud.com'.$path;
|
||||
$response = curl_client($url, $body, null, null, $headers, $this->proxy);
|
||||
$response = http_request($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'] : true;
|
||||
return $result['data'] ?? true;
|
||||
}elseif(isset($result['msg'])){
|
||||
throw new Exception($result['msg']);
|
||||
}else{
|
||||
|
||||
@@ -42,13 +42,13 @@ class gcore implements DeployInterface
|
||||
private function request($path, $params = null, $method = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = ['Authorization: APIKey ' . $this->apikey];
|
||||
$headers = ['Authorization' => 'APIKey ' . $this->apikey];
|
||||
$body = null;
|
||||
if ($params) {
|
||||
$headers[] = 'Content-Type: application/json';
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
$body = json_encode($params);
|
||||
}
|
||||
$response = curl_client($url, $body, null, null, $headers, $this->proxy, $method);
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy, $method);
|
||||
$result = json_decode($response['body'], true);
|
||||
if ($response['code'] >= 200 && $response['code'] < 300) {
|
||||
return $result;
|
||||
|
||||
@@ -50,20 +50,39 @@ class goedge implements DeployInterface
|
||||
throw new Exception('获取证书列表失败:' . $e->getMessage());
|
||||
}
|
||||
$list = json_decode(base64_decode($data['sslCertsJSON']), true);
|
||||
if (!$list || empty($list)) {
|
||||
if ($list === false) {
|
||||
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'];
|
||||
|
||||
foreach ($list as $row) {
|
||||
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 = [
|
||||
'sslCertId' => $row['id'],
|
||||
'isOn' => true,
|
||||
'name' => $row['name'],
|
||||
'description' => $row['description'],
|
||||
'serverName' => $row['serverName'],
|
||||
'name' => $cert_name,
|
||||
'description' => $cert_name,
|
||||
'serverName' => $certInfo['subject']['CN'],
|
||||
'isCA' => false,
|
||||
'certData' => base64_encode($fullchain),
|
||||
'keyData' => base64_encode($privatekey),
|
||||
@@ -72,8 +91,8 @@ class goedge implements DeployInterface
|
||||
'dnsNames' => $domains,
|
||||
'commonNames' => [$certInfo['issuer']['CN']],
|
||||
];
|
||||
$this->request('/SSLCertService/updateSSLCert', $params);
|
||||
$this->log('证书ID:' . $row['id'] . '更新成功!');
|
||||
$result = $this->request('/SSLCertService/createSSLCert', $params);
|
||||
$this->log('证书ID:' . $result['sslCertId'] . '添加成功!');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,19 +119,19 @@ class goedge implements DeployInterface
|
||||
$body = null;
|
||||
if ($this->accessToken) {
|
||||
if ($this->systype == '1') {
|
||||
$headers[] = 'X-Cloud-Access-Token: ' . $this->accessToken;
|
||||
$headers['X-Cloud-Access-Token'] = $this->accessToken;
|
||||
} else {
|
||||
$headers[] = 'X-Edge-Access-Token: ' . $this->accessToken;
|
||||
$headers['X-Edge-Access-Token'] = $this->accessToken;
|
||||
}
|
||||
}
|
||||
if ($params) {
|
||||
$headers[] = 'Content-Type: application/json';
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
$body = json_encode($params);
|
||||
}
|
||||
$response = curl_client($url, $body, null, null, $headers, $this->proxy);
|
||||
$response = http_request($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;
|
||||
return $result['data'] ?? null;
|
||||
} elseif (isset($result['message'])) {
|
||||
throw new Exception($result['message']);
|
||||
} else {
|
||||
|
||||
@@ -58,8 +58,11 @@ class huawei implements DeployInterface
|
||||
],
|
||||
],
|
||||
];
|
||||
$client->request('PUT', '/v1.1/cdn/configuration/domains/' . $config['domain'] . '/configs', null, $param);
|
||||
$this->log('CDN域名 ' . $config['domain'] . ' 部署证书成功!');
|
||||
foreach (explode(',', $config['domain']) as $domain) {
|
||||
if (empty($domain)) continue;
|
||||
$client->request('PUT', '/v1.1/cdn/configuration/domains/' . $domain . '/configs', null, $param);
|
||||
$this->log('CDN域名 ' . $domain . ' 部署证书成功!');
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_elb($fullchain, $privatekey, $config)
|
||||
|
||||
@@ -23,18 +23,33 @@ class huoshan implements DeployInterface
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->AccessKeyId) || empty($this->SecretAccessKey)) throw new Exception('必填参数不能为空');
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'cdn.volcengineapi.com', 'cdn', '2021-03-01', 'cn-north-1', $this->proxy);
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'open.volcengineapi.com', 'cdn', '2021-03-01', 'cn-north-1', $this->proxy);
|
||||
$client->request('POST', 'ListCertInfo', ['Source' => 'volc_cert_center']);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey);
|
||||
if (!$cert_id) throw new Exception('获取证书ID失败');
|
||||
$info['cert_id'] = $cert_id;
|
||||
$this->deploy_cdn($cert_id, $config);
|
||||
if ($config['product'] == 'live') {
|
||||
$this->deploy_live($fullchain, $privatekey, $config);
|
||||
} else {
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey);
|
||||
if (!$cert_id) throw new Exception('获取证书ID失败');
|
||||
$info['cert_id'] = $cert_id;
|
||||
if (!isset($config['product']) || $config['product'] == 'cdn') {
|
||||
$this->deploy_cdn($cert_id, $config);
|
||||
} elseif ($config['product'] == 'dcdn') {
|
||||
$this->deploy_dcdn($cert_id, $config);
|
||||
} elseif ($config['product'] == 'tos') {
|
||||
$this->deploy_tos($cert_id, $config);
|
||||
} elseif ($config['product'] == 'imagex') {
|
||||
$this->deploy_imagex($cert_id, $config);
|
||||
} elseif ($config['product'] == 'clb') {
|
||||
$this->deploy_clb($cert_id, $config);
|
||||
} elseif ($config['product'] == 'alb') {
|
||||
$this->deploy_alb($cert_id, $config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_cdn($cert_id, $config)
|
||||
@@ -51,37 +66,152 @@ class huoshan implements DeployInterface
|
||||
if ($row['Status'] == 'success') {
|
||||
$this->log('CDN域名 ' . $row['Domain'] . ' 部署证书成功!');
|
||||
} else {
|
||||
$this->log('CDN域名 ' . $row['Domain'] . ' 部署证书失败:' . isset($row['ErrorMsg']) ? $row['ErrorMsg'] : '');
|
||||
$this->log('CDN域名 ' . $row['Domain'] . ' 部署证书失败:' . (isset($row['ErrorMsg']) ? $row['ErrorMsg'] : ''));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_dcdn($cert_id, $config)
|
||||
{
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'open.volcengineapi.com', 'dcdn', '2021-04-01', 'cn-north-1', $this->proxy);
|
||||
$param = [
|
||||
'CertId' => $cert_id,
|
||||
'DomainNames' => explode(',', $config['domain']),
|
||||
];
|
||||
$client->request('POST', 'CreateCertBind', $param);
|
||||
$this->log('DCDN域名 ' . $config['domain'] . ' 部署证书成功!');
|
||||
}
|
||||
|
||||
private function deploy_tos($cert_id, $config)
|
||||
{
|
||||
if (empty($config['bucket_domain'])) throw new Exception('Bucket域名不能为空');
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, $config['bucket_domain'], 'tos', '2021-04-01', 'cn-beijing', $this->proxy);
|
||||
foreach (explode(',', $config['domain']) as $domain) {
|
||||
if (empty($domain)) continue;
|
||||
$param = [
|
||||
'CustomDomainRule' => [
|
||||
'Domain' => $domain,
|
||||
'CertId' => $cert_id,
|
||||
]
|
||||
];
|
||||
$query = ['customdomain' => ''];
|
||||
$client->tos_request('PUT', $param, $query);
|
||||
$this->log('对象存储域名 ' . $config['domain'] . ' 部署证书成功!');
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_live($fullchain, $privatekey, $config)
|
||||
{
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'live.volcengineapi.com', 'live', '2023-01-01', 'cn-north-1', $this->proxy);
|
||||
$param = [
|
||||
'CertName' => $cert_name,
|
||||
'Rsa' => [
|
||||
'Pubkey' => $fullchain,
|
||||
'Prikey' => $privatekey,
|
||||
],
|
||||
'UseWay' => 'https',
|
||||
];
|
||||
$result = $client->request('POST', 'CreateCert', $param);
|
||||
$this->log('上传证书成功 ChainID=' . $result['ChainID']);
|
||||
|
||||
foreach (explode(',', $config['domain']) as $domain) {
|
||||
if (empty($domain)) continue;
|
||||
$param = [
|
||||
'ChainID' => $result['ChainID'],
|
||||
'Domain' => $domain,
|
||||
'HTTPS' => true,
|
||||
'HTTP2' => true,
|
||||
];
|
||||
$client->request('POST', 'BindCert', $param);
|
||||
$this->log('视频直播域名 ' . $domain . ' 部署证书成功!');
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_imagex($cert_id, $config)
|
||||
{
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'imagex.volcengineapi.com', 'imagex', '2018-08-01', 'cn-north-1', $this->proxy);
|
||||
foreach (explode(',', $config['domain']) as $domain) {
|
||||
if (empty($domain)) continue;
|
||||
$param = [
|
||||
[
|
||||
'domain' => $domain,
|
||||
'cert_id' => $cert_id,
|
||||
]
|
||||
];
|
||||
$result = $client->request('POST', 'UpdateImageBatchDomainCert', $param);
|
||||
if (isset($result['SuccessDomains']) && count($result['SuccessDomains']) > 0) {
|
||||
$this->log('veImageX域名 ' . $domain . ' 部署证书成功!');
|
||||
} elseif (isset($result['FailedDomains']) && count($result['FailedDomains']) > 0) {
|
||||
$errmsg = $result['FailedDomains'][0]['ErrMsg'];
|
||||
$this->log('veImageX域名 ' . $domain . ' 部署证书失败:' . $errmsg);
|
||||
} else {
|
||||
$this->log('veImageX域名 ' . $domain . ' 部署证书失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_clb($cert_id, $config)
|
||||
{
|
||||
if (empty($config['listener_id'])) throw new Exception('监听器ID不能为空');
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'open.volcengineapi.com', 'clb', '2020-04-01', 'cn-beijing', $this->proxy);
|
||||
$param = [
|
||||
'ListenerId' => $config['listener_id'],
|
||||
'CertificateSource' => 'cert_center',
|
||||
'CertCenterCertificateId' => $cert_id,
|
||||
];
|
||||
$client->request('GET', 'ModifyListenerAttributes', $param);
|
||||
$this->log('CLB监听器 ' . $config['listener_id'] . ' 部署证书成功!');
|
||||
}
|
||||
|
||||
private function deploy_alb($cert_id, $config)
|
||||
{
|
||||
if (empty($config['listener_id'])) throw new Exception('监听器ID不能为空');
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'open.volcengineapi.com', 'alb', '2020-04-01', 'cn-beijing', $this->proxy);
|
||||
$param = [
|
||||
'ListenerId' => $config['listener_id'],
|
||||
'CertificateSource' => 'cert_center',
|
||||
'CertCenterCertificateId' => $cert_id,
|
||||
];
|
||||
$client->request('GET', 'ModifyListenerAttributes', $param);
|
||||
$this->log('ALB监听器 ' . $config['listener_id'] . ' 部署证书成功!');
|
||||
}
|
||||
|
||||
private function get_cert_id($fullchain, $privatekey)
|
||||
{
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'cdn.volcengineapi.com', 'cdn', '2021-03-01', 'cn-north-1', $this->proxy);
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'open.volcengineapi.com', 'certificate_service', '2024-10-01', 'cn-beijing', $this->proxy);
|
||||
$param = [
|
||||
'Source' => 'volc_cert_center',
|
||||
'Certificate' => $fullchain,
|
||||
'PrivateKey' => $privatekey,
|
||||
'Desc' => $cert_name,
|
||||
'Tag' => $cert_name,
|
||||
'Repeatable' => false,
|
||||
'CertificateInfo' => [
|
||||
'CertificateChain' => $fullchain,
|
||||
'PrivateKey' => $privatekey,
|
||||
],
|
||||
];
|
||||
try {
|
||||
$data = $client->request('POST', 'AddCertificate', $param);
|
||||
$data = $client->request('POST', 'ImportCertificate', $param);
|
||||
} catch (Exception $e) {
|
||||
if (strpos($e->getMessage(), '证书已存在,ID为') !== false) {
|
||||
$cert_id = trim(getSubstr($e->getMessage(), '证书已存在,ID为', '。'));
|
||||
$this->log('证书已存在 CertId=' . $cert_id);
|
||||
return $cert_id;
|
||||
}
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
$this->log('上传证书成功 CertId=' . $data['CertId']);
|
||||
return $data['CertId'];
|
||||
if (!empty($data['InstanceId'])) {
|
||||
$cert_id = $data['InstanceId'];
|
||||
} else {
|
||||
$cert_id = $data['RepeatId'];
|
||||
}
|
||||
$this->log('上传证书成功 CertId=' . $cert_id);
|
||||
return $cert_id;
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
|
||||
@@ -71,7 +71,7 @@ class kangle implements DeployInterface
|
||||
'certificate' => $fullchain,
|
||||
'certificate_key' => $privatekey,
|
||||
];
|
||||
$response = curl_client($this->url . $path, http_build_query($post), null, $this->cookie, null, $this->proxy);
|
||||
$response = http_request($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)) {
|
||||
@@ -90,7 +90,7 @@ class kangle implements DeployInterface
|
||||
'certificate' => $fullchain,
|
||||
'certificate_key' => $privatekey,
|
||||
];
|
||||
$response = curl_client($this->url . $path, http_build_query($post), null, $this->cookie, null, $this->proxy);
|
||||
$response = http_request($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)) {
|
||||
@@ -114,11 +114,11 @@ class kangle implements DeployInterface
|
||||
private function loginBySkey()
|
||||
{
|
||||
$url = $this->url . '/vhost/index.php?c=sso&a=hello&url=' . urlencode($this->url . '/index.php?');
|
||||
$response = curl_client($url, null, null, null, null, $this->proxy);
|
||||
$response = http_request($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) {
|
||||
if (isset($response['headers']['Set-Cookie'])) {
|
||||
foreach ($response['headers']['Set-Cookie'] as $val) {
|
||||
$arr = explode('=', $val);
|
||||
if ($arr[1] == '' || $arr[1] == 'deleted') continue;
|
||||
$cookie .= $val . '; ';
|
||||
@@ -147,7 +147,7 @@ class kangle implements DeployInterface
|
||||
{
|
||||
$s = md5($sess_key . $this->username . $sess_key . $this->skey);
|
||||
$url = $this->url . '/vhost/index.php?c=sso&a=login&name=' . $this->username . '&r=' . $sess_key . '&s=' . $s;
|
||||
$response = curl_client($url, null, null, $cookie, null, $this->proxy);
|
||||
$response = http_request($url, null, null, $cookie, null, $this->proxy);
|
||||
if ($response['code'] == 302) {
|
||||
return true;
|
||||
} elseif (strlen($response['body']) > 3 && strlen($response['body']) < 50) {
|
||||
@@ -165,11 +165,11 @@ class kangle implements DeployInterface
|
||||
'username' => $this->username,
|
||||
'passwd' => $this->password,
|
||||
];
|
||||
$response = curl_client($url, http_build_query($post), $referer, null, null, $this->proxy);
|
||||
$response = http_request($url, http_build_query($post), $referer, null, null, $this->proxy);
|
||||
if ($response['code'] == 302) {
|
||||
$cookie = '';
|
||||
if (preg_match_all('/Set-Cookie: (.*);/iU', $response['header'], $matchs)) {
|
||||
foreach ($matchs[1] as $val) {
|
||||
if (isset($response['headers']['Set-Cookie'])) {
|
||||
foreach ($response['headers']['Set-Cookie'] as $val) {
|
||||
$arr = explode('=', $val);
|
||||
if ($arr[1] == '' || $arr[1] == 'deleted') continue;
|
||||
$cookie .= $val . '; ';
|
||||
@@ -191,7 +191,7 @@ class kangle implements DeployInterface
|
||||
private function getMain()
|
||||
{
|
||||
$path = '/vhost/';
|
||||
curl_client($this->url . $path, null, null, $this->cookie, null, $this->proxy);
|
||||
http_request($this->url . $path, null, null, $this->cookie, null, $this->proxy);
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
|
||||
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 = http_request($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 = http_request($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 = http_request($url, null, null, null, null, $this->proxy);
|
||||
if ($response['code'] == 302 && !empty($response['redirect_url'])) {
|
||||
$cookie = '';
|
||||
if (isset($response['headers']['Set-Cookie'])) {
|
||||
foreach ($response['headers']['Set-Cookie'] 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 = http_request($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 = http_request($url, null, null, $this->cookie, null, $this->proxy);
|
||||
if ($response['code'] == 302) {
|
||||
http_request($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);
|
||||
}
|
||||
}
|
||||
}
|
||||
94
app/lib/deploy/kuocai.php
Normal file
94
app/lib/deploy/kuocai.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class kuocai implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $username;
|
||||
private $password;
|
||||
private $proxy;
|
||||
private $token = null;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->username = $config['username'];
|
||||
$this->password = $config['password'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->username) || empty($this->password)) {
|
||||
throw new Exception('请填写控制台账号和密码');
|
||||
}
|
||||
$this->request('/login/loginUser', [
|
||||
'userAccount' => $this->username,
|
||||
'userPwd' => $this->password,
|
||||
'remember' => 'true'
|
||||
]);
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$id = $config['id'];
|
||||
if (empty($id)) {
|
||||
throw new Exception('域名ID不能为空');
|
||||
}
|
||||
$this->token = $this->request('/login/loginUser', [
|
||||
'userAccount' => $this->username,
|
||||
'userPwd' => $this->password,
|
||||
'remember' => 'true'
|
||||
]);
|
||||
$this->request('/CdnDomainHttps/httpsConfiguration', [
|
||||
'doMainId' => $id,
|
||||
'https' => [
|
||||
'certificate_name' => uniqid('cert_'),
|
||||
'certificate_source' => '0',
|
||||
'certificate_value' => $fullchain,
|
||||
'https_status' => 'on',
|
||||
'private_key' => $privatekey,
|
||||
]
|
||||
], true);
|
||||
$this->log("域名ID:{$id}更新成功!");
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
private function request($path, $params = null, $json = false)
|
||||
{
|
||||
$url = 'https://kuocai.cn' . $path;
|
||||
$body = $json ? json_encode($params) : $params;
|
||||
$headers = [];
|
||||
if ($json) $headers['Content-Type'] = 'application/json';
|
||||
$response = http_request(
|
||||
$url,
|
||||
$body,
|
||||
null,
|
||||
$this->token ? "kuocai_cdn_token={$this->token}" : null,
|
||||
$headers,
|
||||
$this->proxy
|
||||
);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['code']) && $result['code'] == 'SUCCESS') {
|
||||
return isset($result['data']) ? $result['data'] : null;
|
||||
} elseif (isset($result['message'])) {
|
||||
throw new Exception($result['message']);
|
||||
} else {
|
||||
throw new Exception('请求失败(httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -59,6 +72,7 @@ class lecdn implements DeployInterface
|
||||
$path = '/prod-api/login';
|
||||
$params = [
|
||||
'email' => $this->email,
|
||||
'username' => $this->email,
|
||||
'password' => $this->password,
|
||||
];
|
||||
$result = $this->request($path, $params);
|
||||
@@ -75,16 +89,18 @@ class lecdn implements DeployInterface
|
||||
$headers = [];
|
||||
$body = null;
|
||||
if ($this->accessToken) {
|
||||
$headers[] = 'Authorization: Bearer ' . $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';
|
||||
$headers['Content-Type'] = 'application/json;charset=UTF-8';
|
||||
$body = json_encode($params);
|
||||
}
|
||||
$response = curl_client($url, $body, null, null, $headers, $this->proxy, $method);
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy, $method);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['code']) && $result['code'] == 200) {
|
||||
return isset($result['data']) ? $result['data'] : null;
|
||||
return $result['data'] ?? null;
|
||||
} elseif (isset($result['message'])) {
|
||||
throw new Exception($result['message']);
|
||||
} else {
|
||||
|
||||
114
app/lib/deploy/lucky.php
Normal file
114
app/lib/deploy/lucky.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class lucky implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $opentoken;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/') . (!empty($config['path']) ? $config['path'] : '');
|
||||
$this->opentoken = $config['opentoken'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->opentoken)) throw new Exception('请填写面板地址和OpenToken');
|
||||
$this->request("/api/modules/list");
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$domains = $config['domainList'];
|
||||
if (empty($domains)) throw new Exception('没有设置要部署的域名');
|
||||
|
||||
try {
|
||||
$data = $this->request("/api/ssl");
|
||||
$this->log('获取证书列表成功');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取证书列表失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$success = 0;
|
||||
$errmsg = null;
|
||||
if (!empty($data['list'])) {
|
||||
foreach ($data['list'] as $row) {
|
||||
if (empty($row['CertsInfo']['Domains'])) continue;
|
||||
$cert_domains = $row['CertsInfo']['Domains'];
|
||||
$flag = false;
|
||||
foreach ($cert_domains as $domain) {
|
||||
if (in_array($domain, $domains)) {
|
||||
$flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($flag) {
|
||||
$params = [
|
||||
'Key' => $row['Key'],
|
||||
'CertBase64' => base64_encode($fullchain),
|
||||
'KeyBase64' => base64_encode($privatekey),
|
||||
'AddFrom' => 'file',
|
||||
'Enable' => true,
|
||||
'MappingToPath' => false,
|
||||
'Remark' => $row['Remark'] ?: '',
|
||||
'AllSyncClient' => false,
|
||||
];
|
||||
try {
|
||||
$this->request('/api/ssl', $params, 'PUT');
|
||||
$this->log("证书ID:{$row['Key']}更新成功!");
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
$errmsg = $e->getMessage();
|
||||
$this->log("证书ID:{$row['Key']}更新失败:" . $errmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($success == 0) {
|
||||
throw new Exception($errmsg ? $errmsg : '没有要更新的证书');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
private function request($path, $params = null, $method = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
|
||||
$headers = [
|
||||
'openToken' => $this->opentoken,
|
||||
];
|
||||
$body = null;
|
||||
if ($params) {
|
||||
$body = json_encode($params);
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy, $method);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['ret']) && $result['ret'] == 0) {
|
||||
return $result;
|
||||
} elseif (isset($result['msg'])) {
|
||||
throw new Exception($result['msg']);
|
||||
} else {
|
||||
throw new Exception('请求失败(httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,10 +118,10 @@ class mwpanel implements DeployInterface
|
||||
$url = $this->url . $path;
|
||||
|
||||
$headers = [
|
||||
'app-id: '.$this->appid,
|
||||
'app-secret: '.$this->appsecret,
|
||||
'app-id' => $this->appid,
|
||||
'app-secret' => $this->appsecret,
|
||||
];
|
||||
$response = curl_client($url, $params ? http_build_query($params) : null, null, null, $headers, $this->proxy);
|
||||
$response = http_request($url, $params ? http_build_query($params) : null, null, null, $headers, $this->proxy);
|
||||
return $response['body'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class opanel implements DeployInterface
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
$this->url = rtrim($config['url'], '/') . '/api/' . (isset($config['version']) ? $config['version'] : 'v1');
|
||||
$this->key = $config['key'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
@@ -22,7 +22,7 @@ class opanel implements DeployInterface
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->key)) throw new Exception('请填写面板地址和接口密钥');
|
||||
$this->request('/api/v1/settings/search');
|
||||
$this->request("/settings/search");
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
@@ -30,9 +30,9 @@ class opanel implements DeployInterface
|
||||
$domains = $config['domainList'];
|
||||
if (empty($domains)) throw new Exception('没有设置要部署的域名');
|
||||
|
||||
$params = ['page'=>1, 'pageSize'=>500];
|
||||
$params = ['page' => 1, 'pageSize' => 500];
|
||||
try {
|
||||
$data = $this->request('/api/v1/websites/ssl/search', $params);
|
||||
$data = $this->request("/websites/ssl/search", $params);
|
||||
$this->log('获取证书列表成功(total=' . $data['total'] . ')');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取证书列表失败:' . $e->getMessage());
|
||||
@@ -40,33 +40,35 @@ class opanel implements DeployInterface
|
||||
|
||||
$success = 0;
|
||||
$errmsg = null;
|
||||
foreach ($data['items'] as $row) {
|
||||
if (empty($row['primaryDomain'])) continue;
|
||||
$cert_domains = [];
|
||||
$cert_domains[] = $row['primaryDomain'];
|
||||
if(!empty($row['domains'])) $cert_domains += explode(',', $row['domains']);
|
||||
$flag = false;
|
||||
foreach ($cert_domains as $domain) {
|
||||
if (in_array($domain, $domains)) {
|
||||
$flag = true;
|
||||
break;
|
||||
if (!empty($data['items'])) {
|
||||
foreach ($data['items'] as $row) {
|
||||
if (empty($row['primaryDomain'])) continue;
|
||||
$cert_domains = [];
|
||||
$cert_domains[] = $row['primaryDomain'];
|
||||
if (!empty($row['domains'])) $cert_domains += explode(',', $row['domains']);
|
||||
$flag = false;
|
||||
foreach ($cert_domains as $domain) {
|
||||
if (in_array($domain, $domains) || in_array('*' . substr($domain, strpos($domain, '.')), $domains)) {
|
||||
$flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($flag) {
|
||||
$params = [
|
||||
'sslID' => $row['id'],
|
||||
'type' => 'paste',
|
||||
'certificate' => $fullchain,
|
||||
'privateKey' => $privatekey,
|
||||
'description' => '',
|
||||
];
|
||||
try {
|
||||
$this->request('/api/v1/websites/ssl/upload', $params);
|
||||
$this->log("证书ID:{$row['id']}更新成功!");
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
$errmsg = $e->getMessage();
|
||||
$this->log("证书ID:{$row['id']}更新失败:" . $errmsg);
|
||||
if ($flag) {
|
||||
$params = [
|
||||
'sslID' => $row['id'],
|
||||
'type' => 'paste',
|
||||
'certificate' => $fullchain,
|
||||
'privateKey' => $privatekey,
|
||||
'description' => '',
|
||||
];
|
||||
try {
|
||||
$this->request('/websites/ssl/upload', $params);
|
||||
$this->log("证书ID:{$row['id']}更新成功!");
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
$errmsg = $e->getMessage();
|
||||
$this->log("证书ID:{$row['id']}更新失败:" . $errmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,21 +93,21 @@ class opanel implements DeployInterface
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
|
||||
$timestamp = time().'';
|
||||
$timestamp = time() . '';
|
||||
$token = md5('1panel' . $this->key . $timestamp);
|
||||
$headers = [
|
||||
'1Panel-Token: '.$token,
|
||||
'1Panel-Timestamp: '.$timestamp
|
||||
'1Panel-Token' => $token,
|
||||
'1Panel-Timestamp' => $timestamp
|
||||
];
|
||||
$body = $params ? json_encode($params) : '{}';
|
||||
if($body) $headers[] = 'Content-Type: application/json';
|
||||
$response = curl_client($url, $body, null, null, $headers, $this->proxy);
|
||||
if ($body) $headers['Content-Type'] = 'application/json';
|
||||
$response = http_request($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'])){
|
||||
if (isset($result['code']) && $result['code'] == 200) {
|
||||
return $result['data'] ?? null;
|
||||
} elseif (isset($result['message'])) {
|
||||
throw new Exception($result['message']);
|
||||
}else{
|
||||
} else {
|
||||
throw new Exception('请求失败(httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
}
|
||||
|
||||
93
app/lib/deploy/proxmox.php
Normal file
93
app/lib/deploy/proxmox.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class proxmox implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $api_user;
|
||||
private $api_key;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
$this->api_user = $config['api_user'];
|
||||
$this->api_key = $config['api_key'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->api_user) || empty($this->api_key)) throw new Exception('必填内容不能为空');
|
||||
|
||||
$path = '/api2/json/access';
|
||||
$this->send_request($path);
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (empty($config['node'])) throw new Exception('节点名称不能为空');
|
||||
$cert_hash = openssl_x509_fingerprint($fullchain, 'sha256');
|
||||
if (!$cert_hash) throw new Exception('证书解析失败');
|
||||
|
||||
$path = '/api2/json/nodes/' . $config['node'] . '/certificates/info';
|
||||
$list = $this->send_request($path);
|
||||
foreach ($list as $item) {
|
||||
$fingerprint = strtolower(str_replace(':', '', $item['fingerprint']));
|
||||
if ($fingerprint == $cert_hash) {
|
||||
$this->log('节点:' . $config['node'] . ' 证书已存在');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$path = '/api2/json/nodes/' . $config['node'] . '/certificates/custom';
|
||||
$params = [
|
||||
'certificates' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
'force' => 1,
|
||||
'restart' => 1,
|
||||
];
|
||||
$this->send_request($path, $params);
|
||||
$this->log('节点:' . $config['node'] . ' 证书部署成功!');
|
||||
}
|
||||
|
||||
private function send_request($path, $params = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = ['Authorization' => 'PVEAPIToken=' . $this->api_user . '=' . $this->api_key];
|
||||
$post = $params ? http_build_query($params) : null;
|
||||
$response = http_request($url, $post, null, null, $headers, $this->proxy);
|
||||
if ($response['code'] == 200) {
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['data'])) {
|
||||
return $result['data'];
|
||||
} elseif (isset($result['errors'])) {
|
||||
if (is_array($result['errors'])) {
|
||||
$result['errors'] = implode(';', $result['errors']);
|
||||
}
|
||||
throw new Exception($result['errors']);
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
} else {
|
||||
throw new Exception('请求失败(httpCode=' . $response['code'] . ', body=' . $response['body'] . ')');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,8 @@ class qiniu implements DeployInterface
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$domain = $config['domain'];
|
||||
if (empty($domain)) throw new Exception('绑定的域名不能为空');
|
||||
$domains = $config['domain'];
|
||||
if (empty($domains)) throw new Exception('绑定的域名不能为空');
|
||||
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
@@ -38,12 +38,17 @@ class qiniu implements DeployInterface
|
||||
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey, $certInfo['subject']['CN'], $cert_name);
|
||||
|
||||
if ($config['product'] == 'cdn') {
|
||||
$this->deploy_cdn($domain, $cert_id);
|
||||
} elseif ($config['product'] == 'oss') {
|
||||
$this->deploy_oss($domain, $cert_id);
|
||||
} else {
|
||||
throw new Exception('未知的产品类型');
|
||||
foreach (explode(',', $domains) as $domain) {
|
||||
if (empty($domain)) continue;
|
||||
if ($config['product'] == 'cdn') {
|
||||
$this->deploy_cdn($domain, $cert_id);
|
||||
} elseif ($config['product'] == 'oss') {
|
||||
$this->deploy_oss($domain, $cert_id);
|
||||
} elseif ($config['product'] == 'pili') {
|
||||
$this->deploy_pili($config['pili_hub'], $domain, $cert_name);
|
||||
} else {
|
||||
throw new Exception('未知的产品类型');
|
||||
}
|
||||
}
|
||||
$info['cert_id'] = $cert_id;
|
||||
$info['cert_name'] = $cert_name;
|
||||
@@ -87,6 +92,15 @@ class qiniu implements DeployInterface
|
||||
$this->log('OSS域名 ' . $domain . ' 证书部署成功!');
|
||||
}
|
||||
|
||||
private function deploy_pili($hub, $domain, $cert_name)
|
||||
{
|
||||
$param = [
|
||||
'CertName' => $cert_name,
|
||||
];
|
||||
$this->client->pili_request('POST', '/v2/hubs/'.$hub.'/domains/'.$domain.'/cert', null, $param);
|
||||
$this->log('视频直播域名 ' . $domain . ' 证书部署成功!');
|
||||
}
|
||||
|
||||
private function get_cert_id($fullchain, $privatekey, $common_name, $cert_name)
|
||||
{
|
||||
$cert_id = null;
|
||||
|
||||
78
app/lib/deploy/rainyun.php
Normal file
78
app/lib/deploy/rainyun.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class rainyun implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url = 'https://api.v2.rainyun.com';
|
||||
private $apikey;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->apikey = $config['apikey'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->apikey)) throw new Exception('ApiKey不能为空');
|
||||
$this->request('/product/');
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (empty($config['id'])) throw new Exception('证书ID不能为空');
|
||||
|
||||
$params = [
|
||||
'cert' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
];
|
||||
try {
|
||||
$this->request('/product/sslcenter/' . $config['id'], $params, 'PUT');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
|
||||
$this->log('证书ID:' . $config['id'] . '更新成功!');
|
||||
}
|
||||
|
||||
private function request($path, $params = null, $method = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = [
|
||||
'x-api-key' => $this->apikey,
|
||||
];
|
||||
$body = null;
|
||||
if ($params) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
$body = json_encode($params);
|
||||
}
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy, $method);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['code']) && $result['code'] == 200) {
|
||||
return $result;
|
||||
} elseif (isset($result['message'])) {
|
||||
throw new Exception($result['message']);
|
||||
} else {
|
||||
if (!empty($response['body'])) $this->log('Response:' . $response['body']);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
163
app/lib/deploy/ratpanel.php
Normal file
163
app/lib/deploy/ratpanel.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class ratpanel implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $id;
|
||||
private $token;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
$this->id = $config['id'];
|
||||
$this->token = $config['token'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->id) || empty($this->token)) throw new Exception('请填写完整面板地址和访问令牌');
|
||||
|
||||
$response = $this->request('/user/info', null, 'GET');
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['msg']) && $result['msg'] == "success") {
|
||||
return true;
|
||||
} else {
|
||||
throw new Exception($result['msg'] ?? '面板地址无法连接');
|
||||
}
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if ($config['type'] == '1') {
|
||||
$this->deployPanel($fullchain, $privatekey);
|
||||
$this->log("面板证书部署成功");
|
||||
return;
|
||||
}
|
||||
$sites = explode("\n", $config['sites']);
|
||||
$success = 0;
|
||||
$errmsg = null;
|
||||
foreach ($sites as $site) {
|
||||
$site = trim($site);
|
||||
if (empty($site)) continue;
|
||||
try {
|
||||
$this->deploySite($site, $fullchain, $privatekey);
|
||||
$this->log("网站 {$site} 证书部署成功");
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
$errmsg = $e->getMessage();
|
||||
$this->log("网站 {$site} 证书部署失败:" . $errmsg);
|
||||
}
|
||||
}
|
||||
if ($success == 0) {
|
||||
throw new Exception($errmsg ?: '要部署的网站不存在');
|
||||
}
|
||||
}
|
||||
|
||||
private function deployPanel($fullchain, $privatekey)
|
||||
{
|
||||
$data = [
|
||||
'cert' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
];
|
||||
$response = $this->request('/setting/cert', $data);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['msg']) && $result['msg'] == "success") {
|
||||
return true;
|
||||
} elseif (isset($result['msg'])) {
|
||||
throw new Exception($result['msg']);
|
||||
} else {
|
||||
throw new Exception($response ?: '返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
private function deploySite($name, $fullchain, $privatekey)
|
||||
{
|
||||
$data = [
|
||||
'name' => $name,
|
||||
'cert' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
];
|
||||
$response = $this->request('/website/cert', $data);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['msg']) && $result['msg'] == "success") {
|
||||
return true;
|
||||
} elseif (isset($result['msg'])) {
|
||||
throw new Exception($result['msg']);
|
||||
} else {
|
||||
throw new Exception($response ?: '返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
private function request($path, $params, $method = 'POST')
|
||||
{
|
||||
$url = $this->url . '/api' . $path;
|
||||
$body = $method == 'GET' ? null : json_encode($params);
|
||||
$sign = $this->signRequest($method, $url, $body, $this->id, $this->token);
|
||||
$response = http_request($url, $body, null, null, [
|
||||
'Content-Type' => 'application/json',
|
||||
'X-Timestamp' => $sign['timestamp'],
|
||||
'Authorization' => 'HMAC-SHA256 Credential=' . $sign['id'] . ', Signature=' . $sign['signature']
|
||||
], $this->proxy, $method);
|
||||
return $response['body'];
|
||||
}
|
||||
|
||||
private function signRequest($method, $url, $body, $id, $token)
|
||||
{
|
||||
// 解析URL并获取路径
|
||||
$parsedUrl = parse_url($url);
|
||||
$path = $parsedUrl['path'];
|
||||
$query = $parsedUrl['query'] ?? '';
|
||||
|
||||
// 规范化路径
|
||||
$canonicalPath = $path;
|
||||
if (!str_starts_with($path, '/api')) {
|
||||
$apiPos = strpos($path, '/api');
|
||||
if ($apiPos !== false) {
|
||||
$canonicalPath = substr($path, $apiPos);
|
||||
}
|
||||
}
|
||||
|
||||
// 构造规范化请求
|
||||
$canonicalRequest = implode("\n", [
|
||||
$method,
|
||||
$canonicalPath,
|
||||
$query,
|
||||
hash('sha256', $body ?: '')
|
||||
]);
|
||||
|
||||
// 计算签名
|
||||
$timestamp = time();
|
||||
$stringToSign = implode("\n", [
|
||||
'HMAC-SHA256',
|
||||
$timestamp,
|
||||
hash('sha256', $canonicalRequest)
|
||||
]);
|
||||
$signature = hash_hmac('sha256', $stringToSign, $token);
|
||||
|
||||
return [
|
||||
'timestamp' => $timestamp,
|
||||
'signature' => $signature,
|
||||
'id' => $id
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,7 @@ class safeline implements DeployInterface
|
||||
if (empty($row['domains'])) continue;
|
||||
$flag = false;
|
||||
foreach ($row['domains'] as $domain) {
|
||||
if (in_array($domain, $domains)) {
|
||||
if (in_array($domain, $domains) || in_array('*' . substr($domain, strpos($domain, '.')), $domains)) {
|
||||
$flag = true;
|
||||
break;
|
||||
}
|
||||
@@ -68,23 +68,31 @@ class safeline implements DeployInterface
|
||||
}
|
||||
}
|
||||
if ($success == 0) {
|
||||
throw new Exception($errmsg ? $errmsg : '没有要更新的证书');
|
||||
$params = [
|
||||
'manual' => [
|
||||
'crt' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
],
|
||||
'type' => 2,
|
||||
];
|
||||
$this->request('/api/open/cert', $params);
|
||||
$this->log("证书上传成功!");
|
||||
}
|
||||
}
|
||||
|
||||
private function request($path, $params = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = ['X-SLCE-API-TOKEN: ' . $this->token];
|
||||
$headers = ['X-SLCE-API-TOKEN' => $this->token];
|
||||
$body = null;
|
||||
if ($params) {
|
||||
$heders[] = 'Content-Type: application/json';
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
$body = json_encode($params);
|
||||
}
|
||||
$response = curl_client($url, $body, null, null, $headers, $this->proxy);
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if ($response['code'] == 200 && $result) {
|
||||
return isset($result['data']) ? $result['data'] : null;
|
||||
return $result['data'] ?? null;
|
||||
} else {
|
||||
throw new Exception(!empty($result['msg']) ? $result['msg'] : '请求失败(httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
|
||||
@@ -23,6 +23,14 @@ class ssh implements DeployInterface
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$connection = $this->connect();
|
||||
if (isset($config['cmd_pre']) && !empty($config['cmd_pre'])) {
|
||||
$cmds = explode("\n", $config['cmd_pre']);
|
||||
foreach ($cmds as $cmd) {
|
||||
$cmd = trim($cmd);
|
||||
if (empty($cmd)) continue;
|
||||
$this->exec($connection, $cmd);
|
||||
}
|
||||
}
|
||||
$sftp = ssh2_sftp($connection);
|
||||
if ($config['format'] == 'pem') {
|
||||
$stream = fopen("ssh2.sftp://$sftp{$config['pem_cert_file']}", 'w');
|
||||
|
||||
167
app/lib/deploy/synology.php
Normal file
167
app/lib/deploy/synology.php
Normal file
@@ -0,0 +1,167 @@
|
||||
<?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',
|
||||
'session' => 'webui',
|
||||
'account' => $this->username,
|
||||
'passwd' => $this->password,
|
||||
'format' => 'sid',
|
||||
'enable_syno_token' => 'yes',
|
||||
];
|
||||
$response = http_request($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)
|
||||
{
|
||||
$this->login();
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
$certInfo['validFrom_time_t'];
|
||||
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 = http_request($url . '?' . http_build_query($params), 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('获取证书列表失败:' . json_encode($result['error']));
|
||||
} else {
|
||||
throw new Exception('获取证书列表失败(httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
|
||||
$id = null;
|
||||
$validFrom = 0;
|
||||
foreach ($result['data']['certificates'] as $certificate) {
|
||||
if ($certificate['subject']['common_name'] == $certInfo['subject']['CN'] || $certificate['desc'] == $config['desc']) {
|
||||
$id = $certificate['id'];
|
||||
$validFrom = \DateTime::createFromFormat('M d H:i:s Y T', $certificate['valid_from'])->getTimestamp();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($id) {
|
||||
if ($validFrom == $certInfo['validFrom_time_t']) {
|
||||
$this->log('证书ID:' . $id . '已存在,无需更新');
|
||||
return;
|
||||
}
|
||||
$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'],
|
||||
];
|
||||
$headers = [
|
||||
'Content-Type' => 'multipart/form-data'
|
||||
];
|
||||
$post = [
|
||||
[
|
||||
'name' => 'key',
|
||||
'filename' => 'key.pem',
|
||||
'contents' => $privatekey
|
||||
],
|
||||
[
|
||||
'name' => 'cert',
|
||||
'filename' => 'cert.pem',
|
||||
'contents' => $fullchain
|
||||
],
|
||||
[
|
||||
'name' => 'id',
|
||||
'contents' => $id
|
||||
],
|
||||
[
|
||||
'name' => 'desc',
|
||||
'contents' => $config['desc']
|
||||
]
|
||||
];
|
||||
$response = http_request($url . '?' . http_build_query($params), $post, null, null, $headers, $this->proxy, null, 15);
|
||||
$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 . '更新失败:' . json_encode($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('证书上传失败:' . json_encode($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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ class tencent implements DeployInterface
|
||||
if (empty($config['regionid'])) throw new Exception('所属地域ID不能为空');
|
||||
if (empty($config['cos_bucket'])) throw new Exception('存储桶名称不能为空');
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$instance_id = $config['regionid'] . '#' . $config['cos_bucket'] . '#' . $config['domain'];
|
||||
$instance_id = $config['regionid'] . '|' . $config['cos_bucket'] . '|' . $config['domain'];
|
||||
$this->client = new TencentCloud($this->SecretId, $this->SecretKey, 'ssl.tencentcloudapi.com', 'ssl', '2019-12-05', $config['regionid'], $this->proxy);
|
||||
} elseif ($config['product'] == 'tke') {
|
||||
if (empty($config['regionid'])) throw new Exception('所属地域ID不能为空');
|
||||
@@ -52,10 +52,16 @@ class tencent implements DeployInterface
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$instance_id = $config['regionid'] . '|' . $config['lighthouse_id'] . '|' . $config['domain'];
|
||||
$this->client = new TencentCloud($this->SecretId, $this->SecretKey, 'ssl.tencentcloudapi.com', 'ssl', '2019-12-05', $config['regionid'], $this->proxy);
|
||||
} elseif ($config['product'] == 'ddos') {
|
||||
if (empty($config['lighthouse_id'])) throw new Exception('实例ID不能为空');
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$instance_id = $config['lighthouse_id'] . '|' . $config['domain'] . '|443';
|
||||
} elseif ($config['product'] == 'clb') {
|
||||
return $this->deploy_clb($cert_id, $config);
|
||||
} elseif ($config['product'] == 'scf') {
|
||||
return $this->deploy_scf($cert_id, $config);
|
||||
} elseif ($config['product'] == 'teo' && isset($config['site_id'])) {
|
||||
return $this->deploy_teo($cert_id, $config);
|
||||
} else {
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
if ($config['product'] == 'waf') {
|
||||
@@ -101,6 +107,13 @@ class tencent implements DeployInterface
|
||||
}
|
||||
$this->log('上传证书成功 CertificateId=' . $data['CertificateId']);
|
||||
usleep(300000);
|
||||
|
||||
$param = [
|
||||
'CertificateIds' => [$data['CertificateId']],
|
||||
'SwitchStatus' => 1,
|
||||
];
|
||||
$this->client->request('ModifyCertificatesExpiringNotificationSwitch', $param);
|
||||
|
||||
return $data['CertificateId'];
|
||||
}
|
||||
|
||||
@@ -111,11 +124,17 @@ class tencent implements DeployInterface
|
||||
} else {
|
||||
$instance_ids = [$instance_id];
|
||||
}
|
||||
if ($product == 'cdn') {
|
||||
$instance_ids = array_map(function ($id) {
|
||||
return $id . '|on';
|
||||
}, $instance_ids);
|
||||
}
|
||||
$param = [
|
||||
'CertificateId' => $cert_id,
|
||||
'InstanceIdList' => $instance_ids,
|
||||
'ResourceType' => $product,
|
||||
];
|
||||
if ($product == 'live') $param['Status'] = 1;
|
||||
$data = $this->client->request('DeployCertificateInstance', $param);
|
||||
$this->log(json_encode($data));
|
||||
$this->log(strtoupper($product) . '实例 ' . $instance_id . ' 部署证书成功!');
|
||||
@@ -242,6 +261,26 @@ class tencent implements DeployInterface
|
||||
$this->log('云函数自定义域名 ' . $config['domain'] . ' 部署证书成功!');
|
||||
}
|
||||
|
||||
private function deploy_teo($cert_id, $config)
|
||||
{
|
||||
if (empty($config['site_id'])) throw new Exception('站点ID不能为空');
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
|
||||
$endpoint = isset($config['site_type']) && $config['site_type'] == 'intl' ? 'teo.intl.tencentcloudapi.com' : 'teo.tencentcloudapi.com';
|
||||
$client = new TencentCloud($this->SecretId, $this->SecretKey, $endpoint, 'teo', '2022-09-01', null, $this->proxy);
|
||||
$hosts = explode(',', $config['domain']);
|
||||
$param = [
|
||||
'ZoneId' => $config['site_id'],
|
||||
'Hosts' => $hosts,
|
||||
'Mode' => 'sslcert',
|
||||
'ServerCertInfo' => [[
|
||||
'CertId' => $cert_id
|
||||
]]
|
||||
];
|
||||
$data = $client->request('ModifyHostsCertificate', $param);
|
||||
$this->log('边缘安全加速域名 ' . $config['domain'] . ' 部署证书成功!');
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
|
||||
212
app/lib/deploy/unicloud.php
Normal file
212
app/lib/deploy/unicloud.php
Normal file
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class unicloud implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $username;
|
||||
private $password;
|
||||
private $deviceId;
|
||||
private $proxy;
|
||||
private $token;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->username = $config['username'];
|
||||
$this->password = $config['password'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
$this->deviceId = getMillisecond() . random(7, 1);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->username) || empty($this->password)) throw new Exception('账号或密码不能为空');
|
||||
$this->login();
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (empty($config['domains'])) throw new Exception('绑定的域名不能为空');
|
||||
$this->getToken();
|
||||
|
||||
$url = 'https://unicloud-api.dcloud.net.cn/unicloud/api/host/create-domain-with-cert';
|
||||
foreach (explode(',', $config['domains']) as $domain) {
|
||||
if (empty($domain)) continue;
|
||||
$params = [
|
||||
'appid' => '',
|
||||
'provider' => $config['provider'],
|
||||
'spaceId' => $config['spaceId'],
|
||||
'domain' => $domain,
|
||||
'cert' => rawurlencode($fullchain),
|
||||
'key' => rawurlencode($privatekey),
|
||||
];
|
||||
$post = json_encode($params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
$headers = [
|
||||
'Token' => $this->token,
|
||||
];
|
||||
$response = http_request($url, $post, null, null, $headers, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['ret']) && $result['ret'] == 0) {
|
||||
$this->log('域名:' . $domain . ' 证书更新成功!');
|
||||
} elseif(isset($result['desc'])) {
|
||||
throw new Exception('域名:' . $domain . ' 证书更新失败:' . $result['desc']);
|
||||
} else {
|
||||
throw new Exception('域名:' . $domain . ' 证书更新失败:' . $response['body']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function login()
|
||||
{
|
||||
$url = 'https://account.dcloud.net.cn/client';
|
||||
$clientInfo = $this->getClientInfo('__UNI__unicloud_console', '账号中心');
|
||||
$bizParams = [
|
||||
'functionTarget' => 'uni-id-co',
|
||||
'functionArgs' => [
|
||||
'method' => 'login',
|
||||
'params' => [[
|
||||
'password' => $this->password,
|
||||
'captcha' => '',
|
||||
'resetAppId' => '__UNI__unicloud_console',
|
||||
'resetUniPlatform' => 'web',
|
||||
'isReturnToken' => false,
|
||||
'email' => $this->username,
|
||||
]],
|
||||
'clientInfo' => $clientInfo,
|
||||
],
|
||||
];
|
||||
$params = [
|
||||
'method' => 'serverless.function.runtime.invoke',
|
||||
'params' => json_encode($bizParams, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
'spaceId' => 'uni-id-server',
|
||||
'timestamp' => getMillisecond(),
|
||||
];
|
||||
$sign = $this->sign($params, 'ba461799-fde8-429f-8cc4-4b6d306e2339');
|
||||
$post = json_encode($params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
$headers = [
|
||||
'Origin' => 'https://account.dcloud.net.cn',
|
||||
'Referer' => 'https://account.dcloud.net.cn/',
|
||||
'X-Client-Info' => json_encode($clientInfo, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
'X-Serverless-Sign' => $sign,
|
||||
];
|
||||
$response = http_request($url, $post, null, null, $headers, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['success']) && $result['success'] == true) {
|
||||
if (isset($result['data']['errCode']) && $result['data']['errCode'] == 0) {
|
||||
return $result['data']['newToken']['token'];
|
||||
} else {
|
||||
throw new Exception('登录失败:' . $result['data']['errMsg']);
|
||||
}
|
||||
} else {
|
||||
throw new Exception('登录失败:' . $response['body']);
|
||||
}
|
||||
}
|
||||
|
||||
private function getToken()
|
||||
{
|
||||
$uniIdToken = $this->login();
|
||||
$url = 'https://unicloud.dcloud.net.cn/client';
|
||||
$clientInfo = $this->getClientInfo('__UNI__unicloud_console', 'uniCloud控制台');
|
||||
$bizParams = [
|
||||
'functionTarget' => 'uni-cloud-kernel',
|
||||
'functionArgs' => [
|
||||
'action' => 'user/getUserToken',
|
||||
'data' => [
|
||||
'isLogin' => true
|
||||
],
|
||||
'clientInfo' => $clientInfo,
|
||||
'uniIdToken' => $uniIdToken,
|
||||
],
|
||||
];
|
||||
$params = [
|
||||
'method' => 'serverless.function.runtime.invoke',
|
||||
'params' => json_encode($bizParams, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
'spaceId' => 'dc-6nfabcn6ada8d3dd',
|
||||
'timestamp' => getMillisecond(),
|
||||
];
|
||||
$sign = $this->sign($params, '4c1f7fbf-c732-42b0-ab10-4634a8bbe834');
|
||||
$post = json_encode($params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
$headers = [
|
||||
'Origin' => 'https://account.dcloud.net.cn',
|
||||
'Referer' => 'https://account.dcloud.net.cn/',
|
||||
'X-Client-Info' => json_encode($clientInfo, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
|
||||
'X-Client-Token' => $uniIdToken,
|
||||
'X-Serverless-Sign' => $sign,
|
||||
];
|
||||
$response = http_request($url, $post, null, null, $headers, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['success']) && $result['success'] == true) {
|
||||
if (isset($result['data']['code']) && $result['data']['code'] == 0) {
|
||||
if (isset($result['data']['data']['ret']) && $result['data']['data']['ret'] == 0) {
|
||||
$this->token = $result['data']['data']['data']['token'];
|
||||
return $result['data']['data']['data']['token'];
|
||||
} else {
|
||||
throw new Exception('获取token失败:' . $result['data']['data']['desc']);
|
||||
}
|
||||
} else {
|
||||
throw new Exception('获取token失败:' . $response['body']);
|
||||
}
|
||||
} else {
|
||||
throw new Exception('获取token失败:' . $response['body']);
|
||||
}
|
||||
}
|
||||
|
||||
private function getClientInfo($appId, $appName, $appVersion = '1.0.0', $appVersionCode = '100')
|
||||
{
|
||||
$clientInfo = [
|
||||
'PLATFORM' => 'web',
|
||||
'OS' => 'windows',
|
||||
'APPID' => $appId,
|
||||
'DEVICEID' => $this->deviceId,
|
||||
'scene' => 1001,
|
||||
'appId' => $appId,
|
||||
'appLanguage' => 'zh-Hans',
|
||||
'appName' => $appName,
|
||||
'appVersion' => $appVersion,
|
||||
'appVersionCode' => $appVersionCode,
|
||||
'browserName' => 'chrome',
|
||||
'browserVersion' => '122.0.6261.95',
|
||||
'deviceId' => $this->deviceId,
|
||||
'deviceModel' => 'PC',
|
||||
'deviceType' => 'pc',
|
||||
'hostName' => 'chrome',
|
||||
'hostVersion' => '122.0.6261.95',
|
||||
'osName' => 'windows',
|
||||
'osVersion' => '10 x64',
|
||||
'ua' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36',
|
||||
'uniCompilerVersion' => '4.45',
|
||||
'uniPlatform' => 'web',
|
||||
'uniRuntimeVersion' => '4.45',
|
||||
'locale' => 'zh-Hans',
|
||||
'LOCALE' => 'zh-Hans',
|
||||
];
|
||||
return $clientInfo;
|
||||
}
|
||||
|
||||
private function sign($data, $key)
|
||||
{
|
||||
ksort($data);
|
||||
$signstr = '';
|
||||
foreach ($data as $k => $v) {
|
||||
$signstr .= $k . '=' . $v . '&';
|
||||
}
|
||||
$signstr = rtrim($signstr, '&');
|
||||
return hash_hmac('md5', $signstr, $key);
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
133
app/lib/deploy/upyun.php
Normal file
133
app/lib/deploy/upyun.php
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class upyun implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $username;
|
||||
private $password;
|
||||
private $proxy;
|
||||
private $cookie;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->username = $config['username'];
|
||||
$this->password = $config['password'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->username) || empty($this->password)) throw new Exception('用户名或密码不能为空');
|
||||
$this->login();
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$this->login();
|
||||
|
||||
$url = 'https://console.upyun.com/api/https/certificate/';
|
||||
$params = [
|
||||
'certificate' => $fullchain,
|
||||
'private_key' => $privatekey,
|
||||
];
|
||||
$response = http_request($url, http_build_query($params), null, $this->cookie, null, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if ($result['data']['status'] === 0) {
|
||||
$common_name = $result['data']['result']['commonName'];
|
||||
$certificate_id = $result['data']['result']['certificate_id'];
|
||||
$this->log('证书上传成功!证书ID:' . $certificate_id);
|
||||
} elseif (isset($result['data']['message'])) {
|
||||
throw new Exception('证书上传失败:' . $result['data']['message']);
|
||||
} else {
|
||||
throw new Exception('证书上传失败');
|
||||
}
|
||||
|
||||
$url = 'https://console.upyun.com/api/https/certificate/search';
|
||||
$params = [
|
||||
'limit' => 100,
|
||||
'domain' => $common_name,
|
||||
];
|
||||
$response = http_request($url . '?' . http_build_query($params), null, null, $this->cookie, null, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['data']['result']) && is_array($result['data']['result'])) {
|
||||
$cert_list = $result['data']['result'];
|
||||
} elseif (isset($result['data']['message'])) {
|
||||
throw new Exception('查找证书失败:' . $result['data']['message']);
|
||||
} else {
|
||||
throw new Exception('查找证书失败');
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
$d = 0;
|
||||
foreach ($cert_list as $crt_id => $item) {
|
||||
if ($crt_id == $certificate_id || $item['commonName'] != $common_name || $item['config_domain'] == 0) {
|
||||
continue;
|
||||
}
|
||||
$url = 'https://console.upyun.com/api/https/migrate/certificate';
|
||||
$params = [
|
||||
'new_crt_id' => $certificate_id,
|
||||
'old_crt_id' => $crt_id,
|
||||
];
|
||||
$response = http_request($url, http_build_query($params), null, $this->cookie, null, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['data']['result']) && $result['data']['result'] == true) {
|
||||
$i++;
|
||||
$d += $item['config_domain'];
|
||||
$this->log('证书ID:' . $crt_id . ' 迁移成功!');
|
||||
} elseif (isset($result['data']['message'])) {
|
||||
throw new Exception('证书迁移失败:' . $result['data']['message']);
|
||||
} else {
|
||||
throw new Exception('证书迁移失败');
|
||||
}
|
||||
}
|
||||
|
||||
if ($i == 0) throw new Exception('未找到可迁移的证书');
|
||||
$this->log('共迁移' . $i . '个证书,关联域名' . $d . '个');
|
||||
}
|
||||
|
||||
private function login()
|
||||
{
|
||||
$url = 'https://console.upyun.com/accounts/signin/';
|
||||
$params = [
|
||||
'username' => $this->username,
|
||||
'password' => $this->password,
|
||||
];
|
||||
$response = http_request($url, http_build_query($params), null, null, null, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['data']['result']) && $result['data']['result'] == true) {
|
||||
$cookie = '';
|
||||
if (isset($response['headers']['set-cookie'])) {
|
||||
foreach ($response['headers']['set-cookie'] as $val) {
|
||||
$arr = explode('=', $val);
|
||||
if ($arr[1] == '' || $arr[1] == 'deleted') continue;
|
||||
$cookie .= $val . '; ';
|
||||
}
|
||||
} else {
|
||||
throw new Exception('登录成功,获取cookie失败');
|
||||
}
|
||||
$this->cookie = $cookie;
|
||||
return true;
|
||||
} elseif (isset($result['data']['message'])) {
|
||||
throw new Exception('登录失败:' . $result['data']['message']);
|
||||
} else {
|
||||
throw new Exception('登录失败');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
474
app/lib/deploy/wangsu.php
Normal file
474
app/lib/deploy/wangsu.php
Normal file
@@ -0,0 +1,474 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class wangsu implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $username;
|
||||
private $apiKey;
|
||||
private $spKey;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->username = $config['username'];
|
||||
$this->apiKey = $config['apiKey'];
|
||||
$this->spKey = $config['spKey'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->username) || empty($this->apiKey)) throw new Exception('必填参数不能为空');
|
||||
$this->request('/api/ssl/certificate');
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if ($config['product'] == 'cdnpro') {
|
||||
$this->deploy_cdnpro($fullchain, $privatekey, $config, $info);
|
||||
|
||||
} elseif ($config['product'] == 'cdn') {
|
||||
$this->deploy_cdn($fullchain, $privatekey, $config, $info);
|
||||
|
||||
} elseif ($config['product'] == 'certificate') {
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) {
|
||||
throw new Exception('证书解析失败');
|
||||
}
|
||||
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
$serial_no = strtolower($certInfo['serialNumberHex']);
|
||||
$this->get_cert_id($fullchain, $privatekey, $cert_name, $config['cert_id'], $serial_no, true);
|
||||
} else {
|
||||
throw new Exception('未知的产品类型');
|
||||
}
|
||||
}
|
||||
|
||||
public function deploy_cdn($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (empty($config['domains'])) {
|
||||
throw new Exception('绑定的域名不能为空');
|
||||
}
|
||||
$domains = explode(',', $config['domains']);
|
||||
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) {
|
||||
throw new Exception('证书解析失败');
|
||||
}
|
||||
|
||||
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
$serial_no = strtolower($certInfo['serialNumberHex']);
|
||||
$this->log('证书序列号:' . $serial_no);
|
||||
$cert_id = isset($info['cert_id']) ? $info['cert_id'] : null;
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey, $cert_name, $cert_id, $serial_no, false);
|
||||
|
||||
$param = [
|
||||
'certificateId' => $cert_id,
|
||||
'domainNames' => $domains
|
||||
];
|
||||
|
||||
try {
|
||||
$data = $this->request('/api/config/certificate/batch', $param, true, null, 'PUT');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('绑定域名失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$this->log('绑定证书成功,证书ID:' . $cert_id);
|
||||
$info['cert_id'] = $cert_id;
|
||||
}
|
||||
|
||||
public function deploy_cdnpro($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (empty($config['domain'])) {
|
||||
throw new Exception('绑定的域名不能为空');
|
||||
}
|
||||
$domain = $config['domain'];
|
||||
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) {
|
||||
throw new Exception('证书解析失败');
|
||||
}
|
||||
|
||||
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
$cert_id = $this->get_cert_id_cdnpro($fullchain, $privatekey, $cert_name);
|
||||
|
||||
try {
|
||||
$hostnameInfo = $this->request('/cdn/hostnames/' . $domain);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取域名信息失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
if (empty($hostnameInfo["propertyInProduction"])) {
|
||||
throw new Exception('域名 ' . $domain . ' 不存在或未部署到生产环境');
|
||||
} else {
|
||||
$this->log('CDN域名 ' . $domain . ' 对应的加速项目ID:' . $hostnameInfo["propertyInProduction"]["propertyId"]);
|
||||
$this->log('CDN域名 ' . $domain . ' 对应的加速项目生产版本:' . $hostnameInfo["propertyInProduction"]["version"]);
|
||||
}
|
||||
|
||||
if ($hostnameInfo["propertyInProduction"]["certificateId"] == $cert_id) {
|
||||
$this->log('CDN域名 ' . $domain . ' 已绑定证书:' . $cert_name);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$properity = $this->request('/cdn/properties/' . $hostnameInfo["propertyInProduction"]["propertyId"] . '/versions/' . $hostnameInfo["propertyInProduction"]["version"]);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取加速项目版本信息失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$properityConfig = $properity["configs"];
|
||||
$properityConfig["tlsCertificateId"] = $cert_id;
|
||||
|
||||
try {
|
||||
$data = $this->request('/cdn/properties/' . $hostnameInfo["propertyInProduction"]["propertyId"] . '/versions', $properityConfig, true);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('新增加速项目版本失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$url_parts = parse_url($data);
|
||||
$path_parts = explode('/', $url_parts['path']);
|
||||
$newVersion = end($path_parts);
|
||||
|
||||
$param = [
|
||||
'propertyId' => $hostnameInfo["propertyInProduction"]["propertyId"],
|
||||
'version' => intval($newVersion),
|
||||
];
|
||||
|
||||
try {
|
||||
$data = $this->request('/cdn/validations', $param, true);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('发起加速项目验证失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$url_parts = parse_url($data);
|
||||
$path_parts = explode('/', $url_parts['path']);
|
||||
$validationTaskId = end($path_parts);
|
||||
$this->log('验证任务ID:' . $validationTaskId);
|
||||
|
||||
$attempts = 0;
|
||||
$maxAttempts = 12;
|
||||
$status = null;
|
||||
|
||||
do {
|
||||
sleep(5);
|
||||
|
||||
try {
|
||||
$data = $this->request('/cdn/validations/' . $validationTaskId);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取验证任务状态失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$status = $data['status'];
|
||||
|
||||
if ($status === 'failed') {
|
||||
throw new Exception('证书绑定失败,加速项目验证失败');
|
||||
}
|
||||
|
||||
if ($status === 'succeeded') {
|
||||
break; // 验证成功立即退出循环
|
||||
}
|
||||
|
||||
$attempts++;
|
||||
} while ($attempts < $maxAttempts);
|
||||
|
||||
if ($status !== 'succeeded') {
|
||||
throw new Exception('证书绑定超时,加速项目验证时间过长');
|
||||
}
|
||||
|
||||
$this->log('加速项目验证成功,开始部署...');
|
||||
|
||||
$deploymentTasks = [
|
||||
'target' => 'production',
|
||||
'actions' => [
|
||||
[
|
||||
'action' => 'deploy_cert',
|
||||
'certificateId' => $cert_id,
|
||||
'version' => 1,
|
||||
],
|
||||
[
|
||||
'action' => 'deploy_property',
|
||||
'propertyId' => $hostnameInfo["propertyInProduction"]["propertyId"],
|
||||
'version' => intval($newVersion),
|
||||
]
|
||||
],
|
||||
'name' => 'Deploy certificate and property for ' . $hostnameInfo["propertyInProduction"]["propertyId"],
|
||||
];
|
||||
|
||||
try {
|
||||
$data = $this->request('/cdn/deploymentTasks', $deploymentTasks, true, null, 'POST', false, ['Check-Certificate' => 'no', 'Check-Usage' => 'no']);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('下发证书部署任务失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$url_parts = parse_url($data);
|
||||
$path_parts = explode('/', $url_parts['path']);
|
||||
$deploymentTaskId = end($path_parts);
|
||||
|
||||
$this->log('CDN域名 ' . $domain . ' 绑定证书部署任务下发成功,部署任务ID:' . $deploymentTaskId);
|
||||
$info['cert_id'] = $cert_id;
|
||||
}
|
||||
|
||||
private function get_cert_id($fullchain, $privatekey, $cert_name, $cert_id = null, $serial_no = null, $overwrite = false)
|
||||
{
|
||||
if ($cert_id) {
|
||||
try {
|
||||
$data = $this->request('/api/certificate/' . $cert_id);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取证书详情失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
if (isset($data['message']) && $data['message'] == 'success' && $data['data']['name'] == $cert_name && $data['data']['serial'] == $serial_no) {
|
||||
$this->log('证书已是最新,证书ID:' . $cert_id);
|
||||
return $cert_id;
|
||||
}
|
||||
|
||||
$this->log('证书已过期或被删除,准备重新上传');
|
||||
|
||||
} elseif ($overwrite === true) {
|
||||
throw new Exception('证书ID不能为空');
|
||||
}
|
||||
|
||||
if ($overwrite === true) {
|
||||
$param = [
|
||||
'name' => $cert_name,
|
||||
'certificate' => $fullchain,
|
||||
'privateKey' => $privatekey,
|
||||
];
|
||||
|
||||
try {
|
||||
$data = $this->request('/api/certificate/' . $cert_id, $param, true, null, 'PUT');
|
||||
$this->log('更新证书成功,证书ID:' . $cert_id);
|
||||
return $cert_id;
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('更新证书失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $this->request('/api/ssl/certificate');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取证书列表失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$certificates = $data['ssl-certificate'];
|
||||
|
||||
if (!empty($certificates)) {
|
||||
foreach ($certificates as $cert) {
|
||||
if ($serial_no == $cert['certificate-serial']) {
|
||||
$cert_id = $cert['certificate-id'];
|
||||
$this->log('证书' . $cert_name . '已存在,新证书ID:' . $cert_id);
|
||||
try {
|
||||
$this->request('/api/certificate/' . $cert_id, ['name' => $cert_name], true, null, 'PUT');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('证书更名失败:' . $e->getMessage());
|
||||
}
|
||||
$this->log('将证书ID为' . $cert_id . '的证书更名为:' . $cert_name);
|
||||
return $cert_id;
|
||||
|
||||
} elseif ($cert_name == $cert['name']) {
|
||||
$this->log('证书' . $cert_name . '已存在,但序列号(' . $cert['certificate-id'] . ')不匹配,准备重新上传');
|
||||
try {
|
||||
$this->request('/api/certificate/' . $cert['certificate-id'], ['name' => $cert_name . '-bak'], true, null, 'PUT');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('证书更名失败:' . $e->getMessage());
|
||||
}
|
||||
$this->log('将证书ID为' . $cert['certificate-id'] . '的证书更名为:' . $cert_name . '-bak');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$param = [
|
||||
'name' => $cert_name,
|
||||
'certificate' => $fullchain,
|
||||
'privateKey' => $privatekey,
|
||||
];
|
||||
|
||||
try {
|
||||
$data = $this->request('/api/certificate', $param, true, null, 'POST', true);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$url_parts = parse_url($data);
|
||||
$path_parts = explode('/', $url_parts['path']);
|
||||
$cert_id = end($path_parts);
|
||||
$this->log('上传证书成功,证书ID:' . $cert_id);
|
||||
|
||||
return $cert_id;
|
||||
}
|
||||
|
||||
private function get_cert_id_cdnpro($fullchain, $privatekey, $cert_name)
|
||||
{
|
||||
$cert_id = null;
|
||||
|
||||
try {
|
||||
$data = $this->request('/cdn/certificates?search=' . urlencode($cert_name));
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取证书列表失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
if ($data['count'] > 0) {
|
||||
foreach ($data['certificates'] as $cert) {
|
||||
if ($cert_name == $cert['name']) {
|
||||
$cert_id = $cert['certificateId'];
|
||||
$this->log('证书' . $cert_name . '已存在,证书ID:' . $cert_id);
|
||||
return $cert_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$date = gmdate("D, d M Y H:i:s T");
|
||||
$encryptedKey = $this->encryptPrivateKey($privatekey, $date);
|
||||
$param = [
|
||||
'name' => $cert_name,
|
||||
'autoRenew' => 'Off',
|
||||
'newVersion' => [
|
||||
'privateKey' => $encryptedKey,
|
||||
'certificate' => $fullchain,
|
||||
]
|
||||
];
|
||||
|
||||
try {
|
||||
$data = $this->request('/cdn/certificates', $param, true, $date);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$url_parts = parse_url($data);
|
||||
$path_parts = explode('/', $url_parts['path']);
|
||||
$cert_id = end($path_parts);
|
||||
$this->log('上传证书成功,证书ID:' . $cert_id);
|
||||
|
||||
usleep(500000);
|
||||
|
||||
return $cert_id;
|
||||
}
|
||||
|
||||
private function encryptPrivateKey($privateKey, $date = null)
|
||||
{
|
||||
// 获取当前 GMT 时间(DATE)
|
||||
if (empty($date)) {
|
||||
$date = gmdate("D, d M Y H:i:s T");
|
||||
}
|
||||
|
||||
// 生成 HMAC-SHA256 密钥材料
|
||||
if (!empty($this->spKey)) {
|
||||
$apiKey = $this->spKey;
|
||||
} else {
|
||||
$apiKey = $this->apiKey;
|
||||
}
|
||||
$hmac = hash_hmac('sha256', $date, $apiKey, true);
|
||||
$aesIvKeyHex = bin2hex($hmac);
|
||||
|
||||
if (strlen($aesIvKeyHex) != 64) {
|
||||
throw new Exception("Invalid HMAC length: " . strlen($aesIvKeyHex));
|
||||
}
|
||||
|
||||
// 提取 IV 和 Key
|
||||
$ivHex = substr($aesIvKeyHex, 0, 32);
|
||||
$keyHex = substr($aesIvKeyHex, 32, 64);
|
||||
|
||||
$iv = hex2bin($ivHex);
|
||||
$key = hex2bin($keyHex);
|
||||
|
||||
$blockSize = 16; // AES 块大小为 16 字节
|
||||
$plainLen = strlen($privateKey);
|
||||
$padLen = $blockSize - ($plainLen % $blockSize);
|
||||
$padding = str_repeat(chr($padLen), $padLen);
|
||||
$plainText = $privateKey . $padding;
|
||||
|
||||
// AES-128-CBC 加密
|
||||
$encrypted = openssl_encrypt(
|
||||
$plainText,
|
||||
'AES-128-CBC',
|
||||
$key,
|
||||
OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING,
|
||||
$iv
|
||||
);
|
||||
|
||||
if ($encrypted === false) {
|
||||
throw new Exception("Encryption failed: " . openssl_error_string());
|
||||
}
|
||||
|
||||
// 返回 Base64 编码结果
|
||||
return base64_encode($encrypted);
|
||||
}
|
||||
|
||||
private function request($path, $data = null, $json = false, $date = null, $method = null, $getLocation = false, $headers = [])
|
||||
{
|
||||
$body = null;
|
||||
if ($data) {
|
||||
$body = $json ? json_encode($data) : http_build_query($data);
|
||||
}
|
||||
|
||||
if (empty($date)) {
|
||||
$date = gmdate("D, d M Y H:i:s T");
|
||||
}
|
||||
|
||||
$hmac = hash_hmac('sha1', $date, $this->apiKey, true);
|
||||
$signature = base64_encode($hmac);
|
||||
$authorization = 'Basic ' . base64_encode($this->username . ':' . $signature);
|
||||
|
||||
if (empty($headers)) {
|
||||
$headers = [
|
||||
'Authorization' => $authorization,
|
||||
'Date' => $date,
|
||||
'Accept' => 'application/json',
|
||||
'Connection' => 'close',
|
||||
];
|
||||
} else {
|
||||
$headers['Authorization'] = $authorization;
|
||||
$headers['Date'] = $date;
|
||||
$headers['Accept'] = 'application/json';
|
||||
$headers['Connection'] = 'close';
|
||||
}
|
||||
|
||||
if ($body && $json) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
$url = 'https://open.chinanetcenter.com' . $path;
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy, $method, 30);
|
||||
$result = json_decode($response['body'], true);
|
||||
|
||||
if ((isset($response['code']) && $response['code'] == 201) || (isset($response['code']) && $response['code'] == 200 && $getLocation === true)) {
|
||||
if (isset($response['headers']['Location'])) {
|
||||
$location = trim(array_shift($response['headers']['Location'])); // 提取 Location 头部的值并去除多余空格
|
||||
if (!empty($location)) {
|
||||
return $location;
|
||||
}
|
||||
}
|
||||
// 如果没有找到 Location 头部,返回默认值 true
|
||||
return true;
|
||||
|
||||
} elseif (isset($response['code']) && $response['code'] >= 200 && $response['code'] <= 299) {
|
||||
return isset($result) ? $result : true;
|
||||
|
||||
} elseif (isset($result['message'])) {
|
||||
throw new Exception($result['message']);
|
||||
|
||||
} elseif (isset($result['result'])) {
|
||||
throw new Exception($result['result']);
|
||||
|
||||
} else {
|
||||
throw new Exception('请求失败');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
131
app/lib/deploy/west.php
Normal file
131
app/lib/deploy/west.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class west implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $username;
|
||||
private $api_password;
|
||||
private $baseUrl = 'https://api.west.cn/api/v2';
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->username = $config['username'];
|
||||
$this->api_password = $config['api_password'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->username) || empty($this->api_password)) throw new Exception('用户名或API密码不能为空');
|
||||
$this->execute('/vhost/', ['act' => 'products']);
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (empty($config['sitename'])) throw new Exception('FTP账号不能为空');
|
||||
$params = [
|
||||
'act' => 'vhostssl',
|
||||
'sitename' => $config['sitename'],
|
||||
'cmd' => 'info'
|
||||
];
|
||||
try {
|
||||
$data = $this->execute('/vhost/', $params);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取虚拟主机SSL配置失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$params = [
|
||||
'act' => 'vhostssl',
|
||||
'sitename' => $config['sitename'],
|
||||
'cmd' => 'import',
|
||||
'keycontent' => $privatekey,
|
||||
'certcontent' => $fullchain,
|
||||
];
|
||||
try {
|
||||
$this->execute('/vhost/', $params);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传SSL证书失败:' . $e->getMessage());
|
||||
}
|
||||
$this->log('SSL证书上传成功');
|
||||
|
||||
if (!isset($data['SSLEnabled']) || $data['SSLEnabled'] == 0) {
|
||||
$params = [
|
||||
'act' => 'vhostssl',
|
||||
'sitename' => $config['sitename'],
|
||||
'cmd' => 'openssl',
|
||||
];
|
||||
try {
|
||||
$this->execute('/vhost/', $params);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('虚拟主机部署SSL失败:' . $e->getMessage());
|
||||
}
|
||||
} else {
|
||||
$params = [
|
||||
'act' => 'vhostssl',
|
||||
'sitename' => $config['sitename'],
|
||||
'cmd' => 'info'
|
||||
];
|
||||
try {
|
||||
$data = $this->execute('/vhost/', $params);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取虚拟主机SSL配置失败:' . $e->getMessage());
|
||||
}
|
||||
if (!empty($data['sslcert']['ssl'])) {
|
||||
foreach ($data['sslcert']['ssl'] as $domain => $row) {
|
||||
if (!in_array($domain, $config['domainList'])) continue;
|
||||
$params = [
|
||||
'act' => 'vhostssl',
|
||||
'sitename' => $config['sitename'],
|
||||
'cmd' => 'clearsslcache',
|
||||
'sslid' => $row['sysid'],
|
||||
'dm' => $domain,
|
||||
];
|
||||
try {
|
||||
$this->execute('/vhost/', $params);
|
||||
$this->log('更新' . $domain . '证书缓存成功');
|
||||
} catch (Exception $e) {
|
||||
$this->log('更新' . $domain . '证书缓存失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->log('虚拟主机' . $config['sitename'] . '部署SSL成功');
|
||||
}
|
||||
|
||||
private function execute($path, $params)
|
||||
{
|
||||
$params['username'] = $this->username;
|
||||
$params['time'] = getMillisecond();
|
||||
$params['token'] = md5($this->username . $this->api_password . $params['time']);
|
||||
$response = http_request($this->baseUrl . $path, str_replace('+', '%20', http_build_query($params)), null, null, null, $this->proxy);
|
||||
$response = mb_convert_encoding($response['body'], 'UTF-8', 'GBK');
|
||||
$arr = json_decode($response, true);
|
||||
if ($arr) {
|
||||
if ($arr['result'] == 200) {
|
||||
return isset($arr['data']) ? $arr['data'] : [];
|
||||
} else {
|
||||
throw new Exception($arr['msg']);
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,6 +253,56 @@ class aliyun implements DnsInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取权重配置子域名列表
|
||||
public function getWeightSubDomains($PageNumber = 1, $PageSize = 20, $SubDomain = null)
|
||||
{
|
||||
$param = ['Action' => 'DescribeDNSSLBSubDomains', 'DomainName' => $this->domain, 'PageNumber' => $PageNumber, 'PageSize' => $PageSize];
|
||||
if (!empty($SubDomain)) {
|
||||
$param += ['Rr' => $SubDomain];
|
||||
}
|
||||
$data = $this->request($param, true);
|
||||
if ($data) {
|
||||
$list = $data['SlbSubDomains']['SlbSubDomain'];
|
||||
$i = 1;
|
||||
foreach ($list as &$v) {
|
||||
$v['id'] = $i++;
|
||||
$v['rr'] = substr($v['SubDomain'], 0, -strlen($this->domain) - 1);
|
||||
}
|
||||
return ['total' => $data['TotalCount'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//开启关闭权重配置
|
||||
public function setWeightStatus($SubDomain, $Open, $Type = null, $Line = null)
|
||||
{
|
||||
$param = ['Action' => 'SetDNSSLBStatus', 'DomainName' => $this->domain, 'SubDomain' => $SubDomain, 'Open' => $Open == '1' ? 'true' : 'false'];
|
||||
if (!empty($Type)) {
|
||||
$param += ['Type' => $Type];
|
||||
}
|
||||
if (!empty($Line)) {
|
||||
$param += ['Line' => $Line];
|
||||
}
|
||||
return $this->request($param);
|
||||
}
|
||||
|
||||
//修改权重
|
||||
public function updateRecordWeight($RecordId, $Weight)
|
||||
{
|
||||
$param = ['Action' => 'UpdateDNSSLBWeight', 'RecordId' => $RecordId, 'Weight' => $Weight];
|
||||
return $this->request($param);
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
$param = ['Action' => 'AddDomain', 'DomainName' => $Domain];
|
||||
$result = $this->request($param, true);
|
||||
if ($result) {
|
||||
return ['id' => $result['DomainId'], 'name' => $result['DomainName']];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function convertLineCode($line)
|
||||
{
|
||||
$convert_dict = ['0' => 'default', '10=1' => 'unicom', '10=0' => 'telecom', '10=3' => 'mobile', '10=2' => 'edu', '3=0' => 'oversea', '10=22' => 'btvn', '80=0' => 'search', '7=0' => 'internal'];
|
||||
@@ -265,13 +315,13 @@ class aliyun implements DnsInterface
|
||||
private function request($param, $returnData = false)
|
||||
{
|
||||
if (empty($this->AccessKeyId) || empty($this->AccessKeySecret)) return false;
|
||||
try{
|
||||
try {
|
||||
$result = $this->client->request($param);
|
||||
}catch(Exception $e){
|
||||
try{
|
||||
} catch (Exception $e) {
|
||||
try {
|
||||
usleep(50000);
|
||||
$result = $this->client->request($param);
|
||||
}catch(Exception $e){
|
||||
} catch (Exception $e) {
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -61,8 +61,9 @@ class baidu implements DnsInterface
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
$query = ['rr' => $KeyWord];
|
||||
$query = [];
|
||||
if (!isNullOrEmpty($SubDomain)) {
|
||||
$SubDomain = strtolower($SubDomain);
|
||||
$query['rr'] = $SubDomain;
|
||||
}
|
||||
$data = $this->send_reuqest('GET', '/v1/dns/zone/'.$this->domain.'/record', $query);
|
||||
@@ -84,6 +85,32 @@ class baidu implements DnsInterface
|
||||
'UpdateTime' => null,
|
||||
];
|
||||
}
|
||||
if (!isNullOrEmpty($SubDomain)) {
|
||||
$list = array_values(array_filter($list, function ($v) use ($SubDomain) {
|
||||
return $v['Name'] == $SubDomain;
|
||||
}));
|
||||
} else {
|
||||
if (!isNullOrEmpty($KeyWord)) {
|
||||
$list = array_values(array_filter($list, function ($v) use ($KeyWord) {
|
||||
return strpos($v['Name'], $KeyWord) !== false || strpos($v['Value'], $KeyWord) !== false;
|
||||
}));
|
||||
}
|
||||
if (!isNullOrEmpty($Value)) {
|
||||
$list = array_values(array_filter($list, function ($v) use ($Value) {
|
||||
return $v['Value'] == $Value;
|
||||
}));
|
||||
}
|
||||
if (!isNullOrEmpty($Type)) {
|
||||
$list = array_values(array_filter($list, function ($v) use ($Type) {
|
||||
return $v['Type'] == $Type;
|
||||
}));
|
||||
}
|
||||
if (!isNullOrEmpty($Status)) {
|
||||
$list = array_values(array_filter($list, function ($v) use ($Status) {
|
||||
return $v['Status'] == $Status;
|
||||
}));
|
||||
}
|
||||
}
|
||||
return ['total' => count($list), 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
@@ -195,6 +222,19 @@ class baidu implements DnsInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
$query = ['clientToken' => getSid(), 'name' => $Domain];
|
||||
$res = $this->send_reuqest('POST', '/v1/dns/zone', null, $query);
|
||||
if ($res) {
|
||||
$data = $this->getDomainInfo($Domain);
|
||||
if ($data) {
|
||||
return ['id' => $data['DomainId'], 'name' => $data['Domain']];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function convertType($type)
|
||||
{
|
||||
return $type;
|
||||
|
||||
@@ -75,17 +75,19 @@ class cloudflare implements DnsInterface
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['result'] as $row) {
|
||||
$name = $row['zone_name'] == $row['name'] ? '@' : str_replace('.'.$row['zone_name'], '', $row['name']);
|
||||
$name = $this->domain == $row['name'] ? '@' : str_replace('.'.$this->domain, '', $row['name']);
|
||||
$status = str_ends_with($name, '_pause') ? '0' : '1';
|
||||
$name = $status == '0' ? substr($name, 0, -6) : $name;
|
||||
$list[] = [
|
||||
'RecordId' => $row['id'],
|
||||
'Domain' => $row['zone_name'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $name,
|
||||
'Type' => $row['type'],
|
||||
'Value' => $row['content'],
|
||||
'Line' => $row['proxied'] ? '1' : '0',
|
||||
'TTL' => $row['ttl'],
|
||||
'MX' => isset($row['priority']) ? $row['priority'] : null,
|
||||
'Status' => '1',
|
||||
'Status' => $status,
|
||||
'Weight' => null,
|
||||
'Remark' => $row['comment'],
|
||||
'UpdateTime' => $row['modified_on'],
|
||||
@@ -107,17 +109,19 @@ class cloudflare implements DnsInterface
|
||||
{
|
||||
$data = $this->send_reuqest('GET', '/zones/'.$this->domainid.'/dns_records/'.$RecordId);
|
||||
if ($data) {
|
||||
$name = $data['result']['zone_name'] == $data['result']['name'] ? '@' : str_replace('.' . $data['result']['zone_name'], '', $data['result']['name']);
|
||||
$name = $this->domain == $data['result']['name'] ? '@' : str_replace('.' . $this->domain, '', $data['result']['name']);
|
||||
$status = str_ends_with($name, '_pause') ? '0' : '1';
|
||||
$name = $status == '0' ? substr($name, 0, -6) : $name;
|
||||
return [
|
||||
'RecordId' => $data['result']['id'],
|
||||
'Domain' => $data['result']['zone_name'],
|
||||
'Name' => str_replace('.'.$data['result']['zone_name'], '', $data['result']['name']),
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $name,
|
||||
'Type' => $data['result']['type'],
|
||||
'Value' => $data['result']['content'],
|
||||
'Line' => $data['result']['proxied'] ? '1' : '0',
|
||||
'TTL' => $data['result']['ttl'],
|
||||
'MX' => isset($data['result']['priority']) ? $data['result']['priority'] : null,
|
||||
'Status' => '1',
|
||||
'Status' => $status,
|
||||
'Weight' => null,
|
||||
'Remark' => $data['result']['comment'],
|
||||
'UpdateTime' => $data['result']['modified_on'],
|
||||
@@ -168,7 +172,9 @@ class cloudflare implements DnsInterface
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
return false;
|
||||
$info = $this->getDomainRecordInfo($RecordId);
|
||||
$Name = $Status == '1' ? str_replace('_pause', '', $info['Name']) : $info['Name'] . '_pause';
|
||||
return $this->updateDomainRecord($RecordId, $Name, $info['Type'], $info['Value'], $info['Line'], $info['TTL'], $info['MX'], $info['Weight'], $info['Remark']);
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
@@ -199,6 +205,16 @@ class cloudflare implements DnsInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
$param = ['name' => $Domain];
|
||||
$data = $this->send_reuqest('POST', '/zones', $param);
|
||||
if ($data) {
|
||||
return ['id' => $data['result']['id'], 'name' => $data['result']['name']];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function convertType($type)
|
||||
{
|
||||
$convert_dict = ['REDIRECT_URL' => 'URI', 'FORWARD_URL' => 'URI'];
|
||||
|
||||
@@ -210,6 +210,16 @@ class dnsla implements DnsInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
$param = ['domain' => $Domain];
|
||||
$data = $this->execute('POST', '/api/domain', $param);
|
||||
if ($data) {
|
||||
return ['id' => $data['id'], 'name' => $Domain];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function convertType($type)
|
||||
{
|
||||
$typeList = array_flip($this->typeList);
|
||||
|
||||
@@ -314,6 +314,19 @@ class dnspod implements DnsInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
$action = 'CreateDomain';
|
||||
$param = [
|
||||
'Domain' => $Domain,
|
||||
];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
return ['id' => $data['DomainInfo']['Id'], 'name' => $data['DomainInfo']['Domain']];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function convertLineCode($line)
|
||||
{
|
||||
$convert_dict = ['default' => '0', 'unicom' => '10=1', 'telecom' => '10=0', 'mobile' => '10=3', 'edu' => '10=2', 'oversea' => '3=0', 'btvn' => '10=22', 'search' => '80=0', 'internal' => '7=0'];
|
||||
|
||||
@@ -229,6 +229,18 @@ class huawei implements DnsInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
$params = [
|
||||
'name' => $Domain,
|
||||
];
|
||||
$data = $this->send_request('POST', '/v2/zones', null, $params);
|
||||
if ($data) {
|
||||
return ['id' => $data['id'], 'name' => rtrim($data['name'], '.')];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function convertType($type)
|
||||
{
|
||||
return $type;
|
||||
|
||||
@@ -77,7 +77,7 @@ class huoshan implements DnsInterface
|
||||
{
|
||||
$query = ['ZID' => intval($this->domainid), 'PageNumber' => $PageNumber, 'PageSize' => $PageSize, 'SearchOrder' => 'desc'];
|
||||
if (!empty($SubDomain) || !empty($Type) || !empty($Line) || !empty($Value)) {
|
||||
$query += ['Host' => $SubDomain, 'Value' => $Value, 'Type' => $Type, 'Line' => $Line];
|
||||
$query += ['Host' => $SubDomain, 'Value' => $Value, 'Type' => $Type, 'Line' => $Line, 'SearchMode' => 'exact'];
|
||||
} elseif (!empty($KeyWord)) {
|
||||
$query += ['Host' => $KeyWord];
|
||||
}
|
||||
@@ -237,6 +237,16 @@ class huoshan implements DnsInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
$params = ['ZoneName' => $Domain];
|
||||
$data = $this->send_request('POST', 'CreateZone', $params);
|
||||
if ($data) {
|
||||
return ['id' => $data['ZID'], 'name' => $data['ZoneName']];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function convertType($type)
|
||||
{
|
||||
return $type;
|
||||
|
||||
263
app/lib/dns/jdcloud.php
Normal file
263
app/lib/dns/jdcloud.php
Normal file
@@ -0,0 +1,263 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
use app\lib\client\Jdcloud as JdcloudClient;
|
||||
use Exception;
|
||||
|
||||
class jdcloud implements DnsInterface
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $AccessKeySecret;
|
||||
private $endpoint = "domainservice.jdcloud-api.com";
|
||||
private $service = "domainservice";
|
||||
private $version = "v2";
|
||||
private $region = "cn-north-1";
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private $domainInfo;
|
||||
private JdcloudClient $client;
|
||||
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKeyId = $config['ak'];
|
||||
$this->AccessKeySecret = $config['sk'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new JdcloudClient($this->AccessKeyId, $this->AccessKeySecret, $this->endpoint, $this->service, $this->region, $proxy);
|
||||
$this->domain = $config['domain'];
|
||||
$this->domainid = $config['domainid'];
|
||||
}
|
||||
|
||||
public function getError()
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if ($this->getDomainList() != false) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名列表
|
||||
public function getDomainList($KeyWord = null, $PageNumber = 1, $PageSize = 20)
|
||||
{
|
||||
$query = ['pageNumber' => $PageNumber, 'pageSize' => $PageSize, 'domainName' => $KeyWord];
|
||||
$data = $this->send_request('GET', '/domain', $query);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
if (!empty($data['dataList'])) {
|
||||
foreach ($data['dataList'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['id'],
|
||||
'Domain' => $row['domainName'],
|
||||
'RecordCount' => 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
return ['total' => $data['totalCount'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
if ($PageSize > 99) $PageSize = 99;
|
||||
$query = ['pageNumber' => $PageNumber, 'pageSize' => $PageSize];
|
||||
if (!isNullOrEmpty($SubDomain)) {
|
||||
$SubDomain = strtolower($SubDomain);
|
||||
$query += ['search' => $SubDomain];
|
||||
} elseif (!isNullOrEmpty($KeyWord)) {
|
||||
$query += ['search' => $KeyWord];
|
||||
}
|
||||
$data = $this->send_request('GET', '/domain/'.$this->domainid.'/ResourceRecord', $query);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['dataList'] as $row) {
|
||||
if ($row['type'] == 'SRV') {
|
||||
$row['hostValue'] = $row['mxPriority'].' '.$row['weight'].' '.$row['port'].' '.$row['hostValue'];
|
||||
}
|
||||
$list[] = [
|
||||
'RecordId' => $row['id'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $row['hostRecord'],
|
||||
'Type' => $row['type'],
|
||||
'Value' => $row['hostValue'],
|
||||
'Line' => array_pop($row['viewValue']),
|
||||
'TTL' => $row['ttl'],
|
||||
'MX' => isset($row['mxPriority']) ? $row['mxPriority'] : null,
|
||||
'Status' => $row['resolvingStatus'] == '2' ? '1' : '0',
|
||||
'Weight' => $row['weight'],
|
||||
'Remark' => null,
|
||||
'UpdateTime' => date('Y-m-d H:i:s', $row['updateTime']),
|
||||
];
|
||||
}
|
||||
if (!isNullOrEmpty($SubDomain) && !empty($list)) {
|
||||
$list = array_values(array_filter($list, function ($v) use ($SubDomain) {
|
||||
return $v['Name'] == $SubDomain;
|
||||
}));
|
||||
}
|
||||
return ['total' => $data['totalCount'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null)
|
||||
{
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line);
|
||||
}
|
||||
|
||||
//获取解析记录详细信息
|
||||
public function getDomainRecordInfo($RecordId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//添加解析记录
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$params = ['hostRecord' => $Name, 'type' => $this->convertType($Type), 'hostValue' => $Value, 'viewValue' => intval($Line), 'ttl' => intval($TTL)];
|
||||
if ($Type == 'MX') $params['mxPriority'] = intval($MX);
|
||||
if (!isNullOrEmpty($Weight)) $params['weight'] = intval($Weight);
|
||||
if ($Type == 'SRV') {
|
||||
$values = explode(' ', $Value);
|
||||
$params['mxPriority'] = intval($values[0]);
|
||||
$params['weight'] = intval($values[1]);
|
||||
$params['port'] = intval($values[2]);
|
||||
$params['hostValue'] = $values[3];
|
||||
}
|
||||
$data = $this->send_request('POST', '/domain/'.$this->domainid.'/ResourceRecord', ['req'=>$params]);
|
||||
return is_array($data) ? $data['dataList']['id'] : false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$params = ['domainName'=>$this->domain, 'hostRecord' => $Name, 'type' => $this->convertType($Type), 'hostValue' => $Value, 'viewValue' => intval($Line), 'ttl' => intval($TTL)];
|
||||
if ($Type == 'MX') $params['mxPriority'] = intval($MX);
|
||||
if (!isNullOrEmpty($Weight)) $params['weight'] = intval($Weight);
|
||||
if ($Type == 'SRV') {
|
||||
$values = explode(' ', $Value);
|
||||
$params['mxPriority'] = intval($values[0]);
|
||||
$params['weight'] = intval($values[1]);
|
||||
$params['port'] = intval($values[2]);
|
||||
$params['hostValue'] = $values[3];
|
||||
}
|
||||
return $this->send_request('PUT', '/domain/'.$this->domainid.'/ResourceRecord/'.$RecordId, ['req'=>$params]);
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
return $this->send_request('DELETE', '/domain/'.$this->domainid.'/ResourceRecord/'.$RecordId);
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$params = ['action' => $Status == '1' ? 'enable' : 'disable'];
|
||||
$data = $this->send_request('PUT', '/domain/'.$this->domainid.'/ResourceRecord/'.$RecordId.'/status', $params);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
public function getRecordLine()
|
||||
{
|
||||
$domainInfo = $this->getDomainInfo();
|
||||
if (!$domainInfo) return false;
|
||||
$packId = $domainInfo['packId'];
|
||||
$data = $this->send_request('GET', '/domain/'.$this->domainid.'/viewTree', ['packId'=>$packId, 'viewId'=>'0']);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
$this->processLineList($list, $data['data'], null);
|
||||
return $list;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function processLineList(&$list, $line_list, $parent)
|
||||
{
|
||||
foreach ($line_list as $row) {
|
||||
if ($row['disabled']) continue;
|
||||
if (!isset($list[$row['value']])) {
|
||||
$list[$row['value']] = ['name' => $row['label'], 'parent' => $parent];
|
||||
if (!$row['leaf'] && $row['children']) {
|
||||
$this->processLineList($list, $row['children'], $row['value']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//获取域名概览信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
if (!empty($this->domainInfo)) return $this->domainInfo;
|
||||
$query = ['domainId' => intval($this->domainid)];
|
||||
$data = $this->send_request('GET', '/domain', $query);
|
||||
if ($data && $data['dataList']) {
|
||||
return $data['dataList'][0];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
$params = ['packId' => 0, 'domainName' => $Domain];
|
||||
$data = $this->send_request('POST', '/domain', $params);
|
||||
if ($data) {
|
||||
return ['id' => $data['data']['id'], 'name' => $data['data']['domainName']];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function convertType($type)
|
||||
{
|
||||
$convert_dict = ['REDIRECT_URL' => 'EXPLICIT_URL', 'FORWARD_URL' => 'IMPLICIT_URL'];
|
||||
if (array_key_exists($type, $convert_dict)) {
|
||||
return $convert_dict[$type];
|
||||
}
|
||||
return $type;
|
||||
}
|
||||
|
||||
private function send_request($method, $action, $params = [])
|
||||
{
|
||||
$path = '/'.$this->version.'/regions/'.$this->region.$action;
|
||||
try{
|
||||
return $this->client->request($method, $path, $params);
|
||||
}catch(Exception $e){
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function setError($message)
|
||||
{
|
||||
$this->error = $message;
|
||||
//file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
@@ -79,22 +79,22 @@ class namesilo implements DnsInterface
|
||||
'UpdateTime' => null,
|
||||
];
|
||||
}
|
||||
if(!empty($SubDomain)){
|
||||
if(!isNullOrEmpty($SubDomain)){
|
||||
$list = array_values(array_filter($list, function($v) use ($SubDomain){
|
||||
return $v['Name'] == $SubDomain;
|
||||
return strcasecmp($v['Name'], $SubDomain) === 0;
|
||||
}));
|
||||
}else{
|
||||
if(!empty($KeyWord)){
|
||||
if(!isNullOrEmpty($KeyWord)){
|
||||
$list = array_values(array_filter($list, function($v) use ($KeyWord){
|
||||
return strpos($v['Name'], $KeyWord) !== false || strpos($v['Value'], $KeyWord) !== false;
|
||||
}));
|
||||
}
|
||||
if(!empty($Value)){
|
||||
if(!isNullOrEmpty($Value)){
|
||||
$list = array_values(array_filter($list, function($v) use ($Value){
|
||||
return $v['Value'] == $Value;
|
||||
}));
|
||||
}
|
||||
if(!empty($Type)){
|
||||
if(!isNullOrEmpty($Type)){
|
||||
$list = array_values(array_filter($list, function($v) use ($Type){
|
||||
return $v['Type'] == $Type;
|
||||
}));
|
||||
@@ -118,8 +118,9 @@ class namesilo implements DnsInterface
|
||||
}
|
||||
|
||||
//添加解析记录
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
if ($Name == '@') $Name = '';
|
||||
$param = ['domain' => $this->domain, 'rrtype' => $Type, 'rrhost' => $Name, 'rrvalue' => $Value, 'rrttl' => $TTL];
|
||||
if ($Type == 'MX') $param['rrdistance'] = intval($MX);
|
||||
$data = $this->send_reuqest('dnsAddRecord', $param);
|
||||
@@ -127,8 +128,9 @@ class namesilo implements DnsInterface
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
if ($Name == '@') $Name = '';
|
||||
$param = ['domain' => $this->domain, 'rrid' => $RecordId, 'rrtype' => $Type, 'rrhost' => $Name, 'rrvalue' => $Value, 'rrttl' => $TTL];
|
||||
if ($Type == 'MX') $param['rrdistance'] = intval($MX);
|
||||
$data = $this->send_reuqest('dnsUpdateRecord', $param);
|
||||
@@ -179,6 +181,11 @@ class namesilo implements DnsInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function send_reuqest($operation, $param = null)
|
||||
{
|
||||
$url = $this->baseUrl . $operation;
|
||||
@@ -195,7 +202,7 @@ class namesilo implements DnsInterface
|
||||
$url .= '?' . http_build_query($params);
|
||||
|
||||
try{
|
||||
$response = curl_client($url, null, null, null, null, $this->proxy);
|
||||
$response = http_request($url, null, null, null, null, $this->proxy);
|
||||
}catch(Exception $e){
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
|
||||
425
app/lib/dns/powerdns.php
Normal file
425
app/lib/dns/powerdns.php
Normal file
@@ -0,0 +1,425 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
use Exception;
|
||||
|
||||
class powerdns implements DnsInterface
|
||||
{
|
||||
private $url;
|
||||
private $apikey;
|
||||
private $server_id = 'localhost';
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private $proxy;
|
||||
|
||||
function __construct($config)
|
||||
{
|
||||
$this->url = 'http://' . $config['ak'] . ':' . $config['sk'] . '/api/v1';
|
||||
$this->apikey = $config['ext'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->domain = $config['domain'];
|
||||
$this->domainid = $config['domainid'];
|
||||
}
|
||||
|
||||
public function getError()
|
||||
{
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if ($this->getDomainList() !== false) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名列表
|
||||
public function getDomainList($KeyWord = null, $PageNumber = 1, $PageSize = 20)
|
||||
{
|
||||
$data = $this->send_reuqest('GET', '/servers/' . $this->server_id . '/zones');
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['id'],
|
||||
'Domain' => rtrim($row['name'], '.'),
|
||||
'RecordCount' => 0,
|
||||
];
|
||||
}
|
||||
return ['total' => count($list), 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
$data = $this->send_reuqest('GET', '/servers/' . $this->server_id . '/zones/' . $this->domainid);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
$rrset_id = 0;
|
||||
foreach ($data['rrsets'] as &$row) {
|
||||
$rrset_id++;
|
||||
$name = $row['name'] == $this->domainid ? '@' : str_replace('.' . $this->domainid, '', $row['name']);
|
||||
$row['host'] = $name;
|
||||
$row['id'] = $rrset_id;
|
||||
$record_id = 0;
|
||||
foreach ($row['records'] as &$record) {
|
||||
$record_id++;
|
||||
$record['id'] = $record_id;
|
||||
$remark = !empty($row['comments']) ? $row['comments'][0]['content'] : null;
|
||||
$value = $record['content'];
|
||||
if ($row['type'] == 'MX') list($record['mx'], $value) = explode(' ', $record['content']);
|
||||
$list[] = [
|
||||
'RecordId' => $rrset_id . '_' . $record_id,
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $name,
|
||||
'Type' => $row['type'],
|
||||
'Value' => $value,
|
||||
'Line' => 'default',
|
||||
'TTL' => $row['ttl'],
|
||||
'MX' => isset($record['mx']) ? $record['mx'] : null,
|
||||
'Status' => $record['disabled'] ? '0' : '1',
|
||||
'Weight' => null,
|
||||
'Remark' => $remark,
|
||||
'UpdateTime' => null,
|
||||
];
|
||||
}
|
||||
}
|
||||
cache('powerdns_' . $this->domainid, $data['rrsets'], 86400);
|
||||
if (!isNullOrEmpty($SubDomain)) {
|
||||
$list = array_values(array_filter($list, function ($v) use ($SubDomain) {
|
||||
return strcasecmp($v['Name'], $SubDomain) === 0;
|
||||
}));
|
||||
} else {
|
||||
if (!isNullOrEmpty($KeyWord)) {
|
||||
$list = array_values(array_filter($list, function ($v) use ($KeyWord) {
|
||||
return strpos($v['Name'], $KeyWord) !== false || strpos($v['Value'], $KeyWord) !== false;
|
||||
}));
|
||||
}
|
||||
if (!isNullOrEmpty($Value)) {
|
||||
$list = array_values(array_filter($list, function ($v) use ($Value) {
|
||||
return $v['Value'] == $Value;
|
||||
}));
|
||||
}
|
||||
if (!isNullOrEmpty($Type)) {
|
||||
$list = array_values(array_filter($list, function ($v) use ($Type) {
|
||||
return $v['Type'] == $Type;
|
||||
}));
|
||||
}
|
||||
if (!isNullOrEmpty($Status)) {
|
||||
$list = array_values(array_filter($list, function ($v) use ($Status) {
|
||||
return $v['Status'] == $Status;
|
||||
}));
|
||||
}
|
||||
}
|
||||
return ['total' => count($list), 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null)
|
||||
{
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line);
|
||||
}
|
||||
|
||||
//获取解析记录详细信息
|
||||
public function getDomainRecordInfo($RecordId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//添加解析记录
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
if ($Type == 'TXT' && substr($Value, 0, 1) != '"') $Value = '"' . $Value . '"';
|
||||
if (($Type == 'CNAME' || $Type == 'MX') && substr($Value, -1) != '.') $Value .= '.';
|
||||
if ($Type == 'MX') $Value = intval($MX) . ' ' . $Value;
|
||||
$records = [];
|
||||
$rrsets = cache('powerdns_' . $this->domainid);
|
||||
if ($rrsets) {
|
||||
$rrsets_filter = array_filter($rrsets, function ($row) use ($Name, $Type) {
|
||||
return $row['host'] == $Name && $row['type'] == $Type;
|
||||
});
|
||||
if (!empty($rrsets_filter)) {
|
||||
$rrset = $rrsets_filter[array_key_first($rrsets_filter)];
|
||||
$records = $rrset['records'];
|
||||
$records_filter = array_filter($records, function ($record) use ($Value) {
|
||||
return $record['content'] == $Value;
|
||||
});
|
||||
if (!empty($records_filter)) {
|
||||
$this->setError('已存在相同记录');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
$records[] = ['content' => $Value, 'disabled' => false];
|
||||
return $this->rrset_replace($Name, $Type, $TTL, $records, $Remark);
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
if ($Type == 'TXT' && substr($Value, 0, 1) != '"') $Value = '"' . $Value . '"';
|
||||
if (($Type == 'CNAME' || $Type == 'MX') && substr($Value, -1) != '.') $Value .= '.';
|
||||
if ($Type == 'MX') $Value = intval($MX) . ' ' . $Value;
|
||||
$rrsets = cache('powerdns_' . $this->domainid);
|
||||
$add = false;
|
||||
$res = false;
|
||||
if ($rrsets) {
|
||||
[$rrset_id, $record_id] = explode('_', $RecordId);
|
||||
$exist = false;
|
||||
foreach ($rrsets as &$rrset) {
|
||||
if ($rrset['id'] == $rrset_id) {
|
||||
$records = $rrset['records'];
|
||||
$records_filter = array_filter($records, function ($record) use ($Value, $record_id) {
|
||||
return $record['content'] == $Value && $record['id'] != $record_id;
|
||||
});
|
||||
if (!empty($records_filter)) {
|
||||
$this->setError('已存在相同记录');
|
||||
return false;
|
||||
}
|
||||
foreach ($records as $i => &$record) {
|
||||
if ($record['id'] == $record_id) {
|
||||
$exist = true;
|
||||
if ($rrset['host'] == $Name && $rrset['type'] == $Type) {
|
||||
$record['content'] = $Value;
|
||||
} else {
|
||||
unset($records[$i]);
|
||||
$add = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$exist) break;
|
||||
$records = array_values($records);
|
||||
if (!empty($records)) {
|
||||
$res = $this->rrset_replace($rrset['host'], $rrset['type'], $TTL, $records, $Remark);
|
||||
} else {
|
||||
$res = $this->rrset_delete($rrset['host'], $rrset['type']);
|
||||
}
|
||||
$rrset['records'] = $records;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$exist) {
|
||||
$this->setError('记录不存在,请刷新页面重试');
|
||||
return false;
|
||||
}
|
||||
cache('powerdns_' . $this->domainid, $rrsets, 86400);
|
||||
if ($res && $add) {
|
||||
$res = $this->addDomainRecord($Name, $Type, $Value, $Line, $TTL, $MX, $Weight, $Remark);
|
||||
}
|
||||
return $res;
|
||||
} else {
|
||||
$records[] = ['content' => $Value, 'disabled' => false];
|
||||
return $this->addDomainRecord($Name, $Type, $Value, $Line, $TTL, $MX, $Weight, $Remark);
|
||||
}
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$rrsets = cache('powerdns_' . $this->domainid);
|
||||
if (!$rrsets) {
|
||||
$this->setError('记录不存在,请刷新页面重试');
|
||||
return false;
|
||||
}
|
||||
[$rrset_id, $record_id] = explode('_', $RecordId);
|
||||
$exist = false;
|
||||
$res = false;
|
||||
foreach ($rrsets as &$rrset) {
|
||||
if ($rrset['id'] == $rrset_id) {
|
||||
$records = $rrset['records'];
|
||||
foreach ($records as $i => &$record) {
|
||||
if ($record['id'] == $record_id) {
|
||||
$exist = true;
|
||||
unset($records[$i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$exist) break;
|
||||
$records = array_values($records);
|
||||
if (!empty($records)) {
|
||||
$res = $this->rrset_replace($rrset['host'], $rrset['type'], $rrset['ttl'], $records);
|
||||
} else {
|
||||
$res = $this->rrset_delete($rrset['host'], $rrset['type']);
|
||||
}
|
||||
$rrset['records'] = $records;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$exist) {
|
||||
$this->setError('记录不存在,请刷新页面重试');
|
||||
return false;
|
||||
}
|
||||
cache('powerdns_' . $this->domainid, $rrsets, 86400);
|
||||
return $res;
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$rrsets = cache('powerdns_' . $this->domainid);
|
||||
if (!$rrsets) {
|
||||
$this->setError('记录不存在,请刷新页面重试');
|
||||
return false;
|
||||
}
|
||||
[$rrset_id, $record_id] = explode('_', $RecordId);
|
||||
$exist = false;
|
||||
$res = false;
|
||||
foreach ($rrsets as &$rrset) {
|
||||
if ($rrset['id'] == $rrset_id) {
|
||||
$records = $rrset['records'];
|
||||
foreach ($records as &$record) {
|
||||
if ($record['id'] == $record_id) {
|
||||
$exist = true;
|
||||
$record['disabled'] = $Status == '0';
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$exist) break;
|
||||
$res = $this->rrset_replace($rrset['host'], $rrset['type'], $rrset['ttl'], $records);
|
||||
$rrset['records'] = $records;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$exist) {
|
||||
$this->setError('记录不存在,请刷新页面重试');
|
||||
return false;
|
||||
}
|
||||
cache('powerdns_' . $this->domainid, $rrsets, 86400);
|
||||
return $res;
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
public function getRecordLine()
|
||||
{
|
||||
return ['default' => ['name' => '默认', 'parent' => null]];
|
||||
}
|
||||
|
||||
//获取域名信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
if (substr($Domain, -1) != '.') {
|
||||
$Domain .= '.';
|
||||
}
|
||||
$param = [
|
||||
'name' => $Domain,
|
||||
'kind' => 'Native',
|
||||
'soa_edit_api' => 'INCREASE',
|
||||
];
|
||||
$result = $this->send_reuqest('POST', '/servers/' . $this->server_id . '/zones', $param);
|
||||
if ($result) {
|
||||
return ['id' => $result['id'], 'name' => rtrim($result['name'], '.')];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function rrset_replace($host, $type, $ttl, $records, $remark = null)
|
||||
{
|
||||
$name = $host == '@' ? $this->domainid : $host . '.' . $this->domainid;
|
||||
$rrset = [
|
||||
'name' => $name,
|
||||
'type' => $type,
|
||||
'ttl' => intval($ttl),
|
||||
'changetype' => 'REPLACE',
|
||||
'records' => $records,
|
||||
'comments' => [],
|
||||
];
|
||||
if (!empty($remark)) {
|
||||
$rrset['comments'] = [
|
||||
['account' => '', 'content' => $remark]
|
||||
];
|
||||
}
|
||||
$param = [
|
||||
'rrsets' => [
|
||||
$rrset
|
||||
],
|
||||
];
|
||||
return $this->send_reuqest('PATCH', '/servers/' . $this->server_id . '/zones/' . $this->domainid, $param);
|
||||
}
|
||||
|
||||
private function rrset_delete($host, $type)
|
||||
{
|
||||
$name = $host == '@' ? $this->domainid : $host . '.' . $this->domainid;
|
||||
$param = [
|
||||
'rrsets' => [
|
||||
[
|
||||
'name' => $name,
|
||||
'type' => $type,
|
||||
'changetype' => 'DELETE',
|
||||
]
|
||||
],
|
||||
];
|
||||
return $this->send_reuqest('PATCH', '/servers/' . $this->server_id . '/zones/' . $this->domainid, $param);
|
||||
}
|
||||
|
||||
private function send_reuqest($method, $path, $params = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers['X-API-Key'] = $this->apikey;
|
||||
$body = null;
|
||||
if ($method == 'GET' || $method == 'DELETE') {
|
||||
if ($params) {
|
||||
$url .= '?' . http_build_query($params);
|
||||
}
|
||||
} else {
|
||||
$body = json_encode($params);
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
try {
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy, $method);
|
||||
} catch (Exception $e) {
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
$arr = json_decode($response['body'], true);
|
||||
if ($response['code'] < 400) {
|
||||
return is_array($arr) ? $arr : true;
|
||||
} elseif (isset($arr['error'])) {
|
||||
$this->setError($arr['error']);
|
||||
return false;
|
||||
} elseif (isset($arr['errors'])) {
|
||||
$this->setError(implode(',', $arr['errors']));
|
||||
return false;
|
||||
} else {
|
||||
$this->setError($response['body']);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function setError($message)
|
||||
{
|
||||
$this->error = $message;
|
||||
}
|
||||
}
|
||||
@@ -171,6 +171,11 @@ class west implements DnsInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function convertType($type)
|
||||
{
|
||||
return $type;
|
||||
@@ -182,7 +187,7 @@ class west implements DnsInterface
|
||||
$params['time'] = getMillisecond();
|
||||
$params['token'] = md5($this->username.$this->api_password.$params['time']);
|
||||
try{
|
||||
$response = curl_client($this->baseUrl . $path, http_build_query($params), null, null, null, $this->proxy);
|
||||
$response = http_request($this->baseUrl . $path, http_build_query($params), null, null, null, $this->proxy);
|
||||
}catch(\Exception $e){
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
<?php
|
||||
namespace app\lib\mail\PHPMailer; class Exception extends \Exception { public function errorMessage() { return '<strong>' . htmlspecialchars($this->getMessage(), ENT_COMPAT | ENT_HTML401) . "</strong><br />\n"; } }
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -29,7 +29,7 @@ class CertDeployService
|
||||
$this->client = DeployHelper::getModel($this->aid);
|
||||
if (!$this->client) throw new Exception('该自动部署任务类型不存在', 102);
|
||||
|
||||
$this->info = $task['info'] ? json_decode($task['info'], true) : null;
|
||||
$this->info = $task['info'] ? json_decode($task['info'], true) : [];
|
||||
}
|
||||
|
||||
public function process($isManual = false)
|
||||
@@ -79,7 +79,7 @@ class CertDeployService
|
||||
//重置任务
|
||||
public function reset()
|
||||
{
|
||||
Db::name('cert_deploy')->where('id', $this->task['id'])->data(['status' => 0, 'retry' => 0, 'retrytime' => null, 'issend' => 0])->update();
|
||||
Db::name('cert_deploy')->where('id', $this->task['id'])->data(['status' => 0, 'retry' => 0, 'retrytime' => null, 'issend' => 0, 'islock' => 0])->update();
|
||||
//$file_name = app()->getRuntimePath().'log/'.$this->task['processid'].'.log';
|
||||
//if (file_exists($file_name)) unlink($file_name);
|
||||
$this->task['status'] = 0;
|
||||
@@ -89,6 +89,9 @@ class CertDeployService
|
||||
private function saveResult($status, $error = null, $retrytime = null)
|
||||
{
|
||||
$this->task['status'] = $status;
|
||||
if (!empty($error) && strlen($error) > 300) {
|
||||
$error = mb_strcut($error, 0, 300);
|
||||
}
|
||||
$update = ['status' => $status, 'error' => $error, 'retrytime' => $retrytime];
|
||||
if ($status == 1){
|
||||
$update['retry'] = 0;
|
||||
|
||||
@@ -68,7 +68,11 @@ class CertOrderService
|
||||
$cname = CertHelper::$cert_config[$this->atype]['cname'];
|
||||
foreach($this->domainList as $domain){
|
||||
$mainDomain = getMainDomain($domain);
|
||||
if (!Db::name('domain')->where('name', $mainDomain)->find()) {
|
||||
$drow = Db::name('domain')->where('name', $mainDomain)->find();
|
||||
if (!$drow && preg_match('/^xn--/', $mainDomain)) {
|
||||
$drow = Db::name('domain')->where('name', idn_to_utf8($mainDomain))->find();
|
||||
}
|
||||
if (!$drow) {
|
||||
if (substr($domain, 0, 2) == '*.') $domain = substr($domain, 2);
|
||||
$cname_row = Db::name('cert_cname')->where('domain', $domain)->where('status', 1)->find();
|
||||
if (!$cname || !$cname_row) {
|
||||
@@ -117,7 +121,9 @@ class CertOrderService
|
||||
$this->saveLog(date('Y-m-d H:i:s').' - 开始添加DNS记录');
|
||||
$this->addDns();
|
||||
$this->saveLog('添加DNS记录成功,请等待生效后进行验证...');
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->update(['retrytime' => date('Y-m-d H:i:s', time() + 300)]);
|
||||
if (CertHelper::$cert_config[$this->atype]['cname']) {
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->update(['retrytime' => date('Y-m-d H:i:s', time() + 180)]);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
// step4: 查询DNS
|
||||
@@ -172,6 +178,9 @@ class CertOrderService
|
||||
private function saveResult($status, $error = null, $retrytime = null)
|
||||
{
|
||||
$this->order['status'] = $status;
|
||||
if (!empty($error) && 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) {
|
||||
@@ -203,7 +212,7 @@ class CertOrderService
|
||||
//重置订单
|
||||
public function reset()
|
||||
{
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->data(['status' => 0, 'retry' => 0, 'retry2' => 0, 'retrytime' => null, 'processid' => null, 'updatetime' => date('Y-m-d H:i:s'), 'issend' => 0])->update();
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->data(['status' => 0, 'retry' => 0, 'retry2' => 0, 'retrytime' => null, 'processid' => null, 'updatetime' => date('Y-m-d H:i:s'), 'issend' => 0, 'islock' => 0])->update();
|
||||
$file_name = app()->getRuntimePath().'log/'.$this->order['processid'].'.log';
|
||||
if (file_exists($file_name)) unlink($file_name);
|
||||
$this->order['status'] = 0;
|
||||
|
||||
@@ -11,18 +11,26 @@ class CertTaskService
|
||||
|
||||
public function execute()
|
||||
{
|
||||
$this->execute_deploy();
|
||||
$this->execute_order();
|
||||
echo 'done'.PHP_EOL;
|
||||
if ($this->execute_deploy()) {
|
||||
config_set('certdeploy_time', date("Y-m-d H:i:s"));
|
||||
}
|
||||
if ($this->execute_order()) {
|
||||
config_set('certtask_time', date("Y-m-d H:i:s"));
|
||||
}
|
||||
}
|
||||
|
||||
private function execute_order()
|
||||
{
|
||||
echo '开始执行SSL证书签发任务...'.PHP_EOL;
|
||||
$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 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) {
|
||||
@@ -49,6 +57,7 @@ class CertTaskService
|
||||
if ($failcount >= 3) break;
|
||||
sleep(1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function execute_deploy()
|
||||
@@ -58,15 +67,16 @@ class CertTaskService
|
||||
$hour = date('H');
|
||||
if($start <= $end){
|
||||
if($hour < $start || $hour > $end){
|
||||
echo '不在部署任务运行时间范围内'.PHP_EOL; return;
|
||||
echo '不在部署任务运行时间范围内'.PHP_EOL; return false;
|
||||
}
|
||||
}else{
|
||||
if($hour < $start && $hour > $end){
|
||||
echo '不在部署任务运行时间范围内'.PHP_EOL; return;
|
||||
echo '不在部署任务运行时间范围内'.PHP_EOL; return false;
|
||||
}
|
||||
}
|
||||
|
||||
$list = Db::name('cert_deploy')->field('id,status,issend')->whereRaw('status IN (0,-1) AND (retrytime IS NULL OR retrytime<NOW())')->select();
|
||||
echo '开始执行SSL证书部署任务...'.PHP_EOL;
|
||||
$list = Db::name('cert_deploy')->field('id,status,issend')->whereRaw('active=1 AND status IN (0,-1) AND (retrytime IS NULL OR retrytime<NOW())')->select();
|
||||
//print_r($list);exit;
|
||||
$count = 0;
|
||||
foreach ($list as $row) {
|
||||
@@ -89,5 +99,6 @@ class CertTaskService
|
||||
if ($count >= 3) break;
|
||||
sleep(1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
104
app/service/ExpireNoticeService.php
Normal file
104
app/service/ExpireNoticeService.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use Exception;
|
||||
use think\facade\Db;
|
||||
use app\utils\MsgNotice;
|
||||
|
||||
/**
|
||||
* 域名到期提醒
|
||||
*/
|
||||
class ExpireNoticeService
|
||||
{
|
||||
|
||||
public function updateDomainDate($id, $domain)
|
||||
{
|
||||
try {
|
||||
[$regTime, $expireTime] = getDomainDate($domain);
|
||||
Db::name('domain')->where('id', $id)->update(['regtime' => $regTime, 'expiretime' => $expireTime, 'checktime' => date('Y-m-d H:i:s'), 'checkstatus' => 1]);
|
||||
return ['code' => 0, 'regTime' => $regTime, 'expireTime' => $expireTime, 'msg' => 'Success'];
|
||||
} catch (Exception $e) {
|
||||
Db::name('domain')->where('id', $id)->update(['checktime' => date('Y-m-d H:i:s'), 'checkstatus' => 2]);
|
||||
return ['code' => -1, 'msg' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
public function task()
|
||||
{
|
||||
echo '开始执行域名到期提醒任务...' . PHP_EOL;
|
||||
config_set('domain_expire_time', date("Y-m-d H:i:s"));
|
||||
$count = $this->refreshDomainList();
|
||||
if ($count > 0) return;
|
||||
|
||||
$days = config_get('expire_noticedays');
|
||||
$max_day = 30;
|
||||
if (!empty($days)) {
|
||||
$days = explode(',', $days);
|
||||
$days = array_map('intval', $days);
|
||||
$max_day = max($days) + 1;
|
||||
}
|
||||
$count = $this->refreshExpiringDomainList($max_day);
|
||||
if ($count > 0) return;
|
||||
|
||||
if (!empty($days) && (config_get('expire_notice_mail') == '1' || config_get('expire_notice_wxtpl') == '1' || config_get('expire_notice_tgbot') == '1' || config_get('expire_notice_webhook') == '1') && date('H') >= 9) {
|
||||
$this->noticeExpiringDomainList($max_day, $days);
|
||||
}
|
||||
}
|
||||
|
||||
private function refreshDomainList()
|
||||
{
|
||||
$domainList = Db::name('domain')->field('id,name')->where('expiretime', null)->where('checkstatus', 0)->select();
|
||||
$count = 0;
|
||||
foreach ($domainList as $domain) {
|
||||
$res = $this->updateDomainDate($domain['id'], $domain['name']);
|
||||
if ($res['code'] == 0) {
|
||||
echo '域名: ' . $domain['name'] . ' 注册时间: ' . $res['regTime'] . ' 到期时间: ' . $res['expireTime'] . PHP_EOL;
|
||||
} else {
|
||||
echo '域名: ' . $domain['name'] . ' 更新失败,' . $res['msg'] . PHP_EOL;
|
||||
}
|
||||
$count++;
|
||||
if ($count >= 5) break;
|
||||
sleep(1);
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
private function refreshExpiringDomainList($max_day)
|
||||
{
|
||||
$domainList = Db::name('domain')->field('id,name')->whereRaw('expiretime>=(NOW() - INTERVAL 5 DAY) AND expiretime<=(NOW() + INTERVAL ' . $max_day . ' DAY) AND checktime<=(NOW() - INTERVAL 1 DAY)')->select();
|
||||
$count = 0;
|
||||
foreach ($domainList as $domain) {
|
||||
$res = $this->updateDomainDate($domain['id'], $domain['name']);
|
||||
if ($res['code'] == 0) {
|
||||
echo '域名: ' . $domain['name'] . ' 注册时间: ' . $res['regTime'] . ' 到期时间: ' . $res['expireTime'] . PHP_EOL;
|
||||
} else {
|
||||
echo '域名: ' . $domain['name'] . ' 更新失败,' . $res['msg'] . PHP_EOL;
|
||||
}
|
||||
$count++;
|
||||
if ($count >= 5) break;
|
||||
sleep(1);
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
private function noticeExpiringDomainList($max_day, $days)
|
||||
{
|
||||
$domainList = Db::name('domain')->field('id,name,expiretime')->whereRaw('expiretime>=NOW() AND expiretime<=(NOW() + INTERVAL ' . $max_day . ' DAY) AND is_notice=1 AND (noticetime IS NULL OR noticetime<=(NOW() - INTERVAL 20 HOUR))')->order('expiretime', 'asc')->select();
|
||||
$noticeList = [];
|
||||
foreach ($domainList as $domain) {
|
||||
$expireDay = intval((strtotime($domain['expiretime']) - time()) / 86400);
|
||||
if (in_array($expireDay, $days)) {
|
||||
$noticeList[$expireDay][] = ['id' => $domain['id'], 'name' => $domain['name'], 'expiretime' => $domain['expiretime']];
|
||||
}
|
||||
}
|
||||
if (!empty($noticeList)) {
|
||||
foreach ($noticeList as $day => $list) {
|
||||
$ids = array_column($list, 'id');
|
||||
Db::name('domain')->whereIn('id', $ids)->update(['noticetime' => date('Y-m-d H:i:s')]);
|
||||
MsgNotice::expire_notice_send($day, $list);
|
||||
echo '域名到期提醒: ' . $day . '天内到期的' . count($ids) . '个域名已发送' . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,9 +39,7 @@ class OptimizeService
|
||||
public function get_ip_address($cdn_type = 1, $ip_type = 'v4')
|
||||
{
|
||||
$api = config_get('optimize_ip_api', 0);
|
||||
if ($api == 2) {
|
||||
$url = 'https://api.345673.xyz/get_data';
|
||||
} elseif ($api == 1) {
|
||||
if ($api == 1) {
|
||||
$url = 'https://api.hostmonit.com/get_optimization_ip';
|
||||
} else {
|
||||
$url = 'https://www.wetest.vip/api/cf2dns/';
|
||||
@@ -51,13 +49,15 @@ class OptimizeService
|
||||
$url .= 'get_cloudfront_ip';
|
||||
} elseif ($cdn_type == 3) {
|
||||
$url .= 'get_gcore_ip';
|
||||
} elseif ($cdn_type == 4) {
|
||||
$url .= 'get_edgeone_ip';
|
||||
}
|
||||
}
|
||||
$params = [
|
||||
'key' => config_get('optimize_ip_key', 'o1zrmHAF'),
|
||||
'type' => $ip_type,
|
||||
];
|
||||
$response = get_curl($url, json_encode($params), 0, 0, 0, 0, 0, ['Content-Type: application/json; charset=UTF-8']);
|
||||
$response = get_curl($url, json_encode($params), 0, 0, 0, 0, ['Content-Type' => 'application/json; charset=UTF-8']);
|
||||
$arr = json_decode($response, true);
|
||||
if (isset($arr['code']) && $arr['code'] == 200) {
|
||||
return $arr['info'];
|
||||
@@ -96,7 +96,15 @@ class OptimizeService
|
||||
//批量执行优选任务
|
||||
public function execute()
|
||||
{
|
||||
$minute = config_get('optimize_ip_min', '30');
|
||||
$last = config_get('optimize_ip_time', null, true);
|
||||
if ($last && strtotime($last) > time() - $minute * 60) {
|
||||
return false;
|
||||
}
|
||||
$list = Db::name('optimizeip')->where('active', 1)->select();
|
||||
if (count($list) == 0) {
|
||||
return false;
|
||||
}
|
||||
echo '开始执行IP优选任务,共获取到'.count($list).'个待执行任务'."\n";
|
||||
foreach ($list as $row) {
|
||||
try {
|
||||
@@ -108,6 +116,8 @@ class OptimizeService
|
||||
echo '优选任务'.$row['id'].'执行失败:'.$e->getMessage()."\n";
|
||||
}
|
||||
}
|
||||
config_set('optimize_ip_time', date("Y-m-d H:i:s"));
|
||||
return true;
|
||||
}
|
||||
|
||||
//执行单个优选任务
|
||||
@@ -146,17 +156,22 @@ class OptimizeService
|
||||
if (empty($iplist)) {
|
||||
continue;
|
||||
}
|
||||
$record_num = $row['recordnum'];
|
||||
$get_ips = array_column($iplist, 'ip');
|
||||
if ($drow['type'] == 'huawei') {
|
||||
sort($get_ips);
|
||||
$get_ips = array_slice($get_ips, 0, $row['recordnum']);
|
||||
$get_ips = [implode(',', $get_ips)];
|
||||
$row['recordnum'] = 1;
|
||||
$record_num = 1;
|
||||
}
|
||||
if ($row['type'] == 1 && $line == 'CT') {
|
||||
$line = 'DEF';
|
||||
}
|
||||
if (!isset(DnsHelper::$line_name[$drow['type']][$line])) {
|
||||
continue;
|
||||
}
|
||||
$line_name = DnsHelper::$line_name[$drow['type']][$line];
|
||||
$this->process_dns_line($dns, $row, $domainRecords['list'], $get_ips, $line_name, $ip_type);
|
||||
$this->process_dns_line($dns, $row, $domainRecords['list'], $record_num, $get_ips, $line_name, $ip_type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,9 +179,8 @@ class OptimizeService
|
||||
}
|
||||
|
||||
//处理单个线路的解析记录
|
||||
private function process_dns_line($dns, $row, $record_list, $get_ips, $line_name, $ip_type)
|
||||
private function process_dns_line($dns, $row, $record_list, $record_num, $get_ips, $line_name, $ip_type)
|
||||
{
|
||||
$record_num = $row['recordnum'];
|
||||
$records = array_filter($record_list, function ($v) use ($line_name) {
|
||||
return $v['Line'] == $line_name;
|
||||
});
|
||||
|
||||
118
app/service/ScheduleService.php
Normal file
118
app/service/ScheduleService.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use Exception;
|
||||
use think\facade\Db;
|
||||
use app\lib\DnsHelper;
|
||||
|
||||
/**
|
||||
* 域名定时切换解析
|
||||
*/
|
||||
class ScheduleService
|
||||
{
|
||||
|
||||
public function execute()
|
||||
{
|
||||
$list = Db::name('sctask')->where('nexttime', '>', 0)->where('nexttime', '<=', time())->where('active', 1)->select();
|
||||
if (count($list) == 0) {
|
||||
return false;
|
||||
}
|
||||
echo '开始执行定时切换解析任务,共获取到' . count($list) . '个待执行任务' . "\n";
|
||||
foreach ($list as $row) {
|
||||
try {
|
||||
$this->execute_one($row);
|
||||
echo '定时切换任务' . $row['id'] . '执行成功' . "\n";
|
||||
} catch (Exception $e) {
|
||||
echo '定时切换任务' . $row['id'] . '执行失败,' . $e->getMessage() . "\n";
|
||||
}
|
||||
}
|
||||
config_set('schedule_time', date("Y-m-d H:i:s"));
|
||||
return true;
|
||||
}
|
||||
|
||||
public function execute_one($row)
|
||||
{
|
||||
$drow = Db::name('domain')->alias('A')->join('account B', 'A.aid = B.id')->where('A.id', $row['did'])->field('A.*,B.type,B.ak,B.sk,B.ext')->find();
|
||||
if (!$drow) throw new Exception('域名不存在');
|
||||
|
||||
Db::name('sctask')->where('id', $row['id'])->update(['updatetime' => time()]);
|
||||
|
||||
$domain = $row['rr'] . '.' . $drow['name'];
|
||||
$dns = DnsHelper::getModel2($drow);
|
||||
if ($row['switchtype'] == 1) {
|
||||
$res = $dns->setDomainRecordStatus($row['recordid'], '1');
|
||||
if ($res) {
|
||||
$this->add_log($domain, '启用解析', '定时启用解析成功');
|
||||
} else {
|
||||
$this->add_log($domain, '启用解析失败', $dns->getError());
|
||||
}
|
||||
} elseif ($row['switchtype'] == 2) {
|
||||
$res = $dns->setDomainRecordStatus($row['recordid'], '0');
|
||||
if ($res) {
|
||||
$this->add_log($domain, '暂停解析', '定时暂停解析成功');
|
||||
} else {
|
||||
$this->add_log($domain, '暂停解析失败', $dns->getError());
|
||||
}
|
||||
} elseif ($row['switchtype'] == 3) {
|
||||
$res = $dns->deleteDomainRecord($row['recordid']);
|
||||
if ($res) {
|
||||
$this->add_log($domain, '删除解析', '定时删除解析成功');
|
||||
} else {
|
||||
$this->add_log($domain, '删除解析失败', $dns->getError());
|
||||
}
|
||||
} else {
|
||||
$recordinfo = json_decode($row['recordinfo'], true);
|
||||
if ($drow['type'] == 'cloudflare' && !isNullOrEmpty($row['line'])) {
|
||||
$recordinfo['Line'] = $row['line'];
|
||||
}
|
||||
$res = $dns->updateDomainRecord($row['recordid'], $row['rr'], getDnsType($row['value']), $row['value'], $recordinfo['Line'], $recordinfo['TTL']);
|
||||
if ($res) {
|
||||
$this->add_log($domain, '修改解析', $row['rr'].' ['.getDnsType($row['value']).'] '.$row['value'].' (线路:'.$recordinfo['Line'].' TTL:'.$recordinfo['TTL'].')');
|
||||
} else {
|
||||
$this->add_log($domain, '修改解析失败', $dns->getError());
|
||||
}
|
||||
}
|
||||
|
||||
$this->update_nexttime($row);
|
||||
}
|
||||
|
||||
public function update_nexttime($row)
|
||||
{
|
||||
if ($row['type'] == 1) {
|
||||
if ($row['cycle'] == 2) {
|
||||
$date = intval($row['switchdate']);
|
||||
$nexttime = strtotime(date('Y-m-') . $date . ' ' . $row['switchtime'] . ':00');
|
||||
if ($nexttime <= time()) {
|
||||
$nexttime = strtotime("+1 month", $nexttime);
|
||||
}
|
||||
} elseif ($row['cycle'] == 1) {
|
||||
$weekday = intval($row['switchdate']); // 0-6, 0=周日
|
||||
$nexttime = strtotime("last Sunday +{$weekday} days {$row['switchtime']}:00");
|
||||
if ($nexttime <= time()) {
|
||||
$nexttime = strtotime("+1 week", $nexttime);
|
||||
if ($nexttime <= time()) {
|
||||
$nexttime = strtotime("+1 week", $nexttime);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$nexttime = strtotime(date('Y-m-d') . ' ' . $row['switchtime'] . ':00');
|
||||
if ($nexttime <= time()) {
|
||||
$nexttime = strtotime("+1 day", $nexttime);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$nexttime = strtotime($row['switchtime'] . ':00');
|
||||
if ($nexttime <= time()) {
|
||||
$nexttime = 0;
|
||||
}
|
||||
}
|
||||
Db::name('sctask')->where('id', $row['id'])->update(['nexttime' => $nexttime]);
|
||||
}
|
||||
|
||||
private function add_log($domain, $action, $data)
|
||||
{
|
||||
if (strlen($data) > 500) $data = substr($data, 0, 500);
|
||||
Db::name('log')->insert(['uid' => 0, 'domain' => $domain, 'action' => $action, 'data' => $data, 'addtime' => date("Y-m-d H:i:s")]);
|
||||
}
|
||||
}
|
||||
@@ -45,9 +45,9 @@ class TaskRunner
|
||||
if ($row['checktype'] == 2) {
|
||||
$result = CheckUtils::curl($row['checkurl'], $row['timeout'], $row['main_value'], $row['proxy'] == 1);
|
||||
} elseif ($row['checktype'] == 1) {
|
||||
$result = CheckUtils::tcp($row['main_value'], $row['tcpport'], $row['timeout']);
|
||||
$result = CheckUtils::tcp($row['main_value'], $row['checkurl'], $row['tcpport'], $row['timeout']);
|
||||
} else {
|
||||
$result = CheckUtils::ping($row['main_value']);
|
||||
$result = CheckUtils::ping($row['main_value'], $row['checkurl']);
|
||||
}
|
||||
|
||||
$action = 0;
|
||||
@@ -83,6 +83,9 @@ class TaskRunner
|
||||
if ($row['type'] == 2) {
|
||||
$dns = DnsHelper::getModel2($drow);
|
||||
$recordinfo = json_decode($row['recordinfo'], true);
|
||||
if ($drow['type'] == 'cloudflare' && $row['cdn'] == 1) {
|
||||
$recordinfo['Line'] = '1';
|
||||
}
|
||||
$res = $dns->updateDomainRecord($row['recordid'], $row['rr'], getDnsType($row['backup_value']), $row['backup_value'], $recordinfo['Line'], $recordinfo['TTL']);
|
||||
if (!$res) {
|
||||
$this->db()->name('log')->insert(['uid' => 0, 'domain' => $drow['name'], 'action' => '修改解析失败', 'data' => $dns->getError(), 'addtime' => date("Y-m-d H:i:s")]);
|
||||
@@ -98,6 +101,9 @@ class TaskRunner
|
||||
if ($row['type'] == 2) {
|
||||
$dns = DnsHelper::getModel2($drow);
|
||||
$recordinfo = json_decode($row['recordinfo'], true);
|
||||
if ($drow['type'] == 'cloudflare' && $row['cdn'] == 1) {
|
||||
$recordinfo['Line'] = '0';
|
||||
}
|
||||
$res = $dns->updateDomainRecord($row['recordid'], $row['rr'], getDnsType($row['main_value']), $row['main_value'], $recordinfo['Line'], $recordinfo['TTL']);
|
||||
if (!$res) {
|
||||
$this->db()->name('log')->insert(['uid' => 0, 'domain' => $drow['name'], 'action' => '修改解析失败', 'data' => $dns->getError(), 'addtime' => date("Y-m-d H:i:s")]);
|
||||
|
||||
@@ -5,7 +5,7 @@ CREATE TABLE `dnsmgr_config` (
|
||||
PRIMARY KEY (`key`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
INSERT INTO `dnsmgr_config` VALUES ('version', '1021');
|
||||
INSERT INTO `dnsmgr_config` VALUES ('version', '1040');
|
||||
INSERT INTO `dnsmgr_config` VALUES ('notice_mail', '0');
|
||||
INSERT INTO `dnsmgr_config` VALUES ('notice_wxtpl', '0');
|
||||
INSERT INTO `dnsmgr_config` VALUES ('mail_smtp', 'smtp.qq.com');
|
||||
@@ -28,13 +28,19 @@ DROP TABLE IF EXISTS `dnsmgr_domain`;
|
||||
CREATE TABLE `dnsmgr_domain` (
|
||||
`id` int(11) unsigned NOT NULL auto_increment,
|
||||
`aid` int(11) unsigned NOT NULL,
|
||||
`name` varchar(128) NOT NULL,
|
||||
`name` varchar(255) NOT NULL,
|
||||
`thirdid` varchar(60) DEFAULT NULL,
|
||||
`addtime` datetime DEFAULT NULL,
|
||||
`is_hide` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`is_sso` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`recordcount` int(1) NOT NULL DEFAULT '0',
|
||||
`remark` varchar(100) DEFAULT NULL,
|
||||
`is_notice` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`regtime` datetime DEFAULT NULL,
|
||||
`expiretime` datetime DEFAULT NULL,
|
||||
`checktime` datetime DEFAULT NULL,
|
||||
`noticetime` datetime DEFAULT NULL,
|
||||
`checkstatus` tinyint(1) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
@@ -60,7 +66,7 @@ DROP TABLE IF EXISTS `dnsmgr_permission`;
|
||||
CREATE TABLE `dnsmgr_permission` (
|
||||
`id` int(11) unsigned NOT NULL auto_increment,
|
||||
`uid` int(11) unsigned NOT NULL,
|
||||
`domain` varchar(128) NOT NULL,
|
||||
`domain` varchar(255) NOT NULL,
|
||||
`sub` varchar(80) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `uid` (`uid`)
|
||||
@@ -71,7 +77,7 @@ CREATE TABLE `dnsmgr_log` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`uid` int(11) unsigned NOT NULL,
|
||||
`action` varchar(40) NOT NULL,
|
||||
`domain` varchar(128) NOT NULL DEFAULT '',
|
||||
`domain` varchar(255) NOT NULL DEFAULT '',
|
||||
`data` varchar(500) DEFAULT NULL,
|
||||
`addtime` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
@@ -96,6 +102,7 @@ CREATE TABLE `dnsmgr_dmtask` (
|
||||
`timeout` tinyint(5) NOT NULL DEFAULT 2,
|
||||
`remark` varchar(100) DEFAULT NULL,
|
||||
`proxy` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`cdn` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`addtime` int(11) NOT NULL DEFAULT 0,
|
||||
`checktime` int(11) NOT NULL DEFAULT 0,
|
||||
`checknexttime` int(11) NOT NULL DEFAULT 0,
|
||||
@@ -223,4 +230,27 @@ CREATE TABLE `dnsmgr_cert_cname` (
|
||||
`addtime` datetime DEFAULT NULL,
|
||||
`status` tinyint(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
DROP TABLE IF EXISTS `dnsmgr_sctask`;
|
||||
CREATE TABLE `dnsmgr_sctask` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`did` int(11) unsigned NOT NULL,
|
||||
`rr` varchar(128) NOT NULL,
|
||||
`recordid` varchar(60) NOT NULL,
|
||||
`type` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`cycle` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`switchtype` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`switchdate` varchar(10) DEFAULT NULL,
|
||||
`switchtime` varchar(20) DEFAULT NULL,
|
||||
`value` varchar(128) DEFAULT NULL,
|
||||
`line` varchar(20) DEFAULT NULL,
|
||||
`addtime` int(11) NOT NULL DEFAULT 0,
|
||||
`updatetime` int(11) NOT NULL DEFAULT 0,
|
||||
`nexttime` int(11) NOT NULL DEFAULT 0,
|
||||
`active` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`recordinfo` varchar(200) DEFAULT NULL,
|
||||
`remark` varchar(100) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `did` (`did`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
@@ -152,4 +152,37 @@ CREATE TABLE IF NOT EXISTS `dnsmgr_cert_cname` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
ALTER TABLE `dnsmgr_account`
|
||||
ADD COLUMN `proxy` tinyint(1) NOT NULL DEFAULT '0';
|
||||
ADD COLUMN `proxy` tinyint(1) NOT NULL DEFAULT '0';
|
||||
|
||||
ALTER TABLE `dnsmgr_dmtask`
|
||||
ADD COLUMN `cdn` tinyint(1) NOT NULL DEFAULT 0;
|
||||
|
||||
ALTER TABLE `dnsmgr_domain`
|
||||
ADD COLUMN `is_notice` tinyint(1) NOT NULL DEFAULT '0',
|
||||
ADD COLUMN `regtime` datetime DEFAULT NULL,
|
||||
ADD COLUMN `expiretime` datetime DEFAULT NULL,
|
||||
ADD COLUMN `checktime` datetime DEFAULT NULL,
|
||||
ADD COLUMN `noticetime` datetime DEFAULT NULL,
|
||||
ADD COLUMN `checkstatus` tinyint(1) NOT NULL DEFAULT '0';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `dnsmgr_sctask` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`did` int(11) unsigned NOT NULL,
|
||||
`rr` varchar(128) NOT NULL,
|
||||
`recordid` varchar(60) NOT NULL,
|
||||
`type` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`cycle` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`switchtype` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`switchdate` varchar(10) DEFAULT NULL,
|
||||
`switchtime` varchar(20) DEFAULT NULL,
|
||||
`value` varchar(128) DEFAULT NULL,
|
||||
`line` varchar(20) DEFAULT NULL,
|
||||
`addtime` int(11) NOT NULL DEFAULT 0,
|
||||
`updatetime` int(11) NOT NULL DEFAULT 0,
|
||||
`nexttime` int(11) NOT NULL DEFAULT 0,
|
||||
`active` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`recordinfo` varchar(200) DEFAULT NULL,
|
||||
`remark` varchar(100) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `did` (`did`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user