Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96ff262333 | ||
|
|
17ffe5704f | ||
|
|
d4c11b520f | ||
|
|
ba418da84c | ||
|
|
b58db855ca | ||
|
|
3bd45367b0 | ||
|
|
a22bc4fa37 | ||
|
|
3499583e0d | ||
|
|
63636e660d | ||
|
|
4bf87156e3 | ||
|
|
475c14804a | ||
|
|
531ad68847 | ||
|
|
f86c68fc6a | ||
|
|
460067a5e7 | ||
|
|
6ffa9e003a | ||
|
|
c5ed1c6990 | ||
|
|
2b51a2d015 |
34
.example.env
34
.example.env
@@ -1,18 +1,18 @@
|
||||
APP_DEBUG = false
|
||||
|
||||
[APP]
|
||||
DEFAULT_TIMEZONE = Asia/Shanghai
|
||||
|
||||
[DATABASE]
|
||||
TYPE = mysql
|
||||
HOSTNAME = {dbhost}
|
||||
DATABASE = {dbname}
|
||||
USERNAME = {dbuser}
|
||||
PASSWORD = {dbpwd}
|
||||
HOSTPORT = {dbport}
|
||||
CHARSET = utf8mb4
|
||||
PREFIX = {dbprefix}
|
||||
DEBUG = false
|
||||
|
||||
[LANG]
|
||||
APP_DEBUG = false
|
||||
|
||||
[APP]
|
||||
DEFAULT_TIMEZONE = Asia/Shanghai
|
||||
|
||||
[DATABASE]
|
||||
TYPE = mysql
|
||||
HOSTNAME = {dbhost}
|
||||
DATABASE = {dbname}
|
||||
USERNAME = {dbuser}
|
||||
PASSWORD = {dbpwd}
|
||||
HOSTPORT = {dbport}
|
||||
CHARSET = utf8mb4
|
||||
PREFIX = {dbprefix}
|
||||
DEBUG = false
|
||||
|
||||
[LANG]
|
||||
default_lang = zh-cn
|
||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* text=auto eol=lf
|
||||
418
README.md
418
README.md
@@ -1,209 +1,209 @@
|
||||
# 彩虹聚合DNS管理系统
|
||||
|
||||
<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)
|
||||
|
||||
</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
|
||||
|
||||
## 演示截图
|
||||
|
||||
添加域名账户
|
||||
|
||||

|
||||
|
||||
域名管理列表
|
||||
|
||||

|
||||
|
||||
域名DNS解析管理,支持解析批量操作
|
||||
|
||||

|
||||
|
||||
用户管理添加用户,支持为用户开启API接口
|
||||
|
||||

|
||||
|
||||
CF优选IP功能,添加优选IP任务
|
||||
|
||||

|
||||
|
||||
SSL证书申请功能
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
SSL证书自动部署功能
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 支持与反馈
|
||||
|
||||
🌐 作者信息:消失的彩虹海(https://blog.cccyun.cn)
|
||||
|
||||
⭐ 如果您觉得本项目对您有帮助,欢迎给项目点个 Star
|
||||
|
||||
🤝 捐赠:
|
||||
|
||||
<img height="240" src="https://wkphoto.bj.bcebos.com/d8f9d72a6059252db065f556249b033b5bb5b976.jpg">
|
||||
|
||||
### 其他推荐
|
||||
|
||||
- [彩虹云主机 - 免备案CDN/虚拟主机](https://www.cccyun.net/)
|
||||
- [小白云高防云服务器](https://www.xiaobaiyun.cn/aff/GMLPMFOV)
|
||||
|
||||
# 彩虹聚合DNS管理系统
|
||||
|
||||
<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)
|
||||
|
||||
</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
|
||||
|
||||
## 演示截图
|
||||
|
||||
添加域名账户
|
||||
|
||||

|
||||
|
||||
域名管理列表
|
||||
|
||||

|
||||
|
||||
域名DNS解析管理,支持解析批量操作
|
||||
|
||||

|
||||
|
||||
用户管理添加用户,支持为用户开启API接口
|
||||
|
||||

|
||||
|
||||
CF优选IP功能,添加优选IP任务
|
||||
|
||||

|
||||
|
||||
SSL证书申请功能
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
SSL证书自动部署功能
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 支持与反馈
|
||||
|
||||
🌐 作者信息:消失的彩虹海(https://blog.cccyun.cn)
|
||||
|
||||
⭐ 如果您觉得本项目对您有帮助,欢迎给项目点个 Star
|
||||
|
||||
🤝 捐赠:
|
||||
|
||||
<img height="240" src="https://wkphoto.bj.bcebos.com/d8f9d72a6059252db065f556249b033b5bb5b976.jpg">
|
||||
|
||||
### 其他推荐
|
||||
|
||||
- [彩虹云主机 - 免备案CDN/虚拟主机](https://www.cccyun.net/)
|
||||
- [小白云高防云服务器](https://www.xiaobaiyun.cn/aff/GMLPMFOV)
|
||||
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
<?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;
|
||||
use app\service\CertTaskService;
|
||||
use app\service\ExpireNoticeService;
|
||||
use app\service\ScheduleService;
|
||||
|
||||
class Certtask extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
// 指令配置
|
||||
$this->setName('certtask')
|
||||
->setDescription('SSL证书续签与部署、域名到期提醒、定时切换解析、CF优选IP更新');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$res = Db::name('config')->cache('configs', 0)->column('value', 'key');
|
||||
Config::set($res, 'sys');
|
||||
|
||||
(new ScheduleService())->execute();
|
||||
$res = (new OptimizeService())->execute();
|
||||
if (!$res) {
|
||||
(new CertTaskService())->execute();
|
||||
(new ExpireNoticeService())->task();
|
||||
}
|
||||
echo 'done'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
<?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;
|
||||
use app\service\CertTaskService;
|
||||
use app\service\ExpireNoticeService;
|
||||
use app\service\ScheduleService;
|
||||
|
||||
class Certtask extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
// 指令配置
|
||||
$this->setName('certtask')
|
||||
->setDescription('SSL证书续签与部署、域名到期提醒、定时切换解析、CF优选IP更新');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$res = Db::name('config')->cache('configs', 0)->column('value', 'key');
|
||||
Config::set($res, 'sys');
|
||||
|
||||
(new ScheduleService())->execute();
|
||||
$res = (new OptimizeService())->execute();
|
||||
if (!$res) {
|
||||
(new CertTaskService())->execute();
|
||||
(new ExpireNoticeService())->task();
|
||||
}
|
||||
echo 'done'.PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,82 +1,82 @@
|
||||
<?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\TaskRunner;
|
||||
|
||||
class Dmtask extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
// 指令配置
|
||||
$this->setName('dmtask')
|
||||
->setDescription('容灾切换任务');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$res = Db::name('config')->cache('configs', 0)->column('value', 'key');
|
||||
Config::set($res, 'sys');
|
||||
|
||||
config_set('run_error', '');
|
||||
if (!extension_loaded('swoole')) {
|
||||
$output->writeln('[Error] 未安装Swoole扩展');
|
||||
config_set('run_error', '未安装Swoole扩展');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$output->writeln('进程启动成功.');
|
||||
$this->runtask();
|
||||
} catch (Exception $e) {
|
||||
$output->writeln('[Error] ' . $e->getMessage());
|
||||
config_set('run_error', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function runtask()
|
||||
{
|
||||
\Co::set(['hook_flags' => SWOOLE_HOOK_ALL]);
|
||||
\Co\run(function () {
|
||||
$date = date("Ymd");
|
||||
$count = config_get('run_count', null, true) ?? 0;
|
||||
while (true) {
|
||||
sleep(1);
|
||||
if ($date != date("Ymd")) {
|
||||
$count = 0;
|
||||
$date = date("Ymd");
|
||||
}
|
||||
|
||||
$rows = Db::name('dmtask')->where('checknexttime', '<=', time())->where('active', 1)->order('id', 'ASC')->select();
|
||||
foreach ($rows as $row) {
|
||||
\go(function () use ($row) {
|
||||
try {
|
||||
(new TaskRunner())->execute($row);
|
||||
} catch (\Swoole\ExitException $e) {
|
||||
echo $e->getStatus() . "\n";
|
||||
} catch (Exception $e) {
|
||||
echo $e->__toString() . "\n";
|
||||
}
|
||||
});
|
||||
Db::name('dmtask')->where('id', $row['id'])->update([
|
||||
'checktime' => time(),
|
||||
'checknexttime' => time() + $row['frequency']
|
||||
]);
|
||||
$count++;
|
||||
}
|
||||
|
||||
config_set('run_time', date("Y-m-d H:i:s"));
|
||||
config_set('run_count', $count);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
<?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\TaskRunner;
|
||||
|
||||
class Dmtask extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
// 指令配置
|
||||
$this->setName('dmtask')
|
||||
->setDescription('容灾切换任务');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$res = Db::name('config')->cache('configs', 0)->column('value', 'key');
|
||||
Config::set($res, 'sys');
|
||||
|
||||
config_set('run_error', '');
|
||||
if (!extension_loaded('swoole')) {
|
||||
$output->writeln('[Error] 未安装Swoole扩展');
|
||||
config_set('run_error', '未安装Swoole扩展');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$output->writeln('进程启动成功.');
|
||||
$this->runtask();
|
||||
} catch (Exception $e) {
|
||||
$output->writeln('[Error] ' . $e->getMessage());
|
||||
config_set('run_error', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function runtask()
|
||||
{
|
||||
\Co::set(['hook_flags' => SWOOLE_HOOK_ALL]);
|
||||
\Co\run(function () {
|
||||
$date = date("Ymd");
|
||||
$count = config_get('run_count', null, true) ?? 0;
|
||||
while (true) {
|
||||
sleep(1);
|
||||
if ($date != date("Ymd")) {
|
||||
$count = 0;
|
||||
$date = date("Ymd");
|
||||
}
|
||||
|
||||
$rows = Db::name('dmtask')->where('checknexttime', '<=', time())->where('active', 1)->order('id', 'ASC')->select();
|
||||
foreach ($rows as $row) {
|
||||
\go(function () use ($row) {
|
||||
try {
|
||||
(new TaskRunner())->execute($row);
|
||||
} catch (\Swoole\ExitException $e) {
|
||||
echo $e->getStatus() . "\n";
|
||||
} catch (Exception $e) {
|
||||
echo $e->__toString() . "\n";
|
||||
}
|
||||
});
|
||||
Db::name('dmtask')->where('id', $row['id'])->update([
|
||||
'checktime' => time(),
|
||||
'checknexttime' => time() + $row['frequency']
|
||||
]);
|
||||
$count++;
|
||||
}
|
||||
|
||||
config_set('run_time', date("Y-m-d H:i:s"));
|
||||
config_set('run_count', $count);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
<?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;
|
||||
|
||||
class Reset extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
// 指令配置
|
||||
$this->setName('reset')
|
||||
->addArgument('type', Argument::REQUIRED, '操作类型,pwd:重置密码,totp:关闭TOTP')
|
||||
->addArgument('username', Argument::REQUIRED, '用户名')
|
||||
->addArgument('password', Argument::OPTIONAL, '密码')
|
||||
->setDescription('重置密码');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$type = trim($input->getArgument('type'));
|
||||
$username = trim($input->getArgument('username'));
|
||||
$user = Db::name('user')->where('username', $username)->find();
|
||||
if (!$user) {
|
||||
$output->writeln('用户 ' . $username . ' 不存在');
|
||||
return;
|
||||
}
|
||||
if ($type == 'pwd') {
|
||||
$password = $input->getArgument('password');
|
||||
if (empty($password)) $password = '123456';
|
||||
Db::name('user')->where('id', $user['id'])->update(['password' => password_hash($password, PASSWORD_DEFAULT)]);
|
||||
$output->writeln('用户 ' . $username . ' 密码重置成功');
|
||||
} elseif ($type == 'totp') {
|
||||
Db::name('user')->where('id', $user['id'])->update(['totp_open' => 0, 'totp_secret' => null]);
|
||||
$output->writeln('用户 ' . $username . ' TOTP关闭成功');
|
||||
}
|
||||
}
|
||||
}
|
||||
<?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;
|
||||
|
||||
class Reset extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
// 指令配置
|
||||
$this->setName('reset')
|
||||
->addArgument('type', Argument::REQUIRED, '操作类型,pwd:重置密码,totp:关闭TOTP')
|
||||
->addArgument('username', Argument::REQUIRED, '用户名')
|
||||
->addArgument('password', Argument::OPTIONAL, '密码')
|
||||
->setDescription('重置密码');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$type = trim($input->getArgument('type'));
|
||||
$username = trim($input->getArgument('username'));
|
||||
$user = Db::name('user')->where('username', $username)->find();
|
||||
if (!$user) {
|
||||
$output->writeln('用户 ' . $username . ' 不存在');
|
||||
return;
|
||||
}
|
||||
if ($type == 'pwd') {
|
||||
$password = $input->getArgument('password');
|
||||
if (empty($password)) $password = '123456';
|
||||
Db::name('user')->where('id', $user['id'])->update(['password' => password_hash($password, PASSWORD_DEFAULT)]);
|
||||
$output->writeln('用户 ' . $username . ' 密码重置成功');
|
||||
} elseif ($type == 'totp') {
|
||||
Db::name('user')->where('id', $user['id'])->update(['totp_open' => 0, 'totp_secret' => null]);
|
||||
$output->writeln('用户 ' . $username . ' TOTP关闭成功');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,266 +1,266 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\BaseController;
|
||||
use think\facade\Db;
|
||||
use think\facade\View;
|
||||
use think\facade\Cache;
|
||||
|
||||
class Dmonitor extends BaseController
|
||||
{
|
||||
public function overview()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$switch_count = Db::name('dmlog')->where('date', '>=', date("Y-m-d H:i:s", strtotime("-1 days")))->count();
|
||||
$fail_count = Db::name('dmlog')->where('date', '>=', date("Y-m-d H:i:s", strtotime("-1 days")))->where('action', 1)->count();
|
||||
|
||||
$run_time = config_get('run_time', null, true);
|
||||
$run_state = $run_time ? (time() - strtotime($run_time) > 10 ? 0 : 1) : 0;
|
||||
View::assign('info', [
|
||||
'run_count' => config_get('run_count', null, true) ?? 0,
|
||||
'run_time' => $run_time ?? '无',
|
||||
'run_state' => $run_state,
|
||||
'run_error' => config_get('run_error', null, true),
|
||||
'switch_count' => $switch_count,
|
||||
'fail_count' => $fail_count,
|
||||
'swoole' => extension_loaded('swoole') ? '<font color="green">已安装</font>' : '<font color="red">未安装</font>',
|
||||
]);
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function task()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function task_data()
|
||||
{
|
||||
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');
|
||||
|
||||
$select = Db::name('dmtask')->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('main_value', $kw);
|
||||
} elseif ($type == 4) {
|
||||
$select->where('backup_value', $kw);
|
||||
} elseif ($type == 5) {
|
||||
$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['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');
|
||||
$task = null;
|
||||
if ($action == 'edit') {
|
||||
$id = input('get.id/d');
|
||||
$task = Db::name('dmtask')->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);
|
||||
View::assign('support_ping', function_exists('exec') ? '1' : '0');
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function taskinfo()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$id = input('param.id/d');
|
||||
$task = Db::name('dmtask')->where('id', $id)->find();
|
||||
if (empty($task)) return $this->alert('error', '切换策略不存在');
|
||||
|
||||
$switch_count = Db::name('dmlog')->where('taskid', $id)->where('date', '>=', date("Y-m-d H:i:s", strtotime("-1 days")))->count();
|
||||
$fail_count = Db::name('dmlog')->where('taskid', $id)->where('date', '>=', date("Y-m-d H:i:s", strtotime("-1 days")))->where('action', 1)->count();
|
||||
|
||||
$task['switch_count'] = $switch_count;
|
||||
$task['fail_count'] = $fail_count;
|
||||
if ($task['type'] == 3) {
|
||||
$task['action_name'] = ['未知', '<font color="red">开启解析</font>', '<font color="green">暂停解析</font>'];
|
||||
} elseif ($task['type'] == 2) {
|
||||
$task['action_name'] = ['未知', '<font color="red">切换备用解析记录</font>', '<font color="green">恢复主解析记录</font>'];
|
||||
} else {
|
||||
$task['action_name'] = ['未知', '<font color="red">暂停解析</font>', '<font color="green">启用解析</font>'];
|
||||
}
|
||||
View::assign('info', $task);
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function tasklog_data()
|
||||
{
|
||||
if (!checkPermission(2)) return json(['total' => 0, 'rows' => []]);
|
||||
$taskid = input('param.id/d');
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
$action = input('post.action/d', 0);
|
||||
|
||||
$select = Db::name('dmlog')->where('taskid', $taskid);
|
||||
if ($action > 0) {
|
||||
$select->where('action', $action);
|
||||
}
|
||||
$total = $select->count();
|
||||
$list = $select->order('id', 'desc')->limit($offset, $limit)->select();
|
||||
|
||||
return json(['total' => $total, 'rows' => $list]);
|
||||
}
|
||||
|
||||
public function clean()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
if ($this->request->isPost()) {
|
||||
$days = input('post.days/d');
|
||||
if (!$days || $days < 0) return json(['code' => -1, 'msg' => '参数错误']);
|
||||
Db::execute("DELETE FROM `" . config('database.connections.mysql.prefix') . "dmlog` WHERE `date`<'" . date("Y-m-d H:i:s", strtotime("-" . $days . " days")) . "'");
|
||||
Db::execute("OPTIMIZE TABLE `" . config('database.connections.mysql.prefix') . "dmlog`");
|
||||
return json(['code' => 0, 'msg' => '清理成功']);
|
||||
}
|
||||
}
|
||||
|
||||
public function status()
|
||||
{
|
||||
$run_time = config_get('run_time', null, true);
|
||||
$run_state = $run_time ? (time() - strtotime($run_time) > 10 ? 0 : 1) : 0;
|
||||
return $run_state == 1 ? 'ok' : 'error';
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\BaseController;
|
||||
use think\facade\Db;
|
||||
use think\facade\View;
|
||||
use think\facade\Cache;
|
||||
|
||||
class Dmonitor extends BaseController
|
||||
{
|
||||
public function overview()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$switch_count = Db::name('dmlog')->where('date', '>=', date("Y-m-d H:i:s", strtotime("-1 days")))->count();
|
||||
$fail_count = Db::name('dmlog')->where('date', '>=', date("Y-m-d H:i:s", strtotime("-1 days")))->where('action', 1)->count();
|
||||
|
||||
$run_time = config_get('run_time', null, true);
|
||||
$run_state = $run_time ? (time() - strtotime($run_time) > 10 ? 0 : 1) : 0;
|
||||
View::assign('info', [
|
||||
'run_count' => config_get('run_count', null, true) ?? 0,
|
||||
'run_time' => $run_time ?? '无',
|
||||
'run_state' => $run_state,
|
||||
'run_error' => config_get('run_error', null, true),
|
||||
'switch_count' => $switch_count,
|
||||
'fail_count' => $fail_count,
|
||||
'swoole' => extension_loaded('swoole') ? '<font color="green">已安装</font>' : '<font color="red">未安装</font>',
|
||||
]);
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function task()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function task_data()
|
||||
{
|
||||
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');
|
||||
|
||||
$select = Db::name('dmtask')->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('main_value', $kw);
|
||||
} elseif ($type == 4) {
|
||||
$select->where('backup_value', $kw);
|
||||
} elseif ($type == 5) {
|
||||
$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['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');
|
||||
$task = null;
|
||||
if ($action == 'edit') {
|
||||
$id = input('get.id/d');
|
||||
$task = Db::name('dmtask')->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);
|
||||
View::assign('support_ping', function_exists('exec') ? '1' : '0');
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function taskinfo()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$id = input('param.id/d');
|
||||
$task = Db::name('dmtask')->where('id', $id)->find();
|
||||
if (empty($task)) return $this->alert('error', '切换策略不存在');
|
||||
|
||||
$switch_count = Db::name('dmlog')->where('taskid', $id)->where('date', '>=', date("Y-m-d H:i:s", strtotime("-1 days")))->count();
|
||||
$fail_count = Db::name('dmlog')->where('taskid', $id)->where('date', '>=', date("Y-m-d H:i:s", strtotime("-1 days")))->where('action', 1)->count();
|
||||
|
||||
$task['switch_count'] = $switch_count;
|
||||
$task['fail_count'] = $fail_count;
|
||||
if ($task['type'] == 3) {
|
||||
$task['action_name'] = ['未知', '<font color="red">开启解析</font>', '<font color="green">暂停解析</font>'];
|
||||
} elseif ($task['type'] == 2) {
|
||||
$task['action_name'] = ['未知', '<font color="red">切换备用解析记录</font>', '<font color="green">恢复主解析记录</font>'];
|
||||
} else {
|
||||
$task['action_name'] = ['未知', '<font color="red">暂停解析</font>', '<font color="green">启用解析</font>'];
|
||||
}
|
||||
View::assign('info', $task);
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function tasklog_data()
|
||||
{
|
||||
if (!checkPermission(2)) return json(['total' => 0, 'rows' => []]);
|
||||
$taskid = input('param.id/d');
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
$action = input('post.action/d', 0);
|
||||
|
||||
$select = Db::name('dmlog')->where('taskid', $taskid);
|
||||
if ($action > 0) {
|
||||
$select->where('action', $action);
|
||||
}
|
||||
$total = $select->count();
|
||||
$list = $select->order('id', 'desc')->limit($offset, $limit)->select();
|
||||
|
||||
return json(['total' => $total, 'rows' => $list]);
|
||||
}
|
||||
|
||||
public function clean()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
if ($this->request->isPost()) {
|
||||
$days = input('post.days/d');
|
||||
if (!$days || $days < 0) return json(['code' => -1, 'msg' => '参数错误']);
|
||||
Db::execute("DELETE FROM `" . config('database.connections.mysql.prefix') . "dmlog` WHERE `date`<'" . date("Y-m-d H:i:s", strtotime("-" . $days . " days")) . "'");
|
||||
Db::execute("OPTIMIZE TABLE `" . config('database.connections.mysql.prefix') . "dmlog`");
|
||||
return json(['code' => 0, 'msg' => '清理成功']);
|
||||
}
|
||||
}
|
||||
|
||||
public function status()
|
||||
{
|
||||
$run_time = config_get('run_time', null, true);
|
||||
$run_state = $run_time ? (time() - strtotime($run_time) > 10 ? 0 : 1) : 0;
|
||||
return $run_state == 1 ? 'ok' : 'error';
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,183 +1,183 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\BaseController;
|
||||
use Exception;
|
||||
use think\facade\Db;
|
||||
use think\facade\View;
|
||||
use think\facade\Cache;
|
||||
use app\service\OptimizeService;
|
||||
|
||||
class Optimizeip extends BaseController
|
||||
{
|
||||
public function opipset()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
if ($this->request->isPost()) {
|
||||
$params = input('post.');
|
||||
foreach ($params as $key => $value) {
|
||||
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');
|
||||
}
|
||||
return json(['code' => 0, 'msg' => 'succ']);
|
||||
}
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function opiplist()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function opiplist_data()
|
||||
{
|
||||
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');
|
||||
|
||||
$select = Db::name('optimizeip')->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->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();
|
||||
|
||||
return json(['total' => $total, 'rows' => $list]);
|
||||
}
|
||||
|
||||
public function opipform()
|
||||
{
|
||||
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'),
|
||||
'type' => input('post.type/d'),
|
||||
'ip_type' => input('post.ip_type', null, 'trim'),
|
||||
'cdn_type' => input('post.cdn_type/d'),
|
||||
'recordnum' => input('post.recordnum/d'),
|
||||
'ttl' => input('post.ttl/d'),
|
||||
'remark' => input('post.remark', null, 'trim'),
|
||||
'addtime' => date('Y-m-d H:i:s'),
|
||||
'active' => 1
|
||||
];
|
||||
|
||||
if (empty($task['did']) || empty($task['rr']) || empty($task['ip_type']) || empty($task['recordnum']) || empty($task['ttl'])) {
|
||||
return json(['code' => -1, 'msg' => '必填项不能为空']);
|
||||
}
|
||||
if ($task['recordnum'] > 5) {
|
||||
return json(['code' => -1, 'msg' => '解析数量不能超过5个']);
|
||||
}
|
||||
if (Db::name('optimizeip')->where('did', $task['did'])->where('rr', $task['rr'])->find()) {
|
||||
return json(['code' => -1, 'msg' => '当前域名的优选IP任务已存在']);
|
||||
}
|
||||
Db::name('optimizeip')->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'),
|
||||
'type' => input('post.type/d'),
|
||||
'ip_type' => input('post.ip_type', null, 'trim'),
|
||||
'cdn_type' => input('post.cdn_type/d'),
|
||||
'recordnum' => input('post.recordnum/d'),
|
||||
'ttl' => input('post.ttl/d'),
|
||||
'remark' => input('post.remark', null, 'trim'),
|
||||
];
|
||||
|
||||
if (empty($task['did']) || empty($task['rr']) || empty($task['ip_type']) || empty($task['recordnum']) || empty($task['ttl'])) {
|
||||
return json(['code' => -1, 'msg' => '必填项不能为空']);
|
||||
}
|
||||
if ($task['recordnum'] > 5) {
|
||||
return json(['code' => -1, 'msg' => '解析数量不能超过5个']);
|
||||
}
|
||||
if (Db::name('optimizeip')->where('did', $task['did'])->where('rr', $task['rr'])->where('id', '<>', $id)->find()) {
|
||||
return json(['code' => -1, 'msg' => '当前域名的优选IP任务已存在']);
|
||||
}
|
||||
Db::name('optimizeip')->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('optimizeip')->where('id', $id)->update(['active' => $active]);
|
||||
return json(['code' => 0, 'msg' => '设置成功']);
|
||||
} elseif ($action == 'del') {
|
||||
$id = input('post.id/d');
|
||||
Db::name('optimizeip')->where('id', $id)->delete();
|
||||
return json(['code' => 0, 'msg' => '删除成功']);
|
||||
} elseif ($action == 'run') {
|
||||
$id = input('post.id/d');
|
||||
$task = Db::name('optimizeip')->where('id', $id)->find();
|
||||
if (empty($task)) return json(['code' => -1, 'msg' => '任务不存在']);
|
||||
try {
|
||||
$result = (new OptimizeService())->execute_one($task);
|
||||
Db::name('optimizeip')->where('id', $id)->update(['status' => 1, 'errmsg' => null, 'updatetime' => date('Y-m-d H:i:s')]);
|
||||
return json(['code' => 0, 'msg' => '优选任务执行成功:' . $result]);
|
||||
} catch (Exception $e) {
|
||||
Db::name('optimizeip')->where('id', $id)->update(['status' => 2, 'errmsg' => $e->getMessage(), 'updatetime' => date('Y-m-d H:i:s')]);
|
||||
return json(['code' => -1, 'msg' => '优选任务执行失败:' . $e->getMessage(), 'stack' => $e->__toString()]);
|
||||
}
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '参数错误']);
|
||||
}
|
||||
}
|
||||
$task = null;
|
||||
if ($action == 'edit') {
|
||||
$id = input('get.id/d');
|
||||
$task = Db::name('optimizeip')->where('id', $id)->find();
|
||||
if (empty($task)) return $this->alert('error', '任务不存在');
|
||||
}
|
||||
|
||||
$domains = [];
|
||||
foreach (Db::name('domain')->alias('A')->join('account B', 'A.aid = B.id')->field('A.*')->where('B.type', '<>', 'cloudflare')->select() as $row) {
|
||||
$domains[$row['id']] = $row['name'];
|
||||
}
|
||||
View::assign('domains', $domains);
|
||||
|
||||
View::assign('info', $task);
|
||||
View::assign('action', $action);
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function queryapi()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$optimize_ip_api = input('post.optimize_ip_api/d');
|
||||
$optimize_ip_key = input('post.optimize_ip_key', null, 'trim');
|
||||
if (empty($optimize_ip_key)) return json(['code' => -1, 'msg' => '参数不能为空']);
|
||||
try {
|
||||
$result = (new OptimizeService())->get_license($optimize_ip_api, $optimize_ip_key);
|
||||
return json(['code' => 0, 'msg' => '当前积分余额:' . $result]);
|
||||
} catch (Exception $e) {
|
||||
return json(['code' => -1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function status()
|
||||
{
|
||||
$run_time = Db::name('optimizeip')->where('active', 1)->order('updatetime', 'desc')->value('updatetime');
|
||||
$run_state = $run_time ? (time() - strtotime($run_time) > 3600 ? 0 : 1) : 0;
|
||||
return $run_state == 1 ? 'ok' : 'error';
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\BaseController;
|
||||
use Exception;
|
||||
use think\facade\Db;
|
||||
use think\facade\View;
|
||||
use think\facade\Cache;
|
||||
use app\service\OptimizeService;
|
||||
|
||||
class Optimizeip extends BaseController
|
||||
{
|
||||
public function opipset()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
if ($this->request->isPost()) {
|
||||
$params = input('post.');
|
||||
foreach ($params as $key => $value) {
|
||||
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');
|
||||
}
|
||||
return json(['code' => 0, 'msg' => 'succ']);
|
||||
}
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function opiplist()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function opiplist_data()
|
||||
{
|
||||
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');
|
||||
|
||||
$select = Db::name('optimizeip')->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->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();
|
||||
|
||||
return json(['total' => $total, 'rows' => $list]);
|
||||
}
|
||||
|
||||
public function opipform()
|
||||
{
|
||||
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'),
|
||||
'type' => input('post.type/d'),
|
||||
'ip_type' => input('post.ip_type', null, 'trim'),
|
||||
'cdn_type' => input('post.cdn_type/d'),
|
||||
'recordnum' => input('post.recordnum/d'),
|
||||
'ttl' => input('post.ttl/d'),
|
||||
'remark' => input('post.remark', null, 'trim'),
|
||||
'addtime' => date('Y-m-d H:i:s'),
|
||||
'active' => 1
|
||||
];
|
||||
|
||||
if (empty($task['did']) || empty($task['rr']) || empty($task['ip_type']) || empty($task['recordnum']) || empty($task['ttl'])) {
|
||||
return json(['code' => -1, 'msg' => '必填项不能为空']);
|
||||
}
|
||||
if ($task['recordnum'] > 5) {
|
||||
return json(['code' => -1, 'msg' => '解析数量不能超过5个']);
|
||||
}
|
||||
if (Db::name('optimizeip')->where('did', $task['did'])->where('rr', $task['rr'])->find()) {
|
||||
return json(['code' => -1, 'msg' => '当前域名的优选IP任务已存在']);
|
||||
}
|
||||
Db::name('optimizeip')->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'),
|
||||
'type' => input('post.type/d'),
|
||||
'ip_type' => input('post.ip_type', null, 'trim'),
|
||||
'cdn_type' => input('post.cdn_type/d'),
|
||||
'recordnum' => input('post.recordnum/d'),
|
||||
'ttl' => input('post.ttl/d'),
|
||||
'remark' => input('post.remark', null, 'trim'),
|
||||
];
|
||||
|
||||
if (empty($task['did']) || empty($task['rr']) || empty($task['ip_type']) || empty($task['recordnum']) || empty($task['ttl'])) {
|
||||
return json(['code' => -1, 'msg' => '必填项不能为空']);
|
||||
}
|
||||
if ($task['recordnum'] > 5) {
|
||||
return json(['code' => -1, 'msg' => '解析数量不能超过5个']);
|
||||
}
|
||||
if (Db::name('optimizeip')->where('did', $task['did'])->where('rr', $task['rr'])->where('id', '<>', $id)->find()) {
|
||||
return json(['code' => -1, 'msg' => '当前域名的优选IP任务已存在']);
|
||||
}
|
||||
Db::name('optimizeip')->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('optimizeip')->where('id', $id)->update(['active' => $active]);
|
||||
return json(['code' => 0, 'msg' => '设置成功']);
|
||||
} elseif ($action == 'del') {
|
||||
$id = input('post.id/d');
|
||||
Db::name('optimizeip')->where('id', $id)->delete();
|
||||
return json(['code' => 0, 'msg' => '删除成功']);
|
||||
} elseif ($action == 'run') {
|
||||
$id = input('post.id/d');
|
||||
$task = Db::name('optimizeip')->where('id', $id)->find();
|
||||
if (empty($task)) return json(['code' => -1, 'msg' => '任务不存在']);
|
||||
try {
|
||||
$result = (new OptimizeService())->execute_one($task);
|
||||
Db::name('optimizeip')->where('id', $id)->update(['status' => 1, 'errmsg' => null, 'updatetime' => date('Y-m-d H:i:s')]);
|
||||
return json(['code' => 0, 'msg' => '优选任务执行成功:' . $result]);
|
||||
} catch (Exception $e) {
|
||||
Db::name('optimizeip')->where('id', $id)->update(['status' => 2, 'errmsg' => $e->getMessage(), 'updatetime' => date('Y-m-d H:i:s')]);
|
||||
return json(['code' => -1, 'msg' => '优选任务执行失败:' . $e->getMessage(), 'stack' => $e->__toString()]);
|
||||
}
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '参数错误']);
|
||||
}
|
||||
}
|
||||
$task = null;
|
||||
if ($action == 'edit') {
|
||||
$id = input('get.id/d');
|
||||
$task = Db::name('optimizeip')->where('id', $id)->find();
|
||||
if (empty($task)) return $this->alert('error', '任务不存在');
|
||||
}
|
||||
|
||||
$domains = [];
|
||||
foreach (Db::name('domain')->alias('A')->join('account B', 'A.aid = B.id')->field('A.*')->where('B.type', '<>', 'cloudflare')->select() as $row) {
|
||||
$domains[$row['id']] = $row['name'];
|
||||
}
|
||||
View::assign('domains', $domains);
|
||||
|
||||
View::assign('info', $task);
|
||||
View::assign('action', $action);
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function queryapi()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$optimize_ip_api = input('post.optimize_ip_api/d');
|
||||
$optimize_ip_key = input('post.optimize_ip_key', null, 'trim');
|
||||
if (empty($optimize_ip_key)) return json(['code' => -1, 'msg' => '参数不能为空']);
|
||||
try {
|
||||
$result = (new OptimizeService())->get_license($optimize_ip_api, $optimize_ip_key);
|
||||
return json(['code' => 0, 'msg' => '当前积分余额:' . $result]);
|
||||
} catch (Exception $e) {
|
||||
return json(['code' => -1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function status()
|
||||
{
|
||||
$run_time = Db::name('optimizeip')->where('active', 1)->order('updatetime', 'desc')->value('updatetime');
|
||||
$run_state = $run_time ? (time() - strtotime($run_time) > 3600 ? 0 : 1) : 0;
|
||||
return $run_state == 1 ? 'ok' : 'error';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,165 +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();
|
||||
}
|
||||
|
||||
}
|
||||
<?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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,149 +1,149 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\BaseController;
|
||||
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', '无权限');
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function proxyset()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function mailtest()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$mail_name = config_get('mail_recv') ? config_get('mail_recv') : config_get('mail_name');
|
||||
if (empty($mail_name)) return json(['code' => -1, 'msg' => '您还未设置邮箱!']);
|
||||
$result = \app\utils\MsgNotice::send_mail($mail_name, '邮件发送测试。', '这是一封测试邮件!<br/><br/>来自:' . $this->request->root(true));
|
||||
if ($result === true) {
|
||||
return json(['code' => 0, 'msg' => '邮件发送成功!']);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '邮件发送失败!' . $result]);
|
||||
}
|
||||
}
|
||||
|
||||
public function tgbottest()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$tgbot_token = config_get('tgbot_token');
|
||||
$tgbot_chatid = config_get('tgbot_chatid');
|
||||
if (empty($tgbot_token) || empty($tgbot_chatid)) return json(['code' => -1, 'msg' => '请先保存设置']);
|
||||
$content = "<strong>消息发送测试</strong>\n\n这是一封测试消息!\n\n来自:" . $this->request->root(true);
|
||||
$result = \app\utils\MsgNotice::send_telegram_bot($content);
|
||||
if ($result === true) {
|
||||
return json(['code' => 0, 'msg' => '消息发送成功!']);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '消息发送失败!' . $result]);
|
||||
}
|
||||
}
|
||||
|
||||
public function webhooktest()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$webhook_url = config_get('webhook_url');
|
||||
if (empty($webhook_url)) return json(['code' => -1, 'msg' => '请先保存设置']);
|
||||
$content = "这是一封测试消息!\n来自:" . $this->request->root(true);
|
||||
$result = \app\utils\MsgNotice::send_webhook('消息发送测试', $content);
|
||||
if ($result === true) {
|
||||
return json(['code' => 0, 'msg' => '消息发送成功!']);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '消息发送失败!' . $result]);
|
||||
}
|
||||
}
|
||||
|
||||
public function proxytest()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$proxy_server = trim($_POST['proxy_server']);
|
||||
$proxy_port = $_POST['proxy_port'];
|
||||
$proxy_user = trim($_POST['proxy_user']);
|
||||
$proxy_pwd = trim($_POST['proxy_pwd']);
|
||||
$proxy_type = $_POST['proxy_type'];
|
||||
try {
|
||||
check_proxy('https://dl.amh.sh/ip.htm', $proxy_server, $proxy_port, $proxy_type, $proxy_user, $proxy_pwd);
|
||||
} catch (Exception $e) {
|
||||
try {
|
||||
check_proxy('https://myip.ipip.net/', $proxy_server, $proxy_port, $proxy_type, $proxy_user, $proxy_pwd);
|
||||
} catch (Exception $e) {
|
||||
return json(['code' => -1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
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!';
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\BaseController;
|
||||
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', '无权限');
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function proxyset()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
return View::fetch();
|
||||
}
|
||||
|
||||
public function mailtest()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$mail_name = config_get('mail_recv') ? config_get('mail_recv') : config_get('mail_name');
|
||||
if (empty($mail_name)) return json(['code' => -1, 'msg' => '您还未设置邮箱!']);
|
||||
$result = \app\utils\MsgNotice::send_mail($mail_name, '邮件发送测试。', '这是一封测试邮件!<br/><br/>来自:' . $this->request->root(true));
|
||||
if ($result === true) {
|
||||
return json(['code' => 0, 'msg' => '邮件发送成功!']);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '邮件发送失败!' . $result]);
|
||||
}
|
||||
}
|
||||
|
||||
public function tgbottest()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$tgbot_token = config_get('tgbot_token');
|
||||
$tgbot_chatid = config_get('tgbot_chatid');
|
||||
if (empty($tgbot_token) || empty($tgbot_chatid)) return json(['code' => -1, 'msg' => '请先保存设置']);
|
||||
$content = "<strong>消息发送测试</strong>\n\n这是一封测试消息!\n\n来自:" . $this->request->root(true);
|
||||
$result = \app\utils\MsgNotice::send_telegram_bot($content);
|
||||
if ($result === true) {
|
||||
return json(['code' => 0, 'msg' => '消息发送成功!']);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '消息发送失败!' . $result]);
|
||||
}
|
||||
}
|
||||
|
||||
public function webhooktest()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$webhook_url = config_get('webhook_url');
|
||||
if (empty($webhook_url)) return json(['code' => -1, 'msg' => '请先保存设置']);
|
||||
$content = "这是一封测试消息!\n来自:" . $this->request->root(true);
|
||||
$result = \app\utils\MsgNotice::send_webhook('消息发送测试', $content);
|
||||
if ($result === true) {
|
||||
return json(['code' => 0, 'msg' => '消息发送成功!']);
|
||||
} else {
|
||||
return json(['code' => -1, 'msg' => '消息发送失败!' . $result]);
|
||||
}
|
||||
}
|
||||
|
||||
public function proxytest()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$proxy_server = trim($_POST['proxy_server']);
|
||||
$proxy_port = $_POST['proxy_port'];
|
||||
$proxy_user = trim($_POST['proxy_user']);
|
||||
$proxy_pwd = trim($_POST['proxy_pwd']);
|
||||
$proxy_type = $_POST['proxy_type'];
|
||||
try {
|
||||
check_proxy('https://dl.amh.sh/ip.htm', $proxy_server, $proxy_port, $proxy_type, $proxy_user, $proxy_pwd);
|
||||
} catch (Exception $e) {
|
||||
try {
|
||||
check_proxy('https://myip.ipip.net/', $proxy_server, $proxy_port, $proxy_type, $proxy_user, $proxy_pwd);
|
||||
} catch (Exception $e) {
|
||||
return json(['code' => -1, 'msg' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
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!';
|
||||
}
|
||||
}
|
||||
@@ -1,188 +1,188 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\BaseController;
|
||||
use think\facade\Db;
|
||||
use think\facade\View;
|
||||
use think\facade\Request;
|
||||
|
||||
class User extends BaseController
|
||||
{
|
||||
|
||||
public function user()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$list = Db::name('domain')->select();
|
||||
$domains = [];
|
||||
foreach ($list as $row) {
|
||||
$domains[] = $row['name'];
|
||||
}
|
||||
View::assign('domains', $domains);
|
||||
return view();
|
||||
}
|
||||
|
||||
public function user_data()
|
||||
{
|
||||
if (!checkPermission(2)) return json(['total' => 0, 'rows' => []]);
|
||||
$kw = input('post.kw', null, 'trim');
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
|
||||
$select = Db::name('user');
|
||||
if (!empty($kw)) {
|
||||
$select->whereLike('id|username', $kw);
|
||||
}
|
||||
$total = $select->count();
|
||||
$rows = $select->order('id', 'desc')->limit($offset, $limit)->select();
|
||||
|
||||
return json(['total' => $total, 'rows' => $rows]);
|
||||
}
|
||||
|
||||
public function user_op()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$act = input('param.act');
|
||||
if ($act == 'get') {
|
||||
$id = input('post.id/d');
|
||||
$row = Db::name('user')->where('id', $id)->find();
|
||||
if (!$row) {
|
||||
return json(['code' => -1, 'msg' => '用户不存在']);
|
||||
}
|
||||
$row['permission'] = Db::name('permission')->where('uid', $id)->column('domain');
|
||||
return json(['code' => 0, 'data' => $row]);
|
||||
} elseif ($act == 'add') {
|
||||
$username = input('post.username', null, 'trim');
|
||||
$password = input('post.password', null, 'trim');
|
||||
$is_api = input('post.is_api/d');
|
||||
$apikey = input('post.apikey', null, 'trim');
|
||||
$level = input('post.level/d');
|
||||
if (empty($username) || empty($password)) {
|
||||
return json(['code' => -1, 'msg' => '用户名或密码不能为空']);
|
||||
}
|
||||
if ($is_api == 1 && empty($apikey)) {
|
||||
return json(['code' => -1, 'msg' => 'API密钥不能为空']);
|
||||
}
|
||||
if (Db::name('user')->where('username', $username)->find()) {
|
||||
return json(['code' => -1, 'msg' => '用户名已存在']);
|
||||
}
|
||||
$uid = Db::name('user')->insertGetId([
|
||||
'username' => $username,
|
||||
'password' => password_hash($password, PASSWORD_DEFAULT),
|
||||
'is_api' => $is_api,
|
||||
'apikey' => $apikey,
|
||||
'level' => $level,
|
||||
'regtime' => date('Y-m-d H:i:s'),
|
||||
'status' => 1,
|
||||
]);
|
||||
if ($level == 1) {
|
||||
$permission = input('post.permission/a');
|
||||
if (!empty($permission)) {
|
||||
$data = [];
|
||||
foreach ($permission as $domain) {
|
||||
$data[] = ['uid' => $uid, 'domain' => $domain];
|
||||
}
|
||||
Db::name('permission')->insertAll($data);
|
||||
}
|
||||
}
|
||||
return json(['code' => 0, 'msg' => '添加用户成功!']);
|
||||
} elseif ($act == 'edit') {
|
||||
$id = input('post.id/d');
|
||||
$row = Db::name('user')->where('id', $id)->find();
|
||||
if (!$row) return json(['code' => -1, 'msg' => '用户不存在']);
|
||||
$username = input('post.username', null, 'trim');
|
||||
$is_api = input('post.is_api/d');
|
||||
$apikey = input('post.apikey', null, 'trim');
|
||||
$level = input('post.level/d');
|
||||
$repwd = input('post.repwd', null, 'trim');
|
||||
if (empty($username)) {
|
||||
return json(['code' => -1, 'msg' => '用户名不能为空']);
|
||||
}
|
||||
if ($is_api == 1 && empty($apikey)) {
|
||||
return json(['code' => -1, 'msg' => 'API密钥不能为空']);
|
||||
}
|
||||
if (Db::name('user')->where('username', $username)->where('id', '<>', $id)->find()) {
|
||||
return json(['code' => -1, 'msg' => '用户名已存在']);
|
||||
}
|
||||
if ($level == 1 && ($id == 1000 || $id == $this->request->user['id'])) {
|
||||
$level = 2;
|
||||
}
|
||||
Db::name('user')->where('id', $id)->update([
|
||||
'username' => $username,
|
||||
'is_api' => $is_api,
|
||||
'apikey' => $apikey,
|
||||
'level' => $level,
|
||||
]);
|
||||
Db::name('permission')->where(['uid' => $id])->delete();
|
||||
if ($level == 1) {
|
||||
$permission = input('post.permission/a');
|
||||
if (!empty($permission)) {
|
||||
$data = [];
|
||||
foreach ($permission as $domain) {
|
||||
$data[] = ['uid' => $id, 'domain' => $domain];
|
||||
}
|
||||
Db::name('permission')->insertAll($data);
|
||||
}
|
||||
}
|
||||
if (!empty($repwd)) {
|
||||
Db::name('user')->where('id', $id)->update(['password' => password_hash($repwd, PASSWORD_DEFAULT)]);
|
||||
}
|
||||
return json(['code' => 0, 'msg' => '修改用户成功!']);
|
||||
} elseif ($act == 'set') {
|
||||
$id = input('post.id/d');
|
||||
$status = input('post.status/d');
|
||||
if ($id == 1000) {
|
||||
return json(['code' => -1, 'msg' => '此用户无法修改状态']);
|
||||
}
|
||||
if ($id == $this->request->user['id']) {
|
||||
return json(['code' => -1, 'msg' => '当前登录用户无法修改状态']);
|
||||
}
|
||||
Db::name('user')->where('id', $id)->update(['status' => $status]);
|
||||
return json(['code' => 0]);
|
||||
} elseif ($act == 'del') {
|
||||
$id = input('post.id/d');
|
||||
if ($id == 1000) {
|
||||
return json(['code' => -1, 'msg' => '此用户无法删除']);
|
||||
}
|
||||
if ($id == $this->request->user['id']) {
|
||||
return json(['code' => -1, 'msg' => '当前登录用户无法删除']);
|
||||
}
|
||||
Db::name('user')->where('id', $id)->delete();
|
||||
return json(['code' => 0]);
|
||||
}
|
||||
return json(['code' => -3]);
|
||||
}
|
||||
|
||||
public function log()
|
||||
{
|
||||
return view();
|
||||
}
|
||||
|
||||
public function log_data()
|
||||
{
|
||||
$uid = input('post.uid', null, 'trim');
|
||||
$kw = input('post.kw', null, 'trim');
|
||||
$domain = input('post.domain', null, 'trim');
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
|
||||
$select = Db::name('log');
|
||||
if ($this->request->user['type'] == 'domain') {
|
||||
$select->where('domain', $this->request->user['name']);
|
||||
} elseif ($this->request->user['level'] == 1) {
|
||||
$select->where('uid', $this->request->user['id']);
|
||||
} elseif (!isNullOrEmpty($uid)) {
|
||||
$select->where('uid', $uid);
|
||||
}
|
||||
if (!empty($kw)) {
|
||||
$select->whereLike('action|data', '%' . $kw . '%');
|
||||
}
|
||||
if (!empty($domain)) {
|
||||
$select->where('domain', $domain);
|
||||
}
|
||||
$total = $select->count();
|
||||
$rows = $select->order('id', 'desc')->limit($offset, $limit)->select();
|
||||
|
||||
return json(['total' => $total, 'rows' => $rows]);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\BaseController;
|
||||
use think\facade\Db;
|
||||
use think\facade\View;
|
||||
use think\facade\Request;
|
||||
|
||||
class User extends BaseController
|
||||
{
|
||||
|
||||
public function user()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$list = Db::name('domain')->select();
|
||||
$domains = [];
|
||||
foreach ($list as $row) {
|
||||
$domains[] = $row['name'];
|
||||
}
|
||||
View::assign('domains', $domains);
|
||||
return view();
|
||||
}
|
||||
|
||||
public function user_data()
|
||||
{
|
||||
if (!checkPermission(2)) return json(['total' => 0, 'rows' => []]);
|
||||
$kw = input('post.kw', null, 'trim');
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
|
||||
$select = Db::name('user');
|
||||
if (!empty($kw)) {
|
||||
$select->whereLike('id|username', $kw);
|
||||
}
|
||||
$total = $select->count();
|
||||
$rows = $select->order('id', 'desc')->limit($offset, $limit)->select();
|
||||
|
||||
return json(['total' => $total, 'rows' => $rows]);
|
||||
}
|
||||
|
||||
public function user_op()
|
||||
{
|
||||
if (!checkPermission(2)) return $this->alert('error', '无权限');
|
||||
$act = input('param.act');
|
||||
if ($act == 'get') {
|
||||
$id = input('post.id/d');
|
||||
$row = Db::name('user')->where('id', $id)->find();
|
||||
if (!$row) {
|
||||
return json(['code' => -1, 'msg' => '用户不存在']);
|
||||
}
|
||||
$row['permission'] = Db::name('permission')->where('uid', $id)->column('domain');
|
||||
return json(['code' => 0, 'data' => $row]);
|
||||
} elseif ($act == 'add') {
|
||||
$username = input('post.username', null, 'trim');
|
||||
$password = input('post.password', null, 'trim');
|
||||
$is_api = input('post.is_api/d');
|
||||
$apikey = input('post.apikey', null, 'trim');
|
||||
$level = input('post.level/d');
|
||||
if (empty($username) || empty($password)) {
|
||||
return json(['code' => -1, 'msg' => '用户名或密码不能为空']);
|
||||
}
|
||||
if ($is_api == 1 && empty($apikey)) {
|
||||
return json(['code' => -1, 'msg' => 'API密钥不能为空']);
|
||||
}
|
||||
if (Db::name('user')->where('username', $username)->find()) {
|
||||
return json(['code' => -1, 'msg' => '用户名已存在']);
|
||||
}
|
||||
$uid = Db::name('user')->insertGetId([
|
||||
'username' => $username,
|
||||
'password' => password_hash($password, PASSWORD_DEFAULT),
|
||||
'is_api' => $is_api,
|
||||
'apikey' => $apikey,
|
||||
'level' => $level,
|
||||
'regtime' => date('Y-m-d H:i:s'),
|
||||
'status' => 1,
|
||||
]);
|
||||
if ($level == 1) {
|
||||
$permission = input('post.permission/a');
|
||||
if (!empty($permission)) {
|
||||
$data = [];
|
||||
foreach ($permission as $domain) {
|
||||
$data[] = ['uid' => $uid, 'domain' => $domain];
|
||||
}
|
||||
Db::name('permission')->insertAll($data);
|
||||
}
|
||||
}
|
||||
return json(['code' => 0, 'msg' => '添加用户成功!']);
|
||||
} elseif ($act == 'edit') {
|
||||
$id = input('post.id/d');
|
||||
$row = Db::name('user')->where('id', $id)->find();
|
||||
if (!$row) return json(['code' => -1, 'msg' => '用户不存在']);
|
||||
$username = input('post.username', null, 'trim');
|
||||
$is_api = input('post.is_api/d');
|
||||
$apikey = input('post.apikey', null, 'trim');
|
||||
$level = input('post.level/d');
|
||||
$repwd = input('post.repwd', null, 'trim');
|
||||
if (empty($username)) {
|
||||
return json(['code' => -1, 'msg' => '用户名不能为空']);
|
||||
}
|
||||
if ($is_api == 1 && empty($apikey)) {
|
||||
return json(['code' => -1, 'msg' => 'API密钥不能为空']);
|
||||
}
|
||||
if (Db::name('user')->where('username', $username)->where('id', '<>', $id)->find()) {
|
||||
return json(['code' => -1, 'msg' => '用户名已存在']);
|
||||
}
|
||||
if ($level == 1 && ($id == 1000 || $id == $this->request->user['id'])) {
|
||||
$level = 2;
|
||||
}
|
||||
Db::name('user')->where('id', $id)->update([
|
||||
'username' => $username,
|
||||
'is_api' => $is_api,
|
||||
'apikey' => $apikey,
|
||||
'level' => $level,
|
||||
]);
|
||||
Db::name('permission')->where(['uid' => $id])->delete();
|
||||
if ($level == 1) {
|
||||
$permission = input('post.permission/a');
|
||||
if (!empty($permission)) {
|
||||
$data = [];
|
||||
foreach ($permission as $domain) {
|
||||
$data[] = ['uid' => $id, 'domain' => $domain];
|
||||
}
|
||||
Db::name('permission')->insertAll($data);
|
||||
}
|
||||
}
|
||||
if (!empty($repwd)) {
|
||||
Db::name('user')->where('id', $id)->update(['password' => password_hash($repwd, PASSWORD_DEFAULT)]);
|
||||
}
|
||||
return json(['code' => 0, 'msg' => '修改用户成功!']);
|
||||
} elseif ($act == 'set') {
|
||||
$id = input('post.id/d');
|
||||
$status = input('post.status/d');
|
||||
if ($id == 1000) {
|
||||
return json(['code' => -1, 'msg' => '此用户无法修改状态']);
|
||||
}
|
||||
if ($id == $this->request->user['id']) {
|
||||
return json(['code' => -1, 'msg' => '当前登录用户无法修改状态']);
|
||||
}
|
||||
Db::name('user')->where('id', $id)->update(['status' => $status]);
|
||||
return json(['code' => 0]);
|
||||
} elseif ($act == 'del') {
|
||||
$id = input('post.id/d');
|
||||
if ($id == 1000) {
|
||||
return json(['code' => -1, 'msg' => '此用户无法删除']);
|
||||
}
|
||||
if ($id == $this->request->user['id']) {
|
||||
return json(['code' => -1, 'msg' => '当前登录用户无法删除']);
|
||||
}
|
||||
Db::name('user')->where('id', $id)->delete();
|
||||
return json(['code' => 0]);
|
||||
}
|
||||
return json(['code' => -3]);
|
||||
}
|
||||
|
||||
public function log()
|
||||
{
|
||||
return view();
|
||||
}
|
||||
|
||||
public function log_data()
|
||||
{
|
||||
$uid = input('post.uid', null, 'trim');
|
||||
$kw = input('post.kw', null, 'trim');
|
||||
$domain = input('post.domain', null, 'trim');
|
||||
$offset = input('post.offset/d');
|
||||
$limit = input('post.limit/d');
|
||||
|
||||
$select = Db::name('log');
|
||||
if ($this->request->user['type'] == 'domain') {
|
||||
$select->where('domain', $this->request->user['name']);
|
||||
} elseif ($this->request->user['level'] == 1) {
|
||||
$select->where('uid', $this->request->user['id']);
|
||||
} elseif (!isNullOrEmpty($uid)) {
|
||||
$select->where('uid', $uid);
|
||||
}
|
||||
if (!empty($kw)) {
|
||||
$select->whereLike('action|data', '%' . $kw . '%');
|
||||
}
|
||||
if (!empty($domain)) {
|
||||
$select->where('domain', $domain);
|
||||
}
|
||||
$total = $select->count();
|
||||
$rows = $select->order('id', 'desc')->limit($offset, $limit)->select();
|
||||
|
||||
return json(['total' => $total, 'rows' => $rows]);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -407,6 +407,24 @@ location / {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保ECC私钥使用EC专用格式标识
|
||||
* 某些程序需要EC标识才能正确识别ECC私钥
|
||||
*/
|
||||
public static function ensureECPrivateKeyFormat($private_key)
|
||||
{
|
||||
if (strpos($private_key, '-----BEGIN EC PRIVATE KEY-----') !== false) {
|
||||
return $private_key;
|
||||
}
|
||||
|
||||
if (strpos($private_key, '-----BEGIN PRIVATE KEY-----') !== false) {
|
||||
$private_key = preg_replace('/^-----BEGIN PRIVATE KEY-----$/m', '-----BEGIN EC PRIVATE KEY-----', $private_key);
|
||||
$private_key = preg_replace('/^-----END PRIVATE KEY-----$/m', '-----END EC PRIVATE KEY-----', $private_key);
|
||||
}
|
||||
|
||||
return $private_key;
|
||||
}
|
||||
|
||||
public static function getPfx($fullchain, $privatekey, $pwd = '123456')
|
||||
{
|
||||
openssl_pkcs12_export($fullchain, $pfx, $privatekey, $pwd);
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib;
|
||||
|
||||
interface CertInterface
|
||||
{
|
||||
function register();
|
||||
|
||||
function buyCert($domainList, &$order);
|
||||
|
||||
function createOrder($domainList, &$order, $keytype, $keysize);
|
||||
|
||||
function authOrder($domainList, $order);
|
||||
|
||||
function getAuthStatus($domainList, $order);
|
||||
|
||||
function finalizeOrder($domainList, $order, $keytype, $keysize);
|
||||
|
||||
function revoke($order, $pem);
|
||||
|
||||
function cancel($order);
|
||||
|
||||
function setLogger($func);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib;
|
||||
|
||||
interface CertInterface
|
||||
{
|
||||
function register();
|
||||
|
||||
function buyCert($domainList, &$order);
|
||||
|
||||
function createOrder($domainList, &$order, $keytype, $keysize);
|
||||
|
||||
function authOrder($domainList, $order);
|
||||
|
||||
function getAuthStatus($domainList, $order);
|
||||
|
||||
function finalizeOrder($domainList, $order, $keytype, $keysize);
|
||||
|
||||
function revoke($order, $pem);
|
||||
|
||||
function cancel($order);
|
||||
|
||||
function setLogger($func);
|
||||
}
|
||||
|
||||
@@ -285,11 +285,22 @@ class DeployHelper
|
||||
],
|
||||
],
|
||||
'taskinputs' => [
|
||||
'type' => [
|
||||
'name' => '部署类型',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '网站的证书',
|
||||
'1' => '面板本身的证书',
|
||||
],
|
||||
'value' => '0',
|
||||
'required' => true,
|
||||
],
|
||||
'sites' => [
|
||||
'name' => '网站名称列表',
|
||||
'type' => 'textarea',
|
||||
'placeholder' => '填写要部署证书的网站名称,每行一个',
|
||||
'required' => true,
|
||||
'show' => 'type==0',
|
||||
],
|
||||
],
|
||||
],
|
||||
@@ -489,13 +500,66 @@ class DeployHelper
|
||||
],
|
||||
'taskinputs' => [],
|
||||
],
|
||||
'uusec' => [
|
||||
'name' => '南墙WAF',
|
||||
'class' => 1,
|
||||
'icon' => 'waf.png',
|
||||
'desc' => '',
|
||||
'note' => null,
|
||||
'inputs' => [
|
||||
'url' => [
|
||||
'name' => '控制台地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => '南墙WAF控制台地址',
|
||||
'note' => '填写规则如:http://192.168.1.100:4443 ,不要带其他后缀',
|
||||
'required' => true,
|
||||
],
|
||||
'username' => [
|
||||
'name' => '用户名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'password' => [
|
||||
'name' => '密码',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'taskinputs' => [
|
||||
'id' => [
|
||||
'name' => '证书ID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'note' => '在证书管理查看证书的ID,注意域名是否与证书匹配',
|
||||
'required' => true,
|
||||
],
|
||||
'name' => [
|
||||
'name' => '证书名称',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'note' => '在证书管理查看证书的名称',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
'opanel' => [
|
||||
'name' => '1Panel',
|
||||
'class' => 1,
|
||||
'icon' => 'opanel.png',
|
||||
'desc' => '更新面板证书管理内的SSL证书',
|
||||
'note' => null,
|
||||
'tasknote' => '系统会根据关联SSL证书的域名,自动更新对应证书',
|
||||
'tasknote' => '',
|
||||
'inputs' => [
|
||||
'url' => [
|
||||
'name' => '面板地址',
|
||||
@@ -530,7 +594,32 @@ class DeployHelper
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'taskinputs' => [],
|
||||
'taskinputs' => [
|
||||
'type' => [
|
||||
'name' => '部署类型',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '更新已有证书',
|
||||
'3' => '面板本身的证书',
|
||||
],
|
||||
'value' => '0',
|
||||
'required' => true,
|
||||
],
|
||||
'id' => [
|
||||
'name' => '证书ID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '在证书列表查看ID',
|
||||
'note' => '留空为根据关联SSL证书的域名,自动更新对应证书',
|
||||
'show' => 'type==0',
|
||||
],
|
||||
'node_name' => [
|
||||
'name' => '子节点名称',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'note' => '不填写时,将替换主控节点证书;否则,将替换被控节点证书',
|
||||
'show' => 'type==0',
|
||||
],
|
||||
],
|
||||
],
|
||||
'mwpanel' => [
|
||||
'name' => 'MW面板',
|
||||
@@ -649,6 +738,47 @@ class DeployHelper
|
||||
],
|
||||
],
|
||||
],
|
||||
'xp' => [
|
||||
'name' => '小皮面板',
|
||||
'class' => 1,
|
||||
'icon' => 'xp.png',
|
||||
'desc' => '',
|
||||
'note' => null,
|
||||
'tasknote' => '',
|
||||
'inputs' => [
|
||||
'url' => [
|
||||
'name' => '面板地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => '小皮面板地址',
|
||||
'note' => '填写规则如:http://192.168.1.100:8888 ,不要带其他后缀',
|
||||
'required' => true,
|
||||
],
|
||||
'apikey' => [
|
||||
'name' => '接口密钥',
|
||||
'type' => 'input',
|
||||
'placeholder' => '设置->OpenAPI接口',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'taskinputs' => [
|
||||
'sites' => [
|
||||
'name' => '网站名称列表',
|
||||
'type' => 'textarea',
|
||||
'placeholder' => '填写要部署证书的网站名称,每行一个',
|
||||
'note' => '网站名称,即为网站创建时绑定的第一个域名',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
'synology' => [
|
||||
'name' => '群晖面板',
|
||||
'class' => 1,
|
||||
@@ -743,6 +873,47 @@ class DeployHelper
|
||||
],
|
||||
'taskinputs' => [],
|
||||
],
|
||||
'fnos' => [
|
||||
'name' => '飞牛OS',
|
||||
'class' => 1,
|
||||
'icon' => 'fnos.png',
|
||||
'desc' => '更新飞牛OS的证书',
|
||||
'note' => '请先配置sudo免密:<br/>
|
||||
sudo visudo<br/>
|
||||
#在文件最后一行增加以下内容,需要将username替换成自己的用户名<br/>
|
||||
username ALL=(ALL) NOPASSWD: NOPASSWD: ALL<br/>
|
||||
ctrl+x 保存退出',
|
||||
'tasknote' => '系统会根据关联SSL证书的域名,自动更新对应证书',
|
||||
'inputs' => [
|
||||
'host' => [
|
||||
'name' => '主机地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => '填写IP地址或域名,需开启SSH功能',
|
||||
'required' => true,
|
||||
],
|
||||
'port' => [
|
||||
'name' => 'SSH端口',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'value' => '22',
|
||||
'required' => true,
|
||||
],
|
||||
'username' => [
|
||||
'name' => '用户名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '登录用户名',
|
||||
'value' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'password' => [
|
||||
'name' => '密码',
|
||||
'type' => 'input',
|
||||
'placeholder' => '登录密码',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
'taskinputs' => [],
|
||||
],
|
||||
'proxmox' => [
|
||||
'name' => 'Proxmox VE',
|
||||
'class' => 1,
|
||||
@@ -789,6 +960,56 @@ class DeployHelper
|
||||
],
|
||||
],
|
||||
],
|
||||
'k8s' => [
|
||||
'name' => 'K8S',
|
||||
'class' => 1,
|
||||
'icon' => 'server.png',
|
||||
'desc' => '部署到K8S集群的Secret和Ingress',
|
||||
'note' => '支持部署到K8S集群的Secret和Ingress',
|
||||
'tasknote' => '',
|
||||
'inputs' => [
|
||||
'name' => [
|
||||
'name' => '名称',
|
||||
'type' => 'input',
|
||||
'placeholder' => '仅用于区分',
|
||||
'required' => true,
|
||||
],
|
||||
'kubeconfig' => [
|
||||
'name' => 'kubeconfig',
|
||||
'type' => 'textarea',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'taskinputs' => [
|
||||
'namespace' => [
|
||||
'name' => '命名空间',
|
||||
'type' => 'input',
|
||||
'value' => 'default',
|
||||
'required' => true,
|
||||
],
|
||||
'secret_name' => [
|
||||
'name' => 'Secret名称',
|
||||
'type' => 'input',
|
||||
'placeholder' => '如果Secret不存在,则自动创建',
|
||||
'required' => true,
|
||||
],
|
||||
'ingresses' => [
|
||||
'name' => 'Ingress名称',
|
||||
'type' => 'input',
|
||||
'placeholder' => '多个用英文逗号分隔,可留空,留空则只更新Secret',
|
||||
],
|
||||
],
|
||||
],
|
||||
'aliyun' => [
|
||||
'name' => '阿里云',
|
||||
'class' => 2,
|
||||
@@ -1418,6 +1639,54 @@ class DeployHelper
|
||||
],
|
||||
],
|
||||
],
|
||||
'ksyun' => [
|
||||
'name' => '金山云',
|
||||
'class' => 2,
|
||||
'icon' => 'ksyun.ico',
|
||||
'desc' => '支持部署到金山云CDN',
|
||||
'note' => '支持部署到金山云CDN',
|
||||
'inputs' => [
|
||||
'AccessKeyId' => [
|
||||
'name' => 'AccessKeyId',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'SecretAccessKey' => [
|
||||
'name' => 'SecretAccessKey',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'taskinputs' => [
|
||||
'product' => [
|
||||
'name' => '要部署的产品',
|
||||
'type' => 'select',
|
||||
'options' => [
|
||||
['value'=>'cdn', 'label'=>'CDN'],
|
||||
],
|
||||
'value' => 'cdn',
|
||||
'required' => true,
|
||||
],
|
||||
'domain' => [
|
||||
'name' => '绑定的域名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '多个域名可使用,分隔',
|
||||
'show' => 'product==\'cdn\'',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
'huoshan' => [
|
||||
'name' => '火山引擎',
|
||||
'class' => 2,
|
||||
@@ -2004,6 +2273,12 @@ class DeployHelper
|
||||
'required' => true,
|
||||
'show' => 'auth==1',
|
||||
],
|
||||
'passphrase' => [
|
||||
'name' => '私钥密码',
|
||||
'type' => 'input',
|
||||
'placeholder' => '若私钥有设置密码,请填写此项',
|
||||
'show' => 'auth==1',
|
||||
],
|
||||
'windows' => [
|
||||
'name' => '是否Windows',
|
||||
'type' => 'radio',
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib;
|
||||
|
||||
interface DeployInterface
|
||||
{
|
||||
function check();
|
||||
|
||||
function deploy($fullchain, $privatekey, $config, &$info);
|
||||
|
||||
function setLogger($func);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib;
|
||||
|
||||
interface DeployInterface
|
||||
{
|
||||
function check();
|
||||
|
||||
function deploy($fullchain, $privatekey, $config, &$info);
|
||||
|
||||
function setLogger($func);
|
||||
}
|
||||
|
||||
@@ -1,226 +1,241 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib;
|
||||
|
||||
use think\facade\Db;
|
||||
|
||||
class DnsHelper
|
||||
{
|
||||
public static $dns_config = [
|
||||
'aliyun' => [
|
||||
'name' => '阿里云',
|
||||
'config' => [
|
||||
'ak' => 'AccessKeyId',
|
||||
'sk' => 'AccessKeySecret',
|
||||
],
|
||||
'remark' => 1, //是否支持备注,1单独设置备注,2和记录一起设置
|
||||
'status' => true, //是否支持启用暂停
|
||||
'redirect' => true, //是否支持域名转发
|
||||
'log' => true, //是否支持查看日志
|
||||
'weight' => false, //是否支持权重
|
||||
'page' => false, //是否客户端分页
|
||||
'add' => true, //是否支持添加域名
|
||||
],
|
||||
'dnspod' => [
|
||||
'name' => '腾讯云',
|
||||
'config' => [
|
||||
'ak' => 'SecretId',
|
||||
'sk' => 'SecretKey',
|
||||
],
|
||||
'remark' => 1,
|
||||
'status' => true,
|
||||
'redirect' => true,
|
||||
'log' => true,
|
||||
'weight' => true,
|
||||
'page' => false,
|
||||
'add' => true,
|
||||
],
|
||||
'huawei' => [
|
||||
'name' => '华为云',
|
||||
'config' => [
|
||||
'ak' => 'AccessKeyId',
|
||||
'sk' => 'SecretAccessKey',
|
||||
],
|
||||
'remark' => 2,
|
||||
'status' => true,
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => true,
|
||||
'page' => false,
|
||||
'add' => true,
|
||||
],
|
||||
'baidu' => [
|
||||
'name' => '百度云',
|
||||
'config' => [
|
||||
'ak' => 'AccessKey',
|
||||
'sk' => 'SecretKey',
|
||||
],
|
||||
'remark' => 2,
|
||||
'status' => false,
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => false,
|
||||
'page' => true,
|
||||
'add' => true,
|
||||
],
|
||||
'west' => [
|
||||
'name' => '西部数码',
|
||||
'config' => [
|
||||
'ak' => '用户名',
|
||||
'sk' => 'API密码',
|
||||
],
|
||||
'remark' => 0,
|
||||
'status' => true,
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => false,
|
||||
'page' => false,
|
||||
'add' => false,
|
||||
],
|
||||
'huoshan' => [
|
||||
'name' => '火山引擎',
|
||||
'config' => [
|
||||
'ak' => 'AccessKeyId',
|
||||
'sk' => 'SecretAccessKey',
|
||||
],
|
||||
'remark' => 2,
|
||||
'status' => true,
|
||||
'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',
|
||||
'config' => [
|
||||
'ak' => 'APIID',
|
||||
'sk' => 'API密钥',
|
||||
],
|
||||
'remark' => 0,
|
||||
'status' => true,
|
||||
'redirect' => true,
|
||||
'log' => false,
|
||||
'weight' => true,
|
||||
'page' => false,
|
||||
'add' => true,
|
||||
],
|
||||
'cloudflare' => [
|
||||
'name' => 'Cloudflare',
|
||||
'config' => [
|
||||
'ak' => '邮箱地址',
|
||||
'sk' => 'API密钥/令牌',
|
||||
],
|
||||
'remark' => 2,
|
||||
'status' => true,
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => false,
|
||||
'page' => false,
|
||||
'add' => true,
|
||||
],
|
||||
'namesilo' => [
|
||||
'name' => 'NameSilo',
|
||||
'config' => [
|
||||
'ak' => '账户名',
|
||||
'sk' => 'API Key',
|
||||
],
|
||||
'remark' => 0,
|
||||
'status' => false,
|
||||
'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,
|
||||
],
|
||||
];
|
||||
|
||||
public static $line_name = [
|
||||
'aliyun' => ['DEF' => 'default', 'CT' => 'telecom', 'CU' => 'unicom', 'CM' => 'mobile', 'AB' => 'oversea'],
|
||||
'dnspod' => ['DEF' => '0', 'CT' => '10=0', 'CU' => '10=1', 'CM' => '10=3', 'AB' => '3=0'],
|
||||
'huawei' => ['DEF' => 'default_view', 'CT' => 'Dianxin', 'CU' => 'Liantong', 'CM' => 'Yidong', 'AB' => 'Abroad'],
|
||||
'west' => ['DEF' => '', 'CT' => 'LTEL', 'CU' => 'LCNC', 'CM' => 'LMOB', 'AB' => 'LFOR'],
|
||||
'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()
|
||||
{
|
||||
return self::$dns_config;
|
||||
}
|
||||
|
||||
private static function getConfig($aid)
|
||||
{
|
||||
$account = Db::name('account')->where('id', $aid)->find();
|
||||
if (!$account) return false;
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DnsInterface|bool
|
||||
*/
|
||||
public static function getModel($aid, $domain = null, $domainid = null)
|
||||
{
|
||||
$config = self::getConfig($aid);
|
||||
if (!$config) return false;
|
||||
$dnstype = $config['type'];
|
||||
$class = "\\app\\lib\\dns\\{$dnstype}";
|
||||
if (class_exists($class)) {
|
||||
$config['domain'] = $domain;
|
||||
$config['domainid'] = $domainid;
|
||||
$model = new $class($config);
|
||||
return $model;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DnsInterface|bool
|
||||
*/
|
||||
public static function getModel2($config)
|
||||
{
|
||||
$dnstype = $config['type'];
|
||||
$class = "\\app\\lib\\dns\\{$dnstype}";
|
||||
if (class_exists($class)) {
|
||||
$config['domain'] = $config['name'];
|
||||
$config['domainid'] = $config['thirdid'];
|
||||
$model = new $class($config);
|
||||
return $model;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib;
|
||||
|
||||
use think\facade\Db;
|
||||
|
||||
class DnsHelper
|
||||
{
|
||||
public static $dns_config = [
|
||||
'aliyun' => [
|
||||
'name' => '阿里云',
|
||||
'config' => [
|
||||
'ak' => 'AccessKeyId',
|
||||
'sk' => 'AccessKeySecret',
|
||||
],
|
||||
'remark' => 1, //是否支持备注,1单独设置备注,2和记录一起设置
|
||||
'status' => true, //是否支持启用暂停
|
||||
'redirect' => true, //是否支持域名转发
|
||||
'log' => true, //是否支持查看日志
|
||||
'weight' => false, //是否支持权重
|
||||
'page' => false, //是否客户端分页
|
||||
'add' => true, //是否支持添加域名
|
||||
],
|
||||
'dnspod' => [
|
||||
'name' => '腾讯云',
|
||||
'config' => [
|
||||
'ak' => 'SecretId',
|
||||
'sk' => 'SecretKey',
|
||||
],
|
||||
'remark' => 1,
|
||||
'status' => true,
|
||||
'redirect' => true,
|
||||
'log' => true,
|
||||
'weight' => true,
|
||||
'page' => false,
|
||||
'add' => true,
|
||||
],
|
||||
'huawei' => [
|
||||
'name' => '华为云',
|
||||
'config' => [
|
||||
'ak' => 'AccessKeyId',
|
||||
'sk' => 'SecretAccessKey',
|
||||
],
|
||||
'remark' => 2,
|
||||
'status' => true,
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => true,
|
||||
'page' => false,
|
||||
'add' => true,
|
||||
],
|
||||
'baidu' => [
|
||||
'name' => '百度云',
|
||||
'config' => [
|
||||
'ak' => 'AccessKey',
|
||||
'sk' => 'SecretKey',
|
||||
],
|
||||
'remark' => 2,
|
||||
'status' => false,
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => false,
|
||||
'page' => true,
|
||||
'add' => true,
|
||||
],
|
||||
'west' => [
|
||||
'name' => '西部数码',
|
||||
'config' => [
|
||||
'ak' => '用户名',
|
||||
'sk' => 'API密码',
|
||||
],
|
||||
'remark' => 0,
|
||||
'status' => true,
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => false,
|
||||
'page' => false,
|
||||
'add' => false,
|
||||
],
|
||||
'huoshan' => [
|
||||
'name' => '火山引擎',
|
||||
'config' => [
|
||||
'ak' => 'AccessKeyId',
|
||||
'sk' => 'SecretAccessKey',
|
||||
],
|
||||
'remark' => 2,
|
||||
'status' => true,
|
||||
'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',
|
||||
'config' => [
|
||||
'ak' => 'APIID',
|
||||
'sk' => 'API密钥',
|
||||
],
|
||||
'remark' => 0,
|
||||
'status' => true,
|
||||
'redirect' => true,
|
||||
'log' => false,
|
||||
'weight' => true,
|
||||
'page' => false,
|
||||
'add' => true,
|
||||
],
|
||||
'cloudflare' => [
|
||||
'name' => 'Cloudflare',
|
||||
'config' => [
|
||||
'ak' => '邮箱地址',
|
||||
'sk' => 'API密钥/令牌',
|
||||
],
|
||||
'remark' => 2,
|
||||
'status' => true,
|
||||
'redirect' => false,
|
||||
'log' => false,
|
||||
'weight' => false,
|
||||
'page' => false,
|
||||
'add' => true,
|
||||
],
|
||||
'namesilo' => [
|
||||
'name' => 'NameSilo',
|
||||
'config' => [
|
||||
'ak' => '账户名',
|
||||
'sk' => 'API Key',
|
||||
],
|
||||
'remark' => 0,
|
||||
'status' => false,
|
||||
'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,
|
||||
],
|
||||
'spaceship' => [
|
||||
'name' => 'Spaceship',
|
||||
'config' => [
|
||||
'ak' => 'AccessKey',
|
||||
'sk' => 'SecretKey',
|
||||
],
|
||||
'remark' => 0,
|
||||
'status' => false,
|
||||
'redirect' => true,
|
||||
'log' => false,
|
||||
'weight' => false,
|
||||
'page' => false,
|
||||
'add' => true,
|
||||
],
|
||||
];
|
||||
|
||||
public static $line_name = [
|
||||
'aliyun' => ['DEF' => 'default', 'CT' => 'telecom', 'CU' => 'unicom', 'CM' => 'mobile', 'AB' => 'oversea'],
|
||||
'dnspod' => ['DEF' => '0', 'CT' => '10=0', 'CU' => '10=1', 'CM' => '10=3', 'AB' => '3=0'],
|
||||
'huawei' => ['DEF' => 'default_view', 'CT' => 'Dianxin', 'CU' => 'Liantong', 'CM' => 'Yidong', 'AB' => 'Abroad'],
|
||||
'west' => ['DEF' => '', 'CT' => 'LTEL', 'CU' => 'LCNC', 'CM' => 'LMOB', 'AB' => 'LFOR'],
|
||||
'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'],
|
||||
'spaceship' => ['DEF' => 'default'],
|
||||
];
|
||||
|
||||
public static function getList()
|
||||
{
|
||||
return self::$dns_config;
|
||||
}
|
||||
|
||||
private static function getConfig($aid)
|
||||
{
|
||||
$account = Db::name('account')->where('id', $aid)->find();
|
||||
if (!$account) return false;
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DnsInterface|bool
|
||||
*/
|
||||
public static function getModel($aid, $domain = null, $domainid = null)
|
||||
{
|
||||
$config = self::getConfig($aid);
|
||||
if (!$config) return false;
|
||||
$dnstype = $config['type'];
|
||||
$class = "\\app\\lib\\dns\\{$dnstype}";
|
||||
if (class_exists($class)) {
|
||||
$config['domain'] = $domain;
|
||||
$config['domainid'] = $domainid;
|
||||
$model = new $class($config);
|
||||
return $model;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DnsInterface|bool
|
||||
*/
|
||||
public static function getModel2($config)
|
||||
{
|
||||
$dnstype = $config['type'];
|
||||
$class = "\\app\\lib\\dns\\{$dnstype}";
|
||||
if (class_exists($class)) {
|
||||
$config['domain'] = $config['name'];
|
||||
$config['domainid'] = $config['thirdid'];
|
||||
$model = new $class($config);
|
||||
return $model;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib;
|
||||
|
||||
interface DnsInterface
|
||||
{
|
||||
function getError();
|
||||
|
||||
function check();
|
||||
|
||||
function getDomainList($KeyWord = null, $PageNumber = 1, $PageSize = 20);
|
||||
|
||||
function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null);
|
||||
|
||||
function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null);
|
||||
|
||||
function getDomainRecordInfo($RecordId);
|
||||
|
||||
function addDomainRecord($Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = 1, $Weight = null, $Remark = null);
|
||||
|
||||
function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = 1, $Weight = null, $Remark = null);
|
||||
|
||||
function updateDomainRecordRemark($RecordId, $Remark);
|
||||
|
||||
function deleteDomainRecord($RecordId);
|
||||
|
||||
function setDomainRecordStatus($RecordId, $Status);
|
||||
|
||||
function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null);
|
||||
|
||||
function getRecordLine();
|
||||
|
||||
function getMinTTL();
|
||||
|
||||
function addDomain($Domain);
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib;
|
||||
|
||||
interface DnsInterface
|
||||
{
|
||||
function getError();
|
||||
|
||||
function check();
|
||||
|
||||
function getDomainList($KeyWord = null, $PageNumber = 1, $PageSize = 20);
|
||||
|
||||
function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null);
|
||||
|
||||
function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null);
|
||||
|
||||
function getDomainRecordInfo($RecordId);
|
||||
|
||||
function addDomainRecord($Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = 1, $Weight = null, $Remark = null);
|
||||
|
||||
function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = 'default', $TTL = 600, $MX = 1, $Weight = null, $Remark = null);
|
||||
|
||||
function updateDomainRecordRemark($RecordId, $Remark);
|
||||
|
||||
function deleteDomainRecord($RecordId);
|
||||
|
||||
function setDomainRecordStatus($RecordId, $Status);
|
||||
|
||||
function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null);
|
||||
|
||||
function getRecordLine();
|
||||
|
||||
function getMinTTL();
|
||||
|
||||
function addDomain($Domain);
|
||||
}
|
||||
|
||||
456
app/lib/TOTP.php
456
app/lib/TOTP.php
@@ -1,228 +1,228 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib;
|
||||
|
||||
class TOTP
|
||||
{
|
||||
private static $BASE32_ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567';
|
||||
|
||||
private $period = 30;
|
||||
private $digest = 'sha1';
|
||||
private $digits = 6;
|
||||
private $epoch = 0;
|
||||
|
||||
private $secret;
|
||||
private $issuer;
|
||||
private $label;
|
||||
|
||||
public function __construct(?string $secret)
|
||||
{
|
||||
if ($secret == null) {
|
||||
$secret = $this->generateSecret();
|
||||
}
|
||||
$this->secret = $secret;
|
||||
}
|
||||
|
||||
public static function create(?string $secret = null)
|
||||
{
|
||||
return new self($secret);
|
||||
}
|
||||
|
||||
public function getSecret(): string
|
||||
{
|
||||
return $this->secret;
|
||||
}
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setLabel(string $label): void
|
||||
{
|
||||
$this->label = $label;
|
||||
}
|
||||
|
||||
public function getIssuer(): ?string
|
||||
{
|
||||
return $this->issuer;
|
||||
}
|
||||
|
||||
public function setIssuer(string $issuer): void
|
||||
{
|
||||
$this->issuer = $issuer;
|
||||
}
|
||||
|
||||
public function verify(string $otp, ?int $timestamp = null, ?int $window = null): bool
|
||||
{
|
||||
$timestamp = $this->getTimestamp($timestamp);
|
||||
|
||||
if (null === $window) {
|
||||
return $this->compareOTP($this->at($timestamp), $otp);
|
||||
}
|
||||
|
||||
return $this->verifyOtpWithWindow($otp, $timestamp, $window);
|
||||
}
|
||||
|
||||
private function verifyOtpWithWindow(string $otp, int $timestamp, int $window): bool
|
||||
{
|
||||
$window = abs($window);
|
||||
|
||||
for ($i = 0; $i <= $window; ++$i) {
|
||||
$next = $i * $this->period + $timestamp;
|
||||
$previous = -$i * $this->period + $timestamp;
|
||||
$valid = $this->compareOTP($this->at($next), $otp) ||
|
||||
$this->compareOTP($this->at($previous), $otp);
|
||||
|
||||
if ($valid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getProvisioningUri(): string
|
||||
{
|
||||
$params = [];
|
||||
if (30 !== $this->period) {
|
||||
$params['period'] = $this->period;
|
||||
}
|
||||
if (0 !== $this->epoch) {
|
||||
$params['epoch'] = $this->epoch;
|
||||
}
|
||||
$label = $this->getLabel();
|
||||
if (null === $label) {
|
||||
throw new \InvalidArgumentException('The label is not set.');
|
||||
}
|
||||
if ($this->hasColon($label)) {
|
||||
throw new \InvalidArgumentException('Label must not contain a colon.');
|
||||
}
|
||||
$params['issuer'] = $this->getIssuer();
|
||||
$params['secret'] = $this->getSecret();
|
||||
$query = str_replace(['+', '%7E'], ['%20', '~'], http_build_query($params));
|
||||
return sprintf('otpauth://totp/%s?%s', rawurlencode((null !== $this->getIssuer() ? $this->getIssuer() . ':' : '') . $label), $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* The OTP at the specified input.
|
||||
*/
|
||||
private function generateOTP(int $input): string
|
||||
{
|
||||
$hash = hash_hmac($this->digest, $this->intToByteString($input), $this->base32_decode($this->getSecret()), true);
|
||||
|
||||
$hmac = array_values(unpack('C*', $hash));
|
||||
|
||||
$offset = ($hmac[\count($hmac) - 1] & 0xF);
|
||||
$code = ($hmac[$offset + 0] & 0x7F) << 24 | ($hmac[$offset + 1] & 0xFF) << 16 | ($hmac[$offset + 2] & 0xFF) << 8 | ($hmac[$offset + 3] & 0xFF);
|
||||
$otp = $code % (10 ** $this->digits);
|
||||
|
||||
return str_pad((string) $otp, $this->digits, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
private function at(int $timestamp): string
|
||||
{
|
||||
return $this->generateOTP($this->timecode($timestamp));
|
||||
}
|
||||
|
||||
private function timecode(int $timestamp): int
|
||||
{
|
||||
return (int) floor(($timestamp - $this->epoch) / $this->period);
|
||||
}
|
||||
|
||||
private function getTimestamp(?int $timestamp): int
|
||||
{
|
||||
$timestamp = $timestamp ?? time();
|
||||
if ($timestamp < 0) {
|
||||
throw new \InvalidArgumentException('Timestamp must be at least 0.');
|
||||
}
|
||||
|
||||
return $timestamp;
|
||||
}
|
||||
|
||||
private function generateSecret(): string
|
||||
{
|
||||
return strtoupper($this->base32_encode(random_bytes(20)));
|
||||
}
|
||||
|
||||
private function base32_encode($data)
|
||||
{
|
||||
$dataSize = strlen($data);
|
||||
$res = '';
|
||||
$remainder = 0;
|
||||
$remainderSize = 0;
|
||||
|
||||
for ($i = 0; $i < $dataSize; $i++) {
|
||||
$b = ord($data[$i]);
|
||||
$remainder = ($remainder << 8) | $b;
|
||||
$remainderSize += 8;
|
||||
while ($remainderSize > 4) {
|
||||
$remainderSize -= 5;
|
||||
$c = $remainder & (31 << $remainderSize);
|
||||
$c >>= $remainderSize;
|
||||
$res .= self::$BASE32_ALPHABET[$c];
|
||||
}
|
||||
}
|
||||
if ($remainderSize > 0) {
|
||||
$remainder <<= (5 - $remainderSize);
|
||||
$c = $remainder & 31;
|
||||
$res .= self::$BASE32_ALPHABET[$c];
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
private function base32_decode($data)
|
||||
{
|
||||
$data = strtolower($data);
|
||||
$dataSize = strlen($data);
|
||||
$buf = 0;
|
||||
$bufSize = 0;
|
||||
$res = '';
|
||||
|
||||
for ($i = 0; $i < $dataSize; $i++) {
|
||||
$c = $data[$i];
|
||||
$b = strpos(self::$BASE32_ALPHABET, $c);
|
||||
if ($b === false) {
|
||||
throw new \Exception('Encoded string is invalid, it contains unknown char #'.ord($c));
|
||||
}
|
||||
$buf = ($buf << 5) | $b;
|
||||
$bufSize += 5;
|
||||
if ($bufSize > 7) {
|
||||
$bufSize -= 8;
|
||||
$b = ($buf & (0xff << $bufSize)) >> $bufSize;
|
||||
$res .= chr($b);
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
private function intToByteString(int $int): string
|
||||
{
|
||||
$result = [];
|
||||
while (0 !== $int) {
|
||||
$result[] = \chr($int & 0xFF);
|
||||
$int >>= 8;
|
||||
}
|
||||
|
||||
return str_pad(implode(array_reverse($result)), 8, "\000", STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
private function compareOTP(string $safe, string $user): bool
|
||||
{
|
||||
return hash_equals($safe, $user);
|
||||
}
|
||||
|
||||
private function hasColon(string $value): bool
|
||||
{
|
||||
$colons = [':', '%3A', '%3a'];
|
||||
foreach ($colons as $colon) {
|
||||
if (false !== mb_strpos($value, $colon)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib;
|
||||
|
||||
class TOTP
|
||||
{
|
||||
private static $BASE32_ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567';
|
||||
|
||||
private $period = 30;
|
||||
private $digest = 'sha1';
|
||||
private $digits = 6;
|
||||
private $epoch = 0;
|
||||
|
||||
private $secret;
|
||||
private $issuer;
|
||||
private $label;
|
||||
|
||||
public function __construct(?string $secret)
|
||||
{
|
||||
if ($secret == null) {
|
||||
$secret = $this->generateSecret();
|
||||
}
|
||||
$this->secret = $secret;
|
||||
}
|
||||
|
||||
public static function create(?string $secret = null)
|
||||
{
|
||||
return new self($secret);
|
||||
}
|
||||
|
||||
public function getSecret(): string
|
||||
{
|
||||
return $this->secret;
|
||||
}
|
||||
|
||||
public function getLabel(): ?string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function setLabel(string $label): void
|
||||
{
|
||||
$this->label = $label;
|
||||
}
|
||||
|
||||
public function getIssuer(): ?string
|
||||
{
|
||||
return $this->issuer;
|
||||
}
|
||||
|
||||
public function setIssuer(string $issuer): void
|
||||
{
|
||||
$this->issuer = $issuer;
|
||||
}
|
||||
|
||||
public function verify(string $otp, ?int $timestamp = null, ?int $window = null): bool
|
||||
{
|
||||
$timestamp = $this->getTimestamp($timestamp);
|
||||
|
||||
if (null === $window) {
|
||||
return $this->compareOTP($this->at($timestamp), $otp);
|
||||
}
|
||||
|
||||
return $this->verifyOtpWithWindow($otp, $timestamp, $window);
|
||||
}
|
||||
|
||||
private function verifyOtpWithWindow(string $otp, int $timestamp, int $window): bool
|
||||
{
|
||||
$window = abs($window);
|
||||
|
||||
for ($i = 0; $i <= $window; ++$i) {
|
||||
$next = $i * $this->period + $timestamp;
|
||||
$previous = -$i * $this->period + $timestamp;
|
||||
$valid = $this->compareOTP($this->at($next), $otp) ||
|
||||
$this->compareOTP($this->at($previous), $otp);
|
||||
|
||||
if ($valid) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getProvisioningUri(): string
|
||||
{
|
||||
$params = [];
|
||||
if (30 !== $this->period) {
|
||||
$params['period'] = $this->period;
|
||||
}
|
||||
if (0 !== $this->epoch) {
|
||||
$params['epoch'] = $this->epoch;
|
||||
}
|
||||
$label = $this->getLabel();
|
||||
if (null === $label) {
|
||||
throw new \InvalidArgumentException('The label is not set.');
|
||||
}
|
||||
if ($this->hasColon($label)) {
|
||||
throw new \InvalidArgumentException('Label must not contain a colon.');
|
||||
}
|
||||
$params['issuer'] = $this->getIssuer();
|
||||
$params['secret'] = $this->getSecret();
|
||||
$query = str_replace(['+', '%7E'], ['%20', '~'], http_build_query($params));
|
||||
return sprintf('otpauth://totp/%s?%s', rawurlencode((null !== $this->getIssuer() ? $this->getIssuer() . ':' : '') . $label), $query);
|
||||
}
|
||||
|
||||
/**
|
||||
* The OTP at the specified input.
|
||||
*/
|
||||
private function generateOTP(int $input): string
|
||||
{
|
||||
$hash = hash_hmac($this->digest, $this->intToByteString($input), $this->base32_decode($this->getSecret()), true);
|
||||
|
||||
$hmac = array_values(unpack('C*', $hash));
|
||||
|
||||
$offset = ($hmac[\count($hmac) - 1] & 0xF);
|
||||
$code = ($hmac[$offset + 0] & 0x7F) << 24 | ($hmac[$offset + 1] & 0xFF) << 16 | ($hmac[$offset + 2] & 0xFF) << 8 | ($hmac[$offset + 3] & 0xFF);
|
||||
$otp = $code % (10 ** $this->digits);
|
||||
|
||||
return str_pad((string) $otp, $this->digits, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
private function at(int $timestamp): string
|
||||
{
|
||||
return $this->generateOTP($this->timecode($timestamp));
|
||||
}
|
||||
|
||||
private function timecode(int $timestamp): int
|
||||
{
|
||||
return (int) floor(($timestamp - $this->epoch) / $this->period);
|
||||
}
|
||||
|
||||
private function getTimestamp(?int $timestamp): int
|
||||
{
|
||||
$timestamp = $timestamp ?? time();
|
||||
if ($timestamp < 0) {
|
||||
throw new \InvalidArgumentException('Timestamp must be at least 0.');
|
||||
}
|
||||
|
||||
return $timestamp;
|
||||
}
|
||||
|
||||
private function generateSecret(): string
|
||||
{
|
||||
return strtoupper($this->base32_encode(random_bytes(20)));
|
||||
}
|
||||
|
||||
private function base32_encode($data)
|
||||
{
|
||||
$dataSize = strlen($data);
|
||||
$res = '';
|
||||
$remainder = 0;
|
||||
$remainderSize = 0;
|
||||
|
||||
for ($i = 0; $i < $dataSize; $i++) {
|
||||
$b = ord($data[$i]);
|
||||
$remainder = ($remainder << 8) | $b;
|
||||
$remainderSize += 8;
|
||||
while ($remainderSize > 4) {
|
||||
$remainderSize -= 5;
|
||||
$c = $remainder & (31 << $remainderSize);
|
||||
$c >>= $remainderSize;
|
||||
$res .= self::$BASE32_ALPHABET[$c];
|
||||
}
|
||||
}
|
||||
if ($remainderSize > 0) {
|
||||
$remainder <<= (5 - $remainderSize);
|
||||
$c = $remainder & 31;
|
||||
$res .= self::$BASE32_ALPHABET[$c];
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
private function base32_decode($data)
|
||||
{
|
||||
$data = strtolower($data);
|
||||
$dataSize = strlen($data);
|
||||
$buf = 0;
|
||||
$bufSize = 0;
|
||||
$res = '';
|
||||
|
||||
for ($i = 0; $i < $dataSize; $i++) {
|
||||
$c = $data[$i];
|
||||
$b = strpos(self::$BASE32_ALPHABET, $c);
|
||||
if ($b === false) {
|
||||
throw new \Exception('Encoded string is invalid, it contains unknown char #'.ord($c));
|
||||
}
|
||||
$buf = ($buf << 5) | $b;
|
||||
$bufSize += 5;
|
||||
if ($bufSize > 7) {
|
||||
$bufSize -= 8;
|
||||
$b = ($buf & (0xff << $bufSize)) >> $bufSize;
|
||||
$res .= chr($b);
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
private function intToByteString(int $int): string
|
||||
{
|
||||
$result = [];
|
||||
while (0 !== $int) {
|
||||
$result[] = \chr($int & 0xFF);
|
||||
$int >>= 8;
|
||||
}
|
||||
|
||||
return str_pad(implode(array_reverse($result)), 8, "\000", STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
private function compareOTP(string $safe, string $user): bool
|
||||
{
|
||||
return hash_equals($safe, $user);
|
||||
}
|
||||
|
||||
private function hasColon(string $value): bool
|
||||
{
|
||||
$colons = [':', '%3A', '%3a'];
|
||||
foreach ($colons as $colon) {
|
||||
if (false !== mb_strpos($value, $colon)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace app\lib\acme;
|
||||
|
||||
use Exception;
|
||||
use stdClass;
|
||||
use app\lib\CertHelper;
|
||||
|
||||
/**
|
||||
* ACMECert
|
||||
@@ -368,10 +369,12 @@ class ACMECert extends ACMEv2
|
||||
if (version_compare(PHP_VERSION, '7.1.0') < 0) throw new Exception('PHP >= 7.1.0 required for EC keys !');
|
||||
$map = array('256' => 'prime256v1', '384' => 'secp384r1', '521' => 'secp521r1');
|
||||
if (isset($map[$curve_name])) $curve_name = $map[$curve_name];
|
||||
return $this->generateKey(array(
|
||||
$pem = $this->generateKey(array(
|
||||
'curve_name' => $curve_name,
|
||||
'private_key_type' => OPENSSL_KEYTYPE_EC
|
||||
));
|
||||
|
||||
return CertHelper::ensureECPrivateKeyFormat($pem);
|
||||
}
|
||||
|
||||
public function parseCertificate($cert_pem)
|
||||
|
||||
@@ -1,168 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\cert;
|
||||
|
||||
use app\lib\CertInterface;
|
||||
use app\lib\client\Aliyun as AliyunClient;
|
||||
use Exception;
|
||||
|
||||
class aliyun implements CertInterface
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $AccessKeySecret;
|
||||
private $Endpoint = 'cas.aliyuncs.com'; //API接入域名
|
||||
private $Version = '2020-04-07'; //API版本号
|
||||
private $config;
|
||||
private $logger;
|
||||
private AliyunClient $client;
|
||||
|
||||
public function __construct($config, $ext = null)
|
||||
{
|
||||
$this->AccessKeyId = $config['AccessKeyId'];
|
||||
$this->AccessKeySecret = $config['AccessKeySecret'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, $this->Endpoint, $this->Version, $proxy);
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
if (empty($this->AccessKeyId) || empty($this->AccessKeySecret) || empty($this->config['username']) || empty($this->config['phone']) || empty($this->config['email'])) throw new Exception('必填参数不能为空');
|
||||
$param = ['Action' => 'ListUserCertificateOrder'];
|
||||
$this->request($param, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function buyCert($domainList, &$order)
|
||||
{
|
||||
$param = ['Action' => 'DescribePackageState', 'ProductCode' => 'digicert-free-1-free'];
|
||||
$data = $this->request($param, true);
|
||||
if (!isset($data['TotalCount']) || $data['TotalCount'] == 0) throw new Exception('没有可用的免费证书资源包');
|
||||
$this->log('证书资源包总数量:' . $data['TotalCount'] . ',已使用数量:' . $data['UsedCount']);
|
||||
}
|
||||
|
||||
public function createOrder($domainList, &$order, $keytype, $keysize)
|
||||
{
|
||||
if (empty($domainList)) throw new Exception('域名列表不能为空');
|
||||
$domain = $domainList[0];
|
||||
$param = [
|
||||
'Action' => 'CreateCertificateRequest',
|
||||
'ProductCode' => 'digicert-free-1-free',
|
||||
'Username' => $this->config['username'],
|
||||
'Phone' => $this->config['phone'],
|
||||
'Email' => $this->config['email'],
|
||||
'Domain' => $domain,
|
||||
'ValidateType' => 'DNS'
|
||||
];
|
||||
$data = $this->request($param, true);
|
||||
if (empty($data['OrderId'])) throw new Exception('证书申请失败,OrderId为空');
|
||||
$order['OrderId'] = $data['OrderId'];
|
||||
|
||||
sleep(3);
|
||||
|
||||
$param = [
|
||||
'Action' => 'DescribeCertificateState',
|
||||
'OrderId' => $order['OrderId'],
|
||||
];
|
||||
$data = $this->request($param, true);
|
||||
|
||||
$dnsList = [];
|
||||
if ($data['Type'] == 'domain_verify') {
|
||||
$mainDomain = getMainDomain($domain);
|
||||
$name = substr($data['RecordDomain'], 0, -(strlen($mainDomain) + 1));
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => $data['RecordType'], 'value' => $data['RecordValue']];
|
||||
}
|
||||
|
||||
return $dnsList;
|
||||
}
|
||||
|
||||
public function authOrder($domainList, $order) {}
|
||||
|
||||
public function getAuthStatus($domainList, $order)
|
||||
{
|
||||
$param = [
|
||||
'Action' => 'DescribeCertificateState',
|
||||
'OrderId' => $order['OrderId'],
|
||||
];
|
||||
$data = $this->request($param, true);
|
||||
if ($data['Type'] == 'certificate') {
|
||||
return true;
|
||||
} elseif ($data['Type'] == 'verify_fail') {
|
||||
throw new Exception('证书审核失败');
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function finalizeOrder($domainList, $order, $keytype, $keysize)
|
||||
{
|
||||
$param = [
|
||||
'Action' => 'DescribeCertificateState',
|
||||
'OrderId' => $order['OrderId'],
|
||||
];
|
||||
$data = $this->request($param, true);
|
||||
$fullchain = $data['Certificate'];
|
||||
$private_key = $data['PrivateKey'];
|
||||
if (empty($fullchain) || empty($private_key)) throw new Exception('证书内容获取失败');
|
||||
|
||||
$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)
|
||||
{
|
||||
$param = [
|
||||
'Action' => 'CancelCertificateForPackageRequest',
|
||||
'OrderId' => $order['OrderId'],
|
||||
];
|
||||
$this->request($param);
|
||||
}
|
||||
|
||||
public function cancel($order)
|
||||
{
|
||||
$param = [
|
||||
'Action' => 'DescribeCertificateState',
|
||||
'OrderId' => $order['OrderId'],
|
||||
];
|
||||
$data = $this->request($param, true);
|
||||
if ($data['Type'] == 'domain_verify' || $data['Type'] == 'process') {
|
||||
$param = [
|
||||
'Action' => 'CancelOrderRequest',
|
||||
'OrderId' => $order['OrderId'],
|
||||
];
|
||||
$this->request($param);
|
||||
usleep(500000);
|
||||
}
|
||||
if ($data['Type'] == 'domain_verify' || $data['Type'] == 'process' || $data['Type'] == 'payed' || $data['Type'] == 'verify_fail') {
|
||||
$param = [
|
||||
'Action' => 'DeleteCertificateRequest',
|
||||
'OrderId' => $order['OrderId'],
|
||||
];
|
||||
$this->request($param);
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
private function request($param, $returnData = false)
|
||||
{
|
||||
$this->log('Request:' . json_encode($param, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
$result = $this->client->request($param);
|
||||
$response = json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
if (!strpos($response, '"Type":"certificate"')) {
|
||||
$this->log('Response:' . $response);
|
||||
}
|
||||
return $returnData ? $result : true;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\cert;
|
||||
|
||||
use app\lib\CertInterface;
|
||||
use app\lib\client\Aliyun as AliyunClient;
|
||||
use Exception;
|
||||
|
||||
class aliyun implements CertInterface
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $AccessKeySecret;
|
||||
private $Endpoint = 'cas.aliyuncs.com'; //API接入域名
|
||||
private $Version = '2020-04-07'; //API版本号
|
||||
private $config;
|
||||
private $logger;
|
||||
private AliyunClient $client;
|
||||
|
||||
public function __construct($config, $ext = null)
|
||||
{
|
||||
$this->AccessKeyId = $config['AccessKeyId'];
|
||||
$this->AccessKeySecret = $config['AccessKeySecret'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, $this->Endpoint, $this->Version, $proxy);
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
if (empty($this->AccessKeyId) || empty($this->AccessKeySecret) || empty($this->config['username']) || empty($this->config['phone']) || empty($this->config['email'])) throw new Exception('必填参数不能为空');
|
||||
$param = ['Action' => 'ListUserCertificateOrder'];
|
||||
$this->request($param, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function buyCert($domainList, &$order)
|
||||
{
|
||||
$param = ['Action' => 'DescribePackageState', 'ProductCode' => 'digicert-free-1-free'];
|
||||
$data = $this->request($param, true);
|
||||
if (!isset($data['TotalCount']) || $data['TotalCount'] == 0) throw new Exception('没有可用的免费证书资源包');
|
||||
$this->log('证书资源包总数量:' . $data['TotalCount'] . ',已使用数量:' . $data['UsedCount']);
|
||||
}
|
||||
|
||||
public function createOrder($domainList, &$order, $keytype, $keysize)
|
||||
{
|
||||
if (empty($domainList)) throw new Exception('域名列表不能为空');
|
||||
$domain = $domainList[0];
|
||||
$param = [
|
||||
'Action' => 'CreateCertificateRequest',
|
||||
'ProductCode' => 'digicert-free-1-free',
|
||||
'Username' => $this->config['username'],
|
||||
'Phone' => $this->config['phone'],
|
||||
'Email' => $this->config['email'],
|
||||
'Domain' => $domain,
|
||||
'ValidateType' => 'DNS'
|
||||
];
|
||||
$data = $this->request($param, true);
|
||||
if (empty($data['OrderId'])) throw new Exception('证书申请失败,OrderId为空');
|
||||
$order['OrderId'] = $data['OrderId'];
|
||||
|
||||
sleep(3);
|
||||
|
||||
$param = [
|
||||
'Action' => 'DescribeCertificateState',
|
||||
'OrderId' => $order['OrderId'],
|
||||
];
|
||||
$data = $this->request($param, true);
|
||||
|
||||
$dnsList = [];
|
||||
if ($data['Type'] == 'domain_verify') {
|
||||
$mainDomain = getMainDomain($domain);
|
||||
$name = substr($data['RecordDomain'], 0, -(strlen($mainDomain) + 1));
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => $data['RecordType'], 'value' => $data['RecordValue']];
|
||||
}
|
||||
|
||||
return $dnsList;
|
||||
}
|
||||
|
||||
public function authOrder($domainList, $order) {}
|
||||
|
||||
public function getAuthStatus($domainList, $order)
|
||||
{
|
||||
$param = [
|
||||
'Action' => 'DescribeCertificateState',
|
||||
'OrderId' => $order['OrderId'],
|
||||
];
|
||||
$data = $this->request($param, true);
|
||||
if ($data['Type'] == 'certificate') {
|
||||
return true;
|
||||
} elseif ($data['Type'] == 'verify_fail') {
|
||||
throw new Exception('证书审核失败');
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function finalizeOrder($domainList, $order, $keytype, $keysize)
|
||||
{
|
||||
$param = [
|
||||
'Action' => 'DescribeCertificateState',
|
||||
'OrderId' => $order['OrderId'],
|
||||
];
|
||||
$data = $this->request($param, true);
|
||||
$fullchain = $data['Certificate'];
|
||||
$private_key = $data['PrivateKey'];
|
||||
if (empty($fullchain) || empty($private_key)) throw new Exception('证书内容获取失败');
|
||||
|
||||
$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)
|
||||
{
|
||||
$param = [
|
||||
'Action' => 'CancelCertificateForPackageRequest',
|
||||
'OrderId' => $order['OrderId'],
|
||||
];
|
||||
$this->request($param);
|
||||
}
|
||||
|
||||
public function cancel($order)
|
||||
{
|
||||
$param = [
|
||||
'Action' => 'DescribeCertificateState',
|
||||
'OrderId' => $order['OrderId'],
|
||||
];
|
||||
$data = $this->request($param, true);
|
||||
if ($data['Type'] == 'domain_verify' || $data['Type'] == 'process') {
|
||||
$param = [
|
||||
'Action' => 'CancelOrderRequest',
|
||||
'OrderId' => $order['OrderId'],
|
||||
];
|
||||
$this->request($param);
|
||||
usleep(500000);
|
||||
}
|
||||
if ($data['Type'] == 'domain_verify' || $data['Type'] == 'process' || $data['Type'] == 'payed' || $data['Type'] == 'verify_fail') {
|
||||
$param = [
|
||||
'Action' => 'DeleteCertificateRequest',
|
||||
'OrderId' => $order['OrderId'],
|
||||
];
|
||||
$this->request($param);
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
private function request($param, $returnData = false)
|
||||
{
|
||||
$this->log('Request:' . json_encode($param, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
$result = $this->client->request($param);
|
||||
$response = json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
if (!strpos($response, '"Type":"certificate"')) {
|
||||
$this->log('Response:' . $response);
|
||||
}
|
||||
return $returnData ? $result : true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,114 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\cert;
|
||||
|
||||
use app\lib\CertInterface;
|
||||
use app\lib\acme\ACMECert;
|
||||
use Exception;
|
||||
|
||||
class customacme implements CertInterface
|
||||
{
|
||||
private $ac;
|
||||
private $config;
|
||||
private $ext;
|
||||
|
||||
public function __construct($config, $ext = null)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->ac = new ACMECert($config['directory'], (int)$config['proxy']);
|
||||
if ($ext) {
|
||||
$this->ext = $ext;
|
||||
$this->ac->loadAccountKey($ext['key']);
|
||||
$this->ac->setAccount($ext['kid']);
|
||||
}
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
if (empty($this->config['directory'])) throw new Exception('ACME地址不能为空');
|
||||
if (empty($this->config['email'])) throw new Exception('邮件地址不能为空');
|
||||
|
||||
if (!empty($this->ext['key'])) {
|
||||
if (!empty($this->config['kid']) && !empty($this->config['key'])) {
|
||||
$kid = $this->ac->registerEAB(true, $this->config['kid'], $this->config['key'], $this->config['email']);
|
||||
} else {
|
||||
$kid = $this->ac->register(true, $this->config['email']);
|
||||
}
|
||||
return ['kid' => $kid, 'key' => $this->ext['key']];
|
||||
}
|
||||
|
||||
$key = $this->ac->generateRSAKey(2048);
|
||||
$this->ac->loadAccountKey($key);
|
||||
if (!empty($this->config['kid']) && !empty($this->config['key'])) {
|
||||
$kid = $this->ac->registerEAB(true, $this->config['kid'], $this->config['key'], $this->config['email']);
|
||||
} else {
|
||||
$kid = $this->ac->register(true, $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));
|
||||
$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 customacme implements CertInterface
|
||||
{
|
||||
private $ac;
|
||||
private $config;
|
||||
private $ext;
|
||||
|
||||
public function __construct($config, $ext = null)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->ac = new ACMECert($config['directory'], (int)$config['proxy']);
|
||||
if ($ext) {
|
||||
$this->ext = $ext;
|
||||
$this->ac->loadAccountKey($ext['key']);
|
||||
$this->ac->setAccount($ext['kid']);
|
||||
}
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
if (empty($this->config['directory'])) throw new Exception('ACME地址不能为空');
|
||||
if (empty($this->config['email'])) throw new Exception('邮件地址不能为空');
|
||||
|
||||
if (!empty($this->ext['key'])) {
|
||||
if (!empty($this->config['kid']) && !empty($this->config['key'])) {
|
||||
$kid = $this->ac->registerEAB(true, $this->config['kid'], $this->config['key'], $this->config['email']);
|
||||
} else {
|
||||
$kid = $this->ac->register(true, $this->config['email']);
|
||||
}
|
||||
return ['kid' => $kid, 'key' => $this->ext['key']];
|
||||
}
|
||||
|
||||
$key = $this->ac->generateRSAKey(2048);
|
||||
$this->ac->loadAccountKey($key);
|
||||
if (!empty($this->config['kid']) && !empty($this->config['key'])) {
|
||||
$kid = $this->ac->registerEAB(true, $this->config['kid'], $this->config['key'], $this->config['email']);
|
||||
} else {
|
||||
$kid = $this->ac->register(true, $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));
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,164 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\cert;
|
||||
|
||||
use app\lib\CertInterface;
|
||||
use app\lib\client\Volcengine;
|
||||
use Exception;
|
||||
|
||||
class huoshan implements CertInterface
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $SecretAccessKey;
|
||||
private $endpoint = "open.volcengineapi.com";
|
||||
private $service = "certificate_service";
|
||||
private $version = "2021-06-01";
|
||||
private $region = "cn-north-1";
|
||||
private $logger;
|
||||
private Volcengine $client;
|
||||
|
||||
public function __construct($config = null, $ext = null)
|
||||
{
|
||||
$this->AccessKeyId = $config['AccessKeyId'];
|
||||
$this->SecretAccessKey = $config['SecretAccessKey'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, $this->endpoint, $this->service, $this->version, $this->region, $proxy);
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
if (empty($this->AccessKeyId) || empty($this->SecretAccessKey)) throw new Exception('必填参数不能为空');
|
||||
$this->request('GET', 'CertificateGetInstance', ['limit'=>1,'offset'=>0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function buyCert($domainList, &$order)
|
||||
{
|
||||
$data = $this->request('GET', 'CertificateGetOrganization');
|
||||
if(empty($data['content'])) throw new Exception('请先添加信息模板');
|
||||
$order['organization_id'] = $data['content'][0]['id'];
|
||||
}
|
||||
|
||||
public function createOrder($domainList, &$order, $keytype, $keysize)
|
||||
{
|
||||
if (empty($domainList)) throw new Exception('域名列表不能为空');
|
||||
$domain = $domainList[0];
|
||||
$param = [
|
||||
'plan' => 'digicert_free_standard_dv',
|
||||
'common_name' => $domain,
|
||||
'organization_id' => $order['organization_id'],
|
||||
'key_alg' => strtolower($keytype),
|
||||
'validation_type' => 'dns_txt',
|
||||
];
|
||||
$instance_id = $this->request('POST', 'QuickApplyCertificate', $param);
|
||||
if(empty($instance_id)) throw new Exception('证书申请失败,证书实例ID为空');
|
||||
$order['instance_id'] = $instance_id;
|
||||
|
||||
sleep(3);
|
||||
|
||||
$param = [
|
||||
'instance_id' => $instance_id,
|
||||
];
|
||||
$data = $this->request('GET', 'CertificateGetDcvParam', $param);
|
||||
|
||||
$dnsList = [];
|
||||
if (!empty($data['domains_to_be_validated'])) {
|
||||
$type = $data['validation_type'] == 'dns_cname' ? 'CNAME' : 'TXT';
|
||||
foreach ($data['domains_to_be_validated'] as $opts) {
|
||||
$mainDomain = getMainDomain($domain);
|
||||
$name = substr($opts['validation_domain'], 0, -(strlen($mainDomain) + 1));
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => $type, 'value' => $opts['value']];
|
||||
}
|
||||
}
|
||||
return $dnsList;
|
||||
}
|
||||
|
||||
public function authOrder($domainList, $order)
|
||||
{
|
||||
$query = [
|
||||
'instance_id' => $order['instance_id'],
|
||||
];
|
||||
$param = [
|
||||
'action' => '',
|
||||
];
|
||||
$this->request('POST', 'CertificateProgressInstanceOrder', $param, $query);
|
||||
}
|
||||
|
||||
public function getAuthStatus($domainList, $order)
|
||||
{
|
||||
$param = [
|
||||
'instance_id' => $order['instance_id'],
|
||||
];
|
||||
$data = $this->request('GET', 'CertificateGetInstance', $param);
|
||||
if(empty($data['content'])) throw new Exception('证书信息获取失败');
|
||||
$data = $data['content'][0];
|
||||
if($data['order_status'] == 300 && $data['certificate_exist'] == 1){
|
||||
return true;
|
||||
}elseif($data['order_status'] == 302){
|
||||
throw new Exception('证书申请失败');
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function finalizeOrder($domainList, $order, $keytype, $keysize)
|
||||
{
|
||||
$param = [
|
||||
'instance_id' => $order['instance_id'],
|
||||
];
|
||||
$data = $this->request('GET', 'CertificateGetInstance', $param);
|
||||
if (empty($data['content'])) throw new Exception('证书信息获取失败');
|
||||
$data = $data['content'][0];
|
||||
if (!isset($data['ssl']['certificate']['chain'])) throw new Exception('证书内容获取失败');
|
||||
|
||||
$fullchain = implode('', $data['ssl']['certificate']['chain']);
|
||||
$private_key = $data['ssl']['certificate']['private_key'];
|
||||
|
||||
return ['private_key' => $private_key, 'fullchain' => $fullchain, 'issuer' => $data['issuer'], 'subject' => $data['common_name']['CN'], 'validFrom' => intval($data['certificate_not_before_ms']/1000), 'validTo' => intval($data['certificate_not_after_ms']/1000)];
|
||||
}
|
||||
|
||||
public function revoke($order, $pem)
|
||||
{
|
||||
$query = [
|
||||
'instance_id' => $order['instance_id'],
|
||||
];
|
||||
$param = [
|
||||
'action' => 'revoke',
|
||||
'reason' => '关联域名错误',
|
||||
];
|
||||
$this->request('POST', 'CertificateProgressInstanceOrder', $param, $query);
|
||||
}
|
||||
|
||||
public function cancel($order)
|
||||
{
|
||||
$query = [
|
||||
'instance_id' => $order['instance_id'],
|
||||
];
|
||||
$param = [
|
||||
'action' => 'cancel',
|
||||
];
|
||||
$this->request('POST', 'CertificateProgressInstanceOrder', $param, $query);
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
private function request($method, $action, $params = [], $query = [])
|
||||
{
|
||||
$this->log('Action:'.$action.PHP_EOL.'Request:'.json_encode($params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
$result = $this->client->request($method, $action, $params, $query);
|
||||
if (is_array($result)) {
|
||||
$this->log('Response:'.json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\cert;
|
||||
|
||||
use app\lib\CertInterface;
|
||||
use app\lib\client\Volcengine;
|
||||
use Exception;
|
||||
|
||||
class huoshan implements CertInterface
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $SecretAccessKey;
|
||||
private $endpoint = "open.volcengineapi.com";
|
||||
private $service = "certificate_service";
|
||||
private $version = "2021-06-01";
|
||||
private $region = "cn-north-1";
|
||||
private $logger;
|
||||
private Volcengine $client;
|
||||
|
||||
public function __construct($config = null, $ext = null)
|
||||
{
|
||||
$this->AccessKeyId = $config['AccessKeyId'];
|
||||
$this->SecretAccessKey = $config['SecretAccessKey'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, $this->endpoint, $this->service, $this->version, $this->region, $proxy);
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
if (empty($this->AccessKeyId) || empty($this->SecretAccessKey)) throw new Exception('必填参数不能为空');
|
||||
$this->request('GET', 'CertificateGetInstance', ['limit'=>1,'offset'=>0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function buyCert($domainList, &$order)
|
||||
{
|
||||
$data = $this->request('GET', 'CertificateGetOrganization');
|
||||
if(empty($data['content'])) throw new Exception('请先添加信息模板');
|
||||
$order['organization_id'] = $data['content'][0]['id'];
|
||||
}
|
||||
|
||||
public function createOrder($domainList, &$order, $keytype, $keysize)
|
||||
{
|
||||
if (empty($domainList)) throw new Exception('域名列表不能为空');
|
||||
$domain = $domainList[0];
|
||||
$param = [
|
||||
'plan' => 'digicert_free_standard_dv',
|
||||
'common_name' => $domain,
|
||||
'organization_id' => $order['organization_id'],
|
||||
'key_alg' => strtolower($keytype),
|
||||
'validation_type' => 'dns_txt',
|
||||
];
|
||||
$instance_id = $this->request('POST', 'QuickApplyCertificate', $param);
|
||||
if(empty($instance_id)) throw new Exception('证书申请失败,证书实例ID为空');
|
||||
$order['instance_id'] = $instance_id;
|
||||
|
||||
sleep(3);
|
||||
|
||||
$param = [
|
||||
'instance_id' => $instance_id,
|
||||
];
|
||||
$data = $this->request('GET', 'CertificateGetDcvParam', $param);
|
||||
|
||||
$dnsList = [];
|
||||
if (!empty($data['domains_to_be_validated'])) {
|
||||
$type = $data['validation_type'] == 'dns_cname' ? 'CNAME' : 'TXT';
|
||||
foreach ($data['domains_to_be_validated'] as $opts) {
|
||||
$mainDomain = getMainDomain($domain);
|
||||
$name = substr($opts['validation_domain'], 0, -(strlen($mainDomain) + 1));
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => $type, 'value' => $opts['value']];
|
||||
}
|
||||
}
|
||||
return $dnsList;
|
||||
}
|
||||
|
||||
public function authOrder($domainList, $order)
|
||||
{
|
||||
$query = [
|
||||
'instance_id' => $order['instance_id'],
|
||||
];
|
||||
$param = [
|
||||
'action' => '',
|
||||
];
|
||||
$this->request('POST', 'CertificateProgressInstanceOrder', $param, $query);
|
||||
}
|
||||
|
||||
public function getAuthStatus($domainList, $order)
|
||||
{
|
||||
$param = [
|
||||
'instance_id' => $order['instance_id'],
|
||||
];
|
||||
$data = $this->request('GET', 'CertificateGetInstance', $param);
|
||||
if(empty($data['content'])) throw new Exception('证书信息获取失败');
|
||||
$data = $data['content'][0];
|
||||
if($data['order_status'] == 300 && $data['certificate_exist'] == 1){
|
||||
return true;
|
||||
}elseif($data['order_status'] == 302){
|
||||
throw new Exception('证书申请失败');
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function finalizeOrder($domainList, $order, $keytype, $keysize)
|
||||
{
|
||||
$param = [
|
||||
'instance_id' => $order['instance_id'],
|
||||
];
|
||||
$data = $this->request('GET', 'CertificateGetInstance', $param);
|
||||
if (empty($data['content'])) throw new Exception('证书信息获取失败');
|
||||
$data = $data['content'][0];
|
||||
if (!isset($data['ssl']['certificate']['chain'])) throw new Exception('证书内容获取失败');
|
||||
|
||||
$fullchain = implode('', $data['ssl']['certificate']['chain']);
|
||||
$private_key = $data['ssl']['certificate']['private_key'];
|
||||
|
||||
return ['private_key' => $private_key, 'fullchain' => $fullchain, 'issuer' => $data['issuer'], 'subject' => $data['common_name']['CN'], 'validFrom' => intval($data['certificate_not_before_ms']/1000), 'validTo' => intval($data['certificate_not_after_ms']/1000)];
|
||||
}
|
||||
|
||||
public function revoke($order, $pem)
|
||||
{
|
||||
$query = [
|
||||
'instance_id' => $order['instance_id'],
|
||||
];
|
||||
$param = [
|
||||
'action' => 'revoke',
|
||||
'reason' => '关联域名错误',
|
||||
];
|
||||
$this->request('POST', 'CertificateProgressInstanceOrder', $param, $query);
|
||||
}
|
||||
|
||||
public function cancel($order)
|
||||
{
|
||||
$query = [
|
||||
'instance_id' => $order['instance_id'],
|
||||
];
|
||||
$param = [
|
||||
'action' => 'cancel',
|
||||
];
|
||||
$this->request('POST', 'CertificateProgressInstanceOrder', $param, $query);
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
private function request($method, $action, $params = [], $query = [])
|
||||
{
|
||||
$this->log('Action:'.$action.PHP_EOL.'Request:'.json_encode($params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
$result = $this->client->request($method, $action, $params, $query);
|
||||
if (is_array($result)) {
|
||||
$this->log('Response:'.json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,113 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\cert;
|
||||
|
||||
use app\lib\CertInterface;
|
||||
use app\lib\acme\ACMECert;
|
||||
use Exception;
|
||||
|
||||
class letsencrypt implements CertInterface
|
||||
{
|
||||
private $directories = array(
|
||||
'live' => 'https://acme-v02.api.letsencrypt.org/directory',
|
||||
'staging' => 'https://acme-staging-v02.api.letsencrypt.org/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']], (int)$config['proxy']);
|
||||
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->ext['key'])) {
|
||||
$kid = $this->ac->register(true, $this->config['email']);
|
||||
return ['kid' => $kid, 'key' => $this->ext['key']];
|
||||
}
|
||||
|
||||
$key = $this->ac->generateRSAKey(2048);
|
||||
$this->ac->loadAccountKey($key);
|
||||
$kid = $this->ac->register(true, $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 "letsencrypt.org"'];
|
||||
}*/
|
||||
$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 letsencrypt implements CertInterface
|
||||
{
|
||||
private $directories = array(
|
||||
'live' => 'https://acme-v02.api.letsencrypt.org/directory',
|
||||
'staging' => 'https://acme-staging-v02.api.letsencrypt.org/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']], (int)$config['proxy']);
|
||||
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->ext['key'])) {
|
||||
$kid = $this->ac->register(true, $this->config['email']);
|
||||
return ['kid' => $kid, 'key' => $this->ext['key']];
|
||||
}
|
||||
|
||||
$key = $this->ac->generateRSAKey(2048);
|
||||
$this->ac->loadAccountKey($key);
|
||||
$kid = $this->ac->register(true, $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 "letsencrypt.org"'];
|
||||
}*/
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,200 +1,200 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\cert;
|
||||
|
||||
use app\lib\CertInterface;
|
||||
use app\lib\client\TencentCloud;
|
||||
use Exception;
|
||||
|
||||
class tencent implements CertInterface
|
||||
{
|
||||
private $SecretId;
|
||||
private $SecretKey;
|
||||
private $email;
|
||||
private $endpoint = "ssl.tencentcloudapi.com";
|
||||
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'];
|
||||
$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'];
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
if (empty($this->SecretId) || empty($this->SecretKey) || empty($this->email)) throw new Exception('必填参数不能为空');
|
||||
$this->request('DescribeCertificates', []);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function buyCert($domainList, &$order) {}
|
||||
|
||||
public function createOrder($domainList, &$order, $keytype, $keysize)
|
||||
{
|
||||
if (empty($domainList)) throw new Exception('域名列表不能为空');
|
||||
$domain = $domainList[0];
|
||||
$param = [
|
||||
'DvAuthMethod' => 'DNS',
|
||||
'DomainName' => $domain,
|
||||
'ContactEmail' => $this->email,
|
||||
'CsrEncryptAlgo' => $keytype,
|
||||
'CsrKeyParameter' => $keytype == 'ECC' ? 'prime256v1' : '2048',
|
||||
];
|
||||
$data = $this->request('ApplyCertificate', $param);
|
||||
if (empty($data['CertificateId'])) throw new Exception('证书申请失败,CertificateId为空');
|
||||
$order['CertificateId'] = $data['CertificateId'];
|
||||
|
||||
$param = [
|
||||
'CertificateId' => $order['CertificateId'],
|
||||
];
|
||||
$data = $this->request('DescribeCertificate', $param);
|
||||
$order['OrderId'] = $data['OrderId'];
|
||||
|
||||
$dnsList = [];
|
||||
if (!empty($data['DvAuthDetail']['DvAuths'])) {
|
||||
foreach ($data['DvAuthDetail']['DvAuths'] as $opts) {
|
||||
$mainDomain = getMainDomain($opts['DvAuthKey']);
|
||||
$name = substr($opts['DvAuthKey'], 0, -(strlen($mainDomain) + 1));
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => $opts['DvAuthVerifyType'] ?? 'CNAME', 'value' => $opts['DvAuthValue']];
|
||||
}
|
||||
}
|
||||
|
||||
return $dnsList;
|
||||
}
|
||||
|
||||
public function authOrder($domainList, $order)
|
||||
{
|
||||
$param = [
|
||||
'CertificateId' => $order['CertificateId'],
|
||||
];
|
||||
$data = $this->request('DescribeCertificate', $param);
|
||||
if ($data['Status'] == 0 || $data['Status'] == 4) {
|
||||
$this->request('CompleteCertificate', $param);
|
||||
sleep(3);
|
||||
}
|
||||
}
|
||||
|
||||
public function getAuthStatus($domainList, $order)
|
||||
{
|
||||
$param = [
|
||||
'CertificateId' => $order['CertificateId'],
|
||||
];
|
||||
$data = $this->request('DescribeCertificate', $param);
|
||||
if ($data['Status'] == 1) {
|
||||
return true;
|
||||
} elseif ($data['Status'] == 2) {
|
||||
throw new Exception('证书审核失败' . (empty($data['StatusMsg'] ? '' : ':' . $data['StatusMsg'])));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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 = 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);
|
||||
|
||||
$zip = new \ZipArchive;
|
||||
if ($zip->open($file_path) === true) {
|
||||
$zip->extractTo(app()->getRuntimePath() . 'cert/');
|
||||
$zip->close();
|
||||
} else {
|
||||
throw new Exception('解压证书失败');
|
||||
}
|
||||
$cert_dir = app()->getRuntimePath() . 'cert/' . $file_name;
|
||||
|
||||
$items = scandir($cert_dir);
|
||||
if ($items === false) throw new Exception('解压后的证书文件夹不存在');
|
||||
$private_key = null;
|
||||
$fullchain = null;
|
||||
foreach ($items as $item) {
|
||||
if (substr($item, -4) == '.key') {
|
||||
$private_key = file_get_contents($cert_dir . '/' . $item);
|
||||
} elseif (substr($item, -4) == '.crt') {
|
||||
$fullchain = file_get_contents($cert_dir . '/' . $item);
|
||||
}
|
||||
}
|
||||
if (empty($private_key) || empty($fullchain)) throw new Exception('解压后的证书文件夹内未找到证书文件');
|
||||
|
||||
clearDirectory($cert_dir);
|
||||
rmdir($cert_dir);
|
||||
unlink($file_path);
|
||||
|
||||
$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)
|
||||
{
|
||||
$param = [
|
||||
'CertificateId' => $order['CertificateId'],
|
||||
];
|
||||
$action = 'RevokeCertificate';
|
||||
$data = $this->request($action, $param);
|
||||
|
||||
if (!empty($data['RevokeDomainValidateAuths'])) {
|
||||
$dnsList = [];
|
||||
foreach ($data['RevokeDomainValidateAuths'] as $opts) {
|
||||
$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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function cancel($order)
|
||||
{
|
||||
$param = [
|
||||
'CertificateId' => $order['CertificateId'],
|
||||
];
|
||||
$action = 'CancelAuditCertificate';
|
||||
$this->request($action, $param);
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
private function request($action, $param)
|
||||
{
|
||||
$this->log('Action:' . $action . PHP_EOL . 'Request:' . json_encode($param, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
$result = $this->client->request($action, $param);
|
||||
$this->log('Response:' . json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\cert;
|
||||
|
||||
use app\lib\CertInterface;
|
||||
use app\lib\client\TencentCloud;
|
||||
use Exception;
|
||||
|
||||
class tencent implements CertInterface
|
||||
{
|
||||
private $SecretId;
|
||||
private $SecretKey;
|
||||
private $email;
|
||||
private $endpoint = "ssl.tencentcloudapi.com";
|
||||
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'];
|
||||
$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'];
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
if (empty($this->SecretId) || empty($this->SecretKey) || empty($this->email)) throw new Exception('必填参数不能为空');
|
||||
$this->request('DescribeCertificates', []);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function buyCert($domainList, &$order) {}
|
||||
|
||||
public function createOrder($domainList, &$order, $keytype, $keysize)
|
||||
{
|
||||
if (empty($domainList)) throw new Exception('域名列表不能为空');
|
||||
$domain = $domainList[0];
|
||||
$param = [
|
||||
'DvAuthMethod' => 'DNS',
|
||||
'DomainName' => $domain,
|
||||
'ContactEmail' => $this->email,
|
||||
'CsrEncryptAlgo' => $keytype,
|
||||
'CsrKeyParameter' => $keytype == 'ECC' ? 'prime256v1' : '2048',
|
||||
];
|
||||
$data = $this->request('ApplyCertificate', $param);
|
||||
if (empty($data['CertificateId'])) throw new Exception('证书申请失败,CertificateId为空');
|
||||
$order['CertificateId'] = $data['CertificateId'];
|
||||
|
||||
$param = [
|
||||
'CertificateId' => $order['CertificateId'],
|
||||
];
|
||||
$data = $this->request('DescribeCertificate', $param);
|
||||
$order['OrderId'] = $data['OrderId'];
|
||||
|
||||
$dnsList = [];
|
||||
if (!empty($data['DvAuthDetail']['DvAuths'])) {
|
||||
foreach ($data['DvAuthDetail']['DvAuths'] as $opts) {
|
||||
$mainDomain = getMainDomain($opts['DvAuthKey']);
|
||||
$name = substr($opts['DvAuthKey'], 0, -(strlen($mainDomain) + 1));
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => $opts['DvAuthVerifyType'] ?? 'CNAME', 'value' => $opts['DvAuthValue']];
|
||||
}
|
||||
}
|
||||
|
||||
return $dnsList;
|
||||
}
|
||||
|
||||
public function authOrder($domainList, $order)
|
||||
{
|
||||
$param = [
|
||||
'CertificateId' => $order['CertificateId'],
|
||||
];
|
||||
$data = $this->request('DescribeCertificate', $param);
|
||||
if ($data['Status'] == 0 || $data['Status'] == 4) {
|
||||
$this->request('CompleteCertificate', $param);
|
||||
sleep(3);
|
||||
}
|
||||
}
|
||||
|
||||
public function getAuthStatus($domainList, $order)
|
||||
{
|
||||
$param = [
|
||||
'CertificateId' => $order['CertificateId'],
|
||||
];
|
||||
$data = $this->request('DescribeCertificate', $param);
|
||||
if ($data['Status'] == 1) {
|
||||
return true;
|
||||
} elseif ($data['Status'] == 2) {
|
||||
throw new Exception('证书审核失败' . (empty($data['StatusMsg'] ? '' : ':' . $data['StatusMsg'])));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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 = 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);
|
||||
|
||||
$zip = new \ZipArchive;
|
||||
if ($zip->open($file_path) === true) {
|
||||
$zip->extractTo(app()->getRuntimePath() . 'cert/');
|
||||
$zip->close();
|
||||
} else {
|
||||
throw new Exception('解压证书失败');
|
||||
}
|
||||
$cert_dir = app()->getRuntimePath() . 'cert/' . $file_name;
|
||||
|
||||
$items = scandir($cert_dir);
|
||||
if ($items === false) throw new Exception('解压后的证书文件夹不存在');
|
||||
$private_key = null;
|
||||
$fullchain = null;
|
||||
foreach ($items as $item) {
|
||||
if (substr($item, -4) == '.key') {
|
||||
$private_key = file_get_contents($cert_dir . '/' . $item);
|
||||
} elseif (substr($item, -4) == '.crt') {
|
||||
$fullchain = file_get_contents($cert_dir . '/' . $item);
|
||||
}
|
||||
}
|
||||
if (empty($private_key) || empty($fullchain)) throw new Exception('解压后的证书文件夹内未找到证书文件');
|
||||
|
||||
clearDirectory($cert_dir);
|
||||
rmdir($cert_dir);
|
||||
unlink($file_path);
|
||||
|
||||
$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)
|
||||
{
|
||||
$param = [
|
||||
'CertificateId' => $order['CertificateId'],
|
||||
];
|
||||
$action = 'RevokeCertificate';
|
||||
$data = $this->request($action, $param);
|
||||
|
||||
if (!empty($data['RevokeDomainValidateAuths'])) {
|
||||
$dnsList = [];
|
||||
foreach ($data['RevokeDomainValidateAuths'] as $opts) {
|
||||
$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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function cancel($order)
|
||||
{
|
||||
$param = [
|
||||
'CertificateId' => $order['CertificateId'],
|
||||
];
|
||||
$action = 'CancelAuditCertificate';
|
||||
$this->request($action, $param);
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
private function request($action, $param)
|
||||
{
|
||||
$this->log('Action:' . $action . PHP_EOL . 'Request:' . json_encode($param, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
$result = $this->client->request($action, $param);
|
||||
$this->log('Response:' . json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,188 +1,188 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\cert;
|
||||
|
||||
use app\lib\CertInterface;
|
||||
use app\lib\client\Ucloud as UcloudClient;
|
||||
use Exception;
|
||||
|
||||
class ucloud implements CertInterface
|
||||
{
|
||||
private $PublicKey;
|
||||
private $PrivateKey;
|
||||
private $config;
|
||||
private $logger;
|
||||
private UcloudClient $client;
|
||||
|
||||
public function __construct($config, $ext = null)
|
||||
{
|
||||
$this->PublicKey = $config['PublicKey'];
|
||||
$this->PrivateKey = $config['PrivateKey'];
|
||||
$this->client = new UcloudClient($this->PublicKey, $this->PrivateKey);
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
if (empty($this->PublicKey) || empty($this->PrivateKey) || empty($this->config['username']) || empty($this->config['phone']) || empty($this->config['email'])) throw new Exception('必填参数不能为空');
|
||||
$param = ['Mode' => 'free'];
|
||||
$this->request('GetCertificateList', $param);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function buyCert($domainList, &$order)
|
||||
{
|
||||
$param = [
|
||||
'CertificateBrand' => 'TrustAsia',
|
||||
'CertificateName' => 'TrustAsiaC1DVFree',
|
||||
'DomainsCount' => 1,
|
||||
'ValidYear' => 1,
|
||||
];
|
||||
$data = $this->request('PurchaseCertificate', $param);
|
||||
if (!isset($data['CertificateID'])) throw new Exception('证书购买失败,CertificateID为空');
|
||||
$order['CertificateID'] = $data['CertificateID'];
|
||||
}
|
||||
|
||||
public function createOrder($domainList, &$order, $keytype, $keysize)
|
||||
{
|
||||
if (empty($domainList)) throw new Exception('域名列表不能为空');
|
||||
$domain = $domainList[0];
|
||||
$param = [
|
||||
'CertificateID' => $order['CertificateID'],
|
||||
'Domains' => $domain,
|
||||
'CSROnline' => 1,
|
||||
'CSREncryptAlgo' => ['RSA' => 'RSA', 'ECC' => 'ECDSA'][$keytype],
|
||||
'CSRKeyParameter' => ['2048' => '2048', '3072' => '3072', '256' => 'prime256v1', '384' => 'prime384v1'][$keysize],
|
||||
'CompanyName' => '公司名称',
|
||||
'CompanyAddress' => '公司地址',
|
||||
'CompanyRegion' => '北京',
|
||||
'CompanyCity' => '北京',
|
||||
'CompanyCountry' => 'CN',
|
||||
'CompanyDivision' => '部门',
|
||||
'CompanyPhone' => $this->config['phone'],
|
||||
'CompanyPostalCode' => '110100',
|
||||
'AdminName' => $this->config['username'],
|
||||
'AdminPhone' => $this->config['phone'],
|
||||
'AdminEmail' => $this->config['email'],
|
||||
'AdminTitle' => '职员',
|
||||
'DVAuthMethod' => 'DNS'
|
||||
];
|
||||
$data = $this->request('ComplementCSRInfo', $param);
|
||||
|
||||
sleep(3);
|
||||
|
||||
$param = [
|
||||
'CertificateID' => $order['CertificateID'],
|
||||
];
|
||||
$data = $this->request('GetDVAuthInfo', $param);
|
||||
|
||||
$dnsList = [];
|
||||
if (!empty($data['Auths'])) {
|
||||
foreach ($data['Auths'] as $auth) {
|
||||
$mainDomain = getMainDomain($auth['Domain']);
|
||||
$name = substr($auth['AuthKey'], 0, -(strlen($mainDomain) + 1));
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => $auth['AuthType'] == 'DNS_TXT' ? 'TXT' : 'CNAME', 'value' => $auth['AuthValue']];
|
||||
}
|
||||
}
|
||||
return $dnsList;
|
||||
}
|
||||
|
||||
public function authOrder($domainList, $order) {}
|
||||
|
||||
public function getAuthStatus($domainList, $order)
|
||||
{
|
||||
$param = [
|
||||
'CertificateID' => $order['CertificateID'],
|
||||
];
|
||||
$data = $this->request('GetCertificateDetailInfo', $param);
|
||||
if ($data['CertificateInfo']['StateCode'] == 'COMPLETED' || $data['CertificateInfo']['StateCode'] == 'RENEWED') {
|
||||
return true;
|
||||
} elseif ($data['CertificateInfo']['StateCode'] == 'REJECTED' || $data['CertificateInfo']['StateCode'] == 'SECURITY_REVIEW_FAILED') {
|
||||
throw new Exception('证书审核失败:' . $data['CertificateInfo']['State']);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function finalizeOrder($domainList, $order, $keytype, $keysize)
|
||||
{
|
||||
if (!is_dir(app()->getRuntimePath() . 'cert')) mkdir(app()->getRuntimePath() . 'cert');
|
||||
$param = [
|
||||
'CertificateID' => $order['CertificateID'],
|
||||
];
|
||||
$info = $this->request('GetCertificateDetailInfo', $param);
|
||||
|
||||
$data = $this->request('DownloadCertificate', $param);
|
||||
$file_data = get_curl($data['CertificateUrl']);
|
||||
$file_path = app()->getRuntimePath() . 'cert/USSL_' . $order['CertificateID'] . '.zip';
|
||||
file_put_contents($file_path, $file_data);
|
||||
|
||||
$zip = new \ZipArchive;
|
||||
if ($zip->open($file_path) === true) {
|
||||
$zip->extractTo(app()->getRuntimePath() . 'cert/');
|
||||
$zip->close();
|
||||
} else {
|
||||
throw new Exception('解压证书失败');
|
||||
}
|
||||
$cert_dir = app()->getRuntimePath() . 'cert/Nginx';
|
||||
|
||||
$items = scandir($cert_dir);
|
||||
if ($items === false) throw new Exception('解压后的证书文件夹不存在');
|
||||
$private_key = null;
|
||||
$fullchain = null;
|
||||
foreach ($items as $item) {
|
||||
if (substr($item, -4) == '.key') {
|
||||
$private_key = file_get_contents($cert_dir . '/' . $item);
|
||||
} elseif (substr($item, -4) == '.pem') {
|
||||
$fullchain = file_get_contents($cert_dir . '/' . $item);
|
||||
}
|
||||
}
|
||||
if (empty($private_key) || empty($fullchain)) throw new Exception('解压后的证书文件夹内未找到证书文件');
|
||||
|
||||
clearDirectory(app()->getRuntimePath() . 'cert');
|
||||
|
||||
return ['private_key' => $private_key, 'fullchain' => $fullchain, 'issuer' => $info['CertificateInfo']['CaOrganization'], 'subject' => $info['CertificateInfo']['Name'], 'validFrom' => $info['CertificateInfo']['IssuedDate'], 'validTo' => $info['CertificateInfo']['ExpiredDate']];
|
||||
}
|
||||
|
||||
public function revoke($order, $pem)
|
||||
{
|
||||
$param = [
|
||||
'CertificateID' => $order['CertificateID'],
|
||||
'Reason' => '业务终止',
|
||||
];
|
||||
$this->request('RevokeCertificate', $param);
|
||||
}
|
||||
|
||||
public function cancel($order)
|
||||
{
|
||||
$param = [
|
||||
'CertificateID' => $order['CertificateID'],
|
||||
];
|
||||
$this->request('CancelCertificateOrder', $param);
|
||||
|
||||
sleep(1);
|
||||
|
||||
$param['CertificateMode'] = 'purchase';
|
||||
$this->request('DeleteSSLCertificate', $param);
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
private function request($action, $params)
|
||||
{
|
||||
$this->log('Action:' . $action . PHP_EOL . 'Request:' . json_encode($params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
$result = $this->client->request($action, $params);
|
||||
$this->log('Response:' . json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\cert;
|
||||
|
||||
use app\lib\CertInterface;
|
||||
use app\lib\client\Ucloud as UcloudClient;
|
||||
use Exception;
|
||||
|
||||
class ucloud implements CertInterface
|
||||
{
|
||||
private $PublicKey;
|
||||
private $PrivateKey;
|
||||
private $config;
|
||||
private $logger;
|
||||
private UcloudClient $client;
|
||||
|
||||
public function __construct($config, $ext = null)
|
||||
{
|
||||
$this->PublicKey = $config['PublicKey'];
|
||||
$this->PrivateKey = $config['PrivateKey'];
|
||||
$this->client = new UcloudClient($this->PublicKey, $this->PrivateKey);
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function register()
|
||||
{
|
||||
if (empty($this->PublicKey) || empty($this->PrivateKey) || empty($this->config['username']) || empty($this->config['phone']) || empty($this->config['email'])) throw new Exception('必填参数不能为空');
|
||||
$param = ['Mode' => 'free'];
|
||||
$this->request('GetCertificateList', $param);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function buyCert($domainList, &$order)
|
||||
{
|
||||
$param = [
|
||||
'CertificateBrand' => 'TrustAsia',
|
||||
'CertificateName' => 'TrustAsiaC1DVFree',
|
||||
'DomainsCount' => 1,
|
||||
'ValidYear' => 1,
|
||||
];
|
||||
$data = $this->request('PurchaseCertificate', $param);
|
||||
if (!isset($data['CertificateID'])) throw new Exception('证书购买失败,CertificateID为空');
|
||||
$order['CertificateID'] = $data['CertificateID'];
|
||||
}
|
||||
|
||||
public function createOrder($domainList, &$order, $keytype, $keysize)
|
||||
{
|
||||
if (empty($domainList)) throw new Exception('域名列表不能为空');
|
||||
$domain = $domainList[0];
|
||||
$param = [
|
||||
'CertificateID' => $order['CertificateID'],
|
||||
'Domains' => $domain,
|
||||
'CSROnline' => 1,
|
||||
'CSREncryptAlgo' => ['RSA' => 'RSA', 'ECC' => 'ECDSA'][$keytype],
|
||||
'CSRKeyParameter' => ['2048' => '2048', '3072' => '3072', '256' => 'prime256v1', '384' => 'prime384v1'][$keysize],
|
||||
'CompanyName' => '公司名称',
|
||||
'CompanyAddress' => '公司地址',
|
||||
'CompanyRegion' => '北京',
|
||||
'CompanyCity' => '北京',
|
||||
'CompanyCountry' => 'CN',
|
||||
'CompanyDivision' => '部门',
|
||||
'CompanyPhone' => $this->config['phone'],
|
||||
'CompanyPostalCode' => '110100',
|
||||
'AdminName' => $this->config['username'],
|
||||
'AdminPhone' => $this->config['phone'],
|
||||
'AdminEmail' => $this->config['email'],
|
||||
'AdminTitle' => '职员',
|
||||
'DVAuthMethod' => 'DNS'
|
||||
];
|
||||
$data = $this->request('ComplementCSRInfo', $param);
|
||||
|
||||
sleep(3);
|
||||
|
||||
$param = [
|
||||
'CertificateID' => $order['CertificateID'],
|
||||
];
|
||||
$data = $this->request('GetDVAuthInfo', $param);
|
||||
|
||||
$dnsList = [];
|
||||
if (!empty($data['Auths'])) {
|
||||
foreach ($data['Auths'] as $auth) {
|
||||
$mainDomain = getMainDomain($auth['Domain']);
|
||||
$name = substr($auth['AuthKey'], 0, -(strlen($mainDomain) + 1));
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => $auth['AuthType'] == 'DNS_TXT' ? 'TXT' : 'CNAME', 'value' => $auth['AuthValue']];
|
||||
}
|
||||
}
|
||||
return $dnsList;
|
||||
}
|
||||
|
||||
public function authOrder($domainList, $order) {}
|
||||
|
||||
public function getAuthStatus($domainList, $order)
|
||||
{
|
||||
$param = [
|
||||
'CertificateID' => $order['CertificateID'],
|
||||
];
|
||||
$data = $this->request('GetCertificateDetailInfo', $param);
|
||||
if ($data['CertificateInfo']['StateCode'] == 'COMPLETED' || $data['CertificateInfo']['StateCode'] == 'RENEWED') {
|
||||
return true;
|
||||
} elseif ($data['CertificateInfo']['StateCode'] == 'REJECTED' || $data['CertificateInfo']['StateCode'] == 'SECURITY_REVIEW_FAILED') {
|
||||
throw new Exception('证书审核失败:' . $data['CertificateInfo']['State']);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function finalizeOrder($domainList, $order, $keytype, $keysize)
|
||||
{
|
||||
if (!is_dir(app()->getRuntimePath() . 'cert')) mkdir(app()->getRuntimePath() . 'cert');
|
||||
$param = [
|
||||
'CertificateID' => $order['CertificateID'],
|
||||
];
|
||||
$info = $this->request('GetCertificateDetailInfo', $param);
|
||||
|
||||
$data = $this->request('DownloadCertificate', $param);
|
||||
$file_data = get_curl($data['CertificateUrl']);
|
||||
$file_path = app()->getRuntimePath() . 'cert/USSL_' . $order['CertificateID'] . '.zip';
|
||||
file_put_contents($file_path, $file_data);
|
||||
|
||||
$zip = new \ZipArchive;
|
||||
if ($zip->open($file_path) === true) {
|
||||
$zip->extractTo(app()->getRuntimePath() . 'cert/');
|
||||
$zip->close();
|
||||
} else {
|
||||
throw new Exception('解压证书失败');
|
||||
}
|
||||
$cert_dir = app()->getRuntimePath() . 'cert/Nginx';
|
||||
|
||||
$items = scandir($cert_dir);
|
||||
if ($items === false) throw new Exception('解压后的证书文件夹不存在');
|
||||
$private_key = null;
|
||||
$fullchain = null;
|
||||
foreach ($items as $item) {
|
||||
if (substr($item, -4) == '.key') {
|
||||
$private_key = file_get_contents($cert_dir . '/' . $item);
|
||||
} elseif (substr($item, -4) == '.pem') {
|
||||
$fullchain = file_get_contents($cert_dir . '/' . $item);
|
||||
}
|
||||
}
|
||||
if (empty($private_key) || empty($fullchain)) throw new Exception('解压后的证书文件夹内未找到证书文件');
|
||||
|
||||
clearDirectory(app()->getRuntimePath() . 'cert');
|
||||
|
||||
return ['private_key' => $private_key, 'fullchain' => $fullchain, 'issuer' => $info['CertificateInfo']['CaOrganization'], 'subject' => $info['CertificateInfo']['Name'], 'validFrom' => $info['CertificateInfo']['IssuedDate'], 'validTo' => $info['CertificateInfo']['ExpiredDate']];
|
||||
}
|
||||
|
||||
public function revoke($order, $pem)
|
||||
{
|
||||
$param = [
|
||||
'CertificateID' => $order['CertificateID'],
|
||||
'Reason' => '业务终止',
|
||||
];
|
||||
$this->request('RevokeCertificate', $param);
|
||||
}
|
||||
|
||||
public function cancel($order)
|
||||
{
|
||||
$param = [
|
||||
'CertificateID' => $order['CertificateID'],
|
||||
];
|
||||
$this->request('CancelCertificateOrder', $param);
|
||||
|
||||
sleep(1);
|
||||
|
||||
$param['CertificateMode'] = 'purchase';
|
||||
$this->request('DeleteSSLCertificate', $param);
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
private function request($action, $params)
|
||||
{
|
||||
$this->log('Action:' . $action . PHP_EOL . 'Request:' . json_encode($params, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
$result = $this->client->request($action, $params);
|
||||
$this->log('Response:' . json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,134 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\cert;
|
||||
|
||||
use app\lib\CertInterface;
|
||||
use app\lib\acme\ACMECert;
|
||||
use Exception;
|
||||
|
||||
class zerossl implements CertInterface
|
||||
{
|
||||
private $directory = 'https://acme.zerossl.com/v2/DV90';
|
||||
private $ac;
|
||||
private $config;
|
||||
private $ext;
|
||||
|
||||
public function __construct($config, $ext = null)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->ac = new ACMECert($this->directory, (int)$config['proxy']);
|
||||
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($this->config['email']);
|
||||
} 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 "sectigo.com"'];
|
||||
}*/
|
||||
$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($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']];
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\cert;
|
||||
|
||||
use app\lib\CertInterface;
|
||||
use app\lib\acme\ACMECert;
|
||||
use Exception;
|
||||
|
||||
class zerossl implements CertInterface
|
||||
{
|
||||
private $directory = 'https://acme.zerossl.com/v2/DV90';
|
||||
private $ac;
|
||||
private $config;
|
||||
private $ext;
|
||||
|
||||
public function __construct($config, $ext = null)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->ac = new ACMECert($this->directory, (int)$config['proxy']);
|
||||
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($this->config['email']);
|
||||
} 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 "sectigo.com"'];
|
||||
}*/
|
||||
$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($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']];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,364 +1,364 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* AWS
|
||||
*/
|
||||
class AWS
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $SecretAccessKey;
|
||||
private $endpoint;
|
||||
private $service;
|
||||
private $version;
|
||||
private $region;
|
||||
private $etag;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($AccessKeyId, $SecretAccessKey, $endpoint, $service, $version, $region, $proxy = false)
|
||||
{
|
||||
$this->AccessKeyId = $AccessKeyId;
|
||||
$this->SecretAccessKey = $SecretAccessKey;
|
||||
$this->endpoint = $endpoint;
|
||||
$this->service = $service;
|
||||
$this->version = $version;
|
||||
$this->region = $region;
|
||||
$this->proxy = $proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method 请求方法
|
||||
* @param string $action 方法名称
|
||||
* @param array $params 请求参数
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function request($method, $action, $params = [])
|
||||
{
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
}
|
||||
|
||||
$body = '';
|
||||
$query = [];
|
||||
if ($method == 'GET' || $method == 'DELETE') {
|
||||
$query = $params;
|
||||
} else {
|
||||
$body = !empty($params) ? json_encode($params) : '';
|
||||
}
|
||||
|
||||
$time = time();
|
||||
$date = gmdate("Ymd\THis\Z", $time);
|
||||
$headers = [
|
||||
'Host' => $this->endpoint,
|
||||
'X-Amz-Target' => $action,
|
||||
'X-Amz-Date' => $date,
|
||||
//'X-Amz-Content-Sha256' => hash("sha256", $body),
|
||||
];
|
||||
if ($body) {
|
||||
$headers['Content-Type'] = 'application/x-amz-json-1.1';
|
||||
}
|
||||
$path = '/';
|
||||
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method 请求方法
|
||||
* @param string $action 方法名称
|
||||
* @param array $params 请求参数
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function requestXml($method, $action, $params = [])
|
||||
{
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
}
|
||||
|
||||
$body = '';
|
||||
$query = [
|
||||
'Action' => $action,
|
||||
'Version' => $this->version,
|
||||
];
|
||||
if ($method == 'GET' || $method == 'DELETE') {
|
||||
$query = array_merge($query, $params);
|
||||
} else {
|
||||
$body = !empty($params) ? http_build_query($params) : '';
|
||||
}
|
||||
|
||||
$time = time();
|
||||
$date = gmdate("Ymd\THis\Z", $time);
|
||||
$headers = [
|
||||
'Host' => $this->endpoint,
|
||||
'X-Amz-Date' => $date,
|
||||
];
|
||||
|
||||
$path = '/';
|
||||
$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, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method 请求方法
|
||||
* @param string $path 请求路径
|
||||
* @param array $params 请求参数
|
||||
* @param \SimpleXMLElement $xml 请求XML
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function requestXmlN($method, $path, $params = [], $xml = null, $etag = false)
|
||||
{
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
}
|
||||
|
||||
$path = '/' . $this->version . $path;
|
||||
$body = '';
|
||||
$query = [];
|
||||
if ($method == 'GET' || $method == 'DELETE') {
|
||||
$query = $params;
|
||||
} else {
|
||||
$body = !empty($params) ? $this->array2xml($params, $xml) : '';
|
||||
}
|
||||
|
||||
$time = time();
|
||||
$date = gmdate("Ymd\THis\Z", $time);
|
||||
$headers = [
|
||||
'Host' => $this->endpoint,
|
||||
'X-Amz-Date' => $date,
|
||||
//'X-Amz-Content-Sha256' => hash("sha256", $body),
|
||||
];
|
||||
if ($this->etag) {
|
||||
$headers['If-Match'] = $this->etag;
|
||||
}
|
||||
|
||||
$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, true, $etag);
|
||||
}
|
||||
|
||||
private function generateSign($method, $path, $query, $headers, $body, $date)
|
||||
{
|
||||
$algorithm = "AWS4-HMAC-SHA256";
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = $method;
|
||||
$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;
|
||||
|
||||
// step 2: build string to sign
|
||||
$shortDate = substr($date, 0, 8);
|
||||
$credentialScope = $shortDate . '/' . $this->region . '/' . $this->service . '/aws4_request';
|
||||
$hashedCanonicalRequest = hash("sha256", $canonicalRequest);
|
||||
$stringToSign = $algorithm . "\n"
|
||||
. $date . "\n"
|
||||
. $credentialScope . "\n"
|
||||
. $hashedCanonicalRequest;
|
||||
|
||||
// step 3: sign string
|
||||
$kDate = hash_hmac("sha256", $shortDate, 'AWS4' . $this->SecretAccessKey, true);
|
||||
$kRegion = hash_hmac("sha256", $this->region, $kDate, true);
|
||||
$kService = hash_hmac("sha256", $this->service, $kRegion, true);
|
||||
$kSigning = hash_hmac("sha256", "aws4_request", $kService, true);
|
||||
$signature = hash_hmac("sha256", $stringToSign, $kSigning);
|
||||
|
||||
// step 4: build authorization
|
||||
$credential = $this->AccessKeyId . '/' . $credentialScope;
|
||||
$authorization = $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 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);
|
||||
}
|
||||
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, $xml = false, $etag = false)
|
||||
{
|
||||
$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);
|
||||
}
|
||||
if ($etag) {
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
}
|
||||
$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);
|
||||
if ($etag) {
|
||||
if (preg_match('/ETag: ([^\r\n]+)/', $response, $matches)) {
|
||||
$this->etag = trim($matches[1]);
|
||||
}
|
||||
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
||||
$response = substr($response, $headerSize);
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode >= 200 && $httpCode < 300) {
|
||||
if (empty($response)) return true;
|
||||
return $xml ? $this->xml2array($response) : json_decode($response, true);
|
||||
}
|
||||
if ($xml) {
|
||||
$arr = $this->xml2array($response);
|
||||
if (isset($arr['Error']['Message'])) {
|
||||
throw new Exception($arr['Error']['Message']);
|
||||
} else {
|
||||
throw new Exception('HTTP Code: ' . $httpCode);
|
||||
}
|
||||
} else {
|
||||
$arr = json_decode($response, true);
|
||||
if (isset($arr['message'])) {
|
||||
throw new Exception($arr['message']);
|
||||
} else {
|
||||
throw new Exception('HTTP Code: ' . $httpCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function xml2array($xml)
|
||||
{
|
||||
if (!$xml) {
|
||||
return false;
|
||||
}
|
||||
LIBXML_VERSION < 20900 && libxml_disable_entity_loader(true);
|
||||
return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
|
||||
}
|
||||
|
||||
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)) {
|
||||
// 检查数组的第一个子节点的键是否为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);
|
||||
}
|
||||
}
|
||||
|
||||
return $xml->asXML();
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* AWS
|
||||
*/
|
||||
class AWS
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $SecretAccessKey;
|
||||
private $endpoint;
|
||||
private $service;
|
||||
private $version;
|
||||
private $region;
|
||||
private $etag;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($AccessKeyId, $SecretAccessKey, $endpoint, $service, $version, $region, $proxy = false)
|
||||
{
|
||||
$this->AccessKeyId = $AccessKeyId;
|
||||
$this->SecretAccessKey = $SecretAccessKey;
|
||||
$this->endpoint = $endpoint;
|
||||
$this->service = $service;
|
||||
$this->version = $version;
|
||||
$this->region = $region;
|
||||
$this->proxy = $proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method 请求方法
|
||||
* @param string $action 方法名称
|
||||
* @param array $params 请求参数
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function request($method, $action, $params = [])
|
||||
{
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
}
|
||||
|
||||
$body = '';
|
||||
$query = [];
|
||||
if ($method == 'GET' || $method == 'DELETE') {
|
||||
$query = $params;
|
||||
} else {
|
||||
$body = !empty($params) ? json_encode($params) : '';
|
||||
}
|
||||
|
||||
$time = time();
|
||||
$date = gmdate("Ymd\THis\Z", $time);
|
||||
$headers = [
|
||||
'Host' => $this->endpoint,
|
||||
'X-Amz-Target' => $action,
|
||||
'X-Amz-Date' => $date,
|
||||
//'X-Amz-Content-Sha256' => hash("sha256", $body),
|
||||
];
|
||||
if ($body) {
|
||||
$headers['Content-Type'] = 'application/x-amz-json-1.1';
|
||||
}
|
||||
$path = '/';
|
||||
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method 请求方法
|
||||
* @param string $action 方法名称
|
||||
* @param array $params 请求参数
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function requestXml($method, $action, $params = [])
|
||||
{
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
}
|
||||
|
||||
$body = '';
|
||||
$query = [
|
||||
'Action' => $action,
|
||||
'Version' => $this->version,
|
||||
];
|
||||
if ($method == 'GET' || $method == 'DELETE') {
|
||||
$query = array_merge($query, $params);
|
||||
} else {
|
||||
$body = !empty($params) ? http_build_query($params) : '';
|
||||
}
|
||||
|
||||
$time = time();
|
||||
$date = gmdate("Ymd\THis\Z", $time);
|
||||
$headers = [
|
||||
'Host' => $this->endpoint,
|
||||
'X-Amz-Date' => $date,
|
||||
];
|
||||
|
||||
$path = '/';
|
||||
$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, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method 请求方法
|
||||
* @param string $path 请求路径
|
||||
* @param array $params 请求参数
|
||||
* @param \SimpleXMLElement $xml 请求XML
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function requestXmlN($method, $path, $params = [], $xml = null, $etag = false)
|
||||
{
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
}
|
||||
|
||||
$path = '/' . $this->version . $path;
|
||||
$body = '';
|
||||
$query = [];
|
||||
if ($method == 'GET' || $method == 'DELETE') {
|
||||
$query = $params;
|
||||
} else {
|
||||
$body = !empty($params) ? $this->array2xml($params, $xml) : '';
|
||||
}
|
||||
|
||||
$time = time();
|
||||
$date = gmdate("Ymd\THis\Z", $time);
|
||||
$headers = [
|
||||
'Host' => $this->endpoint,
|
||||
'X-Amz-Date' => $date,
|
||||
//'X-Amz-Content-Sha256' => hash("sha256", $body),
|
||||
];
|
||||
if ($this->etag) {
|
||||
$headers['If-Match'] = $this->etag;
|
||||
}
|
||||
|
||||
$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, true, $etag);
|
||||
}
|
||||
|
||||
private function generateSign($method, $path, $query, $headers, $body, $date)
|
||||
{
|
||||
$algorithm = "AWS4-HMAC-SHA256";
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = $method;
|
||||
$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;
|
||||
|
||||
// step 2: build string to sign
|
||||
$shortDate = substr($date, 0, 8);
|
||||
$credentialScope = $shortDate . '/' . $this->region . '/' . $this->service . '/aws4_request';
|
||||
$hashedCanonicalRequest = hash("sha256", $canonicalRequest);
|
||||
$stringToSign = $algorithm . "\n"
|
||||
. $date . "\n"
|
||||
. $credentialScope . "\n"
|
||||
. $hashedCanonicalRequest;
|
||||
|
||||
// step 3: sign string
|
||||
$kDate = hash_hmac("sha256", $shortDate, 'AWS4' . $this->SecretAccessKey, true);
|
||||
$kRegion = hash_hmac("sha256", $this->region, $kDate, true);
|
||||
$kService = hash_hmac("sha256", $this->service, $kRegion, true);
|
||||
$kSigning = hash_hmac("sha256", "aws4_request", $kService, true);
|
||||
$signature = hash_hmac("sha256", $stringToSign, $kSigning);
|
||||
|
||||
// step 4: build authorization
|
||||
$credential = $this->AccessKeyId . '/' . $credentialScope;
|
||||
$authorization = $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 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);
|
||||
}
|
||||
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, $xml = false, $etag = false)
|
||||
{
|
||||
$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);
|
||||
}
|
||||
if ($etag) {
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
}
|
||||
$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);
|
||||
if ($etag) {
|
||||
if (preg_match('/ETag: ([^\r\n]+)/', $response, $matches)) {
|
||||
$this->etag = trim($matches[1]);
|
||||
}
|
||||
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
||||
$response = substr($response, $headerSize);
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode >= 200 && $httpCode < 300) {
|
||||
if (empty($response)) return true;
|
||||
return $xml ? $this->xml2array($response) : json_decode($response, true);
|
||||
}
|
||||
if ($xml) {
|
||||
$arr = $this->xml2array($response);
|
||||
if (isset($arr['Error']['Message'])) {
|
||||
throw new Exception($arr['Error']['Message']);
|
||||
} else {
|
||||
throw new Exception('HTTP Code: ' . $httpCode);
|
||||
}
|
||||
} else {
|
||||
$arr = json_decode($response, true);
|
||||
if (isset($arr['message'])) {
|
||||
throw new Exception($arr['message']);
|
||||
} else {
|
||||
throw new Exception('HTTP Code: ' . $httpCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function xml2array($xml)
|
||||
{
|
||||
if (!$xml) {
|
||||
return false;
|
||||
}
|
||||
LIBXML_VERSION < 20900 && libxml_disable_entity_loader(true);
|
||||
return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
|
||||
}
|
||||
|
||||
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)) {
|
||||
// 检查数组的第一个子节点的键是否为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);
|
||||
}
|
||||
}
|
||||
|
||||
return $xml->asXML();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,101 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 阿里云
|
||||
*/
|
||||
class Aliyun
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $AccessKeySecret;
|
||||
private $Endpoint;
|
||||
private $Version;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($AccessKeyId, $AccessKeySecret, $Endpoint, $Version, $proxy = false)
|
||||
{
|
||||
$this->AccessKeyId = $AccessKeyId;
|
||||
$this->AccessKeySecret = $AccessKeySecret;
|
||||
$this->Endpoint = $Endpoint;
|
||||
$this->Version = $Version;
|
||||
$this->proxy = $proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $param 请求参数
|
||||
* @return bool|array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function request($param, $method = 'POST')
|
||||
{
|
||||
$url = 'https://' . $this->Endpoint . '/';
|
||||
$data = array(
|
||||
'Format' => 'JSON',
|
||||
'Version' => $this->Version,
|
||||
'AccessKeyId' => $this->AccessKeyId,
|
||||
'SignatureMethod' => 'HMAC-SHA1',
|
||||
'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
|
||||
'SignatureVersion' => '1.0',
|
||||
'SignatureNonce' => random(8)
|
||||
);
|
||||
$data = array_merge($data, $param);
|
||||
$data['Signature'] = $this->aliyunSignature($data, $this->AccessKeySecret, $method);
|
||||
if ($method == 'GET') {
|
||||
$url .= '?' . http_build_query($data);
|
||||
}
|
||||
$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_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
if ($method == 'POST') {
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
|
||||
}
|
||||
$response = curl_exec($ch);
|
||||
$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: ' . $errmsg);
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
$arr = json_decode($response, true);
|
||||
if ($httpCode == 200) {
|
||||
return $arr;
|
||||
} elseif ($arr) {
|
||||
throw new Exception($arr['Message']);
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
private function aliyunSignature($parameters, $accessKeySecret, $method)
|
||||
{
|
||||
ksort($parameters);
|
||||
$canonicalizedQueryString = '';
|
||||
foreach ($parameters as $key => $value) {
|
||||
if ($value === null) continue;
|
||||
$canonicalizedQueryString .= '&' . $this->percentEncode($key) . '=' . $this->percentEncode($value);
|
||||
}
|
||||
$stringToSign = $method . '&%2F&' . $this->percentEncode(substr($canonicalizedQueryString, 1));
|
||||
$signature = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret . "&", true));
|
||||
|
||||
return $signature;
|
||||
}
|
||||
|
||||
private function percentEncode($str)
|
||||
{
|
||||
$search = ['+', '*', '%7E'];
|
||||
$replace = ['%20', '%2A', '~'];
|
||||
return str_replace($search, $replace, urlencode($str));
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 阿里云
|
||||
*/
|
||||
class Aliyun
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $AccessKeySecret;
|
||||
private $Endpoint;
|
||||
private $Version;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($AccessKeyId, $AccessKeySecret, $Endpoint, $Version, $proxy = false)
|
||||
{
|
||||
$this->AccessKeyId = $AccessKeyId;
|
||||
$this->AccessKeySecret = $AccessKeySecret;
|
||||
$this->Endpoint = $Endpoint;
|
||||
$this->Version = $Version;
|
||||
$this->proxy = $proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $param 请求参数
|
||||
* @return bool|array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function request($param, $method = 'POST')
|
||||
{
|
||||
$url = 'https://' . $this->Endpoint . '/';
|
||||
$data = array(
|
||||
'Format' => 'JSON',
|
||||
'Version' => $this->Version,
|
||||
'AccessKeyId' => $this->AccessKeyId,
|
||||
'SignatureMethod' => 'HMAC-SHA1',
|
||||
'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
|
||||
'SignatureVersion' => '1.0',
|
||||
'SignatureNonce' => random(8)
|
||||
);
|
||||
$data = array_merge($data, $param);
|
||||
$data['Signature'] = $this->aliyunSignature($data, $this->AccessKeySecret, $method);
|
||||
if ($method == 'GET') {
|
||||
$url .= '?' . http_build_query($data);
|
||||
}
|
||||
$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_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
if ($method == 'POST') {
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
|
||||
}
|
||||
$response = curl_exec($ch);
|
||||
$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: ' . $errmsg);
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
$arr = json_decode($response, true);
|
||||
if ($httpCode == 200) {
|
||||
return $arr;
|
||||
} elseif ($arr) {
|
||||
throw new Exception($arr['Message']);
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
private function aliyunSignature($parameters, $accessKeySecret, $method)
|
||||
{
|
||||
ksort($parameters);
|
||||
$canonicalizedQueryString = '';
|
||||
foreach ($parameters as $key => $value) {
|
||||
if ($value === null) continue;
|
||||
$canonicalizedQueryString .= '&' . $this->percentEncode($key) . '=' . $this->percentEncode($value);
|
||||
}
|
||||
$stringToSign = $method . '&%2F&' . $this->percentEncode(substr($canonicalizedQueryString, 1));
|
||||
$signature = base64_encode(hash_hmac("sha1", $stringToSign, $accessKeySecret . "&", true));
|
||||
|
||||
return $signature;
|
||||
}
|
||||
|
||||
private function percentEncode($str)
|
||||
{
|
||||
$search = ['+', '*', '%7E'];
|
||||
$replace = ['%20', '%2A', '~'];
|
||||
return str_replace($search, $replace, urlencode($str));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,188 +1,188 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 阿里云V3
|
||||
*/
|
||||
class AliyunNew
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $AccessKeySecret;
|
||||
private $Endpoint;
|
||||
private $Version;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($AccessKeyId, $AccessKeySecret, $Endpoint, $Version, $proxy = false)
|
||||
{
|
||||
$this->AccessKeyId = $AccessKeyId;
|
||||
$this->AccessKeySecret = $AccessKeySecret;
|
||||
$this->Endpoint = $Endpoint;
|
||||
$this->Version = $Version;
|
||||
$this->proxy = $proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method 请求方法
|
||||
* @param string $action 操作名称
|
||||
* @param array|null $params 请求参数
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function request($method, $action, $path = '/', $params = null)
|
||||
{
|
||||
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) : '';
|
||||
}
|
||||
$headers = [
|
||||
'x-acs-action' => $action,
|
||||
'x-acs-version' => $this->Version,
|
||||
'x-acs-signature-nonce' => md5(uniqid(mt_rand(), true) . microtime()),
|
||||
'x-acs-date' => gmdate('Y-m-d\TH:i:s\Z'),
|
||||
'x-acs-content-sha256' => hash("sha256", $body),
|
||||
'Host' => $this->Endpoint,
|
||||
];
|
||||
if ($body) {
|
||||
$headers['Content-Type'] = 'application/json; charset=utf-8';
|
||||
}
|
||||
|
||||
$authorization = $this->generateSign($method, $path, $query, $headers, $body);
|
||||
$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)
|
||||
{
|
||||
$algorithm = "ACS3-HMAC-SHA256";
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = $method;
|
||||
$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;
|
||||
|
||||
// step 2: build string to sign
|
||||
$hashedCanonicalRequest = hash("sha256", $canonicalRequest);
|
||||
$stringToSign = $algorithm . "\n"
|
||||
. $hashedCanonicalRequest;
|
||||
|
||||
// step 3: sign string
|
||||
$signature = hash_hmac("sha256", $stringToSign, $this->AccessKeySecret);
|
||||
|
||||
// step 4: build authorization
|
||||
$authorization = $algorithm . ' Credential=' . $this->AccessKeyId . ',SignedHeaders=' . $signedHeaders . ',Signature=' . $signature;
|
||||
|
||||
return $authorization;
|
||||
}
|
||||
|
||||
private function escape($str)
|
||||
{
|
||||
$search = ['+', '*', '%7E'];
|
||||
$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)
|
||||
{
|
||||
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) {
|
||||
return $arr;
|
||||
} elseif ($arr) {
|
||||
if (strpos($arr['Message'], '.') > 0) $arr['Message'] = substr($arr['Message'], 0, strpos($arr['Message'], '.') + 1);
|
||||
throw new Exception($arr['Message']);
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 阿里云V3
|
||||
*/
|
||||
class AliyunNew
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $AccessKeySecret;
|
||||
private $Endpoint;
|
||||
private $Version;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($AccessKeyId, $AccessKeySecret, $Endpoint, $Version, $proxy = false)
|
||||
{
|
||||
$this->AccessKeyId = $AccessKeyId;
|
||||
$this->AccessKeySecret = $AccessKeySecret;
|
||||
$this->Endpoint = $Endpoint;
|
||||
$this->Version = $Version;
|
||||
$this->proxy = $proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method 请求方法
|
||||
* @param string $action 操作名称
|
||||
* @param array|null $params 请求参数
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function request($method, $action, $path = '/', $params = null)
|
||||
{
|
||||
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) : '';
|
||||
}
|
||||
$headers = [
|
||||
'x-acs-action' => $action,
|
||||
'x-acs-version' => $this->Version,
|
||||
'x-acs-signature-nonce' => md5(uniqid(mt_rand(), true) . microtime()),
|
||||
'x-acs-date' => gmdate('Y-m-d\TH:i:s\Z'),
|
||||
'x-acs-content-sha256' => hash("sha256", $body),
|
||||
'Host' => $this->Endpoint,
|
||||
];
|
||||
if ($body) {
|
||||
$headers['Content-Type'] = 'application/json; charset=utf-8';
|
||||
}
|
||||
|
||||
$authorization = $this->generateSign($method, $path, $query, $headers, $body);
|
||||
$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)
|
||||
{
|
||||
$algorithm = "ACS3-HMAC-SHA256";
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = $method;
|
||||
$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;
|
||||
|
||||
// step 2: build string to sign
|
||||
$hashedCanonicalRequest = hash("sha256", $canonicalRequest);
|
||||
$stringToSign = $algorithm . "\n"
|
||||
. $hashedCanonicalRequest;
|
||||
|
||||
// step 3: sign string
|
||||
$signature = hash_hmac("sha256", $stringToSign, $this->AccessKeySecret);
|
||||
|
||||
// step 4: build authorization
|
||||
$authorization = $algorithm . ' Credential=' . $this->AccessKeyId . ',SignedHeaders=' . $signedHeaders . ',Signature=' . $signature;
|
||||
|
||||
return $authorization;
|
||||
}
|
||||
|
||||
private function escape($str)
|
||||
{
|
||||
$search = ['+', '*', '%7E'];
|
||||
$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)
|
||||
{
|
||||
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) {
|
||||
return $arr;
|
||||
} elseif ($arr) {
|
||||
if (strpos($arr['Message'], '.') > 0) $arr['Message'] = substr($arr['Message'], 0, strpos($arr['Message'], '.') + 1);
|
||||
throw new Exception($arr['Message']);
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,267 +1,267 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
class AliyunOSS
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $AccessKeySecret;
|
||||
private $Endpoint;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($AccessKeyId, $AccessKeySecret, $Endpoint, $proxy = false)
|
||||
{
|
||||
$this->AccessKeyId = $AccessKeyId;
|
||||
$this->AccessKeySecret = $AccessKeySecret;
|
||||
$this->Endpoint = $Endpoint;
|
||||
$this->proxy = $proxy;
|
||||
}
|
||||
|
||||
public function addBucketCnameCert($bucket, $domain, $cert_id)
|
||||
{
|
||||
$strXml = <<<EOF
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<BucketCnameConfiguration>
|
||||
</BucketCnameConfiguration>
|
||||
EOF;
|
||||
$xml = new \SimpleXMLElement($strXml);
|
||||
$node = $xml->addChild('Cname');
|
||||
$node->addChild('Domain', $domain);
|
||||
$certNode = $node->addChild('CertificateConfiguration');
|
||||
$certNode->addChild('CertId', $cert_id);
|
||||
$certNode->addChild('Force', 'true');
|
||||
$body = $xml->asXML();
|
||||
|
||||
$options = [
|
||||
'bucket' => $bucket,
|
||||
'key' => '',
|
||||
];
|
||||
$query = [
|
||||
'cname' => '',
|
||||
'comp' => 'add'
|
||||
];
|
||||
return $this->request('POST', '/', $query, $body, $options);
|
||||
}
|
||||
|
||||
public function deleteBucketCnameCert($bucket, $domain)
|
||||
{
|
||||
$strXml = <<<EOF
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<BucketCnameConfiguration>
|
||||
</BucketCnameConfiguration>
|
||||
EOF;
|
||||
$xml = new \SimpleXMLElement($strXml);
|
||||
$node = $xml->addChild('Cname');
|
||||
$node->addChild('Domain', $domain);
|
||||
$certNode = $node->addChild('CertificateConfiguration');
|
||||
$certNode->addChild('DeleteCertificate', 'true');
|
||||
$body = $xml->asXML();
|
||||
|
||||
$options = [
|
||||
'bucket' => $bucket,
|
||||
'key' => '',
|
||||
];
|
||||
$query = [
|
||||
'cname' => '',
|
||||
'comp' => 'add'
|
||||
];
|
||||
return $this->request('POST', '/', $query, $body, $options);
|
||||
}
|
||||
|
||||
public function getBucketCname($bucket)
|
||||
{
|
||||
$options = [
|
||||
'bucket' => $bucket,
|
||||
'key' => '',
|
||||
];
|
||||
$query = [
|
||||
'cname' => '',
|
||||
];
|
||||
return $this->request('GET', '/', $query, null, $options);
|
||||
}
|
||||
|
||||
private function request($method, $path, $query, $body, $options)
|
||||
{
|
||||
$hostname = $options['bucket'] . '.' . $this->Endpoint;
|
||||
$query_string = $this->toQueryString($query);
|
||||
$query_string = empty($query_string) ? '' : '?' . $query_string;
|
||||
$requestUrl = 'https://' . $hostname . $path . $query_string;
|
||||
$headers = [
|
||||
'Content-Type' => 'application/xml',
|
||||
'Date' => gmdate('D, d M Y H:i:s \G\M\T'),
|
||||
];
|
||||
$headers['Authorization'] = $this->getAuthorization($method, $path, $query, $headers, $options);
|
||||
$header = [];
|
||||
foreach ($headers as $key => $value) {
|
||||
$header[] = $key . ': ' . $value;
|
||||
}
|
||||
return $this->curl($method, $requestUrl, $body, $header);
|
||||
}
|
||||
|
||||
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);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if ($errno) {
|
||||
$errmsg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . $errmsg);
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode >= 200 && $httpCode < 300) {
|
||||
if (empty($response)) return true;
|
||||
return $this->xml2array($response);
|
||||
}
|
||||
$arr = $this->xml2array($response);
|
||||
if (isset($arr['Message'])) {
|
||||
throw new Exception($arr['Message']);
|
||||
} else {
|
||||
throw new Exception('HTTP Code: ' . $httpCode);
|
||||
}
|
||||
}
|
||||
|
||||
private function toQueryString($params = array())
|
||||
{
|
||||
$temp = array();
|
||||
uksort($params, 'strnatcasecmp');
|
||||
foreach ($params as $key => $value) {
|
||||
if (is_string($key) && !is_array($value)) {
|
||||
if (strlen($value) > 0) {
|
||||
$temp[] = rawurlencode($key) . '=' . rawurlencode($value);
|
||||
} else {
|
||||
$temp[] = rawurlencode($key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return implode('&', $temp);
|
||||
}
|
||||
|
||||
private function xml2array($xml)
|
||||
{
|
||||
if (!$xml) {
|
||||
return false;
|
||||
}
|
||||
LIBXML_VERSION < 20900 && libxml_disable_entity_loader(true);
|
||||
return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
|
||||
}
|
||||
|
||||
private function getAuthorization($method, $url, $query, $headers, $options)
|
||||
{
|
||||
$method = strtoupper($method);
|
||||
$date = $headers['Date'];
|
||||
$resourcePath = $this->getResourcePath($options);
|
||||
$stringToSign = $this->calcStringToSign($method, $date, $headers, $resourcePath, $query);
|
||||
$signature = base64_encode(hash_hmac('sha1', $stringToSign, $this->AccessKeySecret, true));
|
||||
return 'OSS ' . $this->AccessKeyId . ':' . $signature;
|
||||
}
|
||||
|
||||
private function getResourcePath(array $options)
|
||||
{
|
||||
$resourcePath = '/';
|
||||
if (strlen($options['bucket']) > 0) {
|
||||
$resourcePath .= $options['bucket'] . '/';
|
||||
}
|
||||
if (strlen($options['key']) > 0) {
|
||||
$resourcePath .= $options['key'];
|
||||
}
|
||||
return $resourcePath;
|
||||
}
|
||||
|
||||
private function calcStringToSign($method, $date, array $headers, $resourcePath, array $query)
|
||||
{
|
||||
/*
|
||||
SignToString =
|
||||
VERB + "\n"
|
||||
+ Content-MD5 + "\n"
|
||||
+ Content-Type + "\n"
|
||||
+ Date + "\n"
|
||||
+ CanonicalizedOSSHeaders
|
||||
+ CanonicalizedResource
|
||||
Signature = base64(hmac-sha1(AccessKeySecret, SignToString))
|
||||
*/
|
||||
$contentMd5 = '';
|
||||
$contentType = '';
|
||||
// CanonicalizedOSSHeaders
|
||||
$signheaders = array();
|
||||
foreach ($headers as $key => $value) {
|
||||
$lowk = strtolower($key);
|
||||
if (strncmp($lowk, "x-oss-", 6) == 0) {
|
||||
$signheaders[$lowk] = $value;
|
||||
} else if ($lowk === 'content-md5') {
|
||||
$contentMd5 = $value;
|
||||
} else if ($lowk === 'content-type') {
|
||||
$contentType = $value;
|
||||
}
|
||||
}
|
||||
ksort($signheaders);
|
||||
$canonicalizedOSSHeaders = '';
|
||||
foreach ($signheaders as $key => $value) {
|
||||
$canonicalizedOSSHeaders .= $key . ':' . $value . "\n";
|
||||
}
|
||||
// CanonicalizedResource
|
||||
$signquery = array();
|
||||
foreach ($query as $key => $value) {
|
||||
if (in_array($key, $this->signKeyList)) {
|
||||
$signquery[$key] = $value;
|
||||
}
|
||||
}
|
||||
ksort($signquery);
|
||||
$sortedQueryList = array();
|
||||
foreach ($signquery as $key => $value) {
|
||||
if (strlen($value) > 0) {
|
||||
$sortedQueryList[] = $key . '=' . $value;
|
||||
} else {
|
||||
$sortedQueryList[] = $key;
|
||||
}
|
||||
}
|
||||
$queryStringSorted = implode('&', $sortedQueryList);
|
||||
$canonicalizedResource = $resourcePath;
|
||||
if (!empty($queryStringSorted)) {
|
||||
$canonicalizedResource .= '?' . $queryStringSorted;
|
||||
}
|
||||
return $method . "\n" . $contentMd5 . "\n" . $contentType . "\n" . $date . "\n" . $canonicalizedOSSHeaders . $canonicalizedResource;
|
||||
}
|
||||
|
||||
private $signKeyList = array(
|
||||
"acl", "uploads", "location", "cors",
|
||||
"logging", "website", "referer", "lifecycle",
|
||||
"delete", "append", "tagging", "objectMeta",
|
||||
"uploadId", "partNumber", "security-token", "x-oss-security-token",
|
||||
"position", "img", "style", "styleName",
|
||||
"replication", "replicationProgress",
|
||||
"replicationLocation", "cname", "bucketInfo",
|
||||
"comp", "qos", "live", "status", "vod",
|
||||
"startTime", "endTime", "symlink",
|
||||
"x-oss-process", "response-content-type", "x-oss-traffic-limit",
|
||||
"response-content-language", "response-expires",
|
||||
"response-cache-control", "response-content-disposition",
|
||||
"response-content-encoding", "udf", "udfName", "udfImage",
|
||||
"udfId", "udfImageDesc", "udfApplication",
|
||||
"udfApplicationLog", "restore", "callback", "callback-var", "qosInfo",
|
||||
"policy", "stat", "encryption", "versions", "versioning", "versionId", "requestPayment",
|
||||
"x-oss-request-payer", "sequential",
|
||||
"inventory", "inventoryId", "continuation-token", "asyncFetch",
|
||||
"worm", "wormId", "wormExtend", "withHashContext",
|
||||
"x-oss-enable-md5", "x-oss-enable-sha1", "x-oss-enable-sha256",
|
||||
"x-oss-hash-ctx", "x-oss-md5-ctx", "transferAcceleration",
|
||||
"regionList", "cloudboxes", "x-oss-ac-source-ip", "x-oss-ac-subnet-mask", "x-oss-ac-vpc-id", "x-oss-ac-forward-allow",
|
||||
"metaQuery", "resourceGroup", "rtc", "x-oss-async-process", "responseHeader"
|
||||
);
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
class AliyunOSS
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $AccessKeySecret;
|
||||
private $Endpoint;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($AccessKeyId, $AccessKeySecret, $Endpoint, $proxy = false)
|
||||
{
|
||||
$this->AccessKeyId = $AccessKeyId;
|
||||
$this->AccessKeySecret = $AccessKeySecret;
|
||||
$this->Endpoint = $Endpoint;
|
||||
$this->proxy = $proxy;
|
||||
}
|
||||
|
||||
public function addBucketCnameCert($bucket, $domain, $cert_id)
|
||||
{
|
||||
$strXml = <<<EOF
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<BucketCnameConfiguration>
|
||||
</BucketCnameConfiguration>
|
||||
EOF;
|
||||
$xml = new \SimpleXMLElement($strXml);
|
||||
$node = $xml->addChild('Cname');
|
||||
$node->addChild('Domain', $domain);
|
||||
$certNode = $node->addChild('CertificateConfiguration');
|
||||
$certNode->addChild('CertId', $cert_id);
|
||||
$certNode->addChild('Force', 'true');
|
||||
$body = $xml->asXML();
|
||||
|
||||
$options = [
|
||||
'bucket' => $bucket,
|
||||
'key' => '',
|
||||
];
|
||||
$query = [
|
||||
'cname' => '',
|
||||
'comp' => 'add'
|
||||
];
|
||||
return $this->request('POST', '/', $query, $body, $options);
|
||||
}
|
||||
|
||||
public function deleteBucketCnameCert($bucket, $domain)
|
||||
{
|
||||
$strXml = <<<EOF
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<BucketCnameConfiguration>
|
||||
</BucketCnameConfiguration>
|
||||
EOF;
|
||||
$xml = new \SimpleXMLElement($strXml);
|
||||
$node = $xml->addChild('Cname');
|
||||
$node->addChild('Domain', $domain);
|
||||
$certNode = $node->addChild('CertificateConfiguration');
|
||||
$certNode->addChild('DeleteCertificate', 'true');
|
||||
$body = $xml->asXML();
|
||||
|
||||
$options = [
|
||||
'bucket' => $bucket,
|
||||
'key' => '',
|
||||
];
|
||||
$query = [
|
||||
'cname' => '',
|
||||
'comp' => 'add'
|
||||
];
|
||||
return $this->request('POST', '/', $query, $body, $options);
|
||||
}
|
||||
|
||||
public function getBucketCname($bucket)
|
||||
{
|
||||
$options = [
|
||||
'bucket' => $bucket,
|
||||
'key' => '',
|
||||
];
|
||||
$query = [
|
||||
'cname' => '',
|
||||
];
|
||||
return $this->request('GET', '/', $query, null, $options);
|
||||
}
|
||||
|
||||
private function request($method, $path, $query, $body, $options)
|
||||
{
|
||||
$hostname = $options['bucket'] . '.' . $this->Endpoint;
|
||||
$query_string = $this->toQueryString($query);
|
||||
$query_string = empty($query_string) ? '' : '?' . $query_string;
|
||||
$requestUrl = 'https://' . $hostname . $path . $query_string;
|
||||
$headers = [
|
||||
'Content-Type' => 'application/xml',
|
||||
'Date' => gmdate('D, d M Y H:i:s \G\M\T'),
|
||||
];
|
||||
$headers['Authorization'] = $this->getAuthorization($method, $path, $query, $headers, $options);
|
||||
$header = [];
|
||||
foreach ($headers as $key => $value) {
|
||||
$header[] = $key . ': ' . $value;
|
||||
}
|
||||
return $this->curl($method, $requestUrl, $body, $header);
|
||||
}
|
||||
|
||||
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);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if ($errno) {
|
||||
$errmsg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . $errmsg);
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode >= 200 && $httpCode < 300) {
|
||||
if (empty($response)) return true;
|
||||
return $this->xml2array($response);
|
||||
}
|
||||
$arr = $this->xml2array($response);
|
||||
if (isset($arr['Message'])) {
|
||||
throw new Exception($arr['Message']);
|
||||
} else {
|
||||
throw new Exception('HTTP Code: ' . $httpCode);
|
||||
}
|
||||
}
|
||||
|
||||
private function toQueryString($params = array())
|
||||
{
|
||||
$temp = array();
|
||||
uksort($params, 'strnatcasecmp');
|
||||
foreach ($params as $key => $value) {
|
||||
if (is_string($key) && !is_array($value)) {
|
||||
if (strlen($value) > 0) {
|
||||
$temp[] = rawurlencode($key) . '=' . rawurlencode($value);
|
||||
} else {
|
||||
$temp[] = rawurlencode($key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return implode('&', $temp);
|
||||
}
|
||||
|
||||
private function xml2array($xml)
|
||||
{
|
||||
if (!$xml) {
|
||||
return false;
|
||||
}
|
||||
LIBXML_VERSION < 20900 && libxml_disable_entity_loader(true);
|
||||
return json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA), JSON_UNESCAPED_UNICODE), true);
|
||||
}
|
||||
|
||||
private function getAuthorization($method, $url, $query, $headers, $options)
|
||||
{
|
||||
$method = strtoupper($method);
|
||||
$date = $headers['Date'];
|
||||
$resourcePath = $this->getResourcePath($options);
|
||||
$stringToSign = $this->calcStringToSign($method, $date, $headers, $resourcePath, $query);
|
||||
$signature = base64_encode(hash_hmac('sha1', $stringToSign, $this->AccessKeySecret, true));
|
||||
return 'OSS ' . $this->AccessKeyId . ':' . $signature;
|
||||
}
|
||||
|
||||
private function getResourcePath(array $options)
|
||||
{
|
||||
$resourcePath = '/';
|
||||
if (strlen($options['bucket']) > 0) {
|
||||
$resourcePath .= $options['bucket'] . '/';
|
||||
}
|
||||
if (strlen($options['key']) > 0) {
|
||||
$resourcePath .= $options['key'];
|
||||
}
|
||||
return $resourcePath;
|
||||
}
|
||||
|
||||
private function calcStringToSign($method, $date, array $headers, $resourcePath, array $query)
|
||||
{
|
||||
/*
|
||||
SignToString =
|
||||
VERB + "\n"
|
||||
+ Content-MD5 + "\n"
|
||||
+ Content-Type + "\n"
|
||||
+ Date + "\n"
|
||||
+ CanonicalizedOSSHeaders
|
||||
+ CanonicalizedResource
|
||||
Signature = base64(hmac-sha1(AccessKeySecret, SignToString))
|
||||
*/
|
||||
$contentMd5 = '';
|
||||
$contentType = '';
|
||||
// CanonicalizedOSSHeaders
|
||||
$signheaders = array();
|
||||
foreach ($headers as $key => $value) {
|
||||
$lowk = strtolower($key);
|
||||
if (strncmp($lowk, "x-oss-", 6) == 0) {
|
||||
$signheaders[$lowk] = $value;
|
||||
} else if ($lowk === 'content-md5') {
|
||||
$contentMd5 = $value;
|
||||
} else if ($lowk === 'content-type') {
|
||||
$contentType = $value;
|
||||
}
|
||||
}
|
||||
ksort($signheaders);
|
||||
$canonicalizedOSSHeaders = '';
|
||||
foreach ($signheaders as $key => $value) {
|
||||
$canonicalizedOSSHeaders .= $key . ':' . $value . "\n";
|
||||
}
|
||||
// CanonicalizedResource
|
||||
$signquery = array();
|
||||
foreach ($query as $key => $value) {
|
||||
if (in_array($key, $this->signKeyList)) {
|
||||
$signquery[$key] = $value;
|
||||
}
|
||||
}
|
||||
ksort($signquery);
|
||||
$sortedQueryList = array();
|
||||
foreach ($signquery as $key => $value) {
|
||||
if (strlen($value) > 0) {
|
||||
$sortedQueryList[] = $key . '=' . $value;
|
||||
} else {
|
||||
$sortedQueryList[] = $key;
|
||||
}
|
||||
}
|
||||
$queryStringSorted = implode('&', $sortedQueryList);
|
||||
$canonicalizedResource = $resourcePath;
|
||||
if (!empty($queryStringSorted)) {
|
||||
$canonicalizedResource .= '?' . $queryStringSorted;
|
||||
}
|
||||
return $method . "\n" . $contentMd5 . "\n" . $contentType . "\n" . $date . "\n" . $canonicalizedOSSHeaders . $canonicalizedResource;
|
||||
}
|
||||
|
||||
private $signKeyList = array(
|
||||
"acl", "uploads", "location", "cors",
|
||||
"logging", "website", "referer", "lifecycle",
|
||||
"delete", "append", "tagging", "objectMeta",
|
||||
"uploadId", "partNumber", "security-token", "x-oss-security-token",
|
||||
"position", "img", "style", "styleName",
|
||||
"replication", "replicationProgress",
|
||||
"replicationLocation", "cname", "bucketInfo",
|
||||
"comp", "qos", "live", "status", "vod",
|
||||
"startTime", "endTime", "symlink",
|
||||
"x-oss-process", "response-content-type", "x-oss-traffic-limit",
|
||||
"response-content-language", "response-expires",
|
||||
"response-cache-control", "response-content-disposition",
|
||||
"response-content-encoding", "udf", "udfName", "udfImage",
|
||||
"udfId", "udfImageDesc", "udfApplication",
|
||||
"udfApplicationLog", "restore", "callback", "callback-var", "qosInfo",
|
||||
"policy", "stat", "encryption", "versions", "versioning", "versionId", "requestPayment",
|
||||
"x-oss-request-payer", "sequential",
|
||||
"inventory", "inventoryId", "continuation-token", "asyncFetch",
|
||||
"worm", "wormId", "wormExtend", "withHashContext",
|
||||
"x-oss-enable-md5", "x-oss-enable-sha1", "x-oss-enable-sha256",
|
||||
"x-oss-hash-ctx", "x-oss-md5-ctx", "transferAcceleration",
|
||||
"regionList", "cloudboxes", "x-oss-ac-source-ip", "x-oss-ac-subnet-mask", "x-oss-ac-vpc-id", "x-oss-ac-forward-allow",
|
||||
"metaQuery", "resourceGroup", "rtc", "x-oss-async-process", "responseHeader"
|
||||
);
|
||||
}
|
||||
@@ -1,181 +1,181 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 百度云
|
||||
*/
|
||||
class BaiduCloud
|
||||
{
|
||||
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 = gmdate("Y-m-d\TH:i:s\Z", $time);
|
||||
$body = !empty($params) ? json_encode($params) : '';
|
||||
$headers = [
|
||||
'Host' => $this->endpoint,
|
||||
'x-bce-date' => $date,
|
||||
];
|
||||
if ($body) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
$authorization = $this->generateSign($method, $path, $query, $headers, $time);
|
||||
$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, $time)
|
||||
{
|
||||
$algorithm = "bce-auth-v1";
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = $method;
|
||||
$canonicalUri = $this->getCanonicalUri($path);
|
||||
$canonicalQueryString = $this->getCanonicalQueryString($query);
|
||||
[$canonicalHeaders, $signedHeaders] = $this->getCanonicalHeaders($headers);
|
||||
$canonicalRequest = $httpRequestMethod."\n"
|
||||
.$canonicalUri."\n"
|
||||
.$canonicalQueryString."\n"
|
||||
.$canonicalHeaders;
|
||||
|
||||
// step 2: calculate signing key
|
||||
$date = gmdate("Y-m-d\TH:i:s\Z", $time);
|
||||
$expirationInSeconds = 1800;
|
||||
$authString = $algorithm . '/' . $this->AccessKeyId . '/' . $date . '/' . $expirationInSeconds;
|
||||
$signingKey = hash_hmac('sha256', $authString, $this->SecretAccessKey);
|
||||
|
||||
// step 3: sign string
|
||||
$signature = hash_hmac("sha256", $canonicalRequest, $signingKey);
|
||||
|
||||
// step 4: build authorization
|
||||
$authorization = $authString . '/' . $signedHeaders . "/" . $signature;
|
||||
|
||||
return $authorization;
|
||||
}
|
||||
|
||||
private function escape($str)
|
||||
{
|
||||
$search = ['+', '*', '%7E'];
|
||||
$replace = ['%20', '%2A', '~'];
|
||||
return str_replace($search, $replace, urlencode($str));
|
||||
}
|
||||
|
||||
private function getCanonicalUri($path)
|
||||
{
|
||||
if (empty($path)) return '/';
|
||||
$uri = str_replace('%2F', '/', $this->escape($path));
|
||||
if (substr($uri, 0, 1) !== '/') $uri = '/' . $uri;
|
||||
return $uri;
|
||||
}
|
||||
|
||||
private function getCanonicalQueryString($parameters)
|
||||
{
|
||||
if (empty($parameters)) return '';
|
||||
ksort($parameters);
|
||||
$canonicalQueryString = '';
|
||||
foreach ($parameters as $key => $value) {
|
||||
if ($key == 'authorization') continue;
|
||||
$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 .= $this->escape($key) . ':' . $this->escape($value) . "\n";
|
||||
$signedHeaders .= $key . ';';
|
||||
}
|
||||
$canonicalHeaders = substr($canonicalHeaders, 0, -1);
|
||||
$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);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if ($errno) {
|
||||
$errmsg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . $errmsg);
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
if (empty($response) && $httpCode == 200) {
|
||||
return true;
|
||||
}
|
||||
$arr = json_decode($response, true);
|
||||
if ($arr) {
|
||||
if (isset($arr['code']) && isset($arr['message'])) {
|
||||
throw new Exception($arr['message']);
|
||||
} else {
|
||||
return $arr;
|
||||
}
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 百度云
|
||||
*/
|
||||
class BaiduCloud
|
||||
{
|
||||
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 = gmdate("Y-m-d\TH:i:s\Z", $time);
|
||||
$body = !empty($params) ? json_encode($params) : '';
|
||||
$headers = [
|
||||
'Host' => $this->endpoint,
|
||||
'x-bce-date' => $date,
|
||||
];
|
||||
if ($body) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
$authorization = $this->generateSign($method, $path, $query, $headers, $time);
|
||||
$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, $time)
|
||||
{
|
||||
$algorithm = "bce-auth-v1";
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = $method;
|
||||
$canonicalUri = $this->getCanonicalUri($path);
|
||||
$canonicalQueryString = $this->getCanonicalQueryString($query);
|
||||
[$canonicalHeaders, $signedHeaders] = $this->getCanonicalHeaders($headers);
|
||||
$canonicalRequest = $httpRequestMethod."\n"
|
||||
.$canonicalUri."\n"
|
||||
.$canonicalQueryString."\n"
|
||||
.$canonicalHeaders;
|
||||
|
||||
// step 2: calculate signing key
|
||||
$date = gmdate("Y-m-d\TH:i:s\Z", $time);
|
||||
$expirationInSeconds = 1800;
|
||||
$authString = $algorithm . '/' . $this->AccessKeyId . '/' . $date . '/' . $expirationInSeconds;
|
||||
$signingKey = hash_hmac('sha256', $authString, $this->SecretAccessKey);
|
||||
|
||||
// step 3: sign string
|
||||
$signature = hash_hmac("sha256", $canonicalRequest, $signingKey);
|
||||
|
||||
// step 4: build authorization
|
||||
$authorization = $authString . '/' . $signedHeaders . "/" . $signature;
|
||||
|
||||
return $authorization;
|
||||
}
|
||||
|
||||
private function escape($str)
|
||||
{
|
||||
$search = ['+', '*', '%7E'];
|
||||
$replace = ['%20', '%2A', '~'];
|
||||
return str_replace($search, $replace, urlencode($str));
|
||||
}
|
||||
|
||||
private function getCanonicalUri($path)
|
||||
{
|
||||
if (empty($path)) return '/';
|
||||
$uri = str_replace('%2F', '/', $this->escape($path));
|
||||
if (substr($uri, 0, 1) !== '/') $uri = '/' . $uri;
|
||||
return $uri;
|
||||
}
|
||||
|
||||
private function getCanonicalQueryString($parameters)
|
||||
{
|
||||
if (empty($parameters)) return '';
|
||||
ksort($parameters);
|
||||
$canonicalQueryString = '';
|
||||
foreach ($parameters as $key => $value) {
|
||||
if ($key == 'authorization') continue;
|
||||
$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 .= $this->escape($key) . ':' . $this->escape($value) . "\n";
|
||||
$signedHeaders .= $key . ';';
|
||||
}
|
||||
$canonicalHeaders = substr($canonicalHeaders, 0, -1);
|
||||
$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);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
if ($errno) {
|
||||
$errmsg = curl_error($ch);
|
||||
curl_close($ch);
|
||||
throw new Exception('Curl error: ' . $errmsg);
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
if (empty($response) && $httpCode == 200) {
|
||||
return true;
|
||||
}
|
||||
$arr = json_decode($response, true);
|
||||
if ($arr) {
|
||||
if (isset($arr['code']) && isset($arr['message'])) {
|
||||
throw new Exception($arr['message']);
|
||||
} else {
|
||||
return $arr;
|
||||
}
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,164 +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('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
<?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('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,192 +1,192 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 华为云
|
||||
*/
|
||||
class HuaweiCloud
|
||||
{
|
||||
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 = gmdate("Ymd\THis\Z", $time);
|
||||
$body = !empty($params) ? json_encode($params) : '';
|
||||
$headers = [
|
||||
'Host' => $this->endpoint,
|
||||
'X-Sdk-Date' => $date,
|
||||
];
|
||||
if ($body) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
$authorization = $this->generateSign($method, $path, $query, $headers, $body, $time);
|
||||
$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, $time)
|
||||
{
|
||||
$algorithm = "SDK-HMAC-SHA256";
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = $method;
|
||||
$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;
|
||||
|
||||
// step 2: build string to sign
|
||||
$date = gmdate("Ymd\THis\Z", $time);
|
||||
$hashedCanonicalRequest = hash("sha256", $canonicalRequest);
|
||||
$stringToSign = $algorithm . "\n"
|
||||
. $date . "\n"
|
||||
. $hashedCanonicalRequest;
|
||||
|
||||
// step 3: sign string
|
||||
$signature = hash_hmac("sha256", $stringToSign, $this->SecretAccessKey);
|
||||
|
||||
// step 4: build authorization
|
||||
$authorization = $algorithm . ' Access=' . $this->AccessKeyId . ", SignedHeaders=" . $signedHeaders . ", Signature=" . $signature;
|
||||
|
||||
return $authorization;
|
||||
}
|
||||
|
||||
private function escape($str)
|
||||
{
|
||||
$search = ['+', '*', '%7E'];
|
||||
$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);
|
||||
if (substr($canonicalURI, -1) != '/') $canonicalURI .= '/';
|
||||
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);
|
||||
}
|
||||
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 ($arr) {
|
||||
if (isset($arr['error_msg'])) {
|
||||
throw new Exception($arr['error_msg']);
|
||||
} elseif (isset($arr['message'])) {
|
||||
throw new Exception($arr['message']);
|
||||
} elseif (isset($arr['error']['error_msg'])) {
|
||||
throw new Exception($arr['error']['error_msg']);
|
||||
} else {
|
||||
return $arr;
|
||||
}
|
||||
} else {
|
||||
if ($httpCode >= 200 && $httpCode < 300) {
|
||||
return null;
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 华为云
|
||||
*/
|
||||
class HuaweiCloud
|
||||
{
|
||||
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 = gmdate("Ymd\THis\Z", $time);
|
||||
$body = !empty($params) ? json_encode($params) : '';
|
||||
$headers = [
|
||||
'Host' => $this->endpoint,
|
||||
'X-Sdk-Date' => $date,
|
||||
];
|
||||
if ($body) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
$authorization = $this->generateSign($method, $path, $query, $headers, $body, $time);
|
||||
$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, $time)
|
||||
{
|
||||
$algorithm = "SDK-HMAC-SHA256";
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = $method;
|
||||
$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;
|
||||
|
||||
// step 2: build string to sign
|
||||
$date = gmdate("Ymd\THis\Z", $time);
|
||||
$hashedCanonicalRequest = hash("sha256", $canonicalRequest);
|
||||
$stringToSign = $algorithm . "\n"
|
||||
. $date . "\n"
|
||||
. $hashedCanonicalRequest;
|
||||
|
||||
// step 3: sign string
|
||||
$signature = hash_hmac("sha256", $stringToSign, $this->SecretAccessKey);
|
||||
|
||||
// step 4: build authorization
|
||||
$authorization = $algorithm . ' Access=' . $this->AccessKeyId . ", SignedHeaders=" . $signedHeaders . ", Signature=" . $signature;
|
||||
|
||||
return $authorization;
|
||||
}
|
||||
|
||||
private function escape($str)
|
||||
{
|
||||
$search = ['+', '*', '%7E'];
|
||||
$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);
|
||||
if (substr($canonicalURI, -1) != '/') $canonicalURI .= '/';
|
||||
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);
|
||||
}
|
||||
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 ($arr) {
|
||||
if (isset($arr['error_msg'])) {
|
||||
throw new Exception($arr['error_msg']);
|
||||
} elseif (isset($arr['message'])) {
|
||||
throw new Exception($arr['message']);
|
||||
} elseif (isset($arr['error']['error_msg'])) {
|
||||
throw new Exception($arr['error']['error_msg']);
|
||||
} else {
|
||||
return $arr;
|
||||
}
|
||||
} else {
|
||||
if ($httpCode >= 200 && $httpCode < 300) {
|
||||
return null;
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,191 +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 . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
<?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 . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
209
app/lib/client/Ksyun.php
Normal file
209
app/lib/client/Ksyun.php
Normal file
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 金山云
|
||||
*/
|
||||
class Ksyun
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $SecretAccessKey;
|
||||
private $endpoint;
|
||||
private $service;
|
||||
private $region;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($AccessKeyId, $SecretAccessKey, $endpoint, $service, $region, $proxy = false)
|
||||
{
|
||||
$this->AccessKeyId = $AccessKeyId;
|
||||
$this->SecretAccessKey = $SecretAccessKey;
|
||||
$this->endpoint = $endpoint;
|
||||
$this->service = $service;
|
||||
$this->region = $region;
|
||||
$this->proxy = $proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method 请求方法
|
||||
* @param string $action 方法名称
|
||||
* @param array $params 请求参数
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function request($method, $action, $version, $path = '/', $params = [])
|
||||
{
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
}
|
||||
|
||||
$body = '';
|
||||
$query = [];
|
||||
if ($method == 'GET') {
|
||||
$query = $params;
|
||||
} else {
|
||||
$body = !empty($params) ? json_encode($params) : '';
|
||||
}
|
||||
|
||||
$time = time();
|
||||
$headers = [
|
||||
'Host' => $this->endpoint,
|
||||
'X-Amz-Date' => gmdate("Ymd\THis\Z", $time),
|
||||
'X-Version' => $version,
|
||||
'X-Action' => $action,
|
||||
];
|
||||
|
||||
$authorization = $this->generateSign($method, $path, $query, $headers, $body, $time);
|
||||
$headers['Authorization'] = $authorization;
|
||||
$headers['Accept'] = 'application/json';
|
||||
if ($body) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
$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, $time)
|
||||
{
|
||||
$algorithm = "AWS4-HMAC-SHA256";
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = $method;
|
||||
$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;
|
||||
|
||||
// step 2: build string to sign
|
||||
$date = gmdate("Ymd\THis\Z", $time);
|
||||
$shortDate = substr($date, 0, 8);
|
||||
$credentialScope = $shortDate . '/' . $this->region . '/' . $this->service . '/aws4_request';
|
||||
$hashedCanonicalRequest = hash("sha256", $canonicalRequest);
|
||||
$stringToSign = $algorithm . "\n"
|
||||
. $date . "\n"
|
||||
. $credentialScope . "\n"
|
||||
. $hashedCanonicalRequest;
|
||||
|
||||
// step 3: sign string
|
||||
$kDate = hash_hmac("sha256", $shortDate, 'AWS4' . $this->SecretAccessKey, true);
|
||||
$kRegion = hash_hmac("sha256", $this->region, $kDate, true);
|
||||
$kService = hash_hmac("sha256", $this->service, $kRegion, true);
|
||||
$kSigning = hash_hmac("sha256", "aws4_request", $kService, true);
|
||||
$signature = hash_hmac("sha256", $stringToSign, $kSigning);
|
||||
|
||||
// step 4: build authorization
|
||||
$credential = $this->AccessKeyId . '/' . $credentialScope;
|
||||
$authorization = $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 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) {
|
||||
if (!is_array($value)) {
|
||||
$canonicalQueryString .= '&' . $this->escape($key) . '=' . $this->escape($value);
|
||||
} else {
|
||||
sort($value);
|
||||
foreach ($value as $v) {
|
||||
$canonicalQueryString .= '&' . $this->escape($key) . '=' . $this->escape($v);
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
return $arr;
|
||||
} else {
|
||||
if (isset($arr['Error']['Message'])) {
|
||||
throw new Exception($arr['Error']['Message']);
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败(http_code=' . $httpCode . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,143 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 七牛云
|
||||
*/
|
||||
class Qiniu
|
||||
{
|
||||
private $ApiUrl = 'https://api.qiniu.com';
|
||||
private $AccessKey;
|
||||
private $SecretKey;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($AccessKey, $SecretKey, $proxy = false)
|
||||
{
|
||||
$this->AccessKey = $AccessKey;
|
||||
$this->SecretKey = $SecretKey;
|
||||
$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)
|
||||
{
|
||||
$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 = $path . ($query_str ? '?' . $query_str : '') . "\n";
|
||||
$hmac = hash_hmac('sha1', $sign_str, $this->SecretKey, true);
|
||||
$sign = $this->AccessKey . ':' . $this->base64_urlSafeEncode($hmac);
|
||||
|
||||
$header = [
|
||||
'Authorization: QBox ' . $sign,
|
||||
];
|
||||
if ($body) {
|
||||
$header[] = 'Content-Type: application/json';
|
||||
}
|
||||
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('+', '/');
|
||||
$replace = array('-', '_');
|
||||
return str_replace($find, $replace, base64_encode($data));
|
||||
}
|
||||
|
||||
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_USERAGENT, 'QiniuPHP/7.14.0 (' . php_uname("s") . '/' . php_uname("m") . ') PHP/' . phpversion());
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
if (!empty($body)) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
}
|
||||
$response = curl_exec($ch);
|
||||
$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: ' . $errmsg);
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode == 200) {
|
||||
$arr = json_decode($response, true);
|
||||
if ($arr) return $arr;
|
||||
return true;
|
||||
} else {
|
||||
$arr = json_decode($response, true);
|
||||
if ($arr && !empty($arr['error'])) {
|
||||
throw new Exception($arr['error']);
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 七牛云
|
||||
*/
|
||||
class Qiniu
|
||||
{
|
||||
private $ApiUrl = 'https://api.qiniu.com';
|
||||
private $AccessKey;
|
||||
private $SecretKey;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($AccessKey, $SecretKey, $proxy = false)
|
||||
{
|
||||
$this->AccessKey = $AccessKey;
|
||||
$this->SecretKey = $SecretKey;
|
||||
$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)
|
||||
{
|
||||
$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 = $path . ($query_str ? '?' . $query_str : '') . "\n";
|
||||
$hmac = hash_hmac('sha1', $sign_str, $this->SecretKey, true);
|
||||
$sign = $this->AccessKey . ':' . $this->base64_urlSafeEncode($hmac);
|
||||
|
||||
$header = [
|
||||
'Authorization: QBox ' . $sign,
|
||||
];
|
||||
if ($body) {
|
||||
$header[] = 'Content-Type: application/json';
|
||||
}
|
||||
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('+', '/');
|
||||
$replace = array('-', '_');
|
||||
return str_replace($find, $replace, base64_encode($data));
|
||||
}
|
||||
|
||||
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_USERAGENT, 'QiniuPHP/7.14.0 (' . php_uname("s") . '/' . php_uname("m") . ') PHP/' . phpversion());
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
if (!empty($body)) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
}
|
||||
$response = curl_exec($ch);
|
||||
$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: ' . $errmsg);
|
||||
}
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode == 200) {
|
||||
$arr = json_decode($response, true);
|
||||
if ($arr) return $arr;
|
||||
return true;
|
||||
} else {
|
||||
$arr = json_decode($response, true);
|
||||
if ($arr && !empty($arr['error'])) {
|
||||
throw new Exception($arr['error']);
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,133 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 腾讯云
|
||||
*/
|
||||
class TencentCloud
|
||||
{
|
||||
private $SecretId;
|
||||
private $SecretKey;
|
||||
private $endpoint;
|
||||
private $service;
|
||||
private $version;
|
||||
private $region;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($SecretId, $SecretKey, $endpoint, $service, $version, $region = null, $proxy = false)
|
||||
{
|
||||
$this->SecretId = $SecretId;
|
||||
$this->SecretKey = $SecretKey;
|
||||
$this->endpoint = $endpoint;
|
||||
$this->service = $service;
|
||||
$this->version = $version;
|
||||
$this->region = $region;
|
||||
$this->proxy = $proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $action 方法名称
|
||||
* @param array $param 请求参数
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function request($action, $param)
|
||||
{
|
||||
$param = array_filter($param, function ($a) { return $a !== null;});
|
||||
if (!$param) $param = (object)[];
|
||||
$payload = json_encode($param);
|
||||
$time = time();
|
||||
$authorization = $this->generateSign($payload, $time);
|
||||
$header = [
|
||||
'Authorization: '.$authorization,
|
||||
'Content-Type: application/json; charset=utf-8',
|
||||
'X-TC-Action: '.$action,
|
||||
'X-TC-Timestamp: '.$time,
|
||||
'X-TC-Version: '.$this->version,
|
||||
];
|
||||
if($this->region) {
|
||||
$header[] = 'X-TC-Region: '.$this->region;
|
||||
}
|
||||
$res = $this->curl_post($payload, $header);
|
||||
return $res;
|
||||
}
|
||||
|
||||
private function generateSign($payload, $time)
|
||||
{
|
||||
$algorithm = "TC3-HMAC-SHA256";
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = "POST";
|
||||
$canonicalUri = "/";
|
||||
$canonicalQueryString = "";
|
||||
$canonicalHeaders = "content-type:application/json; charset=utf-8\n"."host:".$this->endpoint."\n";
|
||||
$signedHeaders = "content-type;host";
|
||||
$hashedRequestPayload = hash("SHA256", $payload);
|
||||
$canonicalRequest = $httpRequestMethod."\n"
|
||||
.$canonicalUri."\n"
|
||||
.$canonicalQueryString."\n"
|
||||
.$canonicalHeaders."\n"
|
||||
.$signedHeaders."\n"
|
||||
.$hashedRequestPayload;
|
||||
|
||||
// step 2: build string to sign
|
||||
$date = gmdate("Y-m-d", $time);
|
||||
$credentialScope = $date."/".$this->service."/tc3_request";
|
||||
$hashedCanonicalRequest = hash("SHA256", $canonicalRequest);
|
||||
$stringToSign = $algorithm."\n"
|
||||
.$time."\n"
|
||||
.$credentialScope."\n"
|
||||
.$hashedCanonicalRequest;
|
||||
|
||||
// step 3: sign string
|
||||
$secretDate = hash_hmac("SHA256", $date, "TC3".$this->SecretKey, true);
|
||||
$secretService = hash_hmac("SHA256", $this->service, $secretDate, true);
|
||||
$secretSigning = hash_hmac("SHA256", "tc3_request", $secretService, true);
|
||||
$signature = hash_hmac("SHA256", $stringToSign, $secretSigning);
|
||||
|
||||
// step 4: build authorization
|
||||
$authorization = $algorithm
|
||||
." Credential=".$this->SecretId."/".$credentialScope
|
||||
.", SignedHeaders=content-type;host, Signature=".$signature;
|
||||
|
||||
return $authorization;
|
||||
}
|
||||
|
||||
private function curl_post($payload, $header)
|
||||
{
|
||||
$url = 'https://'.$this->endpoint.'/';
|
||||
$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_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||
$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 ($arr) {
|
||||
if (isset($arr['Response']['Error'])) {
|
||||
throw new Exception($arr['Response']['Error']['Message']);
|
||||
} else {
|
||||
return $arr['Response'];
|
||||
}
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 腾讯云
|
||||
*/
|
||||
class TencentCloud
|
||||
{
|
||||
private $SecretId;
|
||||
private $SecretKey;
|
||||
private $endpoint;
|
||||
private $service;
|
||||
private $version;
|
||||
private $region;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($SecretId, $SecretKey, $endpoint, $service, $version, $region = null, $proxy = false)
|
||||
{
|
||||
$this->SecretId = $SecretId;
|
||||
$this->SecretKey = $SecretKey;
|
||||
$this->endpoint = $endpoint;
|
||||
$this->service = $service;
|
||||
$this->version = $version;
|
||||
$this->region = $region;
|
||||
$this->proxy = $proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $action 方法名称
|
||||
* @param array $param 请求参数
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function request($action, $param)
|
||||
{
|
||||
$param = array_filter($param, function ($a) { return $a !== null;});
|
||||
if (!$param) $param = (object)[];
|
||||
$payload = json_encode($param);
|
||||
$time = time();
|
||||
$authorization = $this->generateSign($payload, $time);
|
||||
$header = [
|
||||
'Authorization: '.$authorization,
|
||||
'Content-Type: application/json; charset=utf-8',
|
||||
'X-TC-Action: '.$action,
|
||||
'X-TC-Timestamp: '.$time,
|
||||
'X-TC-Version: '.$this->version,
|
||||
];
|
||||
if($this->region) {
|
||||
$header[] = 'X-TC-Region: '.$this->region;
|
||||
}
|
||||
$res = $this->curl_post($payload, $header);
|
||||
return $res;
|
||||
}
|
||||
|
||||
private function generateSign($payload, $time)
|
||||
{
|
||||
$algorithm = "TC3-HMAC-SHA256";
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = "POST";
|
||||
$canonicalUri = "/";
|
||||
$canonicalQueryString = "";
|
||||
$canonicalHeaders = "content-type:application/json; charset=utf-8\n"."host:".$this->endpoint."\n";
|
||||
$signedHeaders = "content-type;host";
|
||||
$hashedRequestPayload = hash("SHA256", $payload);
|
||||
$canonicalRequest = $httpRequestMethod."\n"
|
||||
.$canonicalUri."\n"
|
||||
.$canonicalQueryString."\n"
|
||||
.$canonicalHeaders."\n"
|
||||
.$signedHeaders."\n"
|
||||
.$hashedRequestPayload;
|
||||
|
||||
// step 2: build string to sign
|
||||
$date = gmdate("Y-m-d", $time);
|
||||
$credentialScope = $date."/".$this->service."/tc3_request";
|
||||
$hashedCanonicalRequest = hash("SHA256", $canonicalRequest);
|
||||
$stringToSign = $algorithm."\n"
|
||||
.$time."\n"
|
||||
.$credentialScope."\n"
|
||||
.$hashedCanonicalRequest;
|
||||
|
||||
// step 3: sign string
|
||||
$secretDate = hash_hmac("SHA256", $date, "TC3".$this->SecretKey, true);
|
||||
$secretService = hash_hmac("SHA256", $this->service, $secretDate, true);
|
||||
$secretSigning = hash_hmac("SHA256", "tc3_request", $secretService, true);
|
||||
$signature = hash_hmac("SHA256", $stringToSign, $secretSigning);
|
||||
|
||||
// step 4: build authorization
|
||||
$authorization = $algorithm
|
||||
." Credential=".$this->SecretId."/".$credentialScope
|
||||
.", SignedHeaders=content-type;host, Signature=".$signature;
|
||||
|
||||
return $authorization;
|
||||
}
|
||||
|
||||
private function curl_post($payload, $header)
|
||||
{
|
||||
$url = 'https://'.$this->endpoint.'/';
|
||||
$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_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||
$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 ($arr) {
|
||||
if (isset($arr['Response']['Error'])) {
|
||||
throw new Exception($arr['Response']['Error']['Message']);
|
||||
} else {
|
||||
return $arr['Response'];
|
||||
}
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
class Ucloud
|
||||
{
|
||||
const VERSION = "0.1.0";
|
||||
|
||||
private $ApiUrl = 'https://api.ucloud.cn/';
|
||||
private $PublicKey;
|
||||
private $PrivateKey;
|
||||
|
||||
public function __construct($PublicKey, $PrivateKey)
|
||||
{
|
||||
$this->PublicKey = $PublicKey;
|
||||
$this->PrivateKey = $PrivateKey;
|
||||
}
|
||||
|
||||
public function request($action, $params)
|
||||
{
|
||||
$param = [
|
||||
'Action' => $action,
|
||||
'PublicKey' => $this->PublicKey,
|
||||
];
|
||||
$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, $ua, 0, ['Content-Type' => 'application/json']);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['RetCode']) && $result['RetCode'] == 0) {
|
||||
return $result;
|
||||
} elseif (isset($result['Message'])) {
|
||||
throw new Exception($result['Message']);
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
private function ucloudSignature($param)
|
||||
{
|
||||
ksort($param);
|
||||
$str = '';
|
||||
foreach ($param as $key => $value) {
|
||||
$str .= $key . $value;
|
||||
}
|
||||
$str .= $this->PrivateKey;
|
||||
return sha1($str);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
class Ucloud
|
||||
{
|
||||
const VERSION = "0.1.0";
|
||||
|
||||
private $ApiUrl = 'https://api.ucloud.cn/';
|
||||
private $PublicKey;
|
||||
private $PrivateKey;
|
||||
|
||||
public function __construct($PublicKey, $PrivateKey)
|
||||
{
|
||||
$this->PublicKey = $PublicKey;
|
||||
$this->PrivateKey = $PrivateKey;
|
||||
}
|
||||
|
||||
public function request($action, $params)
|
||||
{
|
||||
$param = [
|
||||
'Action' => $action,
|
||||
'PublicKey' => $this->PublicKey,
|
||||
];
|
||||
$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, $ua, 0, ['Content-Type' => 'application/json']);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['RetCode']) && $result['RetCode'] == 0) {
|
||||
return $result;
|
||||
} elseif (isset($result['Message'])) {
|
||||
throw new Exception($result['Message']);
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
private function ucloudSignature($param)
|
||||
{
|
||||
ksort($param);
|
||||
$str = '';
|
||||
foreach ($param as $key => $value) {
|
||||
$str .= $key . $value;
|
||||
}
|
||||
$str .= $this->PrivateKey;
|
||||
return sha1($str);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,252 +1,252 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 火山引擎
|
||||
*/
|
||||
class Volcengine
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $SecretAccessKey;
|
||||
private $endpoint = "open.volcengineapi.com";
|
||||
private $service;
|
||||
private $version;
|
||||
private $region;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($AccessKeyId, $SecretAccessKey, $endpoint, $service, $version, $region, $proxy = false)
|
||||
{
|
||||
$this->AccessKeyId = $AccessKeyId;
|
||||
$this->SecretAccessKey = $SecretAccessKey;
|
||||
$this->endpoint = $endpoint;
|
||||
$this->service = $service;
|
||||
$this->version = $version;
|
||||
$this->region = $region;
|
||||
$this->proxy = $proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method 请求方法
|
||||
* @param string $action 方法名称
|
||||
* @param array $params 请求参数
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function request($method, $action, $params = [], $querys = [])
|
||||
{
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
}
|
||||
|
||||
$query = [
|
||||
'Action' => $action,
|
||||
'Version' => $this->version,
|
||||
];
|
||||
|
||||
$body = '';
|
||||
if ($method == 'GET') {
|
||||
$query = array_merge($query, $params);
|
||||
} else {
|
||||
$body = !empty($params) ? json_encode($params) : '';
|
||||
if (!empty($querys)) {
|
||||
$query = array_merge($query, $querys);
|
||||
}
|
||||
}
|
||||
|
||||
$time = time();
|
||||
$headers = [
|
||||
'Host' => $this->endpoint,
|
||||
'X-Date' => gmdate("Ymd\THis\Z", $time),
|
||||
//'X-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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 = $this->service == 'tos' ? "TOS4-HMAC-SHA256" : "HMAC-SHA256";
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = $method;
|
||||
$canonicalUri = $path;
|
||||
if (substr($canonicalUri, -1) != "/") $canonicalUri .= "/";
|
||||
$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
|
||||
$date = gmdate("Ymd\THis\Z", $time);
|
||||
$shortDate = substr($date, 0, 8);
|
||||
$credentialScope = $shortDate . '/' . $this->region . '/' . $this->service . '/request';
|
||||
$hashedCanonicalRequest = hash("sha256", $canonicalRequest);
|
||||
$stringToSign = $algorithm . "\n"
|
||||
. $date . "\n"
|
||||
. $credentialScope . "\n"
|
||||
. $hashedCanonicalRequest;
|
||||
|
||||
// step 3: sign string
|
||||
$kDate = hash_hmac("sha256", $shortDate, $this->SecretAccessKey, true);
|
||||
$kRegion = hash_hmac("sha256", $this->region, $kDate, true);
|
||||
$kService = hash_hmac("sha256", $this->service, $kRegion, true);
|
||||
$kSigning = hash_hmac("sha256", "request", $kService, true);
|
||||
$signature = hash_hmac("sha256", $stringToSign, $kSigning);
|
||||
|
||||
// step 4: build authorization
|
||||
$credential = $this->AccessKeyId . '/' . $credentialScope;
|
||||
$authorization = $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['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'];
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
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 . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\client;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* 火山引擎
|
||||
*/
|
||||
class Volcengine
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $SecretAccessKey;
|
||||
private $endpoint = "open.volcengineapi.com";
|
||||
private $service;
|
||||
private $version;
|
||||
private $region;
|
||||
private $proxy = false;
|
||||
|
||||
public function __construct($AccessKeyId, $SecretAccessKey, $endpoint, $service, $version, $region, $proxy = false)
|
||||
{
|
||||
$this->AccessKeyId = $AccessKeyId;
|
||||
$this->SecretAccessKey = $SecretAccessKey;
|
||||
$this->endpoint = $endpoint;
|
||||
$this->service = $service;
|
||||
$this->version = $version;
|
||||
$this->region = $region;
|
||||
$this->proxy = $proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $method 请求方法
|
||||
* @param string $action 方法名称
|
||||
* @param array $params 请求参数
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
public function request($method, $action, $params = [], $querys = [])
|
||||
{
|
||||
if (!empty($params)) {
|
||||
$params = array_filter($params, function ($a) {
|
||||
return $a !== null;
|
||||
});
|
||||
}
|
||||
|
||||
$query = [
|
||||
'Action' => $action,
|
||||
'Version' => $this->version,
|
||||
];
|
||||
|
||||
$body = '';
|
||||
if ($method == 'GET') {
|
||||
$query = array_merge($query, $params);
|
||||
} else {
|
||||
$body = !empty($params) ? json_encode($params) : '';
|
||||
if (!empty($querys)) {
|
||||
$query = array_merge($query, $querys);
|
||||
}
|
||||
}
|
||||
|
||||
$time = time();
|
||||
$headers = [
|
||||
'Host' => $this->endpoint,
|
||||
'X-Date' => gmdate("Ymd\THis\Z", $time),
|
||||
//'X-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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 = $this->service == 'tos' ? "TOS4-HMAC-SHA256" : "HMAC-SHA256";
|
||||
|
||||
// step 1: build canonical request string
|
||||
$httpRequestMethod = $method;
|
||||
$canonicalUri = $path;
|
||||
if (substr($canonicalUri, -1) != "/") $canonicalUri .= "/";
|
||||
$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
|
||||
$date = gmdate("Ymd\THis\Z", $time);
|
||||
$shortDate = substr($date, 0, 8);
|
||||
$credentialScope = $shortDate . '/' . $this->region . '/' . $this->service . '/request';
|
||||
$hashedCanonicalRequest = hash("sha256", $canonicalRequest);
|
||||
$stringToSign = $algorithm . "\n"
|
||||
. $date . "\n"
|
||||
. $credentialScope . "\n"
|
||||
. $hashedCanonicalRequest;
|
||||
|
||||
// step 3: sign string
|
||||
$kDate = hash_hmac("sha256", $shortDate, $this->SecretAccessKey, true);
|
||||
$kRegion = hash_hmac("sha256", $this->region, $kDate, true);
|
||||
$kService = hash_hmac("sha256", $this->service, $kRegion, true);
|
||||
$kSigning = hash_hmac("sha256", "request", $kService, true);
|
||||
$signature = hash_hmac("sha256", $stringToSign, $kSigning);
|
||||
|
||||
// step 4: build authorization
|
||||
$credential = $this->AccessKeyId . '/' . $credentialScope;
|
||||
$authorization = $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['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'];
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
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 . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,150 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\client\AWS as AWSClient;
|
||||
use Exception;
|
||||
|
||||
class aws 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 AWSClient($this->AccessKeyId, $this->SecretAccessKey, 'iam.amazonaws.com', 'iam', '2010-05-08', 'us-east-1', $this->proxy);
|
||||
$client->requestXml('GET', 'GetUser');
|
||||
return true;
|
||||
}
|
||||
|
||||
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('证书解析失败');
|
||||
|
||||
$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 {
|
||||
$data = $client->requestXmlN('GET', '/distribution/' . $config['distribution_id'] . '/config', [], null, true);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取分配信息失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$data['ViewerCertificate']['ACMCertificateArn'] = $cert_id;
|
||||
$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, $cert_id = null, $acm = false)
|
||||
{
|
||||
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);
|
||||
$cert_id = $data['CertificateArn'];
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$this->log('证书上传成功:' . $cert_id);
|
||||
|
||||
$info['cert_id'] = $cert_id;
|
||||
|
||||
return $cert_id;
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\client\AWS as AWSClient;
|
||||
use Exception;
|
||||
|
||||
class aws 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 AWSClient($this->AccessKeyId, $this->SecretAccessKey, 'iam.amazonaws.com', 'iam', '2010-05-08', 'us-east-1', $this->proxy);
|
||||
$client->requestXml('GET', 'GetUser');
|
||||
return true;
|
||||
}
|
||||
|
||||
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('证书解析失败');
|
||||
|
||||
$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 {
|
||||
$data = $client->requestXmlN('GET', '/distribution/' . $config['distribution_id'] . '/config', [], null, true);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取分配信息失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$data['ViewerCertificate']['ACMCertificateArn'] = $cert_id;
|
||||
$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, $cert_id = null, $acm = false)
|
||||
{
|
||||
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);
|
||||
$cert_id = $data['CertificateArn'];
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$this->log('证书上传成功:' . $cert_id);
|
||||
|
||||
$info['cert_id'] = $cert_id;
|
||||
|
||||
return $cert_id;
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,160 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\client\BaiduCloud;
|
||||
use Exception;
|
||||
|
||||
class baidu 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 BaiduCloud($this->AccessKeyId, $this->SecretAccessKey, 'cdn.baidubce.com', $this->proxy);
|
||||
$client->request('GET', '/v2/domain');
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
$config['cert_name'] = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
|
||||
$client = new BaiduCloud($this->AccessKeyId, $this->SecretAccessKey, 'cdn.baidubce.com', $this->proxy);
|
||||
$param = [
|
||||
'httpsEnable' => 'ON',
|
||||
'certificate' => [
|
||||
'certName' => $config['cert_name'],
|
||||
'certServerData' => $fullchain,
|
||||
'certPrivateData' => $privatekey,
|
||||
],
|
||||
];
|
||||
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)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\client\BaiduCloud;
|
||||
use Exception;
|
||||
|
||||
class baidu 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 BaiduCloud($this->AccessKeyId, $this->SecretAccessKey, 'cdn.baidubce.com', $this->proxy);
|
||||
$client->request('GET', '/v2/domain');
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
$config['cert_name'] = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
|
||||
$client = new BaiduCloud($this->AccessKeyId, $this->SecretAccessKey, 'cdn.baidubce.com', $this->proxy);
|
||||
$param = [
|
||||
'httpsEnable' => 'ON',
|
||||
'certificate' => [
|
||||
'certName' => $config['cert_name'],
|
||||
'certServerData' => $fullchain,
|
||||
'certPrivateData' => $privatekey,
|
||||
],
|
||||
];
|
||||
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)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,85 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class baishan implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url = 'https://cdn.api.baishan.com';
|
||||
private $token;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->token = $config['token'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->token)) throw new Exception('token不能为空');
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (empty($config['id'])) throw new Exception('证书ID不能为空');
|
||||
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
|
||||
$params = [
|
||||
'cert_id' => $config['id'],
|
||||
'name' => $cert_name,
|
||||
'certificate' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
];
|
||||
try {
|
||||
$this->request('/v2/domain/certificate?token=' . $this->token, $params);
|
||||
} catch (Exception $e) {
|
||||
if (strpos($e->getMessage(), 'this certificate is exists') !== false) {
|
||||
$this->log('证书ID:' . $config['id'] . '已存在,无需更新');
|
||||
return;
|
||||
}
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
|
||||
$this->log('证书ID:' . $config['id'] . '更新成功!');
|
||||
}
|
||||
|
||||
private function request($path, $params = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = [];
|
||||
$body = null;
|
||||
if ($params) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
$body = json_encode($params);
|
||||
}
|
||||
$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;
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class baishan implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url = 'https://cdn.api.baishan.com';
|
||||
private $token;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->token = $config['token'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->token)) throw new Exception('token不能为空');
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (empty($config['id'])) throw new Exception('证书ID不能为空');
|
||||
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
$cert_name = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
|
||||
$params = [
|
||||
'cert_id' => $config['id'],
|
||||
'name' => $cert_name,
|
||||
'certificate' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
];
|
||||
try {
|
||||
$this->request('/v2/domain/certificate?token=' . $this->token, $params);
|
||||
} catch (Exception $e) {
|
||||
if (strpos($e->getMessage(), 'this certificate is exists') !== false) {
|
||||
$this->log('证书ID:' . $config['id'] . '已存在,无需更新');
|
||||
return;
|
||||
}
|
||||
throw new Exception($e->getMessage());
|
||||
}
|
||||
|
||||
$this->log('证书ID:' . $config['id'] . '更新成功!');
|
||||
}
|
||||
|
||||
private function request($path, $params = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = [];
|
||||
$body = null;
|
||||
if ($params) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
$body = json_encode($params);
|
||||
}
|
||||
$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;
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,335 +1,335 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\CertHelper;
|
||||
use Exception;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->key)) throw new Exception('请填写面板地址和接口密钥');
|
||||
|
||||
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 {
|
||||
$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'] : '面板地址无法连接');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if ($config['type'] == '1') {
|
||||
$this->deployPanel($fullchain, $privatekey);
|
||||
$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;
|
||||
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) {
|
||||
throw new Exception($errmsg ? $errmsg : '要部署的网站不存在');
|
||||
}
|
||||
}
|
||||
|
||||
private function deployPanel($fullchain, $privatekey)
|
||||
{
|
||||
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 = [
|
||||
'domain' => $domain,
|
||||
'path' => $pfx_path,
|
||||
'password' => $password,
|
||||
];
|
||||
$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 deployMailSys($domain, $fullchain, $privatekey)
|
||||
{
|
||||
$path = '/plugin?action=a&name=mail_sys&s=set_mail_certificate_multiple';
|
||||
$data = [
|
||||
'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,
|
||||
];
|
||||
$response = $this->request($path, $data);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['status']) && $result['status']) {
|
||||
return true;
|
||||
} elseif (isset($result['msg'])) {
|
||||
throw new Exception($result['msg']);
|
||||
} else {
|
||||
throw new Exception($response ? $response : '返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
private function request($path, $params, $file = false)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
|
||||
$now_time = time();
|
||||
$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'];
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\CertHelper;
|
||||
use Exception;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->key)) throw new Exception('请填写面板地址和接口密钥');
|
||||
|
||||
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 {
|
||||
$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'] : '面板地址无法连接');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if ($config['type'] == '1') {
|
||||
$this->deployPanel($fullchain, $privatekey);
|
||||
$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;
|
||||
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) {
|
||||
throw new Exception($errmsg ? $errmsg : '要部署的网站不存在');
|
||||
}
|
||||
}
|
||||
|
||||
private function deployPanel($fullchain, $privatekey)
|
||||
{
|
||||
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 = [
|
||||
'domain' => $domain,
|
||||
'path' => $pfx_path,
|
||||
'password' => $password,
|
||||
];
|
||||
$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 deployMailSys($domain, $fullchain, $privatekey)
|
||||
{
|
||||
$path = '/plugin?action=a&name=mail_sys&s=set_mail_certificate_multiple';
|
||||
$data = [
|
||||
'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,
|
||||
];
|
||||
$response = $this->request($path, $data);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['status']) && $result['status']) {
|
||||
return true;
|
||||
} elseif (isset($result['msg'])) {
|
||||
throw new Exception($result['msg']);
|
||||
} else {
|
||||
throw new Exception($response ? $response : '返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
private function request($path, $params, $file = false)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
|
||||
$now_time = time();
|
||||
$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'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,134 +1,158 @@
|
||||
<?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'];
|
||||
}
|
||||
}
|
||||
<?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)
|
||||
{
|
||||
if ($config['type'] == '1') {
|
||||
$this->deployPanel($fullchain, $privatekey);
|
||||
$this->log("面板证书部署成功");
|
||||
return;
|
||||
}
|
||||
|
||||
$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 : '返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
private function deployPanel($fullchain, $privatekey)
|
||||
{
|
||||
$path = '/api/config/set_cert';
|
||||
$data = [
|
||||
'certContent' => $fullchain,
|
||||
'keyContent' => $privatekey,
|
||||
];
|
||||
$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'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class cachefly implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url = 'https://api.cachefly.com/api/2.5';
|
||||
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('API令牌不能为空');
|
||||
$this->request('/accounts/me');
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$params = [
|
||||
'certificate' => $fullchain,
|
||||
'certificateKey' => $privatekey,
|
||||
];
|
||||
$this->request('/certificates', $params);
|
||||
$this->log('证书上传成功!');
|
||||
}
|
||||
|
||||
private function request($path, $params = null, $method = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = ['x-cf-authorization' => 'Bearer ' . $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 ($response['code'] >= 200 && $response['code'] < 300) {
|
||||
return $result;
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class cachefly implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url = 'https://api.cachefly.com/api/2.5';
|
||||
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('API令牌不能为空');
|
||||
$this->request('/accounts/me');
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$params = [
|
||||
'certificate' => $fullchain,
|
||||
'certificateKey' => $privatekey,
|
||||
];
|
||||
$this->request('/certificates', $params);
|
||||
$this->log('证书上传成功!');
|
||||
}
|
||||
|
||||
private function request($path, $params = null, $method = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = ['x-cf-authorization' => 'Bearer ' . $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 ($response['code'] >= 200 && $response['code'] < 300) {
|
||||
return $result;
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,124 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class cdnfly implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $api_key;
|
||||
private $api_secret;
|
||||
private $auth = 0;
|
||||
private $username;
|
||||
private $password;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$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 ($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)
|
||||
{
|
||||
$id = $config['id'];
|
||||
if (empty($id)) throw new Exception('证书ID不能为空');
|
||||
|
||||
$params = [
|
||||
'type' => 'custom',
|
||||
'cert' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
];
|
||||
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];
|
||||
$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'] == 0) {
|
||||
return isset($result['data']) ? $result['data'] : null;
|
||||
} elseif (isset($result['msg'])) {
|
||||
throw new Exception($result['msg']);
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class cdnfly implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $api_key;
|
||||
private $api_secret;
|
||||
private $auth = 0;
|
||||
private $username;
|
||||
private $password;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$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 ($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)
|
||||
{
|
||||
$id = $config['id'];
|
||||
if (empty($id)) throw new Exception('证书ID不能为空');
|
||||
|
||||
$params = [
|
||||
'type' => 'custom',
|
||||
'cert' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
];
|
||||
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];
|
||||
$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'] == 0) {
|
||||
return isset($result['data']) ? $result['data'] : null;
|
||||
} elseif (isset($result['msg'])) {
|
||||
throw new Exception($result['msg']);
|
||||
} else {
|
||||
throw new Exception('返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,184 +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);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,124 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class doge implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $AccessKey;
|
||||
private $SecretKey;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKey = $config['AccessKey'];
|
||||
$this->SecretKey = $config['SecretKey'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->AccessKey) || empty($this->SecretKey)) throw new Exception('必填参数不能为空');
|
||||
$this->request('/cdn/cert/list.json');
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$domains = $config['domain'];
|
||||
if (empty($domains)) 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'];
|
||||
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey, $cert_name);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private function get_cert_id($fullchain, $privatekey, $cert_name)
|
||||
{
|
||||
$cert_id = null;
|
||||
|
||||
$data = $this->request('/cdn/cert/list.json');
|
||||
foreach ($data['certs'] as $cert) {
|
||||
if ($cert_name == $cert['note']) {
|
||||
$cert_id = $cert['id'];
|
||||
$this->log('证书' . $cert_name . '已存在,证书ID:' . $cert_id);
|
||||
} elseif ($cert['expire'] < time() && $cert['domainCount'] == 0) {
|
||||
try {
|
||||
$this->request('/cdn/cert/delete.json', ['id' => $cert['id']]);
|
||||
$this->log('证书' . $cert['name'] . '已过期,删除证书成功');
|
||||
} catch (Exception $e) {
|
||||
$this->log('证书' . $cert['name'] . '已过期,删除证书失败:' . $e->getMessage());
|
||||
}
|
||||
usleep(300000);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$cert_id) {
|
||||
$param = [
|
||||
'note' => $cert_name,
|
||||
'cert' => $fullchain,
|
||||
'private' => $privatekey,
|
||||
];
|
||||
try {
|
||||
$data = $this->request('/cdn/cert/upload.json', $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
$this->log('上传证书成功,证书ID:' . $data['id']);
|
||||
$cert_id = $data['id'];
|
||||
usleep(500000);
|
||||
}
|
||||
return $cert_id;
|
||||
}
|
||||
|
||||
private function request($path, $data = null, $json = false)
|
||||
{
|
||||
$body = null;
|
||||
if($data){
|
||||
$body = $json ? json_encode($data) : http_build_query($data);
|
||||
}
|
||||
$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';
|
||||
$url = 'https://api.dogecloud.com'.$path;
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if(isset($result['code']) && $result['code'] == 200){
|
||||
return $result['data'] ?? true;
|
||||
}elseif(isset($result['msg'])){
|
||||
throw new Exception($result['msg']);
|
||||
}else{
|
||||
throw new Exception('请求失败');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class doge implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $AccessKey;
|
||||
private $SecretKey;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKey = $config['AccessKey'];
|
||||
$this->SecretKey = $config['SecretKey'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->AccessKey) || empty($this->SecretKey)) throw new Exception('必填参数不能为空');
|
||||
$this->request('/cdn/cert/list.json');
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$domains = $config['domain'];
|
||||
if (empty($domains)) 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'];
|
||||
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey, $cert_name);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private function get_cert_id($fullchain, $privatekey, $cert_name)
|
||||
{
|
||||
$cert_id = null;
|
||||
|
||||
$data = $this->request('/cdn/cert/list.json');
|
||||
foreach ($data['certs'] as $cert) {
|
||||
if ($cert_name == $cert['note']) {
|
||||
$cert_id = $cert['id'];
|
||||
$this->log('证书' . $cert_name . '已存在,证书ID:' . $cert_id);
|
||||
} elseif ($cert['expire'] < time() && $cert['domainCount'] == 0) {
|
||||
try {
|
||||
$this->request('/cdn/cert/delete.json', ['id' => $cert['id']]);
|
||||
$this->log('证书' . $cert['name'] . '已过期,删除证书成功');
|
||||
} catch (Exception $e) {
|
||||
$this->log('证书' . $cert['name'] . '已过期,删除证书失败:' . $e->getMessage());
|
||||
}
|
||||
usleep(300000);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$cert_id) {
|
||||
$param = [
|
||||
'note' => $cert_name,
|
||||
'cert' => $fullchain,
|
||||
'private' => $privatekey,
|
||||
];
|
||||
try {
|
||||
$data = $this->request('/cdn/cert/upload.json', $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
$this->log('上传证书成功,证书ID:' . $data['id']);
|
||||
$cert_id = $data['id'];
|
||||
usleep(500000);
|
||||
}
|
||||
return $cert_id;
|
||||
}
|
||||
|
||||
private function request($path, $data = null, $json = false)
|
||||
{
|
||||
$body = null;
|
||||
if($data){
|
||||
$body = $json ? json_encode($data) : http_build_query($data);
|
||||
}
|
||||
$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';
|
||||
$url = 'https://api.dogecloud.com'.$path;
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if(isset($result['code']) && $result['code'] == 200){
|
||||
return $result['data'] ?? true;
|
||||
}elseif(isset($result['msg'])){
|
||||
throw new Exception($result['msg']);
|
||||
}else{
|
||||
throw new Exception('请求失败');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
132
app/lib/deploy/fnos.php
Normal file
132
app/lib/deploy/fnos.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class fnos implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $config;
|
||||
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$domains = $config['domainList'];
|
||||
if (empty($domains)) throw new Exception('没有设置要部署的域名');
|
||||
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
|
||||
$connection = $this->connect();
|
||||
$cert_all = $this->exec($connection, '获取证书列表', 'cat /usr/trim/etc/network_cert_all.conf');
|
||||
$list = json_decode($cert_all, true);
|
||||
if (!$list) throw new Exception('获取证书列表失败');
|
||||
|
||||
$success = 0;
|
||||
foreach ($list as $row) {
|
||||
if (empty($row['san'])) continue;
|
||||
$cert_domains = $row['san'];
|
||||
$flag = false;
|
||||
foreach ($cert_domains as $domain) {
|
||||
if (in_array($domain, $domains)) {
|
||||
$flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($flag) {
|
||||
$certPath = $row['certificate'];
|
||||
$keyPath = $row['privateKey'];
|
||||
$certDir = dirname($certPath);
|
||||
$this->exec($connection, '上传证书文件', "sudo tee ".$certPath." > /dev/null <<'EOF'\n".$fullchain."\nEOF");
|
||||
$this->exec($connection, '上传私钥文件', "sudo tee ".$keyPath." > /dev/null <<'EOF'\n".$privatekey."\nEOF");
|
||||
$this->exec($connection, '刷新目录权限', 'sudo chmod 0755 "'.$certDir.'" -R');
|
||||
$this->exec($connection, '更新数据表', 'sudo -u postgres psql -d trim_connect -c "UPDATE cert SET valid_to='.$certInfo['validTo_time_t'].'000,valid_from='.$certInfo['validFrom_time_t'].'000,issued_by=\''.$certInfo['issuer']['CN'].'\',updated_time='.getMillisecond().' WHERE private_key=\''.$keyPath.'\'"');
|
||||
$this->log('证书 '.$row['domain'].' 更新成功');
|
||||
$success++;
|
||||
}
|
||||
}
|
||||
if ($success == 0) {
|
||||
throw new Exception('没有要更新的证书');
|
||||
} else {
|
||||
$this->exec($connection, '重启webdav', 'sudo systemctl restart webdav.service');
|
||||
$this->exec($connection, '重启smbftpd', 'sudo systemctl restart smbftpd.service');
|
||||
$this->exec($connection, '重启trim_nginx', 'sudo systemctl restart trim_nginx.service');
|
||||
}
|
||||
}
|
||||
|
||||
private function exec($connection, $name, $cmd)
|
||||
{
|
||||
$stream = ssh2_exec($connection, $cmd);
|
||||
$errorStream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
|
||||
if (!$stream || !$errorStream) {
|
||||
throw new Exception($name.'执行命令失败');
|
||||
}
|
||||
stream_set_blocking($stream, true);
|
||||
stream_set_blocking($errorStream, true);
|
||||
$output = stream_get_contents($stream);
|
||||
$errorOutput = stream_get_contents($errorStream);
|
||||
fclose($stream);
|
||||
fclose($errorStream);
|
||||
if (trim($errorOutput)) {
|
||||
if (strpos($errorOutput, 'a password is required') !== false) {
|
||||
throw new Exception('权限不足,请先配置 sudo 免密');
|
||||
}
|
||||
throw new Exception($name.'失败:' . trim($errorOutput));
|
||||
} else {
|
||||
if (strlen($output) > 200) {
|
||||
return $output;
|
||||
}
|
||||
$this->log($name.'成功 ' . trim($output));
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
private function connect()
|
||||
{
|
||||
if (!function_exists('ssh2_connect')) {
|
||||
throw new Exception('ssh2扩展未安装');
|
||||
}
|
||||
if (empty($this->config['host']) || empty($this->config['port']) || empty($this->config['username']) || empty($this->config['password'])) {
|
||||
throw new Exception('必填参数不能为空');
|
||||
}
|
||||
if (!filter_var($this->config['host'], FILTER_VALIDATE_IP) && !filter_var($this->config['host'], FILTER_VALIDATE_DOMAIN)) {
|
||||
throw new Exception('主机地址不合法');
|
||||
}
|
||||
if (!is_numeric($this->config['port']) || $this->config['port'] < 1 || $this->config['port'] > 65535) {
|
||||
throw new Exception('SSH端口不合法');
|
||||
}
|
||||
|
||||
$connection = ssh2_connect($this->config['host'], intval($this->config['port']));
|
||||
if (!$connection) {
|
||||
throw new Exception('SSH连接失败');
|
||||
}
|
||||
if (!ssh2_auth_password($connection, $this->config['username'], $this->config['password'])) {
|
||||
throw new Exception('用户名或密码错误');
|
||||
}
|
||||
return $connection;
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,113 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class ftp implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $config;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$conn_id = $this->connect();
|
||||
ftp_pasv($conn_id, true);
|
||||
if ($config['format'] == 'pem') {
|
||||
$temp_stream = fopen('php://temp', 'r+');
|
||||
fwrite($temp_stream, $fullchain);
|
||||
rewind($temp_stream);
|
||||
if (ftp_fput($conn_id, $config['pem_cert_file'], $temp_stream, FTP_BINARY)) {
|
||||
$this->log('证书文件上传成功:' . $config['pem_cert_file']);
|
||||
} else {
|
||||
fclose($temp_stream);
|
||||
ftp_close($conn_id);
|
||||
throw new Exception('证书文件上传失败:' . $config['pem_cert_file']);
|
||||
}
|
||||
fclose($temp_stream);
|
||||
|
||||
$temp_stream = fopen('php://temp', 'r+');
|
||||
fwrite($temp_stream, $privatekey);
|
||||
rewind($temp_stream);
|
||||
if (ftp_fput($conn_id, $config['pem_key_file'], $temp_stream, FTP_BINARY)) {
|
||||
$this->log('私钥文件上传成功:' . $config['pem_key_file']);
|
||||
} else {
|
||||
fclose($temp_stream);
|
||||
ftp_close($conn_id);
|
||||
throw new Exception('私钥文件上传失败:' . $config['pem_key_file']);
|
||||
}
|
||||
fclose($temp_stream);
|
||||
} elseif ($config['format'] == 'pfx') {
|
||||
$pfx = \app\lib\CertHelper::getPfx($fullchain, $privatekey, $config['pfx_pass'] ? $config['pfx_pass'] : null);
|
||||
|
||||
$temp_stream = fopen('php://temp', 'r+');
|
||||
fwrite($temp_stream, $pfx);
|
||||
rewind($temp_stream);
|
||||
if (ftp_fput($conn_id, $config['pfx_file'], $temp_stream, FTP_BINARY)) {
|
||||
$this->log('PFX证书文件上传成功:' . $config['pfx_file']);
|
||||
} else {
|
||||
fclose($temp_stream);
|
||||
ftp_close($conn_id);
|
||||
throw new Exception('PFX证书文件上传失败:' . $config['pfx_file']);
|
||||
}
|
||||
fclose($temp_stream);
|
||||
}
|
||||
ftp_close($conn_id);
|
||||
}
|
||||
|
||||
private function connect()
|
||||
{
|
||||
if (!function_exists('ftp_connect')) {
|
||||
throw new Exception('ftp扩展未安装');
|
||||
}
|
||||
if (empty($this->config['host']) || empty($this->config['port']) || empty($this->config['username']) || empty($this->config['password'])) {
|
||||
throw new Exception('必填参数不能为空');
|
||||
}
|
||||
if (!filter_var($this->config['host'], FILTER_VALIDATE_IP) && !filter_var($this->config['host'], FILTER_VALIDATE_DOMAIN)) {
|
||||
throw new Exception('主机地址不合法');
|
||||
}
|
||||
if (!is_numeric($this->config['port']) || $this->config['port'] < 1 || $this->config['port'] > 65535) {
|
||||
throw new Exception('端口不合法');
|
||||
}
|
||||
|
||||
if ($this->config['secure'] == '1') {
|
||||
$conn_id = ftp_ssl_connect($this->config['host'], intval($this->config['port']), 10);
|
||||
if (!$conn_id) {
|
||||
throw new Exception('FTP服务器无法连接(SSL)');
|
||||
}
|
||||
} else {
|
||||
$conn_id = ftp_connect($this->config['host'], intval($this->config['port']), 10);
|
||||
if (!$conn_id) {
|
||||
throw new Exception('FTP服务器无法连接');
|
||||
}
|
||||
}
|
||||
if (!ftp_login($conn_id, $this->config['username'], $this->config['password'])) {
|
||||
ftp_close($conn_id);
|
||||
throw new Exception('FTP登录失败');
|
||||
}
|
||||
return $conn_id;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class ftp implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $config;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$conn_id = $this->connect();
|
||||
ftp_pasv($conn_id, true);
|
||||
if ($config['format'] == 'pem') {
|
||||
$temp_stream = fopen('php://temp', 'r+');
|
||||
fwrite($temp_stream, $fullchain);
|
||||
rewind($temp_stream);
|
||||
if (ftp_fput($conn_id, $config['pem_cert_file'], $temp_stream, FTP_BINARY)) {
|
||||
$this->log('证书文件上传成功:' . $config['pem_cert_file']);
|
||||
} else {
|
||||
fclose($temp_stream);
|
||||
ftp_close($conn_id);
|
||||
throw new Exception('证书文件上传失败:' . $config['pem_cert_file']);
|
||||
}
|
||||
fclose($temp_stream);
|
||||
|
||||
$temp_stream = fopen('php://temp', 'r+');
|
||||
fwrite($temp_stream, $privatekey);
|
||||
rewind($temp_stream);
|
||||
if (ftp_fput($conn_id, $config['pem_key_file'], $temp_stream, FTP_BINARY)) {
|
||||
$this->log('私钥文件上传成功:' . $config['pem_key_file']);
|
||||
} else {
|
||||
fclose($temp_stream);
|
||||
ftp_close($conn_id);
|
||||
throw new Exception('私钥文件上传失败:' . $config['pem_key_file']);
|
||||
}
|
||||
fclose($temp_stream);
|
||||
} elseif ($config['format'] == 'pfx') {
|
||||
$pfx = \app\lib\CertHelper::getPfx($fullchain, $privatekey, $config['pfx_pass'] ? $config['pfx_pass'] : null);
|
||||
|
||||
$temp_stream = fopen('php://temp', 'r+');
|
||||
fwrite($temp_stream, $pfx);
|
||||
rewind($temp_stream);
|
||||
if (ftp_fput($conn_id, $config['pfx_file'], $temp_stream, FTP_BINARY)) {
|
||||
$this->log('PFX证书文件上传成功:' . $config['pfx_file']);
|
||||
} else {
|
||||
fclose($temp_stream);
|
||||
ftp_close($conn_id);
|
||||
throw new Exception('PFX证书文件上传失败:' . $config['pfx_file']);
|
||||
}
|
||||
fclose($temp_stream);
|
||||
}
|
||||
ftp_close($conn_id);
|
||||
}
|
||||
|
||||
private function connect()
|
||||
{
|
||||
if (!function_exists('ftp_connect')) {
|
||||
throw new Exception('ftp扩展未安装');
|
||||
}
|
||||
if (empty($this->config['host']) || empty($this->config['port']) || empty($this->config['username']) || empty($this->config['password'])) {
|
||||
throw new Exception('必填参数不能为空');
|
||||
}
|
||||
if (!filter_var($this->config['host'], FILTER_VALIDATE_IP) && !filter_var($this->config['host'], FILTER_VALIDATE_DOMAIN)) {
|
||||
throw new Exception('主机地址不合法');
|
||||
}
|
||||
if (!is_numeric($this->config['port']) || $this->config['port'] < 1 || $this->config['port'] > 65535) {
|
||||
throw new Exception('端口不合法');
|
||||
}
|
||||
|
||||
if ($this->config['secure'] == '1') {
|
||||
$conn_id = ftp_ssl_connect($this->config['host'], intval($this->config['port']), 10);
|
||||
if (!$conn_id) {
|
||||
throw new Exception('FTP服务器无法连接(SSL)');
|
||||
}
|
||||
} else {
|
||||
$conn_id = ftp_connect($this->config['host'], intval($this->config['port']), 10);
|
||||
if (!$conn_id) {
|
||||
throw new Exception('FTP服务器无法连接');
|
||||
}
|
||||
}
|
||||
if (!ftp_login($conn_id, $this->config['username'], $this->config['password'])) {
|
||||
ftp_close($conn_id);
|
||||
throw new Exception('FTP登录失败');
|
||||
}
|
||||
return $conn_id;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,77 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class gcore implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url = 'https://api.gcore.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('API令牌不能为空');
|
||||
$this->request('/iam/clients/me');
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$id = $config['id'];
|
||||
if (empty($id)) throw new Exception('证书ID不能为空');
|
||||
|
||||
$params = [
|
||||
'name' => $config['name'],
|
||||
'sslCertificate' => $fullchain,
|
||||
'sslPrivateKey' => $privatekey,
|
||||
'validate_root_ca' => true,
|
||||
];
|
||||
$this->request('/cdn/sslData/' . $id, $params, 'PUT');
|
||||
$this->log('证书ID:' . $id . '更新成功!');
|
||||
}
|
||||
|
||||
private function request($path, $params = null, $method = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = ['Authorization' => 'APIKey ' . $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 ($response['code'] >= 200 && $response['code'] < 300) {
|
||||
return $result;
|
||||
} elseif (isset($result['message']['message'])) {
|
||||
throw new Exception($result['message']['message']);
|
||||
} elseif (isset($result['errors'])) {
|
||||
$errors = $result['errors'][array_key_first($result['errors'])];
|
||||
throw new Exception($errors[0]);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class gcore implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url = 'https://api.gcore.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('API令牌不能为空');
|
||||
$this->request('/iam/clients/me');
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$id = $config['id'];
|
||||
if (empty($id)) throw new Exception('证书ID不能为空');
|
||||
|
||||
$params = [
|
||||
'name' => $config['name'],
|
||||
'sslCertificate' => $fullchain,
|
||||
'sslPrivateKey' => $privatekey,
|
||||
'validate_root_ca' => true,
|
||||
];
|
||||
$this->request('/cdn/sslData/' . $id, $params, 'PUT');
|
||||
$this->log('证书ID:' . $id . '更新成功!');
|
||||
}
|
||||
|
||||
private function request($path, $params = null, $method = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = ['Authorization' => 'APIKey ' . $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 ($response['code'] >= 200 && $response['code'] < 300) {
|
||||
return $result;
|
||||
} elseif (isset($result['message']['message'])) {
|
||||
throw new Exception($result['message']['message']);
|
||||
} elseif (isset($result['errors'])) {
|
||||
$errors = $result['errors'][array_key_first($result['errors'])];
|
||||
throw new Exception($errors[0]);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,154 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class goedge implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $accessKeyId;
|
||||
private $accessKey;
|
||||
private $usertype;
|
||||
private $systype;
|
||||
private $proxy;
|
||||
private $accessToken;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
$this->accessKeyId = $config['accessKeyId'];
|
||||
$this->accessKey = $config['accessKey'];
|
||||
$this->usertype = $config['usertype'];
|
||||
$this->systype = $config['systype'];
|
||||
$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 === 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'];
|
||||
|
||||
if (!empty($list)) {
|
||||
foreach ($list as $row) {
|
||||
$params = [
|
||||
'sslCertId' => $row['id'],
|
||||
'isOn' => true,
|
||||
'name' => $row['name'],
|
||||
'description' => $row['description'],
|
||||
'serverName' => $row['serverName'],
|
||||
'isCA' => false,
|
||||
'certData' => base64_encode($fullchain),
|
||||
'keyData' => base64_encode($privatekey),
|
||||
'timeBeginAt' => $certInfo['validFrom_time_t'],
|
||||
'timeEndAt' => $certInfo['validTo_time_t'],
|
||||
'dnsNames' => $domains,
|
||||
'commonNames' => [$certInfo['issuer']['CN']],
|
||||
];
|
||||
$this->request('/SSLCertService/updateSSLCert', $params);
|
||||
$this->log('证书ID:' . $row['id'] . '更新成功!');
|
||||
}
|
||||
} else {
|
||||
$params = [
|
||||
'isOn' => true,
|
||||
'name' => $cert_name,
|
||||
'description' => $cert_name,
|
||||
'serverName' => $certInfo['subject']['CN'],
|
||||
'isCA' => false,
|
||||
'certData' => base64_encode($fullchain),
|
||||
'keyData' => base64_encode($privatekey),
|
||||
'timeBeginAt' => $certInfo['validFrom_time_t'],
|
||||
'timeEndAt' => $certInfo['validTo_time_t'],
|
||||
'dnsNames' => $domains,
|
||||
'commonNames' => [$certInfo['issuer']['CN']],
|
||||
];
|
||||
$result = $this->request('/SSLCertService/createSSLCert', $params);
|
||||
$this->log('证书ID:' . $result['sslCertId'] . '添加成功!');
|
||||
}
|
||||
}
|
||||
|
||||
private function getAccessToken()
|
||||
{
|
||||
$path = '/APIAccessTokenService/getAPIAccessToken';
|
||||
$params = [
|
||||
'type' => $this->usertype,
|
||||
'accessKeyId' => $this->accessKeyId,
|
||||
'accessKey' => $this->accessKey,
|
||||
];
|
||||
$result = $this->request($path, $params);
|
||||
if (isset($result['token'])) {
|
||||
$this->accessToken = $result['token'];
|
||||
} else {
|
||||
throw new Exception('登录成功,获取AccessToken失败');
|
||||
}
|
||||
}
|
||||
|
||||
private function request($path, $params = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = [];
|
||||
$body = null;
|
||||
if ($this->accessToken) {
|
||||
if ($this->systype == '1') {
|
||||
$headers['X-Cloud-Access-Token'] = $this->accessToken;
|
||||
} else {
|
||||
$headers['X-Edge-Access-Token'] = $this->accessToken;
|
||||
}
|
||||
}
|
||||
if ($params) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
$body = json_encode($params);
|
||||
}
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['code']) && $result['code'] == 200) {
|
||||
return $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);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class goedge implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $accessKeyId;
|
||||
private $accessKey;
|
||||
private $usertype;
|
||||
private $systype;
|
||||
private $proxy;
|
||||
private $accessToken;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
$this->accessKeyId = $config['accessKeyId'];
|
||||
$this->accessKey = $config['accessKey'];
|
||||
$this->usertype = $config['usertype'];
|
||||
$this->systype = $config['systype'];
|
||||
$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 === 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'];
|
||||
|
||||
if (!empty($list)) {
|
||||
foreach ($list as $row) {
|
||||
$params = [
|
||||
'sslCertId' => $row['id'],
|
||||
'isOn' => true,
|
||||
'name' => $row['name'],
|
||||
'description' => $row['description'],
|
||||
'serverName' => $row['serverName'],
|
||||
'isCA' => false,
|
||||
'certData' => base64_encode($fullchain),
|
||||
'keyData' => base64_encode($privatekey),
|
||||
'timeBeginAt' => $certInfo['validFrom_time_t'],
|
||||
'timeEndAt' => $certInfo['validTo_time_t'],
|
||||
'dnsNames' => $domains,
|
||||
'commonNames' => [$certInfo['issuer']['CN']],
|
||||
];
|
||||
$this->request('/SSLCertService/updateSSLCert', $params);
|
||||
$this->log('证书ID:' . $row['id'] . '更新成功!');
|
||||
}
|
||||
} else {
|
||||
$params = [
|
||||
'isOn' => true,
|
||||
'name' => $cert_name,
|
||||
'description' => $cert_name,
|
||||
'serverName' => $certInfo['subject']['CN'],
|
||||
'isCA' => false,
|
||||
'certData' => base64_encode($fullchain),
|
||||
'keyData' => base64_encode($privatekey),
|
||||
'timeBeginAt' => $certInfo['validFrom_time_t'],
|
||||
'timeEndAt' => $certInfo['validTo_time_t'],
|
||||
'dnsNames' => $domains,
|
||||
'commonNames' => [$certInfo['issuer']['CN']],
|
||||
];
|
||||
$result = $this->request('/SSLCertService/createSSLCert', $params);
|
||||
$this->log('证书ID:' . $result['sslCertId'] . '添加成功!');
|
||||
}
|
||||
}
|
||||
|
||||
private function getAccessToken()
|
||||
{
|
||||
$path = '/APIAccessTokenService/getAPIAccessToken';
|
||||
$params = [
|
||||
'type' => $this->usertype,
|
||||
'accessKeyId' => $this->accessKeyId,
|
||||
'accessKey' => $this->accessKey,
|
||||
];
|
||||
$result = $this->request($path, $params);
|
||||
if (isset($result['token'])) {
|
||||
$this->accessToken = $result['token'];
|
||||
} else {
|
||||
throw new Exception('登录成功,获取AccessToken失败');
|
||||
}
|
||||
}
|
||||
|
||||
private function request($path, $params = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = [];
|
||||
$body = null;
|
||||
if ($this->accessToken) {
|
||||
if ($this->systype == '1') {
|
||||
$headers['X-Cloud-Access-Token'] = $this->accessToken;
|
||||
} else {
|
||||
$headers['X-Edge-Access-Token'] = $this->accessToken;
|
||||
}
|
||||
}
|
||||
if ($params) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
$body = json_encode($params);
|
||||
}
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if (isset($result['code']) && $result['code'] == 200) {
|
||||
return $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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,152 +1,152 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\client\HuaweiCloud;
|
||||
use Exception;
|
||||
|
||||
class huawei 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 HuaweiCloud($this->AccessKeyId, $this->SecretAccessKey, 'scm.cn-north-4.myhuaweicloud.com', $this->proxy);
|
||||
$client->request('GET', '/v3/scm/certificates');
|
||||
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'] == 'elb') {
|
||||
$this->deploy_elb($fullchain, $privatekey, $config);
|
||||
} elseif ($config['product'] == 'waf') {
|
||||
$this->deploy_waf($fullchain, $privatekey, $config);
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_cdn($fullchain, $privatekey, $config)
|
||||
{
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$client = new HuaweiCloud($this->AccessKeyId, $this->SecretAccessKey, 'cdn.myhuaweicloud.com', $this->proxy);
|
||||
$param = [
|
||||
'configs' => [
|
||||
'https' => [
|
||||
'https_status' => 'on',
|
||||
'certificate_type' => 'server',
|
||||
'certificate_source' => 0,
|
||||
'certificate_name' => $config['cert_name'],
|
||||
'certificate_value' => $fullchain,
|
||||
'private_key' => $privatekey,
|
||||
],
|
||||
],
|
||||
];
|
||||
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)
|
||||
{
|
||||
if (empty($config['project_id'])) throw new Exception('项目ID不能为空');
|
||||
if (empty($config['region_id'])) throw new Exception('区域ID不能为空');
|
||||
if (empty($config['cert_id'])) throw new Exception('证书ID不能为空');
|
||||
$endpoint = 'elb.' . $config['region_id'] . '.myhuaweicloud.com';
|
||||
$client = new HuaweiCloud($this->AccessKeyId, $this->SecretAccessKey, $endpoint, $this->proxy);
|
||||
try {
|
||||
$data = $client->request('GET', '/v3/' . $config['project_id'] . '/elb/certificates/' . $config['cert_id']);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('证书详情查询失败:' . $e->getMessage());
|
||||
}
|
||||
if (isset($data['certificate']['certificate']) && trim($data['certificate']['certificate']) == trim($fullchain)) {
|
||||
$this->log('ELB证书ID ' . $config['cert_id'] . ' 已存在,无需重复部署');
|
||||
return;
|
||||
}
|
||||
$param = [
|
||||
'certificate' => [
|
||||
'certificate' => $fullchain,
|
||||
'private_key' => $privatekey,
|
||||
'domain' => implode(',', $config['domainList']),
|
||||
],
|
||||
];
|
||||
$client->request('PUT', '/v3/' . $config['project_id'] . '/elb/certificates/' . $config['cert_id'], null, $param);
|
||||
$this->log('ELB证书ID ' . $config['cert_id'] . ' 更新证书成功!');
|
||||
}
|
||||
|
||||
private function deploy_waf($fullchain, $privatekey, $config)
|
||||
{
|
||||
if (empty($config['project_id'])) throw new Exception('项目ID不能为空');
|
||||
if (empty($config['region_id'])) throw new Exception('区域ID不能为空');
|
||||
if (empty($config['cert_id'])) throw new Exception('证书ID不能为空');
|
||||
$endpoint = 'waf.' . $config['region_id'] . '.myhuaweicloud.com';
|
||||
$client = new HuaweiCloud($this->AccessKeyId, $this->SecretAccessKey, $endpoint, $this->proxy);
|
||||
try {
|
||||
$data = $client->request('GET', '/v1/' . $config['project_id'] . '/waf/certificates/' . $config['cert_id']);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('证书详情查询失败:' . $e->getMessage());
|
||||
}
|
||||
if (isset($data['content']) && trim($data['content']) == trim($fullchain)) {
|
||||
$this->log('WAF证书ID ' . $config['cert_id'] . ' 已存在,无需重复部署');
|
||||
return;
|
||||
}
|
||||
$param = [
|
||||
'name' => $config['cert_name'],
|
||||
'content' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
];
|
||||
$client->request('PUT', '/v1/' . $config['project_id'] . '/waf/certificates/' . $config['cert_id'], null, $param);
|
||||
$this->log('WAF证书ID ' . $config['cert_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 HuaweiCloud($this->AccessKeyId, $this->SecretAccessKey, 'scm.cn-north-4.myhuaweicloud.com', $this->proxy);
|
||||
$param = [
|
||||
'name' => $cert_name,
|
||||
'certificate' => $fullchain,
|
||||
'private_key' => $privatekey,
|
||||
];
|
||||
try {
|
||||
$data = $client->request('POST', '/v3/scm/certificates/import', null, $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
$this->log('上传证书成功 certificate_id=' . $data['certificate_id']);
|
||||
return $data['certificate_id'];
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\client\HuaweiCloud;
|
||||
use Exception;
|
||||
|
||||
class huawei 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 HuaweiCloud($this->AccessKeyId, $this->SecretAccessKey, 'scm.cn-north-4.myhuaweicloud.com', $this->proxy);
|
||||
$client->request('GET', '/v3/scm/certificates');
|
||||
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'] == 'elb') {
|
||||
$this->deploy_elb($fullchain, $privatekey, $config);
|
||||
} elseif ($config['product'] == 'waf') {
|
||||
$this->deploy_waf($fullchain, $privatekey, $config);
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_cdn($fullchain, $privatekey, $config)
|
||||
{
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$client = new HuaweiCloud($this->AccessKeyId, $this->SecretAccessKey, 'cdn.myhuaweicloud.com', $this->proxy);
|
||||
$param = [
|
||||
'configs' => [
|
||||
'https' => [
|
||||
'https_status' => 'on',
|
||||
'certificate_type' => 'server',
|
||||
'certificate_source' => 0,
|
||||
'certificate_name' => $config['cert_name'],
|
||||
'certificate_value' => $fullchain,
|
||||
'private_key' => $privatekey,
|
||||
],
|
||||
],
|
||||
];
|
||||
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)
|
||||
{
|
||||
if (empty($config['project_id'])) throw new Exception('项目ID不能为空');
|
||||
if (empty($config['region_id'])) throw new Exception('区域ID不能为空');
|
||||
if (empty($config['cert_id'])) throw new Exception('证书ID不能为空');
|
||||
$endpoint = 'elb.' . $config['region_id'] . '.myhuaweicloud.com';
|
||||
$client = new HuaweiCloud($this->AccessKeyId, $this->SecretAccessKey, $endpoint, $this->proxy);
|
||||
try {
|
||||
$data = $client->request('GET', '/v3/' . $config['project_id'] . '/elb/certificates/' . $config['cert_id']);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('证书详情查询失败:' . $e->getMessage());
|
||||
}
|
||||
if (isset($data['certificate']['certificate']) && trim($data['certificate']['certificate']) == trim($fullchain)) {
|
||||
$this->log('ELB证书ID ' . $config['cert_id'] . ' 已存在,无需重复部署');
|
||||
return;
|
||||
}
|
||||
$param = [
|
||||
'certificate' => [
|
||||
'certificate' => $fullchain,
|
||||
'private_key' => $privatekey,
|
||||
'domain' => implode(',', $config['domainList']),
|
||||
],
|
||||
];
|
||||
$client->request('PUT', '/v3/' . $config['project_id'] . '/elb/certificates/' . $config['cert_id'], null, $param);
|
||||
$this->log('ELB证书ID ' . $config['cert_id'] . ' 更新证书成功!');
|
||||
}
|
||||
|
||||
private function deploy_waf($fullchain, $privatekey, $config)
|
||||
{
|
||||
if (empty($config['project_id'])) throw new Exception('项目ID不能为空');
|
||||
if (empty($config['region_id'])) throw new Exception('区域ID不能为空');
|
||||
if (empty($config['cert_id'])) throw new Exception('证书ID不能为空');
|
||||
$endpoint = 'waf.' . $config['region_id'] . '.myhuaweicloud.com';
|
||||
$client = new HuaweiCloud($this->AccessKeyId, $this->SecretAccessKey, $endpoint, $this->proxy);
|
||||
try {
|
||||
$data = $client->request('GET', '/v1/' . $config['project_id'] . '/waf/certificates/' . $config['cert_id']);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('证书详情查询失败:' . $e->getMessage());
|
||||
}
|
||||
if (isset($data['content']) && trim($data['content']) == trim($fullchain)) {
|
||||
$this->log('WAF证书ID ' . $config['cert_id'] . ' 已存在,无需重复部署');
|
||||
return;
|
||||
}
|
||||
$param = [
|
||||
'name' => $config['cert_name'],
|
||||
'content' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
];
|
||||
$client->request('PUT', '/v1/' . $config['project_id'] . '/waf/certificates/' . $config['cert_id'], null, $param);
|
||||
$this->log('WAF证书ID ' . $config['cert_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 HuaweiCloud($this->AccessKeyId, $this->SecretAccessKey, 'scm.cn-north-4.myhuaweicloud.com', $this->proxy);
|
||||
$param = [
|
||||
'name' => $cert_name,
|
||||
'certificate' => $fullchain,
|
||||
'private_key' => $privatekey,
|
||||
];
|
||||
try {
|
||||
$data = $client->request('POST', '/v3/scm/certificates/import', null, $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
$this->log('上传证书成功 certificate_id=' . $data['certificate_id']);
|
||||
return $data['certificate_id'];
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,228 +1,228 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\client\Volcengine;
|
||||
use Exception;
|
||||
|
||||
class huoshan 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 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 ($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)
|
||||
{
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'cdn.volcengineapi.com', 'cdn', '2021-03-01', 'cn-north-1', $this->proxy);
|
||||
$param = [
|
||||
'CertId' => $cert_id,
|
||||
'Domain' => $config['domain'],
|
||||
];
|
||||
$data = $client->request('POST', 'BatchDeployCert', $param);
|
||||
if (empty($data['DeployResult'])) throw new Exception('部署证书失败:DeployResult为空');
|
||||
foreach ($data['DeployResult'] as $row) {
|
||||
if ($row['Status'] == 'success') {
|
||||
$this->log('CDN域名 ' . $row['Domain'] . ' 部署证书成功!');
|
||||
} else {
|
||||
$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, 'open.volcengineapi.com', 'certificate_service', '2024-10-01', 'cn-beijing', $this->proxy);
|
||||
$param = [
|
||||
'Tag' => $cert_name,
|
||||
'Repeatable' => false,
|
||||
'CertificateInfo' => [
|
||||
'CertificateChain' => $fullchain,
|
||||
'PrivateKey' => $privatekey,
|
||||
],
|
||||
];
|
||||
try {
|
||||
$data = $client->request('POST', 'ImportCertificate', $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
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)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\client\Volcengine;
|
||||
use Exception;
|
||||
|
||||
class huoshan 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 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 ($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)
|
||||
{
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, 'cdn.volcengineapi.com', 'cdn', '2021-03-01', 'cn-north-1', $this->proxy);
|
||||
$param = [
|
||||
'CertId' => $cert_id,
|
||||
'Domain' => $config['domain'],
|
||||
];
|
||||
$data = $client->request('POST', 'BatchDeployCert', $param);
|
||||
if (empty($data['DeployResult'])) throw new Exception('部署证书失败:DeployResult为空');
|
||||
foreach ($data['DeployResult'] as $row) {
|
||||
if ($row['Status'] == 'success') {
|
||||
$this->log('CDN域名 ' . $row['Domain'] . ' 部署证书成功!');
|
||||
} else {
|
||||
$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, 'open.volcengineapi.com', 'certificate_service', '2024-10-01', 'cn-beijing', $this->proxy);
|
||||
$param = [
|
||||
'Tag' => $cert_name,
|
||||
'Repeatable' => false,
|
||||
'CertificateInfo' => [
|
||||
'CertificateChain' => $fullchain,
|
||||
'PrivateKey' => $privatekey,
|
||||
],
|
||||
];
|
||||
try {
|
||||
$data = $client->request('POST', 'ImportCertificate', $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
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)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
202
app/lib/deploy/k8s.php
Normal file
202
app/lib/deploy/k8s.php
Normal file
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Exception;
|
||||
|
||||
class k8s implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $kubeconfig;
|
||||
private $server;
|
||||
private $bearerToken;
|
||||
private $tls = [];
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->kubeconfig = $config['kubeconfig'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->kubeconfig)) throw new Exception('Kubeconfig不能为空');
|
||||
$this->verify();
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$namespace = $config['namespace'];
|
||||
$secretName = $config['secret_name'];
|
||||
if (empty($namespace)) throw new Exception('命名空间不能为空');
|
||||
if (empty($secretName)) throw new Exception('Secret名称不能为空');
|
||||
|
||||
$this->parse();
|
||||
|
||||
$secretPayload = [
|
||||
'apiVersion' => 'v1',
|
||||
'kind' => 'Secret',
|
||||
'metadata' => ['name' => $secretName, 'namespace' => $namespace],
|
||||
'type' => 'kubernetes.io/tls',
|
||||
'data' => [
|
||||
'tls.crt' => base64_encode($config['fullchain']),
|
||||
'tls.key' => base64_encode($config['privatekey']),
|
||||
],
|
||||
];
|
||||
|
||||
$secretUrl = '/api/v1/namespaces/' . $namespace . '/secrets/' . $secretName;
|
||||
list($sCode, $sBody, $sErr) = $this->k8s_request('GET', $secretUrl);
|
||||
|
||||
if ($sCode === 404) {
|
||||
$createUrl = '/api/v1/namespaces/' . $namespace . '/secrets';
|
||||
$this->log('Secret:' . $secretName . ' 不存在,正在创建...');
|
||||
list($cCode, $cBody, $cErr) = $this->k8s_request('POST', $createUrl, json_encode($secretPayload));
|
||||
if ($cCode < 200 || $cCode >= 300) throw new Exception("创建Secret失败 (HTTP $cCode): $cBody | $cErr");
|
||||
$this->log('Secret:' . $namespace . ' 创建成功');
|
||||
} elseif ($sCode >= 200 && $sCode < 300) {
|
||||
$this->log('Secret:' . $secretName . ' 已存在,正在更新...');
|
||||
$patch = ['data' => $secretPayload['data'], 'type' => 'kubernetes.io/tls'];
|
||||
list($pCode, $pBody, $pErr) = $this->k8s_request('PATCH', $secretUrl, json_encode($patch));
|
||||
if ($pCode < 200 || $pCode >= 300) throw new Exception("更新Secret失败 (HTTP $pCode): $pBody | $pErr");
|
||||
$this->log('Secret:' . $secretName . ' 更新成功');
|
||||
} else {
|
||||
throw new Exception("获取Secret失败 (HTTP $sCode): $sBody | $sErr");
|
||||
}
|
||||
|
||||
// Bind Secret to specified Ingresses (merge spec.tls & hosts) ----
|
||||
if (!empty($config['ingresses'])) {
|
||||
$ingressUrl = '/apis/networking.k8s.io/v1/namespaces/' . $namespace . '/ingresses';
|
||||
foreach (explode(',', $config['ingresses']) as $ingName) {
|
||||
list($gCode, $gBody, $gErr) = $this->k8s_request('GET', $ingressUrl . '/' . $ingName);
|
||||
if ($gCode < 200 || $gCode >= 300) throw new Exception("获取Ingress '$ingName' 失败 (HTTP $gCode): $gBody | $gErr");
|
||||
$ing = json_decode($gBody, true);
|
||||
if (!$ing) throw new Exception("解析Ingress '$ingName' JSON失败: $gBody");
|
||||
|
||||
// collect hosts from spec.rules
|
||||
$hosts = [];
|
||||
foreach (($ing['spec']['rules'] ?? []) as $rule) {
|
||||
if (!empty($rule['host'])) $hosts[] = $rule['host'];
|
||||
}
|
||||
$hosts = array_values(array_unique($hosts));
|
||||
|
||||
// merge/ensure spec.tls entry
|
||||
$tls = $ing['spec']['tls'] ?? [];
|
||||
$found = false;
|
||||
foreach ($tls as &$entry) {
|
||||
if (($entry['secretName'] ?? '') === $secretName) {
|
||||
$found = true;
|
||||
$existingHosts = $entry['hosts'] ?? [];
|
||||
$entry['hosts'] = array_values(array_unique(array_merge($existingHosts, $hosts)));
|
||||
}
|
||||
}
|
||||
unset($entry);
|
||||
if (!$found) {
|
||||
$tls[] = ['secretName' => $secretName, 'hosts' => $hosts];
|
||||
}
|
||||
|
||||
$patch = ['spec' => ['tls' => $tls]];
|
||||
list($iCode, $iBody, $iErr) = $this->k8s_request('PATCH', $ingressUrl . '/' . $ingName, json_encode($patch, JSON_UNESCAPED_SLASHES));
|
||||
if ($iCode < 200 || $iCode >= 300) throw new Exception("更新Ingress '$ingName' 失败 (HTTP $iCode): $iBody | $iErr");
|
||||
$this->log("Ingress '$ingName' 更新TLS成功");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function parse()
|
||||
{
|
||||
$kcfg = Yaml::parse($this->kubeconfig);
|
||||
if (!$kcfg) throw new Exception('Kubeconfig格式错误');
|
||||
$curr = $kcfg['current-context'] ?? null;
|
||||
if (!$curr) throw new Exception('Kubeconfig缺少current-context');
|
||||
|
||||
$contexts = $this->index_by_name($kcfg['contexts'] ?? []);
|
||||
$clusters = $this->index_by_name($kcfg['clusters'] ?? []);
|
||||
$users = $this->index_by_name($kcfg['users'] ?? []);
|
||||
|
||||
$ctx = $contexts[$curr] ?? null;
|
||||
if (!$ctx) throw new Exception("Kubeconfig中找不到current-context: $curr");
|
||||
|
||||
$clusterName = $ctx['context']['cluster'] ?? null;
|
||||
$userName = $ctx['context']['user'] ?? null;
|
||||
if (!$clusterName || !$userName) throw new Exception("Kubeconfig中context缺少cluster或user: $curr");
|
||||
|
||||
$cluster = $clusters[$clusterName] ?? null;
|
||||
$user = $users[$userName] ?? null;
|
||||
if (!$cluster) throw new Exception("Kubeconfig中找不到cluster: $clusterName");
|
||||
if (!$user) throw new Exception("Kubeconfig中找不到user: $userName");
|
||||
|
||||
$this->server = $cluster['cluster']['server'] ?? null;
|
||||
if (!$this->server) throw new Exception("Kubeconfig中找不到cluster.server");
|
||||
$this->server = rtrim($this->server, '/');
|
||||
|
||||
$this->bearerToken = $user['user']['token'] ?? ($user['user']['auth-provider']['config']['access-token'] ?? null);
|
||||
$clientCertFile = $clientKeyFile = null;
|
||||
if (!empty($user['user']['client-certificate-data']) && !empty($user['user']['client-key-data'])) {
|
||||
$clientCertFile = tempnam(sys_get_temp_dir(), 'kcc_');
|
||||
$clientKeyFile = tempnam(sys_get_temp_dir(), 'kck_');
|
||||
file_put_contents($clientCertFile, base64_decode($user['user']['client-certificate-data']));
|
||||
file_put_contents($clientKeyFile, base64_decode($user['user']['client-key-data']));
|
||||
} elseif (!empty($user['user']['client-certificate']) && !empty($user['user']['client-key'])) {
|
||||
$clientCertFile = $user['user']['client-certificate'];
|
||||
$clientKeyFile = $user['user']['client-key'];
|
||||
}
|
||||
$this->tls = ['cert' => $clientCertFile, 'key' => $clientKeyFile];
|
||||
}
|
||||
|
||||
private function verify()
|
||||
{
|
||||
$this->parse();
|
||||
list($vCode, $vBody, $vErr) = $this->k8s_request('GET', '/version');
|
||||
if ($vErr) throw new Exception("连接Kubernetes API服务器失败: $vErr");
|
||||
if ($vCode != 200) throw new Exception("连接Kubernetes API服务器失败: HTTP $vCode $vBody");
|
||||
}
|
||||
|
||||
private function k8s_request($method, $path, $body = null)
|
||||
{
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $this->server . $path);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
|
||||
$headers = ['Accept: application/json'];
|
||||
if ($this->bearerToken) $headers[] = 'Authorization: Bearer ' . $this->bearerToken;
|
||||
if ($body !== null) $headers[] = 'Content-Type: application/json';
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
if ($body !== null) curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
if (!empty($this->tls['cert']) && !empty($this->tls['key'])) {
|
||||
curl_setopt($ch, CURLOPT_SSLCERT, $this->tls['cert']);
|
||||
curl_setopt($ch, CURLOPT_SSLKEY, $this->tls['key']);
|
||||
}
|
||||
$resp = curl_exec($ch);
|
||||
$code = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
|
||||
$err = curl_error($ch);
|
||||
curl_close($ch);
|
||||
return [$code, $resp, $err];
|
||||
}
|
||||
|
||||
private function index_by_name($arr)
|
||||
{
|
||||
$out = [];
|
||||
foreach ($arr as $item) {
|
||||
if (isset($item['name'])) $out[$item['name']] = $item;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,208 +1,208 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class kangle implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $auth;
|
||||
private $username;
|
||||
private $password;
|
||||
private $skey;
|
||||
private $proxy;
|
||||
private $cookie;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
$this->auth = $config['auth'];
|
||||
$this->username = $config['username'];
|
||||
$this->password = $config['password'];
|
||||
$this->skey = $config['skey'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->username)) throw new Exception('必填参数不能为空');
|
||||
$this->login();
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$this->login();
|
||||
$this->log('登录成功 cookie:' . $this->cookie);
|
||||
$this->getMain();
|
||||
|
||||
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()
|
||||
{
|
||||
if ($this->auth == '1') {
|
||||
return $this->loginBySkey();
|
||||
} else {
|
||||
return $this->loginByPwd();
|
||||
}
|
||||
}
|
||||
|
||||
private function loginBySkey()
|
||||
{
|
||||
$url = $this->url . '/vhost/index.php?c=sso&a=hello&url=' . urlencode($this->url . '/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->loginBySkey2($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 loginBySkey2($cookie, $sess_key)
|
||||
{
|
||||
$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 = 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 loginByPwd()
|
||||
{
|
||||
$referer = $this->url . '/vhost/index.php?c=session&a=loginForm';
|
||||
$url = $this->url . '/vhost/index.php?c=session&a=login';
|
||||
$post = [
|
||||
'username' => $this->username,
|
||||
'passwd' => $this->password,
|
||||
];
|
||||
$response = http_request($url, http_build_query($post), $referer, null, null, $this->proxy);
|
||||
if ($response['code'] == 302) {
|
||||
$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 . '; ';
|
||||
}
|
||||
$this->cookie = $cookie;
|
||||
return true;
|
||||
} else {
|
||||
throw new Exception('登录失败,获取cookie失败');
|
||||
}
|
||||
} elseif (strpos($response['body'], '验证码错误')) {
|
||||
throw new Exception('登录失败,需输入验证码');
|
||||
} elseif (strpos($response['body'], '密码错误')) {
|
||||
throw new Exception('登录失败,用户名或密码错误');
|
||||
} else {
|
||||
throw new Exception('登录失败 (httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
}
|
||||
|
||||
private function getMain()
|
||||
{
|
||||
$path = '/vhost/';
|
||||
http_request($this->url . $path, null, null, $this->cookie, null, $this->proxy);
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class kangle implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $auth;
|
||||
private $username;
|
||||
private $password;
|
||||
private $skey;
|
||||
private $proxy;
|
||||
private $cookie;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
$this->auth = $config['auth'];
|
||||
$this->username = $config['username'];
|
||||
$this->password = $config['password'];
|
||||
$this->skey = $config['skey'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->username)) throw new Exception('必填参数不能为空');
|
||||
$this->login();
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$this->login();
|
||||
$this->log('登录成功 cookie:' . $this->cookie);
|
||||
$this->getMain();
|
||||
|
||||
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()
|
||||
{
|
||||
if ($this->auth == '1') {
|
||||
return $this->loginBySkey();
|
||||
} else {
|
||||
return $this->loginByPwd();
|
||||
}
|
||||
}
|
||||
|
||||
private function loginBySkey()
|
||||
{
|
||||
$url = $this->url . '/vhost/index.php?c=sso&a=hello&url=' . urlencode($this->url . '/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->loginBySkey2($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 loginBySkey2($cookie, $sess_key)
|
||||
{
|
||||
$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 = 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 loginByPwd()
|
||||
{
|
||||
$referer = $this->url . '/vhost/index.php?c=session&a=loginForm';
|
||||
$url = $this->url . '/vhost/index.php?c=session&a=login';
|
||||
$post = [
|
||||
'username' => $this->username,
|
||||
'passwd' => $this->password,
|
||||
];
|
||||
$response = http_request($url, http_build_query($post), $referer, null, null, $this->proxy);
|
||||
if ($response['code'] == 302) {
|
||||
$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 . '; ';
|
||||
}
|
||||
$this->cookie = $cookie;
|
||||
return true;
|
||||
} else {
|
||||
throw new Exception('登录失败,获取cookie失败');
|
||||
}
|
||||
} elseif (strpos($response['body'], '验证码错误')) {
|
||||
throw new Exception('登录失败,需输入验证码');
|
||||
} elseif (strpos($response['body'], '密码错误')) {
|
||||
throw new Exception('登录失败,用户名或密码错误');
|
||||
} else {
|
||||
throw new Exception('登录失败 (httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
}
|
||||
|
||||
private function getMain()
|
||||
{
|
||||
$path = '/vhost/';
|
||||
http_request($this->url . $path, null, null, $this->cookie, null, $this->proxy);
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,173 +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);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
79
app/lib/deploy/ksyun.php
Normal file
79
app/lib/deploy/ksyun.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\client\Ksyun as KsyunClient;
|
||||
use Exception;
|
||||
|
||||
class ksyun 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 KsyunClient($this->AccessKeyId, $this->SecretAccessKey, 'cdn.api.ksyun.com', 'cdn', 'cn-shanghai-2', $this->proxy);
|
||||
$client->request('GET', 'GetCertificates', '2016-09-01', '/2016-09-01/cert/GetCertificates');
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$this->deploy_cdn($fullchain, $privatekey, $config, $info);
|
||||
}
|
||||
|
||||
public function deploy_cdn($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
$config['cert_name'] = str_replace('*.', '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
$domains = explode(',', $config['domain']);
|
||||
|
||||
$client = new KsyunClient($this->AccessKeyId, $this->SecretAccessKey, 'cdn.api.ksyun.com', 'cdn', 'cn-shanghai-2', $this->proxy);
|
||||
$param = [
|
||||
'PageSize' => 100,
|
||||
'PageNumber' => 1,
|
||||
];
|
||||
$domain_ids = [];
|
||||
$result = $client->request('GET', 'GetCdnDomains', '2019-06-01', '/2019-06-01/domain/GetCdnDomains', $param);
|
||||
foreach ($result['Domains'] as $row) {
|
||||
if (in_array($row['DomainName'], $domains)) {
|
||||
$domain_ids[] = $row['DomainId'];
|
||||
}
|
||||
}
|
||||
if (count($domain_ids) == 0) throw new Exception('未找到对应的CDN域名');
|
||||
$param = [
|
||||
'Enable' => 'on',
|
||||
'DomainIds' => implode(',', $domain_ids),
|
||||
'CertificateName' => $config['cert_name'],
|
||||
'ServerCertificate' => $fullchain,
|
||||
'PrivateKey' => $privatekey,
|
||||
];
|
||||
$result = $client->request('POST', 'ConfigCertificate', '2016-09-01', '/2016-09-01/cert/ConfigCertificate', $param);
|
||||
$this->log('CDN证书部署成功,证书ID:' . $result['CertificateId']);
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,122 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class lecdn implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $email;
|
||||
private $password;
|
||||
private $auth;
|
||||
private $apiKey;
|
||||
private $proxy;
|
||||
private $accessToken;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$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 ($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)
|
||||
{
|
||||
$id = $config['id'];
|
||||
if (empty($id)) throw new Exception('证书ID不能为空');
|
||||
|
||||
if ($this->auth == 0) {
|
||||
$this->login();
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $this->request('/prod-api/certificate/' . $id);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('证书ID:' . $id . '获取失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$params = [
|
||||
'id' => intval($id),
|
||||
'name' => $data['name'],
|
||||
'description' => $data['description'],
|
||||
'type' => 'upload',
|
||||
'ssl_pem' => base64_encode($fullchain),
|
||||
'ssl_key' => base64_encode($privatekey),
|
||||
'auto_renewal' => false,
|
||||
];
|
||||
$this->request('/prod-api/certificate/' . $id, $params, 'PUT');
|
||||
$this->log("证书ID:{$id}更新成功!");
|
||||
}
|
||||
|
||||
private function login()
|
||||
{
|
||||
$path = '/prod-api/login';
|
||||
$params = [
|
||||
'email' => $this->email,
|
||||
'username' => $this->email,
|
||||
'password' => $this->password,
|
||||
];
|
||||
$result = $this->request($path, $params);
|
||||
if (isset($result['token'])) {
|
||||
$this->accessToken = $result['token'];
|
||||
} else {
|
||||
throw new Exception('登录成功,获取access_token失败');
|
||||
}
|
||||
}
|
||||
|
||||
private function request($path, $params = null, $method = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = [];
|
||||
$body = null;
|
||||
if ($this->accessToken) {
|
||||
$headers['Authorization'] = 'Bearer ' . $this->accessToken;
|
||||
} elseif ($this->auth == 1 && $this->apiKey) {
|
||||
$headers['Authorization'] = $this->apiKey;
|
||||
}
|
||||
if ($params) {
|
||||
$headers['Content-Type'] = 'application/json;charset=UTF-8';
|
||||
$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['data'] ?? null;
|
||||
} elseif (isset($result['message'])) {
|
||||
throw new Exception($result['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);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class lecdn implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $email;
|
||||
private $password;
|
||||
private $auth;
|
||||
private $apiKey;
|
||||
private $proxy;
|
||||
private $accessToken;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$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 ($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)
|
||||
{
|
||||
$id = $config['id'];
|
||||
if (empty($id)) throw new Exception('证书ID不能为空');
|
||||
|
||||
if ($this->auth == 0) {
|
||||
$this->login();
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $this->request('/prod-api/certificate/' . $id);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('证书ID:' . $id . '获取失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$params = [
|
||||
'id' => intval($id),
|
||||
'name' => $data['name'],
|
||||
'description' => $data['description'],
|
||||
'type' => 'upload',
|
||||
'ssl_pem' => base64_encode($fullchain),
|
||||
'ssl_key' => base64_encode($privatekey),
|
||||
'auto_renewal' => false,
|
||||
];
|
||||
$this->request('/prod-api/certificate/' . $id, $params, 'PUT');
|
||||
$this->log("证书ID:{$id}更新成功!");
|
||||
}
|
||||
|
||||
private function login()
|
||||
{
|
||||
$path = '/prod-api/login';
|
||||
$params = [
|
||||
'email' => $this->email,
|
||||
'username' => $this->email,
|
||||
'password' => $this->password,
|
||||
];
|
||||
$result = $this->request($path, $params);
|
||||
if (isset($result['token'])) {
|
||||
$this->accessToken = $result['token'];
|
||||
} else {
|
||||
throw new Exception('登录成功,获取access_token失败');
|
||||
}
|
||||
}
|
||||
|
||||
private function request($path, $params = null, $method = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = [];
|
||||
$body = null;
|
||||
if ($this->accessToken) {
|
||||
$headers['Authorization'] = 'Bearer ' . $this->accessToken;
|
||||
} elseif ($this->auth == 1 && $this->apiKey) {
|
||||
$headers['Authorization'] = $this->apiKey;
|
||||
}
|
||||
if ($params) {
|
||||
$headers['Content-Type'] = 'application/json;charset=UTF-8';
|
||||
$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['data'] ?? null;
|
||||
} elseif (isset($result['message'])) {
|
||||
throw new Exception($result['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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,77 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class local implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
|
||||
public function __construct($config) {}
|
||||
|
||||
public function check() {}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (!empty($config['cmd']) && !function_exists('exec')) {
|
||||
throw new Exception('exec函数被禁用');
|
||||
}
|
||||
if ($config['format'] == 'pem') {
|
||||
$dir = dirname($config['pem_cert_file']);
|
||||
if (!is_dir($dir)) throw new Exception($dir . ' 目录不存在');
|
||||
if (!is_writable($dir)) throw new Exception($dir . ' 目录不可写');
|
||||
|
||||
if (file_put_contents($config['pem_cert_file'], $fullchain)) {
|
||||
$this->log('证书已保存到:' . $config['pem_cert_file']);
|
||||
} else {
|
||||
throw new Exception('证书保存到' . $config['pem_cert_file'] . '失败,请检查目录权限');
|
||||
}
|
||||
if (file_put_contents($config['pem_key_file'], $privatekey)) {
|
||||
$this->log('私钥已保存到:' . $config['pem_key_file']);
|
||||
} else {
|
||||
throw new Exception('私钥保存到' . $config['pem_key_file'] . '失败,请检查目录权限');
|
||||
}
|
||||
} elseif ($config['format'] == 'pfx') {
|
||||
$dir = dirname($config['pfx_file']);
|
||||
if (!is_dir($dir)) throw new Exception($dir . ' 目录不存在');
|
||||
if (!is_writable($dir)) throw new Exception($dir . ' 目录不可写');
|
||||
|
||||
$pfx = \app\lib\CertHelper::getPfx($fullchain, $privatekey, $config['pfx_pass'] ? $config['pfx_pass'] : null);
|
||||
if (file_put_contents($config['pfx_file'], $pfx)) {
|
||||
$this->log('PFX证书已保存到:' . $config['pfx_file']);
|
||||
} else {
|
||||
throw new Exception('PFX证书保存到' . $config['pfx_file'] . '失败,请检查目录权限');
|
||||
}
|
||||
}
|
||||
if (!empty($config['cmd'])) {
|
||||
$cmds = explode("\n", $config['cmd']);
|
||||
foreach ($cmds as $cmd) {
|
||||
$cmd = trim($cmd);
|
||||
if (empty($cmd)) continue;
|
||||
$this->log('执行命令:' . $cmd);
|
||||
$output = [];
|
||||
$ret = 0;
|
||||
exec($cmd, $output, $ret);
|
||||
if ($ret == 0) {
|
||||
$this->log('执行命令成功:' . implode("\n", $output));
|
||||
} else {
|
||||
throw new Exception('执行命令失败:' . implode("\n", $output));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class local implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
|
||||
public function __construct($config) {}
|
||||
|
||||
public function check() {}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (!empty($config['cmd']) && !function_exists('exec')) {
|
||||
throw new Exception('exec函数被禁用');
|
||||
}
|
||||
if ($config['format'] == 'pem') {
|
||||
$dir = dirname($config['pem_cert_file']);
|
||||
if (!is_dir($dir)) throw new Exception($dir . ' 目录不存在');
|
||||
if (!is_writable($dir)) throw new Exception($dir . ' 目录不可写');
|
||||
|
||||
if (file_put_contents($config['pem_cert_file'], $fullchain)) {
|
||||
$this->log('证书已保存到:' . $config['pem_cert_file']);
|
||||
} else {
|
||||
throw new Exception('证书保存到' . $config['pem_cert_file'] . '失败,请检查目录权限');
|
||||
}
|
||||
if (file_put_contents($config['pem_key_file'], $privatekey)) {
|
||||
$this->log('私钥已保存到:' . $config['pem_key_file']);
|
||||
} else {
|
||||
throw new Exception('私钥保存到' . $config['pem_key_file'] . '失败,请检查目录权限');
|
||||
}
|
||||
} elseif ($config['format'] == 'pfx') {
|
||||
$dir = dirname($config['pfx_file']);
|
||||
if (!is_dir($dir)) throw new Exception($dir . ' 目录不存在');
|
||||
if (!is_writable($dir)) throw new Exception($dir . ' 目录不可写');
|
||||
|
||||
$pfx = \app\lib\CertHelper::getPfx($fullchain, $privatekey, $config['pfx_pass'] ? $config['pfx_pass'] : null);
|
||||
if (file_put_contents($config['pfx_file'], $pfx)) {
|
||||
$this->log('PFX证书已保存到:' . $config['pfx_file']);
|
||||
} else {
|
||||
throw new Exception('PFX证书保存到' . $config['pfx_file'] . '失败,请检查目录权限');
|
||||
}
|
||||
}
|
||||
if (!empty($config['cmd'])) {
|
||||
$cmds = explode("\n", $config['cmd']);
|
||||
foreach ($cmds as $cmd) {
|
||||
$cmd = trim($cmd);
|
||||
if (empty($cmd)) continue;
|
||||
$this->log('执行命令:' . $cmd);
|
||||
$output = [];
|
||||
$ret = 0;
|
||||
exec($cmd, $output, $ret);
|
||||
if ($ret == 0) {
|
||||
$this->log('执行命令成功:' . implode("\n", $output));
|
||||
} else {
|
||||
throw new Exception('执行命令失败:' . implode("\n", $output));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,114 +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'] . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
<?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'] . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,127 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class mwpanel implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $appid;
|
||||
private $appsecret;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
$this->appid = $config['appid'];
|
||||
$this->appsecret = $config['appsecret'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->appid) || empty($this->appsecret)) throw new Exception('请填写面板地址和接口密钥');
|
||||
|
||||
$path = '/task/count';
|
||||
$response = $this->request($path);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['status']) && $result['status'] == true) {
|
||||
return true;
|
||||
} else {
|
||||
throw new Exception(isset($result['msg']) ? $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) {
|
||||
$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 deployPanel($fullchain, $privatekey)
|
||||
{
|
||||
$path = '/setting/save_panel_ssl';
|
||||
$data = [
|
||||
'privateKey' => $privatekey,
|
||||
'certPem' => $fullchain,
|
||||
'choose' => 'local',
|
||||
];
|
||||
$response = $this->request($path, $data);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['status']) && $result['status']) {
|
||||
return true;
|
||||
} elseif (isset($result['msg'])) {
|
||||
throw new Exception($result['msg']);
|
||||
} else {
|
||||
throw new Exception($response ? $response : '返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
private function deploySite($siteName, $fullchain, $privatekey)
|
||||
{
|
||||
$path = '/site/set_ssl';
|
||||
$data = [
|
||||
'type' => '1',
|
||||
'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 : '返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
|
||||
$headers = [
|
||||
'app-id' => $this->appid,
|
||||
'app-secret' => $this->appsecret,
|
||||
];
|
||||
$response = http_request($url, $params ? http_build_query($params) : null, null, null, $headers, $this->proxy);
|
||||
return $response['body'];
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class mwpanel implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $appid;
|
||||
private $appsecret;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
$this->appid = $config['appid'];
|
||||
$this->appsecret = $config['appsecret'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->appid) || empty($this->appsecret)) throw new Exception('请填写面板地址和接口密钥');
|
||||
|
||||
$path = '/task/count';
|
||||
$response = $this->request($path);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['status']) && $result['status'] == true) {
|
||||
return true;
|
||||
} else {
|
||||
throw new Exception(isset($result['msg']) ? $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) {
|
||||
$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 deployPanel($fullchain, $privatekey)
|
||||
{
|
||||
$path = '/setting/save_panel_ssl';
|
||||
$data = [
|
||||
'privateKey' => $privatekey,
|
||||
'certPem' => $fullchain,
|
||||
'choose' => 'local',
|
||||
];
|
||||
$response = $this->request($path, $data);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['status']) && $result['status']) {
|
||||
return true;
|
||||
} elseif (isset($result['msg'])) {
|
||||
throw new Exception($result['msg']);
|
||||
} else {
|
||||
throw new Exception($response ? $response : '返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
private function deploySite($siteName, $fullchain, $privatekey)
|
||||
{
|
||||
$path = '/site/set_ssl';
|
||||
$data = [
|
||||
'type' => '1',
|
||||
'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 : '返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
|
||||
$headers = [
|
||||
'app-id' => $this->appid,
|
||||
'app-secret' => $this->appsecret,
|
||||
];
|
||||
$response = http_request($url, $params ? http_build_query($params) : null, null, null, $headers, $this->proxy);
|
||||
return $response['body'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,114 +1,162 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class opanel implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $key;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/') . '/api/' . (isset($config['version']) ? $config['version'] : 'v1');
|
||||
$this->key = $config['key'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->key)) throw new Exception('请填写面板地址和接口密钥');
|
||||
$this->request("/settings/search");
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$domains = $config['domainList'];
|
||||
if (empty($domains)) throw new Exception('没有设置要部署的域名');
|
||||
|
||||
$params = ['page' => 1, 'pageSize' => 500];
|
||||
try {
|
||||
$data = $this->request("/websites/ssl/search", $params);
|
||||
$this->log('获取证书列表成功(total=' . $data['total'] . ')');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取证书列表失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$success = 0;
|
||||
$errmsg = null;
|
||||
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('/websites/ssl/upload', $params);
|
||||
$this->log("证书ID:{$row['id']}更新成功!");
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
$errmsg = $e->getMessage();
|
||||
$this->log("证书ID:{$row['id']}更新失败:" . $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)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
|
||||
$timestamp = time() . '';
|
||||
$token = md5('1panel' . $this->key . $timestamp);
|
||||
$headers = [
|
||||
'1Panel-Token' => $token,
|
||||
'1Panel-Timestamp' => $timestamp
|
||||
];
|
||||
$body = $params ? json_encode($params) : '{}';
|
||||
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 $result['data'] ?? null;
|
||||
} elseif (isset($result['message'])) {
|
||||
throw new Exception($result['message']);
|
||||
} else {
|
||||
throw new Exception('请求失败(httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class opanel implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $key;
|
||||
private $proxy;
|
||||
private $nodeName;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/') . '/api/' . (isset($config['version']) ? $config['version'] : 'v1');
|
||||
$this->key = $config['key'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->key)) throw new Exception('请填写面板地址和接口密钥');
|
||||
$this->request("/settings/search");
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (isset($config['type']) && $config['type'] == '3') {
|
||||
$params = [
|
||||
'cert' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
'ssl' => 'Enable',
|
||||
'sslID' => null,
|
||||
'sslType' => 'import-paste',
|
||||
];
|
||||
try {
|
||||
$this->request('/core/settings/ssl/update', $params);
|
||||
$this->log("面板证书更新成功!");
|
||||
return;
|
||||
} catch (Exception $e) {
|
||||
throw new Exception("面板证书更新失败:" . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($config['node_name'])) $this->nodeName = $config['node_name'];
|
||||
|
||||
if (!empty($config['id'])) {
|
||||
$params = [
|
||||
'sslID' => intval($config['id']),
|
||||
'type' => 'paste',
|
||||
'certificate' => $fullchain,
|
||||
'privateKey' => $privatekey,
|
||||
'description' => '',
|
||||
];
|
||||
try {
|
||||
$this->request('/websites/ssl/upload', $params);
|
||||
$this->log("证书ID:{$config['id']}更新成功!");
|
||||
return;
|
||||
} catch (Exception $e) {
|
||||
throw new Exception("证书ID:{$config['id']}更新失败:" . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$domains = $config['domainList'];
|
||||
if (empty($domains)) throw new Exception('没有设置要部署的域名');
|
||||
|
||||
$params = ['page' => 1, 'pageSize' => 500];
|
||||
try {
|
||||
$data = $this->request("/websites/ssl/search", $params);
|
||||
$this->log('获取证书列表成功(total=' . $data['total'] . ')');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取证书列表失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$success = 0;
|
||||
$errmsg = null;
|
||||
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('/websites/ssl/upload', $params);
|
||||
$this->log("证书ID:{$row['id']}更新成功!");
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
$errmsg = $e->getMessage();
|
||||
$this->log("证书ID:{$row['id']}更新失败:" . $errmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($success == 0) {
|
||||
$params = [
|
||||
'sslID' => 0,
|
||||
'type' => 'paste',
|
||||
'certificate' => $fullchain,
|
||||
'privateKey' => $privatekey,
|
||||
'description' => '',
|
||||
];
|
||||
$this->request('/websites/ssl/upload', $params);
|
||||
$this->log("证书上传成功!");
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
|
||||
$timestamp = time() . '';
|
||||
$token = md5('1panel' . $this->key . $timestamp);
|
||||
$headers = [
|
||||
'1Panel-Token' => $token,
|
||||
'1Panel-Timestamp' => $timestamp,
|
||||
];
|
||||
if (!empty($this->nodeName)) {
|
||||
$headers['CurrentNode'] = $this->nodeName;
|
||||
}
|
||||
$body = $params ? json_encode($params) : '{}';
|
||||
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 $result['data'] ?? null;
|
||||
} elseif (isset($result['message'])) {
|
||||
throw new Exception($result['message']);
|
||||
} else {
|
||||
throw new Exception('请求失败(httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,93 +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);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,159 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\client\Qiniu as QiniuClient;
|
||||
use Exception;
|
||||
|
||||
class qiniu implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $AccessKey;
|
||||
private $SecretKey;
|
||||
private QiniuClient $client;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKey = $config['AccessKey'];
|
||||
$this->SecretKey = $config['SecretKey'];
|
||||
$this->client = new QiniuClient($this->AccessKey, $this->SecretKey, isset($config['proxy']) ? $config['proxy'] == 1 : false);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->AccessKey) || empty($this->SecretKey)) throw new Exception('必填参数不能为空');
|
||||
$this->client->request('GET', '/sslcert');
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$domains = $config['domain'];
|
||||
if (empty($domains)) 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'];
|
||||
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey, $certInfo['subject']['CN'], $cert_name);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private function deploy_cdn($domain, $cert_id)
|
||||
{
|
||||
try {
|
||||
$data = $this->client->request('GET', '/domain/' . $domain);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取域名信息失败:' . $e->getMessage());
|
||||
}
|
||||
if (isset($data['https']['certId']) && $data['https']['certId'] == $cert_id) {
|
||||
$this->log('域名 ' . $domain . ' 证书已部署,无需重复操作');
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($data['https']['certId'])) {
|
||||
$param = [
|
||||
'certid' => $cert_id,
|
||||
];
|
||||
$this->client->request('PUT', '/domain/' . $domain . '/sslize', null, $param);
|
||||
} else {
|
||||
$param = [
|
||||
'certid' => $cert_id,
|
||||
'forceHttps' => $data['https']['forceHttps'],
|
||||
'http2Enable' => $data['https']['http2Enable'],
|
||||
];
|
||||
$this->client->request('PUT', '/domain/' . $domain . '/httpsconf', null, $param);
|
||||
}
|
||||
$this->log('CDN域名 ' . $domain . ' 证书部署成功!');
|
||||
}
|
||||
|
||||
private function deploy_oss($domain, $cert_id)
|
||||
{
|
||||
$param = [
|
||||
'certid' => $cert_id,
|
||||
'domain' => $domain,
|
||||
];
|
||||
$this->client->request('POST', '/cert/bind', null, $param);
|
||||
$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;
|
||||
$marker = '';
|
||||
do {
|
||||
$query = ['marker' => $marker, 'limit' => 100];
|
||||
$data = $this->client->request('GET', '/sslcert', $query);
|
||||
if (empty($data['certs'])) break;
|
||||
$marker = $data['marker'];
|
||||
foreach ($data['certs'] as $cert) {
|
||||
if ($cert_name == $cert['name']) {
|
||||
$cert_id = $cert['certid'];
|
||||
$this->log('证书' . $cert_name . '已存在,证书ID:' . $cert_id);
|
||||
} elseif ($cert['not_after'] < time()) {
|
||||
try {
|
||||
$this->client->request('DELETE', '/sslcert/' . $cert['certid']);
|
||||
$this->log('证书' . $cert['name'] . '已过期,删除证书成功');
|
||||
} catch (Exception $e) {
|
||||
$this->log('证书' . $cert['name'] . '已过期,删除证书失败:' . $e->getMessage());
|
||||
}
|
||||
usleep(300000);
|
||||
}
|
||||
}
|
||||
} while ($marker != '');
|
||||
|
||||
if (!$cert_id) {
|
||||
$param = [
|
||||
'name' => $cert_name,
|
||||
'common_name' => $common_name,
|
||||
'pri' => $privatekey,
|
||||
'ca' => $fullchain,
|
||||
];
|
||||
try {
|
||||
$data = $this->client->request('POST', '/sslcert', null, $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
$this->log('上传证书成功,证书ID:' . $data['certID']);
|
||||
$cert_id = $data['certID'];
|
||||
usleep(500000);
|
||||
}
|
||||
return $cert_id;
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\client\Qiniu as QiniuClient;
|
||||
use Exception;
|
||||
|
||||
class qiniu implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $AccessKey;
|
||||
private $SecretKey;
|
||||
private QiniuClient $client;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKey = $config['AccessKey'];
|
||||
$this->SecretKey = $config['SecretKey'];
|
||||
$this->client = new QiniuClient($this->AccessKey, $this->SecretKey, isset($config['proxy']) ? $config['proxy'] == 1 : false);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->AccessKey) || empty($this->SecretKey)) throw new Exception('必填参数不能为空');
|
||||
$this->client->request('GET', '/sslcert');
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$domains = $config['domain'];
|
||||
if (empty($domains)) 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'];
|
||||
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey, $certInfo['subject']['CN'], $cert_name);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private function deploy_cdn($domain, $cert_id)
|
||||
{
|
||||
try {
|
||||
$data = $this->client->request('GET', '/domain/' . $domain);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取域名信息失败:' . $e->getMessage());
|
||||
}
|
||||
if (isset($data['https']['certId']) && $data['https']['certId'] == $cert_id) {
|
||||
$this->log('域名 ' . $domain . ' 证书已部署,无需重复操作');
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($data['https']['certId'])) {
|
||||
$param = [
|
||||
'certid' => $cert_id,
|
||||
];
|
||||
$this->client->request('PUT', '/domain/' . $domain . '/sslize', null, $param);
|
||||
} else {
|
||||
$param = [
|
||||
'certid' => $cert_id,
|
||||
'forceHttps' => $data['https']['forceHttps'],
|
||||
'http2Enable' => $data['https']['http2Enable'],
|
||||
];
|
||||
$this->client->request('PUT', '/domain/' . $domain . '/httpsconf', null, $param);
|
||||
}
|
||||
$this->log('CDN域名 ' . $domain . ' 证书部署成功!');
|
||||
}
|
||||
|
||||
private function deploy_oss($domain, $cert_id)
|
||||
{
|
||||
$param = [
|
||||
'certid' => $cert_id,
|
||||
'domain' => $domain,
|
||||
];
|
||||
$this->client->request('POST', '/cert/bind', null, $param);
|
||||
$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;
|
||||
$marker = '';
|
||||
do {
|
||||
$query = ['marker' => $marker, 'limit' => 100];
|
||||
$data = $this->client->request('GET', '/sslcert', $query);
|
||||
if (empty($data['certs'])) break;
|
||||
$marker = $data['marker'];
|
||||
foreach ($data['certs'] as $cert) {
|
||||
if ($cert_name == $cert['name']) {
|
||||
$cert_id = $cert['certid'];
|
||||
$this->log('证书' . $cert_name . '已存在,证书ID:' . $cert_id);
|
||||
} elseif ($cert['not_after'] < time()) {
|
||||
try {
|
||||
$this->client->request('DELETE', '/sslcert/' . $cert['certid']);
|
||||
$this->log('证书' . $cert['name'] . '已过期,删除证书成功');
|
||||
} catch (Exception $e) {
|
||||
$this->log('证书' . $cert['name'] . '已过期,删除证书失败:' . $e->getMessage());
|
||||
}
|
||||
usleep(300000);
|
||||
}
|
||||
}
|
||||
} while ($marker != '');
|
||||
|
||||
if (!$cert_id) {
|
||||
$param = [
|
||||
'name' => $cert_name,
|
||||
'common_name' => $common_name,
|
||||
'pri' => $privatekey,
|
||||
'ca' => $fullchain,
|
||||
];
|
||||
try {
|
||||
$data = $this->client->request('POST', '/sslcert', null, $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
$this->log('上传证书成功,证书ID:' . $data['certID']);
|
||||
$cert_id = $data['certID'];
|
||||
usleep(500000);
|
||||
}
|
||||
return $cert_id;
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,78 +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);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,112 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class safeline implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $token;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
$this->token = $config['token'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->token)) throw new Exception('请填写控制台地址和API Token');
|
||||
$this->request('/api/open/system');
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$domains = $config['domainList'];
|
||||
if (empty($domains)) throw new Exception('没有设置要部署的域名');
|
||||
|
||||
try {
|
||||
$data = $this->request('/api/open/cert');
|
||||
$this->log('获取证书列表成功(total=' . $data['total'] . ')');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取证书列表失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$success = 0;
|
||||
$errmsg = null;
|
||||
foreach ($data['nodes'] as $row) {
|
||||
if (empty($row['domains'])) continue;
|
||||
$flag = false;
|
||||
foreach ($row['domains'] as $domain) {
|
||||
if (in_array($domain, $domains) || in_array('*' . substr($domain, strpos($domain, '.')), $domains)) {
|
||||
$flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($flag) {
|
||||
$params = [
|
||||
'id' => $row['id'],
|
||||
'manual' => [
|
||||
'crt' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
],
|
||||
'type' => 2,
|
||||
];
|
||||
try {
|
||||
$this->request('/api/open/cert', $params);
|
||||
$this->log("证书ID:{$row['id']}更新成功!");
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
$errmsg = $e->getMessage();
|
||||
$this->log("证书ID:{$row['id']}更新失败:" . $errmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($success == 0) {
|
||||
$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];
|
||||
$body = null;
|
||||
if ($params) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
$body = json_encode($params);
|
||||
}
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if ($response['code'] == 200 && $result) {
|
||||
return $result['data'] ?? null;
|
||||
} else {
|
||||
throw new Exception(!empty($result['msg']) ? $result['msg'] : '请求失败(httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class safeline implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $token;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
$this->token = $config['token'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->token)) throw new Exception('请填写控制台地址和API Token');
|
||||
$this->request('/api/open/system');
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$domains = $config['domainList'];
|
||||
if (empty($domains)) throw new Exception('没有设置要部署的域名');
|
||||
|
||||
try {
|
||||
$data = $this->request('/api/open/cert');
|
||||
$this->log('获取证书列表成功(total=' . $data['total'] . ')');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取证书列表失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
$success = 0;
|
||||
$errmsg = null;
|
||||
foreach ($data['nodes'] as $row) {
|
||||
if (empty($row['domains'])) continue;
|
||||
$flag = false;
|
||||
foreach ($row['domains'] as $domain) {
|
||||
if (in_array($domain, $domains) || in_array('*' . substr($domain, strpos($domain, '.')), $domains)) {
|
||||
$flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($flag) {
|
||||
$params = [
|
||||
'id' => $row['id'],
|
||||
'manual' => [
|
||||
'crt' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
],
|
||||
'type' => 2,
|
||||
];
|
||||
try {
|
||||
$this->request('/api/open/cert', $params);
|
||||
$this->log("证书ID:{$row['id']}更新成功!");
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
$errmsg = $e->getMessage();
|
||||
$this->log("证书ID:{$row['id']}更新失败:" . $errmsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($success == 0) {
|
||||
$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];
|
||||
$body = null;
|
||||
if ($params) {
|
||||
$headers['Content-Type'] = 'application/json';
|
||||
$body = json_encode($params);
|
||||
}
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy);
|
||||
$result = json_decode($response['body'], true);
|
||||
if ($response['code'] == 200 && $result) {
|
||||
return $result['data'] ?? null;
|
||||
} else {
|
||||
throw new Exception(!empty($result['msg']) ? $result['msg'] : '请求失败(httpCode=' . $response['code'] . ')');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,213 +1,216 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class ssh implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $config;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
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');
|
||||
if (!$stream) {
|
||||
throw new Exception("无法创建证书文件:{$config['pem_cert_file']}");
|
||||
}
|
||||
fwrite($stream, $fullchain);
|
||||
fclose($stream);
|
||||
$this->log('证书已保存到:' . $config['pem_cert_file']);
|
||||
|
||||
$stream = fopen("ssh2.sftp://$sftp{$config['pem_key_file']}", 'w');
|
||||
if (!$stream) {
|
||||
throw new Exception("无法创建私钥文件:{$config['pem_key_file']}");
|
||||
}
|
||||
fwrite($stream, $privatekey);
|
||||
fclose($stream);
|
||||
$this->log('私钥已保存到:' . $config['pem_key_file']);
|
||||
} elseif ($config['format'] == 'pfx') {
|
||||
$pfx = \app\lib\CertHelper::getPfx($fullchain, $privatekey, $config['pfx_pass'] ? $config['pfx_pass'] : null);
|
||||
|
||||
$stream = fopen("ssh2.sftp://$sftp{$config['pfx_file']}", 'w');
|
||||
if (!$stream) {
|
||||
throw new Exception("无法创建PFX证书文件:{$config['pfx_file']}");
|
||||
}
|
||||
fwrite($stream, $pfx);
|
||||
fclose($stream);
|
||||
$this->log('PFX证书已保存到:' . $config['pfx_file']);
|
||||
|
||||
if ($config['uptype'] == '1' && !empty($config['iis_domain'])) {
|
||||
$cert_hash = openssl_x509_fingerprint($fullchain, 'sha1');
|
||||
$this->deploy_iis($connection, $config['iis_domain'], $config['pfx_file'], $config['pfx_pass'], $cert_hash);
|
||||
$config['cmd'] = null;
|
||||
}
|
||||
}
|
||||
if (!empty($config['cmd'])) {
|
||||
$cmds = explode("\n", $config['cmd']);
|
||||
foreach ($cmds as $cmd) {
|
||||
$cmd = trim($cmd);
|
||||
if (empty($cmd)) continue;
|
||||
$this->exec($connection, $cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_iis($connection, $domain, $pfx_file, $pfx_pass, $cert_hash)
|
||||
{
|
||||
if (!strpos($domain, ':')) {
|
||||
$domain .= ':443';
|
||||
}
|
||||
$ret = $this->exec($connection, 'netsh http show sslcert hostnameport=' . $domain);
|
||||
if (preg_match('/:\s+(\w{40})/', $ret, $match)) {
|
||||
if ($match[1] == $cert_hash) {
|
||||
$this->log('IIS域名 ' . $domain . ' 证书已存在,无需更新');
|
||||
return;
|
||||
}
|
||||
}
|
||||
$p = '-p ""';
|
||||
if (!empty($pfx_pass)) $p = '-p ' . $pfx_pass;
|
||||
if (substr($pfx_file, 0, 1) == '/') $pfx_file = substr($pfx_file, 1);
|
||||
$this->exec($connection, 'certutil ' . $p . ' -importPFX ' . $pfx_file);
|
||||
$this->exec($connection, 'netsh http delete sslcert hostnameport=' . $domain);
|
||||
$this->exec($connection, 'netsh http add sslcert hostnameport=' . $domain . ' certhash=' . $cert_hash . ' certstorename=MY appid=\'{' . $this->uuid() . '}\'');
|
||||
$this->log('IIS域名 ' . $domain . ' 证书已更新');
|
||||
}
|
||||
|
||||
private function uuid()
|
||||
{
|
||||
$guid = md5(uniqid(mt_rand(), true));
|
||||
return substr($guid, 0, 8) . '-' . substr($guid, 8, 4) . '-4' . substr($guid, 12, 3) . '-' . substr($guid, 16, 4) . '-' . substr($guid, 20, 12);
|
||||
}
|
||||
|
||||
private function exec($connection, $cmd)
|
||||
{
|
||||
$this->log('执行命令:' . $cmd);
|
||||
$stream = ssh2_exec($connection, $cmd);
|
||||
$errorStream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
|
||||
if (!$stream || !$errorStream) {
|
||||
throw new Exception('执行命令失败');
|
||||
}
|
||||
stream_set_blocking($stream, true);
|
||||
stream_set_blocking($errorStream, true);
|
||||
$output = stream_get_contents($stream);
|
||||
$errorOutput = stream_get_contents($errorStream);
|
||||
fclose($stream);
|
||||
fclose($errorStream);
|
||||
if (trim($errorOutput)) {
|
||||
if ($this->config['windows'] == '1' && $this->containsGBKChinese($errorOutput)) {
|
||||
$errorOutput = mb_convert_encoding($errorOutput, 'UTF-8', 'GBK');
|
||||
}
|
||||
throw new Exception('执行命令失败:' . trim($errorOutput));
|
||||
} else {
|
||||
if ($this->config['windows'] == '1' && $this->containsGBKChinese($output)) {
|
||||
$output = mb_convert_encoding($output, 'UTF-8', 'GBK');
|
||||
}
|
||||
$this->log('执行命令成功:' . trim($output));
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
private function connect()
|
||||
{
|
||||
if (!function_exists('ssh2_connect')) {
|
||||
throw new Exception('ssh2扩展未安装');
|
||||
}
|
||||
if (empty($this->config['host']) || empty($this->config['port']) || empty($this->config['username']) || $this->config['auth'] == '0' && empty($this->config['password']) || $this->config['auth'] == '1' && empty($this->config['privatekey'])) {
|
||||
throw new Exception('必填参数不能为空');
|
||||
}
|
||||
if (!filter_var($this->config['host'], FILTER_VALIDATE_IP) && !filter_var($this->config['host'], FILTER_VALIDATE_DOMAIN)) {
|
||||
throw new Exception('主机地址不合法');
|
||||
}
|
||||
if (!is_numeric($this->config['port']) || $this->config['port'] < 1 || $this->config['port'] > 65535) {
|
||||
throw new Exception('端口不合法');
|
||||
}
|
||||
|
||||
$connection = ssh2_connect($this->config['host'], intval($this->config['port']));
|
||||
if (!$connection) {
|
||||
throw new Exception('SSH连接失败');
|
||||
}
|
||||
if ($this->config['auth'] == '1') {
|
||||
$publicKey = $this->getPublicKey($this->config['privatekey']);
|
||||
$publicKeyPath = app()->getRuntimePath() . $this->config['host'] . '.pub';
|
||||
$privateKeyPath = app()->getRuntimePath() . $this->config['host'] . '.key';
|
||||
$umask = umask(0066);
|
||||
file_put_contents($privateKeyPath, $this->config['privatekey']);
|
||||
file_put_contents($publicKeyPath, $publicKey);
|
||||
umask($umask);
|
||||
if (!ssh2_auth_pubkey_file($connection, $this->config['username'], $publicKeyPath, $privateKeyPath)) {
|
||||
throw new Exception('私钥认证失败');
|
||||
}
|
||||
} else {
|
||||
if (!ssh2_auth_password($connection, $this->config['username'], $this->config['password'])) {
|
||||
throw new Exception('用户名或密码错误');
|
||||
}
|
||||
}
|
||||
return $connection;
|
||||
}
|
||||
|
||||
private function getPublicKey($privateKey)
|
||||
{
|
||||
$res = openssl_pkey_get_private($privateKey);
|
||||
if (!$res) {
|
||||
throw new Exception('加载私钥失败');
|
||||
}
|
||||
$details = openssl_pkey_get_details($res);
|
||||
if (!$details || !isset($details['key'])) {
|
||||
throw new Exception('从私钥导出公钥失败');
|
||||
}
|
||||
$buffer = pack("N", 7) . "ssh-rsa" .
|
||||
$this->sshEncodeBuffer($details['rsa']['e']) .
|
||||
$this->sshEncodeBuffer($details['rsa']['n']);
|
||||
return "ssh-rsa " . base64_encode($buffer);
|
||||
}
|
||||
|
||||
private function sshEncodeBuffer($buffer)
|
||||
{
|
||||
$len = strlen($buffer);
|
||||
if (ord($buffer[0]) & 0x80) {
|
||||
$len++;
|
||||
$buffer = "\x00" . $buffer;
|
||||
}
|
||||
return pack("Na*", $len, $buffer);
|
||||
}
|
||||
|
||||
private function containsGBKChinese($string)
|
||||
{
|
||||
return preg_match('/[\x81-\xFE][\x40-\xFE]/', $string) === 1;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\CertHelper;
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class ssh implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $config;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
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');
|
||||
if (!$stream) {
|
||||
throw new Exception("无法创建证书文件:{$config['pem_cert_file']}");
|
||||
}
|
||||
fwrite($stream, $fullchain);
|
||||
fclose($stream);
|
||||
$this->log('证书已保存到:' . $config['pem_cert_file']);
|
||||
|
||||
$stream = fopen("ssh2.sftp://$sftp{$config['pem_key_file']}", 'w');
|
||||
if (!$stream) {
|
||||
throw new Exception("无法创建私钥文件:{$config['pem_key_file']}");
|
||||
}
|
||||
fwrite($stream, $privatekey);
|
||||
fclose($stream);
|
||||
$this->log('私钥已保存到:' . $config['pem_key_file']);
|
||||
} elseif ($config['format'] == 'pfx') {
|
||||
$pfx_pass = $config['pfx_pass'] ?? null;
|
||||
$pfx = CertHelper::getPfx($fullchain, $privatekey, $pfx_pass);
|
||||
|
||||
$stream = fopen("ssh2.sftp://$sftp{$config['pfx_file']}", 'w');
|
||||
if (!$stream) {
|
||||
throw new Exception("无法创建PFX证书文件:{$config['pfx_file']}");
|
||||
}
|
||||
fwrite($stream, $pfx);
|
||||
fclose($stream);
|
||||
$this->log('PFX证书已保存到:' . $config['pfx_file']);
|
||||
|
||||
if ($config['uptype'] == '1' && !empty($config['iis_domain'])) {
|
||||
$cert_hash = openssl_x509_fingerprint($fullchain, 'sha1');
|
||||
$this->deploy_iis($connection, $config['iis_domain'], $config['pfx_file'], $config['pfx_pass'], $cert_hash);
|
||||
$config['cmd'] = null;
|
||||
}
|
||||
}
|
||||
if (!empty($config['cmd'])) {
|
||||
$cmds = explode("\n", $config['cmd']);
|
||||
foreach ($cmds as $cmd) {
|
||||
$cmd = trim($cmd);
|
||||
if (empty($cmd)) continue;
|
||||
$this->exec($connection, $cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_iis($connection, $domain, $pfx_file, $pfx_pass, $cert_hash)
|
||||
{
|
||||
if (!strpos($domain, ':')) {
|
||||
$domain .= ':443';
|
||||
}
|
||||
$ret = $this->exec($connection, 'netsh http show sslcert hostnameport=' . $domain);
|
||||
if (preg_match('/:\s+(\w{40})/', $ret, $match)) {
|
||||
if ($match[1] == $cert_hash) {
|
||||
$this->log('IIS域名 ' . $domain . ' 证书已存在,无需更新');
|
||||
return;
|
||||
}
|
||||
}
|
||||
$p = '-p ""';
|
||||
if (!empty($pfx_pass)) $p = '-p ' . $pfx_pass;
|
||||
if (substr($pfx_file, 0, 1) == '/') $pfx_file = substr($pfx_file, 1);
|
||||
$this->exec($connection, 'certutil ' . $p . ' -importPFX ' . $pfx_file);
|
||||
$this->exec($connection, 'netsh http delete sslcert hostnameport=' . $domain);
|
||||
$this->exec($connection, 'netsh http add sslcert hostnameport=' . $domain . ' certhash=' . $cert_hash . ' certstorename=MY appid=\'{' . $this->uuid() . '}\'');
|
||||
$this->log('IIS域名 ' . $domain . ' 证书已更新');
|
||||
}
|
||||
|
||||
private function uuid()
|
||||
{
|
||||
$guid = md5(uniqid(mt_rand(), true));
|
||||
return substr($guid, 0, 8) . '-' . substr($guid, 8, 4) . '-4' . substr($guid, 12, 3) . '-' . substr($guid, 16, 4) . '-' . substr($guid, 20, 12);
|
||||
}
|
||||
|
||||
private function exec($connection, $cmd)
|
||||
{
|
||||
$this->log('执行命令:' . $cmd);
|
||||
$stream = ssh2_exec($connection, $cmd);
|
||||
$errorStream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
|
||||
if (!$stream || !$errorStream) {
|
||||
throw new Exception('执行命令失败');
|
||||
}
|
||||
stream_set_blocking($stream, true);
|
||||
stream_set_blocking($errorStream, true);
|
||||
$output = stream_get_contents($stream);
|
||||
$errorOutput = stream_get_contents($errorStream);
|
||||
fclose($stream);
|
||||
fclose($errorStream);
|
||||
if (trim($errorOutput)) {
|
||||
if ($this->config['windows'] == '1' && $this->containsGBKChinese($errorOutput)) {
|
||||
$errorOutput = mb_convert_encoding($errorOutput, 'UTF-8', 'GBK');
|
||||
}
|
||||
throw new Exception('执行命令失败:' . trim($errorOutput));
|
||||
} else {
|
||||
if ($this->config['windows'] == '1' && $this->containsGBKChinese($output)) {
|
||||
$output = mb_convert_encoding($output, 'UTF-8', 'GBK');
|
||||
}
|
||||
$this->log('执行命令成功:' . trim($output));
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
|
||||
private function connect()
|
||||
{
|
||||
if (!function_exists('ssh2_connect')) {
|
||||
throw new Exception('ssh2扩展未安装');
|
||||
}
|
||||
if (empty($this->config['host']) || empty($this->config['port']) || empty($this->config['username']) || $this->config['auth'] == '0' && empty($this->config['password']) || $this->config['auth'] == '1' && empty($this->config['privatekey'])) {
|
||||
throw new Exception('必填参数不能为空');
|
||||
}
|
||||
if (!filter_var($this->config['host'], FILTER_VALIDATE_IP) && !filter_var($this->config['host'], FILTER_VALIDATE_DOMAIN)) {
|
||||
throw new Exception('主机地址不合法');
|
||||
}
|
||||
if (!is_numeric($this->config['port']) || $this->config['port'] < 1 || $this->config['port'] > 65535) {
|
||||
throw new Exception('端口不合法');
|
||||
}
|
||||
|
||||
$connection = ssh2_connect($this->config['host'], intval($this->config['port']));
|
||||
if (!$connection) {
|
||||
throw new Exception('SSH连接失败');
|
||||
}
|
||||
if ($this->config['auth'] == '1') {
|
||||
$publicKey = $this->getPublicKey($this->config['privatekey']);
|
||||
$publicKeyPath = app()->getRuntimePath() . $this->config['host'] . '.pub';
|
||||
$privateKeyPath = app()->getRuntimePath() . $this->config['host'] . '.key';
|
||||
$umask = umask(0066);
|
||||
file_put_contents($privateKeyPath, $this->config['privatekey']);
|
||||
file_put_contents($publicKeyPath, $publicKey);
|
||||
umask($umask);
|
||||
$passphrase = $this->config['passphrase'] ?? null; // 私钥密码
|
||||
if (!ssh2_auth_pubkey_file($connection, $this->config['username'], $publicKeyPath, $privateKeyPath, $passphrase)) {
|
||||
throw new Exception('私钥认证失败');
|
||||
}
|
||||
} else {
|
||||
if (!ssh2_auth_password($connection, $this->config['username'], $this->config['password'])) {
|
||||
throw new Exception('用户名或密码错误');
|
||||
}
|
||||
}
|
||||
return $connection;
|
||||
}
|
||||
|
||||
private function getPublicKey($privateKey)
|
||||
{
|
||||
$res = openssl_pkey_get_private($privateKey);
|
||||
if (!$res) {
|
||||
throw new Exception('加载私钥失败');
|
||||
}
|
||||
$details = openssl_pkey_get_details($res);
|
||||
if (!$details || !isset($details['key'])) {
|
||||
throw new Exception('从私钥导出公钥失败');
|
||||
}
|
||||
$buffer = pack("N", 7) . "ssh-rsa" .
|
||||
$this->sshEncodeBuffer($details['rsa']['e']) .
|
||||
$this->sshEncodeBuffer($details['rsa']['n']);
|
||||
return "ssh-rsa " . base64_encode($buffer);
|
||||
}
|
||||
|
||||
private function sshEncodeBuffer($buffer)
|
||||
{
|
||||
$len = strlen($buffer);
|
||||
if (ord($buffer[0]) & 0x80) {
|
||||
$len++;
|
||||
$buffer = "\x00" . $buffer;
|
||||
}
|
||||
return pack("Na*", $len, $buffer);
|
||||
}
|
||||
|
||||
private function containsGBKChinese($string)
|
||||
{
|
||||
return preg_match('/[\x81-\xFE][\x40-\xFE]/', $string) === 1;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,167 +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);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,295 +1,295 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\client\TencentCloud;
|
||||
use Exception;
|
||||
|
||||
class tencent implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $SecretId;
|
||||
private $SecretKey;
|
||||
private TencentCloud $client;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->SecretId = $config['SecretId'];
|
||||
$this->SecretKey = $config['SecretKey'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new TencentCloud($this->SecretId, $this->SecretKey, 'ssl.tencentcloudapi.com', 'ssl', '2019-12-05', null, $this->proxy);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->SecretId) || empty($this->SecretKey)) throw new Exception('必填参数不能为空');
|
||||
$this->client->request('DescribeCertificates', []);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey);
|
||||
if (!$cert_id) throw new Exception('证书ID获取失败');
|
||||
if ($config['product'] == 'cos') {
|
||||
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'];
|
||||
$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不能为空');
|
||||
if (empty($config['tke_cluster_id'])) throw new Exception('集群ID不能为空');
|
||||
if (empty($config['tke_namespace'])) throw new Exception('命名空间不能为空');
|
||||
if (empty($config['tke_secret'])) throw new Exception('secret名称不能为空');
|
||||
$instance_id = $config['tke_cluster_id'] . '|' . $config['tke_namespace'] . '|' . $config['tke_secret'];
|
||||
$this->client = new TencentCloud($this->SecretId, $this->SecretKey, 'ssl.tencentcloudapi.com', 'ssl', '2019-12-05', $config['regionid'], $this->proxy);
|
||||
} elseif ($config['product'] == 'lighthouse') {
|
||||
if (empty($config['regionid'])) throw new Exception('所属地域ID不能为空');
|
||||
if (empty($config['lighthouse_id'])) throw new Exception('实例ID不能为空');
|
||||
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') {
|
||||
$this->client = new TencentCloud($this->SecretId, $this->SecretKey, 'ssl.tencentcloudapi.com', 'ssl', '2019-12-05', $config['region'], $this->proxy);
|
||||
} elseif (in_array($config['product'], ['tse', 'scf'])) {
|
||||
if (empty($config['regionid'])) throw new Exception('所属地域ID不能为空');
|
||||
$this->client = new TencentCloud($this->SecretId, $this->SecretKey, 'ssl.tencentcloudapi.com', 'ssl', '2019-12-05', $config['regionid'], $this->proxy);
|
||||
}
|
||||
$instance_id = $config['domain'];
|
||||
}
|
||||
try {
|
||||
$record_id = $this->deploy_common($config['product'], $cert_id, $instance_id);
|
||||
$info['cert_id'] = $cert_id;
|
||||
$info['record_id'] = $record_id;
|
||||
} catch (Exception $e) {
|
||||
if (isset($info['record_id'])) {
|
||||
if ($this->deploy_query($info['record_id'])) {
|
||||
$this->log(strtoupper($config['product']) . '实例 ' . $instance_id . ' 已部署证书,无需重复部署');
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
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'];
|
||||
|
||||
$param = [
|
||||
'CertificatePublicKey' => $fullchain,
|
||||
'CertificatePrivateKey' => $privatekey,
|
||||
'CertificateType' => 'SVR',
|
||||
'Alias' => $cert_name,
|
||||
'Repeatable' => false,
|
||||
];
|
||||
try {
|
||||
$data = $this->client->request('UploadCertificate', $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
$this->log('上传证书成功 CertificateId=' . $data['CertificateId']);
|
||||
usleep(300000);
|
||||
|
||||
$param = [
|
||||
'CertificateIds' => [$data['CertificateId']],
|
||||
'SwitchStatus' => 1,
|
||||
];
|
||||
$this->client->request('ModifyCertificatesExpiringNotificationSwitch', $param);
|
||||
|
||||
return $data['CertificateId'];
|
||||
}
|
||||
|
||||
private function deploy_common($product, $cert_id, $instance_id)
|
||||
{
|
||||
if (in_array($product, ['cdn', 'waf', 'teo', 'ddos', 'live', 'vod']) && strpos($instance_id, ',') !== false) {
|
||||
$instance_ids = explode(',', $instance_id);
|
||||
} 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 . ' 部署证书成功!');
|
||||
return $data['DeployRecordId'];
|
||||
}
|
||||
|
||||
private function deploy_query($record_id)
|
||||
{
|
||||
$param = [
|
||||
'DeployRecordId' => strval($record_id),
|
||||
];
|
||||
try {
|
||||
$data = $this->client->request('DescribeHostDeployRecordDetail', $param);
|
||||
if (isset($data['SuccessTotalCount']) && $data['SuccessTotalCount'] >= 1 || isset($data['RunningTotalCount']) && $data['RunningTotalCount'] >= 1) {
|
||||
return true;
|
||||
}
|
||||
if (isset($data['FailedTotalCount']) && $data['FailedTotalCount'] >= 1 && !empty($data['DeployRecordDetailList'])) {
|
||||
$errmsg = $data['DeployRecordDetailList'][0]['ErrorMsg'];
|
||||
if (strpos($errmsg, '\u')) {
|
||||
$errmsg = json_decode($errmsg);
|
||||
}
|
||||
$this->log('证书部署失败原因:' . $errmsg);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->log('查询证书部署记录失败:' . $e->getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function deploy_clb($cert_id, $config)
|
||||
{
|
||||
if (empty($config['regionid'])) throw new Exception('所属地域ID不能为空');
|
||||
if (empty($config['clb_id'])) throw new Exception('负载均衡ID不能为空');
|
||||
$sni_switch = !empty($config['clb_domain']) ? 1 : 0;
|
||||
|
||||
$client = new TencentCloud($this->SecretId, $this->SecretKey, 'clb.tencentcloudapi.com', 'clb', '2018-03-17', $config['regionid'], $this->proxy);
|
||||
$param = [
|
||||
'LoadBalancerId' => $config['clb_id'],
|
||||
'Protocol' => 'HTTPS',
|
||||
];
|
||||
if (!empty($config['clb_listener_id'])) {
|
||||
$param['ListenerIds'] = [$config['clb_listener_id']];
|
||||
}
|
||||
try {
|
||||
$data = $client->request('DescribeListeners', $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取监听器列表失败:' . $e->getMessage());
|
||||
}
|
||||
if (!isset($data['TotalCount']) || $data['TotalCount'] == 0) throw new Exception('负载均衡:' . $config['clb_id'] . '监听器列表为空');
|
||||
$count = 0;
|
||||
foreach ($data['Listeners'] as $listener) {
|
||||
if ($listener['SniSwitch'] == $sni_switch) {
|
||||
if ($sni_switch == 1) {
|
||||
foreach ($listener['Rules'] as $rule) {
|
||||
if ($rule['Domain'] == $config['clb_domain']) {
|
||||
if (isset($rule['Certificate']['CertId']) && $cert_id == $rule['Certificate']['CertId']) {
|
||||
$this->log('负载均衡监听器 ' . $listener['ListenerId'] . ' 域名 ' . $rule['Domain'] . ' 已部署证书,无需重复部署');
|
||||
} else {
|
||||
$param = [
|
||||
'LoadBalancerId' => $config['clb_id'],
|
||||
'ListenerId' => $listener['ListenerId'],
|
||||
'Domain' => $rule['Domain'],
|
||||
'Certificate' => [
|
||||
'SSLMode' => 'UNIDIRECTIONAL',
|
||||
'CertId' => $cert_id,
|
||||
],
|
||||
];
|
||||
$client->request('ModifyDomainAttributes', $param);
|
||||
$this->log('负载均衡监听器 ' . $listener['ListenerId'] . ' 域名 ' . $rule['Domain'] . ' 部署证书成功!');
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isset($listener['Certificate']['CertId']) && $cert_id == $listener['Certificate']['CertId']) {
|
||||
$this->log('负载均衡监听器 ' . $listener['ListenerId'] . ' 已部署证书,无需重复部署');
|
||||
} else {
|
||||
$param = [
|
||||
'LoadBalancerId' => $config['clb_id'],
|
||||
'ListenerId' => $listener['ListenerId'],
|
||||
'Certificate' => [
|
||||
'SSLMode' => 'UNIDIRECTIONAL',
|
||||
'CertId' => $cert_id,
|
||||
],
|
||||
];
|
||||
$client->request('ModifyListener', $param);
|
||||
$this->log('负载均衡监听器 ' . $listener['ListenerId'] . ' 部署证书成功!');
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($count == 0) throw new Exception('没有找到要更新证书的监听器');
|
||||
}
|
||||
|
||||
private function deploy_scf($cert_id, $config)
|
||||
{
|
||||
if (empty($config['regionid'])) throw new Exception('所属地域ID不能为空');
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
|
||||
$client = new TencentCloud($this->SecretId, $this->SecretKey, 'scf.tencentcloudapi.com', 'scf', '2018-04-16', $config['regionid'], $this->proxy);
|
||||
$param = [
|
||||
'Domain' => $config['domain'],
|
||||
];
|
||||
try {
|
||||
$data = $client->request('GetCustomDomain', $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取云函数自定义域名失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
if (isset($data['CertConfig']['CertificateId']) && $data['CertConfig']['CertificateId'] == $cert_id) {
|
||||
$this->log('云函数自定义域名 ' . $config['domain'] . ' 已部署证书,无需重复部署');
|
||||
return;
|
||||
}
|
||||
$data['CertConfig']['CertificateId'] = $cert_id;
|
||||
if ($data['Protocol'] == 'HTTP') $data['Protocol'] = 'HTTP&HTTPS';
|
||||
|
||||
$param = [
|
||||
'Domain' => $config['domain'],
|
||||
'Protocol' => $data['Protocol'],
|
||||
'CertConfig' => $data['CertConfig'],
|
||||
];
|
||||
$data = $client->request('UpdateCustomDomain', $param);
|
||||
$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;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\client\TencentCloud;
|
||||
use Exception;
|
||||
|
||||
class tencent implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $SecretId;
|
||||
private $SecretKey;
|
||||
private TencentCloud $client;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->SecretId = $config['SecretId'];
|
||||
$this->SecretKey = $config['SecretKey'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new TencentCloud($this->SecretId, $this->SecretKey, 'ssl.tencentcloudapi.com', 'ssl', '2019-12-05', null, $this->proxy);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->SecretId) || empty($this->SecretKey)) throw new Exception('必填参数不能为空');
|
||||
$this->client->request('DescribeCertificates', []);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$cert_id = $this->get_cert_id($fullchain, $privatekey);
|
||||
if (!$cert_id) throw new Exception('证书ID获取失败');
|
||||
if ($config['product'] == 'cos') {
|
||||
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'];
|
||||
$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不能为空');
|
||||
if (empty($config['tke_cluster_id'])) throw new Exception('集群ID不能为空');
|
||||
if (empty($config['tke_namespace'])) throw new Exception('命名空间不能为空');
|
||||
if (empty($config['tke_secret'])) throw new Exception('secret名称不能为空');
|
||||
$instance_id = $config['tke_cluster_id'] . '|' . $config['tke_namespace'] . '|' . $config['tke_secret'];
|
||||
$this->client = new TencentCloud($this->SecretId, $this->SecretKey, 'ssl.tencentcloudapi.com', 'ssl', '2019-12-05', $config['regionid'], $this->proxy);
|
||||
} elseif ($config['product'] == 'lighthouse') {
|
||||
if (empty($config['regionid'])) throw new Exception('所属地域ID不能为空');
|
||||
if (empty($config['lighthouse_id'])) throw new Exception('实例ID不能为空');
|
||||
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') {
|
||||
$this->client = new TencentCloud($this->SecretId, $this->SecretKey, 'ssl.tencentcloudapi.com', 'ssl', '2019-12-05', $config['region'], $this->proxy);
|
||||
} elseif (in_array($config['product'], ['tse', 'scf'])) {
|
||||
if (empty($config['regionid'])) throw new Exception('所属地域ID不能为空');
|
||||
$this->client = new TencentCloud($this->SecretId, $this->SecretKey, 'ssl.tencentcloudapi.com', 'ssl', '2019-12-05', $config['regionid'], $this->proxy);
|
||||
}
|
||||
$instance_id = $config['domain'];
|
||||
}
|
||||
try {
|
||||
$record_id = $this->deploy_common($config['product'], $cert_id, $instance_id);
|
||||
$info['cert_id'] = $cert_id;
|
||||
$info['record_id'] = $record_id;
|
||||
} catch (Exception $e) {
|
||||
if (isset($info['record_id'])) {
|
||||
if ($this->deploy_query($info['record_id'])) {
|
||||
$this->log(strtoupper($config['product']) . '实例 ' . $instance_id . ' 已部署证书,无需重复部署');
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
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'];
|
||||
|
||||
$param = [
|
||||
'CertificatePublicKey' => $fullchain,
|
||||
'CertificatePrivateKey' => $privatekey,
|
||||
'CertificateType' => 'SVR',
|
||||
'Alias' => $cert_name,
|
||||
'Repeatable' => false,
|
||||
];
|
||||
try {
|
||||
$data = $this->client->request('UploadCertificate', $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('上传证书失败:' . $e->getMessage());
|
||||
}
|
||||
$this->log('上传证书成功 CertificateId=' . $data['CertificateId']);
|
||||
usleep(300000);
|
||||
|
||||
$param = [
|
||||
'CertificateIds' => [$data['CertificateId']],
|
||||
'SwitchStatus' => 1,
|
||||
];
|
||||
$this->client->request('ModifyCertificatesExpiringNotificationSwitch', $param);
|
||||
|
||||
return $data['CertificateId'];
|
||||
}
|
||||
|
||||
private function deploy_common($product, $cert_id, $instance_id)
|
||||
{
|
||||
if (in_array($product, ['cdn', 'waf', 'teo', 'ddos', 'live', 'vod']) && strpos($instance_id, ',') !== false) {
|
||||
$instance_ids = explode(',', $instance_id);
|
||||
} 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 . ' 部署证书成功!');
|
||||
return $data['DeployRecordId'];
|
||||
}
|
||||
|
||||
private function deploy_query($record_id)
|
||||
{
|
||||
$param = [
|
||||
'DeployRecordId' => strval($record_id),
|
||||
];
|
||||
try {
|
||||
$data = $this->client->request('DescribeHostDeployRecordDetail', $param);
|
||||
if (isset($data['SuccessTotalCount']) && $data['SuccessTotalCount'] >= 1 || isset($data['RunningTotalCount']) && $data['RunningTotalCount'] >= 1) {
|
||||
return true;
|
||||
}
|
||||
if (isset($data['FailedTotalCount']) && $data['FailedTotalCount'] >= 1 && !empty($data['DeployRecordDetailList'])) {
|
||||
$errmsg = $data['DeployRecordDetailList'][0]['ErrorMsg'];
|
||||
if (strpos($errmsg, '\u')) {
|
||||
$errmsg = json_decode($errmsg);
|
||||
}
|
||||
$this->log('证书部署失败原因:' . $errmsg);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->log('查询证书部署记录失败:' . $e->getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function deploy_clb($cert_id, $config)
|
||||
{
|
||||
if (empty($config['regionid'])) throw new Exception('所属地域ID不能为空');
|
||||
if (empty($config['clb_id'])) throw new Exception('负载均衡ID不能为空');
|
||||
$sni_switch = !empty($config['clb_domain']) ? 1 : 0;
|
||||
|
||||
$client = new TencentCloud($this->SecretId, $this->SecretKey, 'clb.tencentcloudapi.com', 'clb', '2018-03-17', $config['regionid'], $this->proxy);
|
||||
$param = [
|
||||
'LoadBalancerId' => $config['clb_id'],
|
||||
'Protocol' => 'HTTPS',
|
||||
];
|
||||
if (!empty($config['clb_listener_id'])) {
|
||||
$param['ListenerIds'] = [$config['clb_listener_id']];
|
||||
}
|
||||
try {
|
||||
$data = $client->request('DescribeListeners', $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取监听器列表失败:' . $e->getMessage());
|
||||
}
|
||||
if (!isset($data['TotalCount']) || $data['TotalCount'] == 0) throw new Exception('负载均衡:' . $config['clb_id'] . '监听器列表为空');
|
||||
$count = 0;
|
||||
foreach ($data['Listeners'] as $listener) {
|
||||
if ($listener['SniSwitch'] == $sni_switch) {
|
||||
if ($sni_switch == 1) {
|
||||
foreach ($listener['Rules'] as $rule) {
|
||||
if ($rule['Domain'] == $config['clb_domain']) {
|
||||
if (isset($rule['Certificate']['CertId']) && $cert_id == $rule['Certificate']['CertId']) {
|
||||
$this->log('负载均衡监听器 ' . $listener['ListenerId'] . ' 域名 ' . $rule['Domain'] . ' 已部署证书,无需重复部署');
|
||||
} else {
|
||||
$param = [
|
||||
'LoadBalancerId' => $config['clb_id'],
|
||||
'ListenerId' => $listener['ListenerId'],
|
||||
'Domain' => $rule['Domain'],
|
||||
'Certificate' => [
|
||||
'SSLMode' => 'UNIDIRECTIONAL',
|
||||
'CertId' => $cert_id,
|
||||
],
|
||||
];
|
||||
$client->request('ModifyDomainAttributes', $param);
|
||||
$this->log('负载均衡监听器 ' . $listener['ListenerId'] . ' 域名 ' . $rule['Domain'] . ' 部署证书成功!');
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isset($listener['Certificate']['CertId']) && $cert_id == $listener['Certificate']['CertId']) {
|
||||
$this->log('负载均衡监听器 ' . $listener['ListenerId'] . ' 已部署证书,无需重复部署');
|
||||
} else {
|
||||
$param = [
|
||||
'LoadBalancerId' => $config['clb_id'],
|
||||
'ListenerId' => $listener['ListenerId'],
|
||||
'Certificate' => [
|
||||
'SSLMode' => 'UNIDIRECTIONAL',
|
||||
'CertId' => $cert_id,
|
||||
],
|
||||
];
|
||||
$client->request('ModifyListener', $param);
|
||||
$this->log('负载均衡监听器 ' . $listener['ListenerId'] . ' 部署证书成功!');
|
||||
}
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($count == 0) throw new Exception('没有找到要更新证书的监听器');
|
||||
}
|
||||
|
||||
private function deploy_scf($cert_id, $config)
|
||||
{
|
||||
if (empty($config['regionid'])) throw new Exception('所属地域ID不能为空');
|
||||
if (empty($config['domain'])) throw new Exception('绑定的域名不能为空');
|
||||
|
||||
$client = new TencentCloud($this->SecretId, $this->SecretKey, 'scf.tencentcloudapi.com', 'scf', '2018-04-16', $config['regionid'], $this->proxy);
|
||||
$param = [
|
||||
'Domain' => $config['domain'],
|
||||
];
|
||||
try {
|
||||
$data = $client->request('GetCustomDomain', $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取云函数自定义域名失败:' . $e->getMessage());
|
||||
}
|
||||
|
||||
if (isset($data['CertConfig']['CertificateId']) && $data['CertConfig']['CertificateId'] == $cert_id) {
|
||||
$this->log('云函数自定义域名 ' . $config['domain'] . ' 已部署证书,无需重复部署');
|
||||
return;
|
||||
}
|
||||
$data['CertConfig']['CertificateId'] = $cert_id;
|
||||
if ($data['Protocol'] == 'HTTP') $data['Protocol'] = 'HTTP&HTTPS';
|
||||
|
||||
$param = [
|
||||
'Domain' => $config['domain'],
|
||||
'Protocol' => $data['Protocol'],
|
||||
'CertConfig' => $data['CertConfig'],
|
||||
];
|
||||
$data = $client->request('UpdateCustomDomain', $param);
|
||||
$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;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,132 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\client\Ucloud as UcloudClient;
|
||||
use Exception;
|
||||
|
||||
class ucloud implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $PublicKey;
|
||||
private $PrivateKey;
|
||||
private UcloudClient $client;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->PublicKey = $config['PublicKey'];
|
||||
$this->PrivateKey = $config['PrivateKey'];
|
||||
$this->client = new UcloudClient($this->PublicKey, $this->PrivateKey);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->PublicKey) || empty($this->PrivateKey)) throw new Exception('必填参数不能为空');
|
||||
$param = ['Mode' => 'free'];
|
||||
$this->client->request('GetCertificateList', $param);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$domain_id = $config['domain_id'];
|
||||
if (empty($domain_id)) throw new Exception('云分发资源ID不能为空');
|
||||
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
$cert_name = str_replace(['*', '.'], '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
|
||||
try {
|
||||
$data = $this->client->request('GetCertificateV2', []);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取证书列表失败 ' . $e->getMessage());
|
||||
}
|
||||
|
||||
$exist = false;
|
||||
foreach ($data['CertList'] as $cert) {
|
||||
if (trim($cert['UserCert']) == trim($fullchain)) {
|
||||
$cert_name = $cert['CertName'];
|
||||
$exist = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$exist) {
|
||||
$param = [
|
||||
'CertName' => $cert_name,
|
||||
'UserCert' => $fullchain,
|
||||
'PrivateKey' => $privatekey,
|
||||
];
|
||||
try {
|
||||
$data = $this->client->request('AddCertificate', $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('添加证书失败 ' . $e->getMessage());
|
||||
}
|
||||
$this->log('添加证书成功,名称:' . $cert_name);
|
||||
} else {
|
||||
$this->log('获取到已添加的证书,名称:' . $cert_name);
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $this->client->request('GetUcdnDomainConfig', ['DomainId.0' => $domain_id]);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取加速域名配置失败 ' . $e->getMessage());
|
||||
}
|
||||
if (empty($data['DomainList'])) throw new Exception('云分发资源ID:' . $domain_id . '不存在');
|
||||
$domain = $data['DomainList'][0]['Domain'];
|
||||
$HttpsStatusCn = $data['DomainList'][0]['HttpsStatusCn'];
|
||||
$HttpsStatusAbroad = $data['DomainList'][0]['HttpsStatusAbroad'];
|
||||
|
||||
if ($data['DomainList'][0]['CertNameCn'] == $cert_name || $data['DomainList'][0]['CertNameAbroad'] == $cert_name) {
|
||||
$this->log('云分发' . $domain_id . '证书已配置,无需重复操作');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $this->client->request('GetCertificateBaseInfoList', ['Domain' => $domain]);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取可用证书列表失败 ' . $e->getMessage());
|
||||
}
|
||||
if (empty($data['CertList'])) throw new Exception('可用证书列表为空');
|
||||
|
||||
$cert_id = null;
|
||||
foreach ($data['CertList'] as $cert) {
|
||||
if ($cert['CertName'] == $cert_name) {
|
||||
$cert_id = $cert['CertId'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$cert_id) throw new Exception('证书ID不存在');
|
||||
$this->log('证书ID获取成功:' . $cert_id);
|
||||
|
||||
$param = [
|
||||
'DomainId' => $domain_id,
|
||||
'CertName' => $cert_name,
|
||||
'CertId' => $cert_id,
|
||||
'CertType' => 'ucdn',
|
||||
];
|
||||
if ($HttpsStatusCn == 'enable') $param['HttpsStatusCn'] = $HttpsStatusCn;
|
||||
if ($HttpsStatusAbroad == 'enable') $param['HttpsStatusAbroad'] = $HttpsStatusAbroad;
|
||||
if ($HttpsStatusCn != 'enable' && $HttpsStatusAbroad != 'enable') $param['HttpsStatusCn'] = 'enable';
|
||||
try {
|
||||
$data = $this->client->request('UpdateUcdnDomainHttpsConfigV2', $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('https加速配置失败 ' . $e->getMessage());
|
||||
}
|
||||
$this->log('云分发' . $domain_id . '证书配置成功!');
|
||||
$info['cert_id'] = $cert_id;
|
||||
$info['cert_name'] = $cert_name;
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use app\lib\client\Ucloud as UcloudClient;
|
||||
use Exception;
|
||||
|
||||
class ucloud implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $PublicKey;
|
||||
private $PrivateKey;
|
||||
private UcloudClient $client;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->PublicKey = $config['PublicKey'];
|
||||
$this->PrivateKey = $config['PrivateKey'];
|
||||
$this->client = new UcloudClient($this->PublicKey, $this->PrivateKey);
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->PublicKey) || empty($this->PrivateKey)) throw new Exception('必填参数不能为空');
|
||||
$param = ['Mode' => 'free'];
|
||||
$this->client->request('GetCertificateList', $param);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$domain_id = $config['domain_id'];
|
||||
if (empty($domain_id)) throw new Exception('云分发资源ID不能为空');
|
||||
|
||||
$certInfo = openssl_x509_parse($fullchain, true);
|
||||
if (!$certInfo) throw new Exception('证书解析失败');
|
||||
$cert_name = str_replace(['*', '.'], '', $certInfo['subject']['CN']) . '-' . $certInfo['validFrom_time_t'];
|
||||
|
||||
$param = [
|
||||
'CertName' => $cert_name,
|
||||
'UserCert' => $fullchain,
|
||||
'PrivateKey' => $privatekey,
|
||||
];
|
||||
try {
|
||||
$data = $this->client->request('AddCertificate', $param);
|
||||
$this->log('添加证书成功,名称:' . $cert_name);
|
||||
} catch (Exception $e) {
|
||||
if (strpos($e->getMessage(), 'cert already exist') !== false) {
|
||||
$this->log('证书已存在,名称:' . $cert_name);
|
||||
} else {
|
||||
throw new Exception('添加证书失败 ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $this->client->request('GetUcdnDomainConfig', ['DomainId.0' => $domain_id]);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取加速域名配置失败 ' . $e->getMessage());
|
||||
}
|
||||
if (empty($data['DomainList'])) throw new Exception('云分发资源ID:' . $domain_id . '不存在');
|
||||
$domain = $data['DomainList'][0]['Domain'];
|
||||
$HttpsStatusCn = $data['DomainList'][0]['HttpsStatusCn'];
|
||||
$HttpsStatusAbroad = $data['DomainList'][0]['HttpsStatusAbroad'];
|
||||
|
||||
if ($data['DomainList'][0]['CertNameCn'] == $cert_name || $data['DomainList'][0]['CertNameAbroad'] == $cert_name) {
|
||||
$this->log('云分发' . $domain_id . '证书已配置,无需重复操作');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $this->client->request('GetCertificateBaseInfoList', ['Domain' => $domain]);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('获取可用证书列表失败 ' . $e->getMessage());
|
||||
}
|
||||
if (empty($data['CertList'])) throw new Exception('可用证书列表为空');
|
||||
|
||||
$cert_id = null;
|
||||
foreach ($data['CertList'] as $cert) {
|
||||
if ($cert['CertName'] == $cert_name) {
|
||||
$cert_id = $cert['CertId'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$cert_id) throw new Exception('证书ID不存在');
|
||||
$this->log('证书ID获取成功:' . $cert_id);
|
||||
|
||||
$param = [
|
||||
'DomainId' => $domain_id,
|
||||
'CertName' => $cert_name,
|
||||
'CertId' => $cert_id,
|
||||
'CertType' => 'ucdn',
|
||||
];
|
||||
if ($HttpsStatusCn == 'enable') $param['HttpsStatusCn'] = $HttpsStatusCn;
|
||||
if ($HttpsStatusAbroad == 'enable') $param['HttpsStatusAbroad'] = $HttpsStatusAbroad;
|
||||
if ($HttpsStatusCn != 'enable' && $HttpsStatusAbroad != 'enable') $param['HttpsStatusCn'] = 'enable';
|
||||
try {
|
||||
$data = $this->client->request('UpdateUcdnDomainHttpsConfigV2', $param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('https加速配置失败 ' . $e->getMessage());
|
||||
}
|
||||
$this->log('云分发' . $domain_id . '证书配置成功!');
|
||||
$info['cert_id'] = $cert_id;
|
||||
$info['cert_name'] = $cert_name;
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,212 +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);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,133 +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);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
103
app/lib/deploy/uusec.php
Normal file
103
app/lib/deploy/uusec.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class uusec implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $username;
|
||||
private $password;
|
||||
private $proxy;
|
||||
private $accessToken;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
$this->username = $config['username'];
|
||||
$this->password = $config['password'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->password) || empty($this->password)) throw new Exception('用户名和密码不能为空');
|
||||
$this->login();
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$id = $config['id'];
|
||||
if (empty($id)) throw new Exception('证书ID不能为空');
|
||||
|
||||
$this->login();
|
||||
|
||||
$params = [
|
||||
'id' => intval($id),
|
||||
'type' => 1,
|
||||
'name' => $config['name'],
|
||||
'crt' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
];
|
||||
$result = $this->request('/api/v1/certs', $params, 'PUT');
|
||||
if (is_string($result) && $result == 'OK') {
|
||||
$this->log('证书ID:' . $id . '更新成功!');
|
||||
} else {
|
||||
throw new Exception('证书ID:' . $id . '更新失败,' . (isset($result['err']) ? $result['err'] : '未知错误'));
|
||||
}
|
||||
}
|
||||
|
||||
private function login()
|
||||
{
|
||||
$path = '/api/v1/users/login';
|
||||
$params = [
|
||||
'usr' => $this->username,
|
||||
'pwd' => $this->password,
|
||||
'otp' => '',
|
||||
];
|
||||
$result = $this->request($path, $params);
|
||||
if (isset($result['token'])) {
|
||||
$this->accessToken = $result['token'];
|
||||
} else {
|
||||
throw new Exception('登录失败,' . (isset($result['err']) ? $result['err'] : '未知错误'));
|
||||
}
|
||||
}
|
||||
|
||||
private function request($path, $params = null, $method = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$headers = [];
|
||||
$body = null;
|
||||
if ($this->accessToken) {
|
||||
$headers['Authorization'] = 'Bearer ' . $this->accessToken;
|
||||
}
|
||||
if ($params) {
|
||||
$headers['Content-Type'] = 'application/json;charset=UTF-8';
|
||||
$body = json_encode($params);
|
||||
}
|
||||
$response = http_request($url, $body, null, null, $headers, $this->proxy, $method);
|
||||
$result = json_decode($response['body'], true);
|
||||
if ($response['code'] == 200) {
|
||||
return $result;
|
||||
} elseif (isset($result['message'])) {
|
||||
throw new Exception($result['message']);
|
||||
} else {
|
||||
throw new Exception('请求失败,HTTP状态码:' . $response['code']);
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,131 +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);
|
||||
}
|
||||
}
|
||||
}
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
113
app/lib/deploy/xp.php
Normal file
113
app/lib/deploy/xp.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class xp implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $apikey;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
$this->apikey = $config['apikey'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->apikey)) throw new Exception('请填写面板地址和接口密钥');
|
||||
|
||||
$path = '/openApi/siteList';
|
||||
$response = $this->request($path);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['code']) && $result['code'] == 1000) {
|
||||
return true;
|
||||
} else {
|
||||
throw new Exception(isset($result['message']) ? $result['message'] : '面板地址无法连接');
|
||||
}
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
$path = '/openApi/siteList';
|
||||
$response = $this->request($path);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['code']) && $result['code'] == 1000) {
|
||||
|
||||
$sites = explode("\n", $config['sites']);
|
||||
$sites = array_map('trim', $sites);
|
||||
$success = 0;
|
||||
$errmsg = null;
|
||||
|
||||
foreach ($result['data'] as $item) {
|
||||
if (!in_array($item['name'], $sites)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
$this->deploySite($item['id'], $fullchain, $privatekey);
|
||||
$this->log("网站 {$item['name']} 证书部署成功");
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
$errmsg = $e->getMessage();
|
||||
$this->log("网站 {$item['name']} 证书部署失败:" . $errmsg);
|
||||
}
|
||||
}
|
||||
if ($success == 0) {
|
||||
throw new Exception($errmsg ? $errmsg : '要部署的网站不存在');
|
||||
}
|
||||
|
||||
} elseif (isset($result['message'])) {
|
||||
throw new Exception($result['message']);
|
||||
} else {
|
||||
throw new Exception($response ? $response : '返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
private function deploySite($id, $fullchain, $privatekey)
|
||||
{
|
||||
$path = '/openApi/setSSL';
|
||||
$data = [
|
||||
'id' => $id,
|
||||
'key' => $privatekey,
|
||||
'pem' => $fullchain,
|
||||
];
|
||||
$response = $this->request($path, $data);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['code']) && $result['code'] == 1000) {
|
||||
return true;
|
||||
} elseif (isset($result['message'])) {
|
||||
throw new Exception($result['message']);
|
||||
} 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 = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
|
||||
$headers = [
|
||||
'XP-API-KEY' => $this->apikey,
|
||||
];
|
||||
$response = http_request($url, $params ? json_encode($params) : null, null, null, $headers, $this->proxy);
|
||||
return $response['body'];
|
||||
}
|
||||
}
|
||||
@@ -1,258 +1,258 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
use app\lib\client\BaiduCloud;
|
||||
use Exception;
|
||||
|
||||
class baidu implements DnsInterface
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $SecretAccessKey;
|
||||
private $endpoint = "dns.baidubce.com";
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private BaiduCloud $client;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKeyId = $config['ak'];
|
||||
$this->SecretAccessKey = $config['sk'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new BaiduCloud($this->AccessKeyId, $this->SecretAccessKey, $this->endpoint, $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 = ['name' => $KeyWord];
|
||||
$data = $this->send_reuqest('GET', '/v1/dns/zone', $query);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['zones'] 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)
|
||||
{
|
||||
$query = [];
|
||||
if (!isNullOrEmpty($SubDomain)) {
|
||||
$SubDomain = strtolower($SubDomain);
|
||||
$query['rr'] = $SubDomain;
|
||||
}
|
||||
$data = $this->send_reuqest('GET', '/v1/dns/zone/'.$this->domain.'/record', $query);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['records'] as $row) {
|
||||
$list[] = [
|
||||
'RecordId' => $row['id'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $row['rr'],
|
||||
'Type' => $row['type'],
|
||||
'Value' => $row['value'],
|
||||
'Line' => $row['line'],
|
||||
'TTL' => $row['ttl'],
|
||||
'MX' => $row['priority'],
|
||||
'Status' => $row['status'] == 'running' ? '1' : '0',
|
||||
'Weight' => null,
|
||||
'Remark' => $row['description'],
|
||||
'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;
|
||||
}
|
||||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null)
|
||||
{
|
||||
if ($SubDomain == '') $SubDomain = '@';
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line);
|
||||
}
|
||||
|
||||
//获取解析记录详细信息
|
||||
public function getDomainRecordInfo($RecordId)
|
||||
{
|
||||
$query = ['id' => $RecordId];
|
||||
$data = $this->send_reuqest('GET', '/v1/dns/zone/'.$this->domain.'/record', $query);
|
||||
if ($data && !empty($data['records'])) {
|
||||
$data = $data['records'][0];
|
||||
return [
|
||||
'RecordId' => $data['id'],
|
||||
'Domain' => rtrim($data['zone_name'], '.'),
|
||||
'Name' => str_replace('.'.$data['zone_name'], '', $data['name']),
|
||||
'Type' => $data['type'],
|
||||
'Value' => $data['value'],
|
||||
'Line' => $data['line'],
|
||||
'TTL' => $data['ttl'],
|
||||
'MX' => $data['priority'],
|
||||
'Status' => $data['status'] == 'running' ? '1' : '0',
|
||||
'Weight' => null,
|
||||
'Remark' => $data['description'],
|
||||
'UpdateTime' => null,
|
||||
];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//添加解析记录
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$params = ['rr' => $Name, 'type' => $this->convertType($Type), 'value' => $Value, 'line' => $Line, 'ttl' => intval($TTL), 'description' => $Remark];
|
||||
if ($Type == 'MX') $params['priority'] = intval($MX);
|
||||
$query = ['clientToken' => getSid()];
|
||||
return $this->send_reuqest('POST', '/v1/dns/zone/'.$this->domain.'/record', $query, $params);
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$params = ['rr' => $Name, 'type' => $this->convertType($Type), 'value' => $Value, 'line' => $Line, 'ttl' => intval($TTL), 'description' => $Remark];
|
||||
if ($Type == 'MX') $params['priority'] = intval($MX);
|
||||
$query = ['clientToken' => getSid()];
|
||||
return $this->send_reuqest('PUT', '/v1/dns/zone/'.$this->domain.'/record/'.$RecordId, $query, $params);
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$query = ['clientToken' => getSid()];
|
||||
return $this->send_reuqest('DELETE', '/v1/dns/zone/'.$this->domain.'/record/'.$RecordId, $query);
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$Status = $Status == '1' ? 'enable' : 'disable';
|
||||
$query = [$Status => '', 'clientToken' => getSid()];
|
||||
return $this->send_reuqest('PUT', '/v1/dns/zone/'.$this->domain.'/record/'.$RecordId, $query);
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
public function getRecordLine()
|
||||
{
|
||||
return [
|
||||
'default' => ['name' => '默认', 'parent' => null],
|
||||
'ct' => ['name' => '电信', 'parent' => null],
|
||||
'cnc' => ['name' => '联通', 'parent' => null],
|
||||
'cmnet' => ['name' => '移动', 'parent' => null],
|
||||
'edu' => ['name' => '教育网', 'parent' => null],
|
||||
'search' => ['name' => '搜索引擎(百度)', 'parent' => null],
|
||||
];
|
||||
}
|
||||
|
||||
//获取域名概览信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
$res = $this->getDomainList($this->domain);
|
||||
if ($res && !empty($res['list'])) {
|
||||
return $res['list'][0];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
private function send_reuqest($method, $path, $query = null, $params = null)
|
||||
{
|
||||
try{
|
||||
return $this->client->request($method, $path, $query, $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);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
use app\lib\client\BaiduCloud;
|
||||
use Exception;
|
||||
|
||||
class baidu implements DnsInterface
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $SecretAccessKey;
|
||||
private $endpoint = "dns.baidubce.com";
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private BaiduCloud $client;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKeyId = $config['ak'];
|
||||
$this->SecretAccessKey = $config['sk'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new BaiduCloud($this->AccessKeyId, $this->SecretAccessKey, $this->endpoint, $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 = ['name' => $KeyWord];
|
||||
$data = $this->send_reuqest('GET', '/v1/dns/zone', $query);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['zones'] 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)
|
||||
{
|
||||
$query = [];
|
||||
if (!isNullOrEmpty($SubDomain)) {
|
||||
$SubDomain = strtolower($SubDomain);
|
||||
$query['rr'] = $SubDomain;
|
||||
}
|
||||
$data = $this->send_reuqest('GET', '/v1/dns/zone/'.$this->domain.'/record', $query);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['records'] as $row) {
|
||||
$list[] = [
|
||||
'RecordId' => $row['id'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $row['rr'],
|
||||
'Type' => $row['type'],
|
||||
'Value' => $row['value'],
|
||||
'Line' => $row['line'],
|
||||
'TTL' => $row['ttl'],
|
||||
'MX' => $row['priority'],
|
||||
'Status' => $row['status'] == 'running' ? '1' : '0',
|
||||
'Weight' => null,
|
||||
'Remark' => $row['description'],
|
||||
'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;
|
||||
}
|
||||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null)
|
||||
{
|
||||
if ($SubDomain == '') $SubDomain = '@';
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line);
|
||||
}
|
||||
|
||||
//获取解析记录详细信息
|
||||
public function getDomainRecordInfo($RecordId)
|
||||
{
|
||||
$query = ['id' => $RecordId];
|
||||
$data = $this->send_reuqest('GET', '/v1/dns/zone/'.$this->domain.'/record', $query);
|
||||
if ($data && !empty($data['records'])) {
|
||||
$data = $data['records'][0];
|
||||
return [
|
||||
'RecordId' => $data['id'],
|
||||
'Domain' => rtrim($data['zone_name'], '.'),
|
||||
'Name' => str_replace('.'.$data['zone_name'], '', $data['name']),
|
||||
'Type' => $data['type'],
|
||||
'Value' => $data['value'],
|
||||
'Line' => $data['line'],
|
||||
'TTL' => $data['ttl'],
|
||||
'MX' => $data['priority'],
|
||||
'Status' => $data['status'] == 'running' ? '1' : '0',
|
||||
'Weight' => null,
|
||||
'Remark' => $data['description'],
|
||||
'UpdateTime' => null,
|
||||
];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//添加解析记录
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$params = ['rr' => $Name, 'type' => $this->convertType($Type), 'value' => $Value, 'line' => $Line, 'ttl' => intval($TTL), 'description' => $Remark];
|
||||
if ($Type == 'MX') $params['priority'] = intval($MX);
|
||||
$query = ['clientToken' => getSid()];
|
||||
return $this->send_reuqest('POST', '/v1/dns/zone/'.$this->domain.'/record', $query, $params);
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$params = ['rr' => $Name, 'type' => $this->convertType($Type), 'value' => $Value, 'line' => $Line, 'ttl' => intval($TTL), 'description' => $Remark];
|
||||
if ($Type == 'MX') $params['priority'] = intval($MX);
|
||||
$query = ['clientToken' => getSid()];
|
||||
return $this->send_reuqest('PUT', '/v1/dns/zone/'.$this->domain.'/record/'.$RecordId, $query, $params);
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$query = ['clientToken' => getSid()];
|
||||
return $this->send_reuqest('DELETE', '/v1/dns/zone/'.$this->domain.'/record/'.$RecordId, $query);
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$Status = $Status == '1' ? 'enable' : 'disable';
|
||||
$query = [$Status => '', 'clientToken' => getSid()];
|
||||
return $this->send_reuqest('PUT', '/v1/dns/zone/'.$this->domain.'/record/'.$RecordId, $query);
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
public function getRecordLine()
|
||||
{
|
||||
return [
|
||||
'default' => ['name' => '默认', 'parent' => null],
|
||||
'ct' => ['name' => '电信', 'parent' => null],
|
||||
'cnc' => ['name' => '联通', 'parent' => null],
|
||||
'cmnet' => ['name' => '移动', 'parent' => null],
|
||||
'edu' => ['name' => '教育网', 'parent' => null],
|
||||
'search' => ['name' => '搜索引擎(百度)', 'parent' => null],
|
||||
];
|
||||
}
|
||||
|
||||
//获取域名概览信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
$res = $this->getDomainList($this->domain);
|
||||
if ($res && !empty($res['list'])) {
|
||||
return $res['list'][0];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
private function send_reuqest($method, $path, $query = null, $params = null)
|
||||
{
|
||||
try{
|
||||
return $this->client->request($method, $path, $query, $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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,329 +1,335 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
|
||||
class cloudflare implements DnsInterface
|
||||
{
|
||||
private $Email;
|
||||
private $ApiKey;
|
||||
private $baseUrl = 'https://api.cloudflare.com/client/v4';
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private $proxy;
|
||||
|
||||
function __construct($config)
|
||||
{
|
||||
$this->Email = $config['ak'];
|
||||
$this->ApiKey = $config['sk'];
|
||||
$this->domain = $config['domain'];
|
||||
$this->domainid = $config['domainid'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$param = ['page' => $PageNumber, 'per_page' => $PageSize];
|
||||
if (!empty($KeyWord)) {
|
||||
$param['name'] = $KeyWord;
|
||||
}
|
||||
$data = $this->send_reuqest('GET', '/zones', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['result'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['id'],
|
||||
'Domain' => $row['name'],
|
||||
'RecordCount' => 0,
|
||||
];
|
||||
}
|
||||
return ['total' => $data['result_info']['total_count'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
if (!isNullOrEmpty($Value)) $KeyWord = $Value;
|
||||
$param = ['type' => $Type, 'search' => $KeyWord, 'page' => $PageNumber, 'per_page' => $PageSize];
|
||||
if (!isNullOrEmpty($SubDomain)) {
|
||||
if ($SubDomain == '@') $SubDomain = $this->domain;
|
||||
else $SubDomain .= '.' . $this->domain;
|
||||
$param['name'] = $SubDomain;
|
||||
}
|
||||
if (!isNullOrEmpty($Line)) {
|
||||
$param['proxied'] = $Line == '1' ? 'true' : 'false';
|
||||
}
|
||||
$data = $this->send_reuqest('GET', '/zones/'.$this->domainid.'/dns_records', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['result'] as $row) {
|
||||
$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' => $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' => $status,
|
||||
'Weight' => null,
|
||||
'Remark' => $row['comment'],
|
||||
'UpdateTime' => $row['modified_on'],
|
||||
];
|
||||
}
|
||||
return ['total' => $data['result_info']['total_count'], '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)
|
||||
{
|
||||
$data = $this->send_reuqest('GET', '/zones/'.$this->domainid.'/dns_records/'.$RecordId);
|
||||
if ($data) {
|
||||
$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' => $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' => $status,
|
||||
'Weight' => null,
|
||||
'Remark' => $data['result']['comment'],
|
||||
'UpdateTime' => $data['result']['modified_on'],
|
||||
];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//添加解析记录
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$param = ['name' => $Name, 'type' => $this->convertType($Type), 'content' => $Value, 'proxied' => $Line == '1', 'ttl' => intval($TTL), 'comment' => $Remark];
|
||||
if ($Type == 'MX') $param['priority'] = intval($MX);
|
||||
if ($Type == 'CAA' || $Type == 'SRV') {
|
||||
unset($param['content']);
|
||||
$param['data'] = $this->convertValue($Value, $Type);
|
||||
}
|
||||
$data = $this->send_reuqest('POST', '/zones/'.$this->domainid.'/dns_records', $param);
|
||||
return is_array($data) ? $data['result']['id'] : false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$param = ['name' => $Name, 'type' => $this->convertType($Type), 'content' => $Value, 'proxied' => $Line == '1', 'ttl' => intval($TTL), 'comment' => $Remark];
|
||||
if ($Type == 'MX') $param['priority'] = intval($MX);
|
||||
if ($Type == 'CAA' || $Type == 'SRV') {
|
||||
unset($param['content']);
|
||||
$param['data'] = $this->convertValue($Value, $Type);
|
||||
}
|
||||
$data = $this->send_reuqest('PATCH', '/zones/'.$this->domainid.'/dns_records/'.$RecordId, $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$data = $this->send_reuqest('DELETE', '/zones/'.$this->domainid.'/dns_records/'.$RecordId);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$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']);
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
public function getRecordLine()
|
||||
{
|
||||
return ['0' => ['name' => '仅DNS', 'parent' => null], '1' => ['name' => '已代理', 'parent' => null]];
|
||||
}
|
||||
|
||||
//获取域名信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
$data = $this->send_reuqest('GET', '/zones/'.$this->domainid);
|
||||
if ($data) {
|
||||
return $data['result'];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
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'];
|
||||
if (array_key_exists($type, $convert_dict)) {
|
||||
return $convert_dict[$type];
|
||||
}
|
||||
return $type;
|
||||
}
|
||||
|
||||
private function convertValue($value, $type)
|
||||
{
|
||||
if ($type == 'SRV') {
|
||||
$arr = explode(' ', $value);
|
||||
if (count($arr) > 3) {
|
||||
$data = [
|
||||
'priority' => intval($arr[0]),
|
||||
'weight' => intval($arr[1]),
|
||||
'port' => intval($arr[2]),
|
||||
'target' => $arr[3],
|
||||
];
|
||||
} else {
|
||||
$data = [
|
||||
'weight' => intval($arr[0]),
|
||||
'port' => intval($arr[1]),
|
||||
'target' => $arr[2],
|
||||
];
|
||||
}
|
||||
} elseif ($type == 'CAA') {
|
||||
$arr = explode(' ', $value);
|
||||
$data = [
|
||||
'flags' => intval($arr[0]),
|
||||
'tag' => $arr[1],
|
||||
'value' => trim($arr[2], '"'),
|
||||
];
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function send_reuqest($method, $path, $params = null)
|
||||
{
|
||||
$url = $this->baseUrl . $path;
|
||||
|
||||
if (preg_match('/^[0-9a-z]+$/i', $this->ApiKey)) {
|
||||
$headers = [
|
||||
'X-Auth-Email: ' . $this->Email,
|
||||
'X-Auth-Key: ' . $this->ApiKey,
|
||||
];
|
||||
} else {
|
||||
$headers = [
|
||||
'Authorization: Bearer ' . $this->ApiKey,
|
||||
];
|
||||
}
|
||||
|
||||
$body = '';
|
||||
if ($method == 'GET' || $method == 'DELETE') {
|
||||
if ($params) {
|
||||
$url .= '?' . http_build_query($params);
|
||||
}
|
||||
} else {
|
||||
$body = json_encode($params);
|
||||
$headers[] = 'Content-Type: application/json';
|
||||
}
|
||||
|
||||
$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, $headers);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
if ($method == 'POST') {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
} elseif ($method == 'PUT') {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
} elseif ($method == 'PATCH') {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
} elseif ($method == 'DELETE') {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
|
||||
}
|
||||
$response = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
if ($errno) {
|
||||
$this->setError('Curl error: ' . curl_error($ch));
|
||||
}
|
||||
curl_close($ch);
|
||||
if ($errno) return false;
|
||||
|
||||
$arr = json_decode($response, true);
|
||||
if ($arr) {
|
||||
if ($arr['success']) {
|
||||
return $arr;
|
||||
} else {
|
||||
$this->setError(isset($arr['errors'][0]) ? $arr['errors'][0]['message'] : '未知错误');
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->setError('返回数据解析失败');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function setError($message)
|
||||
{
|
||||
$this->error = $message;
|
||||
//file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
|
||||
class cloudflare implements DnsInterface
|
||||
{
|
||||
private $Email;
|
||||
private $ApiKey;
|
||||
private $baseUrl = 'https://api.cloudflare.com/client/v4';
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private $proxy;
|
||||
|
||||
function __construct($config)
|
||||
{
|
||||
$this->Email = $config['ak'];
|
||||
$this->ApiKey = $config['sk'];
|
||||
$this->domain = $config['domain'];
|
||||
$this->domainid = $config['domainid'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$param = ['page' => $PageNumber, 'per_page' => $PageSize];
|
||||
if (!empty($KeyWord)) {
|
||||
$param['name'] = $KeyWord;
|
||||
}
|
||||
$data = $this->send_reuqest('GET', '/zones', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['result'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['id'],
|
||||
'Domain' => $row['name'],
|
||||
'RecordCount' => 0,
|
||||
];
|
||||
}
|
||||
return ['total' => $data['result_info']['total_count'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
if (!isNullOrEmpty($Value)) $KeyWord = $Value;
|
||||
$param = ['type' => $Type, 'search' => $KeyWord, 'page' => $PageNumber, 'per_page' => $PageSize];
|
||||
if (!isNullOrEmpty($SubDomain)) {
|
||||
if ($SubDomain == '@') $SubDomain = $this->domain;
|
||||
else $SubDomain .= '.' . $this->domain;
|
||||
$param['name'] = $SubDomain;
|
||||
}
|
||||
if (!isNullOrEmpty($Line)) {
|
||||
$param['proxied'] = $Line == '1' ? 'true' : 'false';
|
||||
}
|
||||
$data = $this->send_reuqest('GET', '/zones/'.$this->domainid.'/dns_records', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['result'] as $row) {
|
||||
$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;
|
||||
if ($row['type'] == 'SRV' && isset($row['priority'])) {
|
||||
$row['content'] = $row['priority'] . ' ' . $row['content'];
|
||||
}
|
||||
$list[] = [
|
||||
'RecordId' => $row['id'],
|
||||
'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' => $status,
|
||||
'Weight' => null,
|
||||
'Remark' => $row['comment'],
|
||||
'UpdateTime' => $row['modified_on'],
|
||||
];
|
||||
}
|
||||
return ['total' => $data['result_info']['total_count'], '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)
|
||||
{
|
||||
$data = $this->send_reuqest('GET', '/zones/'.$this->domainid.'/dns_records/'.$RecordId);
|
||||
if ($data) {
|
||||
$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;
|
||||
if ($data['result']['type'] == 'SRV' && isset($data['result']['priority'])) {
|
||||
$data['result']['content'] = $data['result']['priority'] . ' ' . $data['result']['content'];
|
||||
}
|
||||
return [
|
||||
'RecordId' => $data['result']['id'],
|
||||
'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' => $status,
|
||||
'Weight' => null,
|
||||
'Remark' => $data['result']['comment'],
|
||||
'UpdateTime' => $data['result']['modified_on'],
|
||||
];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//添加解析记录
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$param = ['name' => $Name, 'type' => $this->convertType($Type), 'content' => $Value, 'proxied' => $Line == '1', 'ttl' => intval($TTL), 'comment' => $Remark];
|
||||
if ($Type == 'MX') $param['priority'] = intval($MX);
|
||||
if ($Type == 'CAA' || $Type == 'SRV') {
|
||||
unset($param['content']);
|
||||
$param['data'] = $this->convertValue($Value, $Type);
|
||||
}
|
||||
$data = $this->send_reuqest('POST', '/zones/'.$this->domainid.'/dns_records', $param);
|
||||
return is_array($data) ? $data['result']['id'] : false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$param = ['name' => $Name, 'type' => $this->convertType($Type), 'content' => $Value, 'proxied' => $Line == '1', 'ttl' => intval($TTL), 'comment' => $Remark];
|
||||
if ($Type == 'MX') $param['priority'] = intval($MX);
|
||||
if ($Type == 'CAA' || $Type == 'SRV') {
|
||||
unset($param['content']);
|
||||
$param['data'] = $this->convertValue($Value, $Type);
|
||||
}
|
||||
$data = $this->send_reuqest('PATCH', '/zones/'.$this->domainid.'/dns_records/'.$RecordId, $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$data = $this->send_reuqest('DELETE', '/zones/'.$this->domainid.'/dns_records/'.$RecordId);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$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']);
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
public function getRecordLine()
|
||||
{
|
||||
return ['0' => ['name' => '仅DNS', 'parent' => null], '1' => ['name' => '已代理', 'parent' => null]];
|
||||
}
|
||||
|
||||
//获取域名信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
$data = $this->send_reuqest('GET', '/zones/'.$this->domainid);
|
||||
if ($data) {
|
||||
return $data['result'];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
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'];
|
||||
if (array_key_exists($type, $convert_dict)) {
|
||||
return $convert_dict[$type];
|
||||
}
|
||||
return $type;
|
||||
}
|
||||
|
||||
private function convertValue($value, $type)
|
||||
{
|
||||
if ($type == 'SRV') {
|
||||
$arr = explode(' ', $value);
|
||||
if (count($arr) > 3) {
|
||||
$data = [
|
||||
'priority' => intval($arr[0]),
|
||||
'weight' => intval($arr[1]),
|
||||
'port' => intval($arr[2]),
|
||||
'target' => $arr[3],
|
||||
];
|
||||
} else {
|
||||
$data = [
|
||||
'weight' => intval($arr[0]),
|
||||
'port' => intval($arr[1]),
|
||||
'target' => $arr[2],
|
||||
];
|
||||
}
|
||||
} elseif ($type == 'CAA') {
|
||||
$arr = explode(' ', $value);
|
||||
$data = [
|
||||
'flags' => intval($arr[0]),
|
||||
'tag' => $arr[1],
|
||||
'value' => trim($arr[2], '"'),
|
||||
];
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function send_reuqest($method, $path, $params = null)
|
||||
{
|
||||
$url = $this->baseUrl . $path;
|
||||
|
||||
if (preg_match('/^[0-9a-f]+$/i', $this->ApiKey)) {
|
||||
$headers = [
|
||||
'X-Auth-Email: ' . $this->Email,
|
||||
'X-Auth-Key: ' . $this->ApiKey,
|
||||
];
|
||||
} else {
|
||||
$headers = [
|
||||
'Authorization: Bearer ' . $this->ApiKey,
|
||||
];
|
||||
}
|
||||
|
||||
$body = '';
|
||||
if ($method == 'GET' || $method == 'DELETE') {
|
||||
if ($params) {
|
||||
$url .= '?' . http_build_query($params);
|
||||
}
|
||||
} else {
|
||||
$body = json_encode($params);
|
||||
$headers[] = 'Content-Type: application/json';
|
||||
}
|
||||
|
||||
$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, $headers);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
if ($method == 'POST') {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
} elseif ($method == 'PUT') {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
} elseif ($method == 'PATCH') {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
} elseif ($method == 'DELETE') {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
|
||||
}
|
||||
$response = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
if ($errno) {
|
||||
$this->setError('Curl error: ' . curl_error($ch));
|
||||
}
|
||||
curl_close($ch);
|
||||
if ($errno) return false;
|
||||
|
||||
$arr = json_decode($response, true);
|
||||
if ($arr) {
|
||||
if ($arr['success']) {
|
||||
return $arr;
|
||||
} else {
|
||||
$this->setError(isset($arr['errors'][0]) ? $arr['errors'][0]['message'] : '未知错误');
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->setError('返回数据解析失败');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function setError($message)
|
||||
{
|
||||
$this->error = $message;
|
||||
//file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,304 +1,304 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
|
||||
class dnsla implements DnsInterface
|
||||
{
|
||||
private $apiid;
|
||||
private $apisecret;
|
||||
private $baseUrl = 'https://api.dns.la';
|
||||
private $typeList = [1 => 'A', 2 => 'NS', 5 => 'CNAME', 15 => 'MX', 16 => 'TXT', 28 => 'AAAA', 33 => 'SRV', 257 => 'CAA', 256 => 'URL转发'];
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->apiid = $config['ak'];
|
||||
$this->apisecret = $config['sk'];
|
||||
$this->domain = $config['domain'];
|
||||
$this->domainid = $config['domainid'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$param = ['pageIndex' => $PageNumber, 'pageSize' => $PageSize];
|
||||
$data = $this->execute('GET', '/api/domainList', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['results'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['id'],
|
||||
'Domain' => rtrim($row['displayDomain'], '.'),
|
||||
'RecordCount' => 0,
|
||||
];
|
||||
}
|
||||
return ['total' => $data['total'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
$param = ['domainId' => $this->domainid, 'pageIndex' => $PageNumber, 'pageSize' => $PageSize];
|
||||
if (!isNullOrEmpty(($KeyWord))) {
|
||||
$param['host'] = $KeyWord;
|
||||
}
|
||||
if (!isNullOrEmpty(($Type))) {
|
||||
$param['type'] = $this->convertType($Type);
|
||||
}
|
||||
if (!isNullOrEmpty(($Line))) {
|
||||
$param['lineId'] = $Line;
|
||||
}
|
||||
if (!isNullOrEmpty(($SubDomain))) {
|
||||
$param['host'] = $SubDomain;
|
||||
}
|
||||
if (!isNullOrEmpty(($Value))) {
|
||||
$param['data'] = $Value;
|
||||
}
|
||||
$data = $this->execute('GET', '/api/recordList', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['results'] as $row) {
|
||||
$list[] = [
|
||||
'RecordId' => $row['id'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $row['host'],
|
||||
'Type' => $this->convertTypeId($row['type'], isset($row['domaint']) ? $row['domaint'] : false),
|
||||
'Value' => $row['data'],
|
||||
'Line' => $row['lineId'],
|
||||
'TTL' => $row['ttl'],
|
||||
'MX' => isset($row['preference']) ? $row['preference'] : null,
|
||||
'Status' => $row['disable'] ? '0' : '1',
|
||||
'Weight' => isset($row['weight']) ? $row['weight'] : null,
|
||||
'Remark' => null,
|
||||
'UpdateTime' => date('Y-m-d H:i:s', $row['updatedAt']),
|
||||
];
|
||||
}
|
||||
return ['total' => $data['total'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null)
|
||||
{
|
||||
if ($SubDomain == '') $SubDomain = '@';
|
||||
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)
|
||||
{
|
||||
$param = ['domainId' => $this->domainid, 'type' => $this->convertType($Type), 'host' => $Name, 'data' => $Value, 'ttl' => intval($TTL), 'lineId' => $Line];
|
||||
if ($Type == 'MX') $param['preference'] = intval($MX);
|
||||
if ($Type == 'REDIRECT_URL') {
|
||||
$param['type'] = 256;
|
||||
$param['dominant'] = true;
|
||||
} elseif ($Type == 'FORWARD_URL') {
|
||||
$param['type'] = 256;
|
||||
$param['dominant'] = false;
|
||||
}
|
||||
if ($Weight > 0) $param['weight'] = $Weight;
|
||||
$data = $this->execute('POST', '/api/record', $param);
|
||||
return is_array($data) ? $data['id'] : false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$param = ['id' => $RecordId, 'type' => $this->convertType($Type), 'host' => $Name, 'data' => $Value, 'ttl' => intval($TTL), 'lineId' => $Line];
|
||||
if ($Type == 'MX') $param['preference'] = intval($MX);
|
||||
if ($Type == 'REDIRECT_URL') {
|
||||
$param['type'] = 256;
|
||||
$param['dominant'] = true;
|
||||
} elseif ($Type == 'FORWARD_URL') {
|
||||
$param['type'] = 256;
|
||||
$param['dominant'] = false;
|
||||
}
|
||||
if ($Weight > 0) $param['weight'] = $Weight;
|
||||
$data = $this->execute('PUT', '/api/record', $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$param = ['id' => $RecordId];
|
||||
$data = $this->execute('DELETE', '/api/record', $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$param = ['id' => $RecordId, 'disable' => $Status == '0' ? true : false];
|
||||
$data = $this->execute('PUT', '/api/recordDisable', $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
public function getRecordLine()
|
||||
{
|
||||
$param = ['domain' => $this->domain];
|
||||
$data = $this->execute('GET', '/api/availableLine', $param);
|
||||
if ($data) {
|
||||
array_multisort(array_column($data, 'order'), SORT_ASC, $data);
|
||||
$list = [];
|
||||
foreach ($data as $row) {
|
||||
if ($row['id'] == '0') $row['id'] = '';
|
||||
$list[$row['id']] = ['name' => $row['value'], 'parent' => !empty($row['pid']) ? $row['pid'] : null];
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
$param = ['id' => $this->domainid];
|
||||
$data = $this->execute('GET', '/api/domain', $param);
|
||||
return $data;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
$param = ['id' => $this->domainid];
|
||||
$data = $this->execute('GET', '/api/dnsMeasures', $param);
|
||||
if ($data && isset($data['minTTL'])) {
|
||||
return $data['minTTL'];
|
||||
}
|
||||
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);
|
||||
return $typeList[$type];
|
||||
}
|
||||
|
||||
private function convertTypeId($typeId, $domaint)
|
||||
{
|
||||
if ($typeId == 256) return $domaint ? 'REDIRECT_URL' : 'FORWARD_URL';
|
||||
return $this->typeList[$typeId];
|
||||
}
|
||||
|
||||
private function execute($method, $path, $params = null)
|
||||
{
|
||||
$token = base64_encode($this->apiid.':'.$this->apisecret);
|
||||
$header = ['Authorization: Basic '.$token, 'Content-Type: application/json; charset=utf-8'];
|
||||
if ($method == 'POST' || $method == 'PUT') {
|
||||
$response = $this->curl($method, $path, $header, json_encode($params));
|
||||
} else {
|
||||
if ($params) {
|
||||
$path .= '?'.http_build_query($params);
|
||||
}
|
||||
$response = $this->curl($method, $path, $header);
|
||||
}
|
||||
if (!$response) {
|
||||
return false;
|
||||
}
|
||||
$arr = json_decode($response, true);
|
||||
if ($arr) {
|
||||
if ($arr['code'] == 200) {
|
||||
return $arr['data'];
|
||||
} else {
|
||||
$this->setError($arr['msg']);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->setError('返回数据解析失败');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function curl($method, $path, $header, $body = null)
|
||||
{
|
||||
$url = $this->baseUrl . $path;
|
||||
$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 ($body) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
}
|
||||
$response = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
if ($errno) {
|
||||
$this->setError('Curl error: ' . curl_error($ch));
|
||||
}
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
if ($errno) return false;
|
||||
if ($httpCode == 200) {
|
||||
return $response;
|
||||
} elseif ($httpCode == 401) {
|
||||
$this->setError('认证失败');
|
||||
return false;
|
||||
} else {
|
||||
$this->setError('http code: '.$httpCode);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function setError($message)
|
||||
{
|
||||
$this->error = $message;
|
||||
//file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
|
||||
class dnsla implements DnsInterface
|
||||
{
|
||||
private $apiid;
|
||||
private $apisecret;
|
||||
private $baseUrl = 'https://api.dns.la';
|
||||
private $typeList = [1 => 'A', 2 => 'NS', 5 => 'CNAME', 15 => 'MX', 16 => 'TXT', 28 => 'AAAA', 33 => 'SRV', 257 => 'CAA', 256 => 'URL转发'];
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->apiid = $config['ak'];
|
||||
$this->apisecret = $config['sk'];
|
||||
$this->domain = $config['domain'];
|
||||
$this->domainid = $config['domainid'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$param = ['pageIndex' => $PageNumber, 'pageSize' => $PageSize];
|
||||
$data = $this->execute('GET', '/api/domainList', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['results'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['id'],
|
||||
'Domain' => rtrim($row['displayDomain'], '.'),
|
||||
'RecordCount' => 0,
|
||||
];
|
||||
}
|
||||
return ['total' => $data['total'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
$param = ['domainId' => $this->domainid, 'pageIndex' => $PageNumber, 'pageSize' => $PageSize];
|
||||
if (!isNullOrEmpty(($KeyWord))) {
|
||||
$param['host'] = $KeyWord;
|
||||
}
|
||||
if (!isNullOrEmpty(($Type))) {
|
||||
$param['type'] = $this->convertType($Type);
|
||||
}
|
||||
if (!isNullOrEmpty(($Line))) {
|
||||
$param['lineId'] = $Line;
|
||||
}
|
||||
if (!isNullOrEmpty(($SubDomain))) {
|
||||
$param['host'] = $SubDomain;
|
||||
}
|
||||
if (!isNullOrEmpty(($Value))) {
|
||||
$param['data'] = $Value;
|
||||
}
|
||||
$data = $this->execute('GET', '/api/recordList', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['results'] as $row) {
|
||||
$list[] = [
|
||||
'RecordId' => $row['id'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $row['host'],
|
||||
'Type' => $this->convertTypeId($row['type'], isset($row['domaint']) ? $row['domaint'] : false),
|
||||
'Value' => $row['data'],
|
||||
'Line' => $row['lineId'],
|
||||
'TTL' => $row['ttl'],
|
||||
'MX' => isset($row['preference']) ? $row['preference'] : null,
|
||||
'Status' => $row['disable'] ? '0' : '1',
|
||||
'Weight' => isset($row['weight']) ? $row['weight'] : null,
|
||||
'Remark' => null,
|
||||
'UpdateTime' => date('Y-m-d H:i:s', $row['updatedAt']),
|
||||
];
|
||||
}
|
||||
return ['total' => $data['total'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null)
|
||||
{
|
||||
if ($SubDomain == '') $SubDomain = '@';
|
||||
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)
|
||||
{
|
||||
$param = ['domainId' => $this->domainid, 'type' => $this->convertType($Type), 'host' => $Name, 'data' => $Value, 'ttl' => intval($TTL), 'lineId' => $Line];
|
||||
if ($Type == 'MX') $param['preference'] = intval($MX);
|
||||
if ($Type == 'REDIRECT_URL') {
|
||||
$param['type'] = 256;
|
||||
$param['dominant'] = true;
|
||||
} elseif ($Type == 'FORWARD_URL') {
|
||||
$param['type'] = 256;
|
||||
$param['dominant'] = false;
|
||||
}
|
||||
if ($Weight > 0) $param['weight'] = $Weight;
|
||||
$data = $this->execute('POST', '/api/record', $param);
|
||||
return is_array($data) ? $data['id'] : false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$param = ['id' => $RecordId, 'type' => $this->convertType($Type), 'host' => $Name, 'data' => $Value, 'ttl' => intval($TTL), 'lineId' => $Line];
|
||||
if ($Type == 'MX') $param['preference'] = intval($MX);
|
||||
if ($Type == 'REDIRECT_URL') {
|
||||
$param['type'] = 256;
|
||||
$param['dominant'] = true;
|
||||
} elseif ($Type == 'FORWARD_URL') {
|
||||
$param['type'] = 256;
|
||||
$param['dominant'] = false;
|
||||
}
|
||||
if ($Weight > 0) $param['weight'] = $Weight;
|
||||
$data = $this->execute('PUT', '/api/record', $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$param = ['id' => $RecordId];
|
||||
$data = $this->execute('DELETE', '/api/record', $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$param = ['id' => $RecordId, 'disable' => $Status == '0' ? true : false];
|
||||
$data = $this->execute('PUT', '/api/recordDisable', $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
public function getRecordLine()
|
||||
{
|
||||
$param = ['domain' => $this->domain];
|
||||
$data = $this->execute('GET', '/api/availableLine', $param);
|
||||
if ($data) {
|
||||
array_multisort(array_column($data, 'order'), SORT_ASC, $data);
|
||||
$list = [];
|
||||
foreach ($data as $row) {
|
||||
if ($row['id'] == '0') $row['id'] = '';
|
||||
$list[$row['id']] = ['name' => $row['value'], 'parent' => !empty($row['pid']) ? $row['pid'] : null];
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
$param = ['id' => $this->domainid];
|
||||
$data = $this->execute('GET', '/api/domain', $param);
|
||||
return $data;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
$param = ['id' => $this->domainid];
|
||||
$data = $this->execute('GET', '/api/dnsMeasures', $param);
|
||||
if ($data && isset($data['minTTL'])) {
|
||||
return $data['minTTL'];
|
||||
}
|
||||
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);
|
||||
return $typeList[$type];
|
||||
}
|
||||
|
||||
private function convertTypeId($typeId, $domaint)
|
||||
{
|
||||
if ($typeId == 256) return $domaint ? 'REDIRECT_URL' : 'FORWARD_URL';
|
||||
return $this->typeList[$typeId];
|
||||
}
|
||||
|
||||
private function execute($method, $path, $params = null)
|
||||
{
|
||||
$token = base64_encode($this->apiid.':'.$this->apisecret);
|
||||
$header = ['Authorization: Basic '.$token, 'Content-Type: application/json; charset=utf-8'];
|
||||
if ($method == 'POST' || $method == 'PUT') {
|
||||
$response = $this->curl($method, $path, $header, json_encode($params));
|
||||
} else {
|
||||
if ($params) {
|
||||
$path .= '?'.http_build_query($params);
|
||||
}
|
||||
$response = $this->curl($method, $path, $header);
|
||||
}
|
||||
if (!$response) {
|
||||
return false;
|
||||
}
|
||||
$arr = json_decode($response, true);
|
||||
if ($arr) {
|
||||
if ($arr['code'] == 200) {
|
||||
return $arr['data'];
|
||||
} else {
|
||||
$this->setError($arr['msg']);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->setError('返回数据解析失败');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function curl($method, $path, $header, $body = null)
|
||||
{
|
||||
$url = $this->baseUrl . $path;
|
||||
$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 ($body) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
}
|
||||
$response = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
if ($errno) {
|
||||
$this->setError('Curl error: ' . curl_error($ch));
|
||||
}
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
if ($errno) return false;
|
||||
if ($httpCode == 200) {
|
||||
return $response;
|
||||
} elseif ($httpCode == 401) {
|
||||
$this->setError('认证失败');
|
||||
return false;
|
||||
} else {
|
||||
$this->setError('http code: '.$httpCode);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function setError($message)
|
||||
{
|
||||
$this->error = $message;
|
||||
//file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,373 +1,373 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
use app\lib\client\TencentCloud;
|
||||
use Exception;
|
||||
|
||||
class dnspod implements DnsInterface
|
||||
{
|
||||
private $SecretId;
|
||||
private $SecretKey;
|
||||
private $endpoint = "dnspod.tencentcloudapi.com";
|
||||
private $service = "dnspod";
|
||||
private $version = "2021-03-23";
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private $domainInfo;
|
||||
private TencentCloud $client;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->SecretId = $config['ak'];
|
||||
$this->SecretKey = $config['sk'];
|
||||
$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->domain = $config['domain'];
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$action = 'DescribeDomainList';
|
||||
$offset = ($PageNumber - 1) * $PageSize;
|
||||
$param = ['Offset' => $offset, 'Limit' => $PageSize, 'Keyword' => $KeyWord];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['DomainList'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['DomainId'],
|
||||
'Domain' => $row['Name'],
|
||||
'RecordCount' => $row['RecordCount'],
|
||||
];
|
||||
}
|
||||
return ['total' => $data['DomainCountInfo']['DomainTotal'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
$offset = ($PageNumber - 1) * $PageSize;
|
||||
if (!isNullOrEmpty($Status) || !isNullOrEmpty($Value)) {
|
||||
$action = 'DescribeRecordFilterList';
|
||||
$param = ['Domain' => $this->domain, 'Offset' => $offset, 'Limit' => $PageSize, 'RecordValue' => $Value];
|
||||
if (!isNullOrEmpty($SubDomain)) $param['SubDomain'] = $SubDomain;
|
||||
if (!isNullOrEmpty($KeyWord)) $param['Keyword'] = $KeyWord;
|
||||
if (!isNullOrEmpty($Value)) $param['RecordValue'] = $Value;
|
||||
if (!isNullOrEmpty($Status)) {
|
||||
$Status = $Status == '1' ? 'ENABLE' : 'DISABLE';
|
||||
$param['RecordStatus'] = [$Status];
|
||||
}
|
||||
if (!isNullOrEmpty($Type)) $param['RecordType'] = [$this->convertType($Type)];
|
||||
if (!isNullOrEmpty($Line)) $param['RecordLine'] = [$Line];
|
||||
} else {
|
||||
$action = 'DescribeRecordList';
|
||||
$param = ['Domain' => $this->domain, 'Subdomain' => $SubDomain, 'RecordType' => $this->convertType($Type), 'RecordLineId' => $Line, 'Keyword' => $KeyWord, 'Offset' => $offset, 'Limit' => $PageSize];
|
||||
}
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['RecordList'] as $row) {
|
||||
//if($row['Name'] == '@' && $row['Type'] == 'NS') continue;
|
||||
$list[] = [
|
||||
'RecordId' => $row['RecordId'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $row['Name'],
|
||||
'Type' => $this->convertTypeId($row['Type']),
|
||||
'Value' => $row['Value'],
|
||||
'Line' => $row['LineId'],
|
||||
'TTL' => $row['TTL'],
|
||||
'MX' => $row['MX'],
|
||||
'Status' => $row['Status'] == 'ENABLE' ? '1' : '0',
|
||||
'Weight' => $row['Weight'],
|
||||
'Remark' => $row['Remark'],
|
||||
'UpdateTime' => $row['UpdatedOn'],
|
||||
];
|
||||
}
|
||||
return ['total' => $data['RecordCountInfo']['TotalCount'], 'list' => $list];
|
||||
} elseif ($this->error == '记录列表为空。' || $this->error == 'No records on the list.') {
|
||||
return ['total' => 0, 'list' => []];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null)
|
||||
{
|
||||
if ($SubDomain == '') $SubDomain = '@';
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line);
|
||||
}
|
||||
|
||||
//获取解析记录详细信息
|
||||
public function getDomainRecordInfo($RecordId)
|
||||
{
|
||||
$action = 'DescribeRecord';
|
||||
$param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId)];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
return [
|
||||
'RecordId' => $data['RecordInfo']['Id'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $data['RecordInfo']['SubDomain'],
|
||||
'Type' => $this->convertTypeId($data['RecordInfo']['RecordType']),
|
||||
'Value' => $data['RecordInfo']['Value'],
|
||||
'Line' => $data['RecordInfo']['RecordLineId'],
|
||||
'TTL' => $data['RecordInfo']['TTL'],
|
||||
'MX' => $data['RecordInfo']['MX'],
|
||||
'Status' => $data['RecordInfo']['Enabled'] == 1 ? '1' : '0',
|
||||
'Weight' => $data['RecordInfo']['Weight'],
|
||||
'Remark' => $data['RecordInfo']['Remark'],
|
||||
'UpdateTime' => $data['RecordInfo']['UpdatedOn'],
|
||||
];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//添加解析记录
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$action = 'CreateRecord';
|
||||
$param = ['Domain' => $this->domain, 'SubDomain' => $Name, 'RecordType' => $this->convertType($Type), 'Value' => $Value, 'RecordLine' => $Line, 'RecordLineId' => $this->convertLineCode($Line), 'TTL' => intval($TTL), 'Weight' => $Weight];
|
||||
if ($Type == 'MX') $param['MX'] = intval($MX);
|
||||
$data = $this->send_request($action, $param);
|
||||
return is_array($data) ? $data['RecordId'] : false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$action = 'ModifyRecord';
|
||||
$param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId), 'SubDomain' => $Name, 'RecordType' => $this->convertType($Type), 'Value' => $Value, 'RecordLine' => $Line, 'RecordLineId' => $this->convertLineCode($Line), 'TTL' => intval($TTL), 'Weight' => $Weight];
|
||||
if ($Type == 'MX') $param['MX'] = intval($MX);
|
||||
$data = $this->send_request($action, $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
$action = 'ModifyRecordRemark';
|
||||
$param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId), 'Remark' => $Remark];
|
||||
$data = $this->send_request($action, $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$action = 'DeleteRecord';
|
||||
$param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId)];
|
||||
$data = $this->send_request($action, $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$Status = $Status == '1' ? 'ENABLE' : 'DISABLE';
|
||||
$action = 'ModifyRecordStatus';
|
||||
$param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId), 'Status' => $Status];
|
||||
$data = $this->send_request($action, $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null)
|
||||
{
|
||||
$action = 'DescribeDomainLogList';
|
||||
$offset = ($PageNumber - 1) * $PageSize;
|
||||
$param = ['Domain' => $this->domain, 'Offset' => $offset, 'Limit' => $PageSize];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['LogList'] as $row) {
|
||||
$list[] = ['time' => substr($row, 0, strpos($row, '(')), 'ip' => substr($row, strpos($row, '(') + 1, strpos($row, ')') - strpos($row, '(') - 1), 'data' => substr($row, strpos($row, ')') + 1, strpos($row, ' Uin:') - strpos($row, ')') - 1)];
|
||||
}
|
||||
return ['total' => $data['TotalCount'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
public function getRecordLine()
|
||||
{
|
||||
$action = 'DescribeRecordLineCategoryList';
|
||||
$param = ['Domain' => $this->domain];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
$this->processLineList($list, $data['LineList'], null);
|
||||
return $list;
|
||||
} else {
|
||||
$data = $this->getRecordLineByGrade();
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data as $row) {
|
||||
$list[$row['LineId']] = ['name' => $row['Name'], 'parent' => null];
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function processLineList(&$list, $line_list, $parent)
|
||||
{
|
||||
foreach ($line_list as $row) {
|
||||
if (isNullOrEmpty($row['LineId'])) $row['LineId'] = 'N.' . $row['LineName'];
|
||||
if ($row['Useful'] && !isset($list[$row['LineId']])) {
|
||||
$list[$row['LineId']] = ['name' => $row['LineName'], 'parent' => $parent];
|
||||
if ($row['SubGroup']) {
|
||||
$this->processLineList($list, $row['SubGroup'], $row['LineId']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//获取域名概览信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
$action = 'DescribeDomain';
|
||||
$param = ['Domain' => $this->domain];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
$this->domainInfo = $data['DomainInfo'];
|
||||
return $data['DomainInfo'];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名权限
|
||||
public function getDomainPurview()
|
||||
{
|
||||
$action = 'DescribeDomainPurview';
|
||||
$param = ['Domain' => $this->domain];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
return $data['PurviewList'];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
if ($this->domainInfo) {
|
||||
return $this->domainInfo['TTL'];
|
||||
}
|
||||
$PurviewList = $this->getDomainPurview();
|
||||
if ($PurviewList) {
|
||||
foreach ($PurviewList as $row) {
|
||||
if ($row['Name'] == '记录 TTL 最低' || $row['Name'] == 'Min TTL value') {
|
||||
return intval($row['Value']);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取等级允许的线路
|
||||
public function getRecordLineByGrade()
|
||||
{
|
||||
$action = 'DescribeRecordLineList';
|
||||
$param = ['Domain' => $this->domain, 'DomainGrade' => ''];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
$line_list = $data['LineList'];
|
||||
if (!empty($data['LineGroupList'])) {
|
||||
foreach ($data['LineGroupList'] as $row) {
|
||||
$line_list[] = ['Name' => $row['Name'], 'LineId' => $row['LineId']];
|
||||
}
|
||||
}
|
||||
return $line_list;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取用户信息
|
||||
public function getAccountInfo()
|
||||
{
|
||||
$action = 'DescribeUserDetail';
|
||||
$param = [];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
return $data['UserInfo'];
|
||||
}
|
||||
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'];
|
||||
if (array_key_exists($line, $convert_dict)) {
|
||||
return $convert_dict[$line];
|
||||
}
|
||||
return $line;
|
||||
}
|
||||
|
||||
private function convertType($type)
|
||||
{
|
||||
$convert_dict = ['REDIRECT_URL' => '显性URL', 'FORWARD_URL' => '隐性URL'];
|
||||
if (array_key_exists($type, $convert_dict)) {
|
||||
return $convert_dict[$type];
|
||||
}
|
||||
return $type;
|
||||
}
|
||||
|
||||
private function convertTypeId($type)
|
||||
{
|
||||
$convert_dict = ['显性URL' => 'REDIRECT_URL', '隐性URL' => 'FORWARD_URL'];
|
||||
if (array_key_exists($type, $convert_dict)) {
|
||||
return $convert_dict[$type];
|
||||
}
|
||||
return $type;
|
||||
}
|
||||
|
||||
|
||||
private function send_request($action, $param)
|
||||
{
|
||||
try{
|
||||
return $this->client->request($action, $param);
|
||||
}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);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
use app\lib\client\TencentCloud;
|
||||
use Exception;
|
||||
|
||||
class dnspod implements DnsInterface
|
||||
{
|
||||
private $SecretId;
|
||||
private $SecretKey;
|
||||
private $endpoint = "dnspod.tencentcloudapi.com";
|
||||
private $service = "dnspod";
|
||||
private $version = "2021-03-23";
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private $domainInfo;
|
||||
private TencentCloud $client;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->SecretId = $config['ak'];
|
||||
$this->SecretKey = $config['sk'];
|
||||
$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->domain = $config['domain'];
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$action = 'DescribeDomainList';
|
||||
$offset = ($PageNumber - 1) * $PageSize;
|
||||
$param = ['Offset' => $offset, 'Limit' => $PageSize, 'Keyword' => $KeyWord];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['DomainList'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['DomainId'],
|
||||
'Domain' => $row['Name'],
|
||||
'RecordCount' => $row['RecordCount'],
|
||||
];
|
||||
}
|
||||
return ['total' => $data['DomainCountInfo']['DomainTotal'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
$offset = ($PageNumber - 1) * $PageSize;
|
||||
if (!isNullOrEmpty($Status) || !isNullOrEmpty($Value)) {
|
||||
$action = 'DescribeRecordFilterList';
|
||||
$param = ['Domain' => $this->domain, 'Offset' => $offset, 'Limit' => $PageSize, 'RecordValue' => $Value];
|
||||
if (!isNullOrEmpty($SubDomain)) $param['SubDomain'] = $SubDomain;
|
||||
if (!isNullOrEmpty($KeyWord)) $param['Keyword'] = $KeyWord;
|
||||
if (!isNullOrEmpty($Value)) $param['RecordValue'] = $Value;
|
||||
if (!isNullOrEmpty($Status)) {
|
||||
$Status = $Status == '1' ? 'ENABLE' : 'DISABLE';
|
||||
$param['RecordStatus'] = [$Status];
|
||||
}
|
||||
if (!isNullOrEmpty($Type)) $param['RecordType'] = [$this->convertType($Type)];
|
||||
if (!isNullOrEmpty($Line)) $param['RecordLine'] = [$Line];
|
||||
} else {
|
||||
$action = 'DescribeRecordList';
|
||||
$param = ['Domain' => $this->domain, 'Subdomain' => $SubDomain, 'RecordType' => $this->convertType($Type), 'RecordLineId' => $Line, 'Keyword' => $KeyWord, 'Offset' => $offset, 'Limit' => $PageSize];
|
||||
}
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['RecordList'] as $row) {
|
||||
//if($row['Name'] == '@' && $row['Type'] == 'NS') continue;
|
||||
$list[] = [
|
||||
'RecordId' => $row['RecordId'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $row['Name'],
|
||||
'Type' => $this->convertTypeId($row['Type']),
|
||||
'Value' => $row['Value'],
|
||||
'Line' => $row['LineId'],
|
||||
'TTL' => $row['TTL'],
|
||||
'MX' => $row['MX'],
|
||||
'Status' => $row['Status'] == 'ENABLE' ? '1' : '0',
|
||||
'Weight' => $row['Weight'],
|
||||
'Remark' => $row['Remark'],
|
||||
'UpdateTime' => $row['UpdatedOn'],
|
||||
];
|
||||
}
|
||||
return ['total' => $data['RecordCountInfo']['TotalCount'], 'list' => $list];
|
||||
} elseif ($this->error == '记录列表为空。' || $this->error == 'No records on the list.') {
|
||||
return ['total' => 0, 'list' => []];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null)
|
||||
{
|
||||
if ($SubDomain == '') $SubDomain = '@';
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line);
|
||||
}
|
||||
|
||||
//获取解析记录详细信息
|
||||
public function getDomainRecordInfo($RecordId)
|
||||
{
|
||||
$action = 'DescribeRecord';
|
||||
$param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId)];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
return [
|
||||
'RecordId' => $data['RecordInfo']['Id'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $data['RecordInfo']['SubDomain'],
|
||||
'Type' => $this->convertTypeId($data['RecordInfo']['RecordType']),
|
||||
'Value' => $data['RecordInfo']['Value'],
|
||||
'Line' => $data['RecordInfo']['RecordLineId'],
|
||||
'TTL' => $data['RecordInfo']['TTL'],
|
||||
'MX' => $data['RecordInfo']['MX'],
|
||||
'Status' => $data['RecordInfo']['Enabled'] == 1 ? '1' : '0',
|
||||
'Weight' => $data['RecordInfo']['Weight'],
|
||||
'Remark' => $data['RecordInfo']['Remark'],
|
||||
'UpdateTime' => $data['RecordInfo']['UpdatedOn'],
|
||||
];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//添加解析记录
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$action = 'CreateRecord';
|
||||
$param = ['Domain' => $this->domain, 'SubDomain' => $Name, 'RecordType' => $this->convertType($Type), 'Value' => $Value, 'RecordLine' => $Line, 'RecordLineId' => $this->convertLineCode($Line), 'TTL' => intval($TTL), 'Weight' => $Weight];
|
||||
if ($Type == 'MX') $param['MX'] = intval($MX);
|
||||
$data = $this->send_request($action, $param);
|
||||
return is_array($data) ? $data['RecordId'] : false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$action = 'ModifyRecord';
|
||||
$param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId), 'SubDomain' => $Name, 'RecordType' => $this->convertType($Type), 'Value' => $Value, 'RecordLine' => $Line, 'RecordLineId' => $this->convertLineCode($Line), 'TTL' => intval($TTL), 'Weight' => $Weight];
|
||||
if ($Type == 'MX') $param['MX'] = intval($MX);
|
||||
$data = $this->send_request($action, $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
$action = 'ModifyRecordRemark';
|
||||
$param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId), 'Remark' => $Remark];
|
||||
$data = $this->send_request($action, $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$action = 'DeleteRecord';
|
||||
$param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId)];
|
||||
$data = $this->send_request($action, $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$Status = $Status == '1' ? 'ENABLE' : 'DISABLE';
|
||||
$action = 'ModifyRecordStatus';
|
||||
$param = ['Domain' => $this->domain, 'RecordId' => intval($RecordId), 'Status' => $Status];
|
||||
$data = $this->send_request($action, $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null)
|
||||
{
|
||||
$action = 'DescribeDomainLogList';
|
||||
$offset = ($PageNumber - 1) * $PageSize;
|
||||
$param = ['Domain' => $this->domain, 'Offset' => $offset, 'Limit' => $PageSize];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['LogList'] as $row) {
|
||||
$list[] = ['time' => substr($row, 0, strpos($row, '(')), 'ip' => substr($row, strpos($row, '(') + 1, strpos($row, ')') - strpos($row, '(') - 1), 'data' => substr($row, strpos($row, ')') + 1, strpos($row, ' Uin:') - strpos($row, ')') - 1)];
|
||||
}
|
||||
return ['total' => $data['TotalCount'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
public function getRecordLine()
|
||||
{
|
||||
$action = 'DescribeRecordLineCategoryList';
|
||||
$param = ['Domain' => $this->domain];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
$this->processLineList($list, $data['LineList'], null);
|
||||
return $list;
|
||||
} else {
|
||||
$data = $this->getRecordLineByGrade();
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data as $row) {
|
||||
$list[$row['LineId']] = ['name' => $row['Name'], 'parent' => null];
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function processLineList(&$list, $line_list, $parent)
|
||||
{
|
||||
foreach ($line_list as $row) {
|
||||
if (isNullOrEmpty($row['LineId'])) $row['LineId'] = 'N.' . $row['LineName'];
|
||||
if ($row['Useful'] && !isset($list[$row['LineId']])) {
|
||||
$list[$row['LineId']] = ['name' => $row['LineName'], 'parent' => $parent];
|
||||
if ($row['SubGroup']) {
|
||||
$this->processLineList($list, $row['SubGroup'], $row['LineId']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//获取域名概览信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
$action = 'DescribeDomain';
|
||||
$param = ['Domain' => $this->domain];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
$this->domainInfo = $data['DomainInfo'];
|
||||
return $data['DomainInfo'];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名权限
|
||||
public function getDomainPurview()
|
||||
{
|
||||
$action = 'DescribeDomainPurview';
|
||||
$param = ['Domain' => $this->domain];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
return $data['PurviewList'];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
if ($this->domainInfo) {
|
||||
return $this->domainInfo['TTL'];
|
||||
}
|
||||
$PurviewList = $this->getDomainPurview();
|
||||
if ($PurviewList) {
|
||||
foreach ($PurviewList as $row) {
|
||||
if ($row['Name'] == '记录 TTL 最低' || $row['Name'] == 'Min TTL value') {
|
||||
return intval($row['Value']);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取等级允许的线路
|
||||
public function getRecordLineByGrade()
|
||||
{
|
||||
$action = 'DescribeRecordLineList';
|
||||
$param = ['Domain' => $this->domain, 'DomainGrade' => ''];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
$line_list = $data['LineList'];
|
||||
if (!empty($data['LineGroupList'])) {
|
||||
foreach ($data['LineGroupList'] as $row) {
|
||||
$line_list[] = ['Name' => $row['Name'], 'LineId' => $row['LineId']];
|
||||
}
|
||||
}
|
||||
return $line_list;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取用户信息
|
||||
public function getAccountInfo()
|
||||
{
|
||||
$action = 'DescribeUserDetail';
|
||||
$param = [];
|
||||
$data = $this->send_request($action, $param);
|
||||
if ($data) {
|
||||
return $data['UserInfo'];
|
||||
}
|
||||
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'];
|
||||
if (array_key_exists($line, $convert_dict)) {
|
||||
return $convert_dict[$line];
|
||||
}
|
||||
return $line;
|
||||
}
|
||||
|
||||
private function convertType($type)
|
||||
{
|
||||
$convert_dict = ['REDIRECT_URL' => '显性URL', 'FORWARD_URL' => '隐性URL'];
|
||||
if (array_key_exists($type, $convert_dict)) {
|
||||
return $convert_dict[$type];
|
||||
}
|
||||
return $type;
|
||||
}
|
||||
|
||||
private function convertTypeId($type)
|
||||
{
|
||||
$convert_dict = ['显性URL' => 'REDIRECT_URL', '隐性URL' => 'FORWARD_URL'];
|
||||
if (array_key_exists($type, $convert_dict)) {
|
||||
return $convert_dict[$type];
|
||||
}
|
||||
return $type;
|
||||
}
|
||||
|
||||
|
||||
private function send_request($action, $param)
|
||||
{
|
||||
try{
|
||||
return $this->client->request($action, $param);
|
||||
}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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,272 +1,272 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
use app\lib\client\HuaweiCloud;
|
||||
use Exception;
|
||||
|
||||
class huawei implements DnsInterface
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $SecretAccessKey;
|
||||
private $endpoint = "dns.myhuaweicloud.com";
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private HuaweiCloud $client;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKeyId = $config['ak'];
|
||||
$this->SecretAccessKey = $config['sk'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new HuaweiCloud($this->AccessKeyId, $this->SecretAccessKey, $this->endpoint, $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)
|
||||
{
|
||||
$offset = ($PageNumber - 1) * $PageSize;
|
||||
$query = ['offset' => $offset, 'limit' => $PageSize, 'name' => $KeyWord];
|
||||
$data = $this->send_request('GET', '/v2/zones', $query);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['zones'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['id'],
|
||||
'Domain' => rtrim($row['name'], '.'),
|
||||
'RecordCount' => $row['record_num'],
|
||||
];
|
||||
}
|
||||
return ['total' => $data['metadata']['total_count'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
$offset = ($PageNumber - 1) * $PageSize;
|
||||
$query = ['type' => $Type, 'line_id' => $Line, 'name' => $KeyWord, 'offset' => $offset, 'limit' => $PageSize];
|
||||
if (!isNullOrEmpty($Status)) {
|
||||
$Status = $Status == '1' ? 'ACTIVE' : 'DISABLE';
|
||||
$query['status'] = $Status;
|
||||
}
|
||||
if (!isNullOrEmpty($SubDomain)) {
|
||||
$SubDomain = $this->getHost($SubDomain);
|
||||
$query['name'] = $SubDomain;
|
||||
$query['search_mode'] = 'equal';
|
||||
}
|
||||
$data = $this->send_request('GET', '/v2.1/zones/'.$this->domainid.'/recordsets', $query);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['recordsets'] as $row) {
|
||||
if ($row['name'] == $row['zone_name']) $row['name'] = '@';
|
||||
if ($row['type'] == 'MX') list($row['mx'], $row['records']) = explode(' ', $row['records'][0]);
|
||||
$list[] = [
|
||||
'RecordId' => $row['id'],
|
||||
'Domain' => rtrim($row['zone_name'], '.'),
|
||||
'Name' => str_replace('.'.$row['zone_name'], '', $row['name']),
|
||||
'Type' => $row['type'],
|
||||
'Value' => $row['records'],
|
||||
'Line' => $row['line'],
|
||||
'TTL' => $row['ttl'],
|
||||
'MX' => isset($row['mx']) ? $row['mx'] : null,
|
||||
'Status' => $row['status'] == 'ACTIVE' ? '1' : '0',
|
||||
'Weight' => $row['weight'],
|
||||
'Remark' => $row['description'],
|
||||
'UpdateTime' => $row['updated_at'],
|
||||
];
|
||||
}
|
||||
return ['total' => $data['metadata']['total_count'], '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)
|
||||
{
|
||||
$data = $this->send_request('GET', '/v2.1/zones/'.$this->domainid.'/recordsets/'.$RecordId);
|
||||
if ($data) {
|
||||
if ($data['name'] == $data['zone_name']) $data['name'] = '@';
|
||||
if ($data['type'] == 'MX') list($data['mx'], $data['records']) = explode(' ', $data['records'][0]);
|
||||
return [
|
||||
'RecordId' => $data['id'],
|
||||
'Domain' => rtrim($data['zone_name'], '.'),
|
||||
'Name' => str_replace('.'.$data['zone_name'], '', $data['name']),
|
||||
'Type' => $data['type'],
|
||||
'Value' => $data['records'],
|
||||
'Line' => $data['line'],
|
||||
'TTL' => $data['ttl'],
|
||||
'MX' => isset($data['mx']) ? $data['mx'] : null,
|
||||
'Status' => $data['status'] == 'ACTIVE' ? '1' : '0',
|
||||
'Weight' => $data['weight'],
|
||||
'Remark' => $data['description'],
|
||||
'UpdateTime' => $data['updated_at'],
|
||||
];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//添加解析记录
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$Name = $this->getHost($Name);
|
||||
if ($Type == 'TXT' && substr($Value, 0, 1) != '"') $Value = '"' . $Value . '"';
|
||||
$records = array_reverse(explode(',', $Value));
|
||||
$params = ['name' => $Name, 'type' => $this->convertType($Type), 'records' => $records, 'line' => $Line, 'ttl' => intval($TTL), 'description' => $Remark];
|
||||
if ($Type == 'MX') $params['records'][0] = intval($MX) . ' ' . $Value;
|
||||
if ($Weight > 0) $params['weight'] = intval($Weight);
|
||||
$data = $this->send_request('POST', '/v2.1/zones/'.$this->domainid.'/recordsets', null, $params);
|
||||
return is_array($data) ? $data['id'] : false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$Name = $this->getHost($Name);
|
||||
if ($Type == 'TXT' && substr($Value, 0, 1) != '"') $Value = '"' . $Value . '"';
|
||||
$records = array_reverse(explode(',', $Value));
|
||||
$params = ['name' => $Name, 'type' => $this->convertType($Type), 'records' => $records, 'line' => $Line, 'ttl' => intval($TTL), 'description' => $Remark];
|
||||
if ($Type == 'MX') $params['records'][0] = intval($MX) . ' ' . $Value;
|
||||
if ($Weight > 0) $params['weight'] = intval($Weight);
|
||||
$data = $this->send_request('PUT', '/v2.1/zones/'.$this->domainid.'/recordsets/'.$RecordId, null, $params);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$data = $this->send_request('DELETE', '/v2.1/zones/'.$this->domainid.'/recordsets/'.$RecordId);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$Status = $Status == '1' ? 'ENABLE' : 'DISABLE';
|
||||
$params = ['status' => $Status];
|
||||
$data = $this->send_request('PUT', '/v2.1/recordsets/'.$RecordId.'/statuses/set', null, $params);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
public function getRecordLine()
|
||||
{
|
||||
$file_path = app()->getBasePath().'data'.DIRECTORY_SEPARATOR.'huawei_line.json';
|
||||
$content = file_get_contents($file_path);
|
||||
$data = json_decode($content, true);
|
||||
if ($data) {
|
||||
return $data;
|
||||
$list = [$data['DEFAULT']['id'] => ['name' => $data['DEFAULT']['zh'], 'parent' => null]];
|
||||
$this->processLineList($list, $data['ISP'], null, 1, 1);
|
||||
$this->processLineList($list, $data['REGION'], null, null, 1);
|
||||
//file_put_contents($file_path, json_encode($list, JSON_UNESCAPED_UNICODE));
|
||||
return $list;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function processLineList(&$list, $line_list, $parent, $rootId = null, $rootName = null)
|
||||
{
|
||||
foreach ($line_list as $row) {
|
||||
if ($rootId && $rootId !== 1) {
|
||||
$row['id'] = $rootId.'_'.$row['id'];
|
||||
}
|
||||
if ($rootName && $rootName !== 1) {
|
||||
$row['zh'] = $rootName.'_'.$row['zh'];
|
||||
}
|
||||
$list[$row['id']] = ['name' => $row['zh'], 'parent' => $parent];
|
||||
if (isset($row['children']) && !empty($row['children'])) {
|
||||
$this->processLineList($list, $row['children'], $row['id'], $rootId === 1 ? $row['id'] : $rootId, $rootName === 1 ? $row['zh'] : $rootName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//获取域名概览信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
return $this->send_request('GET', '/v2/zones/'.$this->domainid);
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
private function getHost($Name)
|
||||
{
|
||||
if ($Name == '@') $Name = '';
|
||||
else $Name .= '.';
|
||||
$Name .= $this->domain . '.';
|
||||
return $Name;
|
||||
}
|
||||
|
||||
private function send_request($method, $path, $query = null, $params = null)
|
||||
{
|
||||
try{
|
||||
return $this->client->request($method, $path, $query, $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);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
use app\lib\client\HuaweiCloud;
|
||||
use Exception;
|
||||
|
||||
class huawei implements DnsInterface
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $SecretAccessKey;
|
||||
private $endpoint = "dns.myhuaweicloud.com";
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private HuaweiCloud $client;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKeyId = $config['ak'];
|
||||
$this->SecretAccessKey = $config['sk'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new HuaweiCloud($this->AccessKeyId, $this->SecretAccessKey, $this->endpoint, $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)
|
||||
{
|
||||
$offset = ($PageNumber - 1) * $PageSize;
|
||||
$query = ['offset' => $offset, 'limit' => $PageSize, 'name' => $KeyWord];
|
||||
$data = $this->send_request('GET', '/v2/zones', $query);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['zones'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['id'],
|
||||
'Domain' => rtrim($row['name'], '.'),
|
||||
'RecordCount' => $row['record_num'],
|
||||
];
|
||||
}
|
||||
return ['total' => $data['metadata']['total_count'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
$offset = ($PageNumber - 1) * $PageSize;
|
||||
$query = ['type' => $Type, 'line_id' => $Line, 'name' => $KeyWord, 'offset' => $offset, 'limit' => $PageSize];
|
||||
if (!isNullOrEmpty($Status)) {
|
||||
$Status = $Status == '1' ? 'ACTIVE' : 'DISABLE';
|
||||
$query['status'] = $Status;
|
||||
}
|
||||
if (!isNullOrEmpty($SubDomain)) {
|
||||
$SubDomain = $this->getHost($SubDomain);
|
||||
$query['name'] = $SubDomain;
|
||||
$query['search_mode'] = 'equal';
|
||||
}
|
||||
$data = $this->send_request('GET', '/v2.1/zones/'.$this->domainid.'/recordsets', $query);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['recordsets'] as $row) {
|
||||
if ($row['name'] == $row['zone_name']) $row['name'] = '@';
|
||||
if ($row['type'] == 'MX') list($row['mx'], $row['records']) = explode(' ', $row['records'][0]);
|
||||
$list[] = [
|
||||
'RecordId' => $row['id'],
|
||||
'Domain' => rtrim($row['zone_name'], '.'),
|
||||
'Name' => str_replace('.'.$row['zone_name'], '', $row['name']),
|
||||
'Type' => $row['type'],
|
||||
'Value' => $row['records'],
|
||||
'Line' => $row['line'],
|
||||
'TTL' => $row['ttl'],
|
||||
'MX' => isset($row['mx']) ? $row['mx'] : null,
|
||||
'Status' => $row['status'] == 'ACTIVE' ? '1' : '0',
|
||||
'Weight' => $row['weight'],
|
||||
'Remark' => $row['description'],
|
||||
'UpdateTime' => $row['updated_at'],
|
||||
];
|
||||
}
|
||||
return ['total' => $data['metadata']['total_count'], '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)
|
||||
{
|
||||
$data = $this->send_request('GET', '/v2.1/zones/'.$this->domainid.'/recordsets/'.$RecordId);
|
||||
if ($data) {
|
||||
if ($data['name'] == $data['zone_name']) $data['name'] = '@';
|
||||
if ($data['type'] == 'MX') list($data['mx'], $data['records']) = explode(' ', $data['records'][0]);
|
||||
return [
|
||||
'RecordId' => $data['id'],
|
||||
'Domain' => rtrim($data['zone_name'], '.'),
|
||||
'Name' => str_replace('.'.$data['zone_name'], '', $data['name']),
|
||||
'Type' => $data['type'],
|
||||
'Value' => $data['records'],
|
||||
'Line' => $data['line'],
|
||||
'TTL' => $data['ttl'],
|
||||
'MX' => isset($data['mx']) ? $data['mx'] : null,
|
||||
'Status' => $data['status'] == 'ACTIVE' ? '1' : '0',
|
||||
'Weight' => $data['weight'],
|
||||
'Remark' => $data['description'],
|
||||
'UpdateTime' => $data['updated_at'],
|
||||
];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//添加解析记录
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$Name = $this->getHost($Name);
|
||||
if ($Type == 'TXT' && substr($Value, 0, 1) != '"') $Value = '"' . $Value . '"';
|
||||
$records = array_reverse(explode(',', $Value));
|
||||
$params = ['name' => $Name, 'type' => $this->convertType($Type), 'records' => $records, 'line' => $Line, 'ttl' => intval($TTL), 'description' => $Remark];
|
||||
if ($Type == 'MX') $params['records'][0] = intval($MX) . ' ' . $Value;
|
||||
if ($Weight > 0) $params['weight'] = intval($Weight);
|
||||
$data = $this->send_request('POST', '/v2.1/zones/'.$this->domainid.'/recordsets', null, $params);
|
||||
return is_array($data) ? $data['id'] : false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$Name = $this->getHost($Name);
|
||||
if ($Type == 'TXT' && substr($Value, 0, 1) != '"') $Value = '"' . $Value . '"';
|
||||
$records = array_reverse(explode(',', $Value));
|
||||
$params = ['name' => $Name, 'type' => $this->convertType($Type), 'records' => $records, 'line' => $Line, 'ttl' => intval($TTL), 'description' => $Remark];
|
||||
if ($Type == 'MX') $params['records'][0] = intval($MX) . ' ' . $Value;
|
||||
if ($Weight > 0) $params['weight'] = intval($Weight);
|
||||
$data = $this->send_request('PUT', '/v2.1/zones/'.$this->domainid.'/recordsets/'.$RecordId, null, $params);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$data = $this->send_request('DELETE', '/v2.1/zones/'.$this->domainid.'/recordsets/'.$RecordId);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$Status = $Status == '1' ? 'ENABLE' : 'DISABLE';
|
||||
$params = ['status' => $Status];
|
||||
$data = $this->send_request('PUT', '/v2.1/recordsets/'.$RecordId.'/statuses/set', null, $params);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
public function getRecordLine()
|
||||
{
|
||||
$file_path = app()->getBasePath().'data'.DIRECTORY_SEPARATOR.'huawei_line.json';
|
||||
$content = file_get_contents($file_path);
|
||||
$data = json_decode($content, true);
|
||||
if ($data) {
|
||||
return $data;
|
||||
$list = [$data['DEFAULT']['id'] => ['name' => $data['DEFAULT']['zh'], 'parent' => null]];
|
||||
$this->processLineList($list, $data['ISP'], null, 1, 1);
|
||||
$this->processLineList($list, $data['REGION'], null, null, 1);
|
||||
//file_put_contents($file_path, json_encode($list, JSON_UNESCAPED_UNICODE));
|
||||
return $list;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function processLineList(&$list, $line_list, $parent, $rootId = null, $rootName = null)
|
||||
{
|
||||
foreach ($line_list as $row) {
|
||||
if ($rootId && $rootId !== 1) {
|
||||
$row['id'] = $rootId.'_'.$row['id'];
|
||||
}
|
||||
if ($rootName && $rootName !== 1) {
|
||||
$row['zh'] = $rootName.'_'.$row['zh'];
|
||||
}
|
||||
$list[$row['id']] = ['name' => $row['zh'], 'parent' => $parent];
|
||||
if (isset($row['children']) && !empty($row['children'])) {
|
||||
$this->processLineList($list, $row['children'], $row['id'], $rootId === 1 ? $row['id'] : $rootId, $rootName === 1 ? $row['zh'] : $rootName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//获取域名概览信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
return $this->send_request('GET', '/v2/zones/'.$this->domainid);
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
private function getHost($Name)
|
||||
{
|
||||
if ($Name == '@') $Name = '';
|
||||
else $Name .= '.';
|
||||
$Name .= $this->domain . '.';
|
||||
return $Name;
|
||||
}
|
||||
|
||||
private function send_request($method, $path, $query = null, $params = null)
|
||||
{
|
||||
try{
|
||||
return $this->client->request($method, $path, $query, $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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,280 +1,280 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
use app\lib\client\Volcengine;
|
||||
use Exception;
|
||||
|
||||
class huoshan implements DnsInterface
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $SecretAccessKey;
|
||||
private $endpoint = "open.volcengineapi.com";
|
||||
private $service = "DNS";
|
||||
private $version = "2018-08-01";
|
||||
private $region = "cn-north-1";
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private $domainInfo;
|
||||
private Volcengine $client;
|
||||
|
||||
private static $trade_code_list = [
|
||||
'free_inner' => ['level' => 1, 'name' => '免费版', 'ttl' => 600],
|
||||
'professional_inner' => ['level' => 2, 'name' => '专业版', 'ttl' => 300],
|
||||
'enterprise_inner' => ['level' => 3, 'name' => '企业版', 'ttl' => 60],
|
||||
'ultimate_inner' => ['level' => 4, 'name' => '旗舰版', 'ttl' => 1],
|
||||
'ultimate_exclusive_inner' => ['level' => 5, 'name' => '尊享版', 'ttl' => 1],
|
||||
];
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKeyId = $config['ak'];
|
||||
$this->SecretAccessKey = $config['sk'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, $this->endpoint, $this->service, $this->version, $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, 'Key' => $KeyWord];
|
||||
$data = $this->send_request('GET', 'ListZones', $query);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
if (!empty($data['Zones'])) {
|
||||
foreach ($data['Zones'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['ZID'],
|
||||
'Domain' => $row['ZoneName'],
|
||||
'RecordCount' => $row['RecordCount'],
|
||||
];
|
||||
}
|
||||
}
|
||||
return ['total' => $data['Total'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
$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, 'SearchMode' => 'exact'];
|
||||
} elseif (!empty($KeyWord)) {
|
||||
$query += ['Host' => $KeyWord];
|
||||
}
|
||||
$data = $this->send_request('GET', 'ListRecords', $query);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['Records'] as $row) {
|
||||
if ($row['Type'] == 'MX') list($row['MX'], $row['Value']) = explode(' ', $row['Value']);
|
||||
$list[] = [
|
||||
'RecordId' => $row['RecordID'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $row['Host'],
|
||||
'Type' => $row['Type'],
|
||||
'Value' => $row['Value'],
|
||||
'Line' => $row['Line'],
|
||||
'TTL' => $row['TTL'],
|
||||
'MX' => isset($row['MX']) ? $row['MX'] : null,
|
||||
'Status' => $row['Enable'] ? '1' : '0',
|
||||
'Weight' => $row['Weight'],
|
||||
'Remark' => $row['Remark'],
|
||||
'UpdateTime' => $row['UpdatedAt'],
|
||||
];
|
||||
}
|
||||
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)
|
||||
{
|
||||
$data = $this->send_request('GET', 'QueryRecord', ['RecordID' => $RecordId]);
|
||||
if ($data) {
|
||||
if ($data['name'] == $data['zone_name']) $data['name'] = '@';
|
||||
if ($data['Type'] == 'MX') list($data['MX'], $data['Value']) = explode(' ', $data['Value']);
|
||||
return [
|
||||
'RecordId' => $data['RecordID'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $data['Host'],
|
||||
'Type' => $data['Type'],
|
||||
'Value' => $data['Value'],
|
||||
'Line' => $data['Line'],
|
||||
'TTL' => $data['TTL'],
|
||||
'MX' => isset($data['MX']) ? $data['MX'] : null,
|
||||
'Status' => $data['Enable'] ? '1' : '0',
|
||||
'Weight' => $data['Weight'],
|
||||
'Remark' => $data['Remark'],
|
||||
'UpdateTime' => $data['UpdatedAt'],
|
||||
];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//添加解析记录
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$params = ['ZID' => intval($this->domainid), 'Host' => $Name, 'Type' => $this->convertType($Type), 'Value' => $Value, 'Line' => $Line, 'TTL' => intval($TTL), 'Remark' => $Remark];
|
||||
if ($Type == 'MX') $params['Value'] = intval($MX) . ' ' . $Value;
|
||||
if ($Weight > 0) $params['Weight'] = $Weight;
|
||||
$data = $this->send_request('POST', 'CreateRecord', $params);
|
||||
return is_array($data) ? $data['RecordID'] : false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$params = ['RecordID' => $RecordId, 'Host' => $Name, 'Type' => $this->convertType($Type), 'Value' => $Value, 'Line' => $Line, 'TTL' => intval($TTL), 'Remark' => $Remark];
|
||||
if ($Type == 'MX') $params['Value'] = intval($MX) . ' ' . $Value;
|
||||
if ($Weight > 0) $params['Weight'] = $Weight;
|
||||
$data = $this->send_request('POST', 'UpdateRecord', $params);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$data = $this->send_request('POST', 'DeleteRecord', ['RecordID' => $RecordId]);
|
||||
return $data;
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$params = ['RecordID' => $RecordId, 'Enable' => $Status == '1'];
|
||||
$data = $this->send_request('POST', 'UpdateRecordStatus', $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;
|
||||
$level = $this->getTradeInfo($domainInfo['TradeCode'])['level'];
|
||||
$data = $this->send_request('GET', 'ListLines', []);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
$list['default'] = ['name' => '默认', 'parent' => null];
|
||||
foreach ($data['Lines'] as $row) {
|
||||
if ($row['Value'] == 'default') continue;
|
||||
if ($row['Level'] > $level) continue;
|
||||
$list[$row['Value']] = ['name' => $row['Name'], 'parent' => isset($row['FatherValue']) ? $row['FatherValue'] : null];
|
||||
}
|
||||
|
||||
$data = $this->send_request('GET', 'ListCustomLines', []);
|
||||
if ($data && $data['TotalCount'] > 0) {
|
||||
$list['N.customer_lines'] = ['name' => '自定义线路', 'parent' => null];
|
||||
foreach ($data['CustomerLines'] as $row) {
|
||||
$list[$row['Line']] = ['name' => $row['NameCN'], 'parent' => 'N.customer_lines'];
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名概览信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
if (!empty($this->domainInfo)) return $this->domainInfo;
|
||||
$query = ['ZID' => intval($this->domainid)];
|
||||
$data = $this->send_request('GET', 'QueryZone', $query);
|
||||
if ($data) {
|
||||
$this->domainInfo = $data;
|
||||
return $data;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
$domainInfo = $this->getDomainInfo();
|
||||
if ($domainInfo) {
|
||||
$ttl = $this->getTradeInfo($domainInfo['TradeCode'])['ttl'];
|
||||
return $ttl;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
private function getTradeInfo($trade_code)
|
||||
{
|
||||
if (array_key_exists($trade_code, self::$trade_code_list)) {
|
||||
$trade_code = $trade_code;
|
||||
} else {
|
||||
$trade_code = 'free_inner';
|
||||
}
|
||||
return self::$trade_code_list[$trade_code];
|
||||
}
|
||||
|
||||
private function send_request($method, $action, $params = [])
|
||||
{
|
||||
try{
|
||||
return $this->client->request($method, $action, $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);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
use app\lib\client\Volcengine;
|
||||
use Exception;
|
||||
|
||||
class huoshan implements DnsInterface
|
||||
{
|
||||
private $AccessKeyId;
|
||||
private $SecretAccessKey;
|
||||
private $endpoint = "open.volcengineapi.com";
|
||||
private $service = "DNS";
|
||||
private $version = "2018-08-01";
|
||||
private $region = "cn-north-1";
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private $domainInfo;
|
||||
private Volcengine $client;
|
||||
|
||||
private static $trade_code_list = [
|
||||
'free_inner' => ['level' => 1, 'name' => '免费版', 'ttl' => 600],
|
||||
'professional_inner' => ['level' => 2, 'name' => '专业版', 'ttl' => 300],
|
||||
'enterprise_inner' => ['level' => 3, 'name' => '企业版', 'ttl' => 60],
|
||||
'ultimate_inner' => ['level' => 4, 'name' => '旗舰版', 'ttl' => 1],
|
||||
'ultimate_exclusive_inner' => ['level' => 5, 'name' => '尊享版', 'ttl' => 1],
|
||||
];
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->AccessKeyId = $config['ak'];
|
||||
$this->SecretAccessKey = $config['sk'];
|
||||
$proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
$this->client = new Volcengine($this->AccessKeyId, $this->SecretAccessKey, $this->endpoint, $this->service, $this->version, $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, 'Key' => $KeyWord];
|
||||
$data = $this->send_request('GET', 'ListZones', $query);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
if (!empty($data['Zones'])) {
|
||||
foreach ($data['Zones'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['ZID'],
|
||||
'Domain' => $row['ZoneName'],
|
||||
'RecordCount' => $row['RecordCount'],
|
||||
];
|
||||
}
|
||||
}
|
||||
return ['total' => $data['Total'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
$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, 'SearchMode' => 'exact'];
|
||||
} elseif (!empty($KeyWord)) {
|
||||
$query += ['Host' => $KeyWord];
|
||||
}
|
||||
$data = $this->send_request('GET', 'ListRecords', $query);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['Records'] as $row) {
|
||||
if ($row['Type'] == 'MX') list($row['MX'], $row['Value']) = explode(' ', $row['Value']);
|
||||
$list[] = [
|
||||
'RecordId' => $row['RecordID'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $row['Host'],
|
||||
'Type' => $row['Type'],
|
||||
'Value' => $row['Value'],
|
||||
'Line' => $row['Line'],
|
||||
'TTL' => $row['TTL'],
|
||||
'MX' => isset($row['MX']) ? $row['MX'] : null,
|
||||
'Status' => $row['Enable'] ? '1' : '0',
|
||||
'Weight' => $row['Weight'],
|
||||
'Remark' => $row['Remark'],
|
||||
'UpdateTime' => $row['UpdatedAt'],
|
||||
];
|
||||
}
|
||||
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)
|
||||
{
|
||||
$data = $this->send_request('GET', 'QueryRecord', ['RecordID' => $RecordId]);
|
||||
if ($data) {
|
||||
if ($data['name'] == $data['zone_name']) $data['name'] = '@';
|
||||
if ($data['Type'] == 'MX') list($data['MX'], $data['Value']) = explode(' ', $data['Value']);
|
||||
return [
|
||||
'RecordId' => $data['RecordID'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $data['Host'],
|
||||
'Type' => $data['Type'],
|
||||
'Value' => $data['Value'],
|
||||
'Line' => $data['Line'],
|
||||
'TTL' => $data['TTL'],
|
||||
'MX' => isset($data['MX']) ? $data['MX'] : null,
|
||||
'Status' => $data['Enable'] ? '1' : '0',
|
||||
'Weight' => $data['Weight'],
|
||||
'Remark' => $data['Remark'],
|
||||
'UpdateTime' => $data['UpdatedAt'],
|
||||
];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//添加解析记录
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$params = ['ZID' => intval($this->domainid), 'Host' => $Name, 'Type' => $this->convertType($Type), 'Value' => $Value, 'Line' => $Line, 'TTL' => intval($TTL), 'Remark' => $Remark];
|
||||
if ($Type == 'MX') $params['Value'] = intval($MX) . ' ' . $Value;
|
||||
if ($Weight > 0) $params['Weight'] = $Weight;
|
||||
$data = $this->send_request('POST', 'CreateRecord', $params);
|
||||
return is_array($data) ? $data['RecordID'] : false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$params = ['RecordID' => $RecordId, 'Host' => $Name, 'Type' => $this->convertType($Type), 'Value' => $Value, 'Line' => $Line, 'TTL' => intval($TTL), 'Remark' => $Remark];
|
||||
if ($Type == 'MX') $params['Value'] = intval($MX) . ' ' . $Value;
|
||||
if ($Weight > 0) $params['Weight'] = $Weight;
|
||||
$data = $this->send_request('POST', 'UpdateRecord', $params);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$data = $this->send_request('POST', 'DeleteRecord', ['RecordID' => $RecordId]);
|
||||
return $data;
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$params = ['RecordID' => $RecordId, 'Enable' => $Status == '1'];
|
||||
$data = $this->send_request('POST', 'UpdateRecordStatus', $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;
|
||||
$level = $this->getTradeInfo($domainInfo['TradeCode'])['level'];
|
||||
$data = $this->send_request('GET', 'ListLines', []);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
$list['default'] = ['name' => '默认', 'parent' => null];
|
||||
foreach ($data['Lines'] as $row) {
|
||||
if ($row['Value'] == 'default') continue;
|
||||
if ($row['Level'] > $level) continue;
|
||||
$list[$row['Value']] = ['name' => $row['Name'], 'parent' => isset($row['FatherValue']) ? $row['FatherValue'] : null];
|
||||
}
|
||||
|
||||
$data = $this->send_request('GET', 'ListCustomLines', []);
|
||||
if ($data && $data['TotalCount'] > 0) {
|
||||
$list['N.customer_lines'] = ['name' => '自定义线路', 'parent' => null];
|
||||
foreach ($data['CustomerLines'] as $row) {
|
||||
$list[$row['Line']] = ['name' => $row['NameCN'], 'parent' => 'N.customer_lines'];
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名概览信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
if (!empty($this->domainInfo)) return $this->domainInfo;
|
||||
$query = ['ZID' => intval($this->domainid)];
|
||||
$data = $this->send_request('GET', 'QueryZone', $query);
|
||||
if ($data) {
|
||||
$this->domainInfo = $data;
|
||||
return $data;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
$domainInfo = $this->getDomainInfo();
|
||||
if ($domainInfo) {
|
||||
$ttl = $this->getTradeInfo($domainInfo['TradeCode'])['ttl'];
|
||||
return $ttl;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
private function getTradeInfo($trade_code)
|
||||
{
|
||||
if (array_key_exists($trade_code, self::$trade_code_list)) {
|
||||
$trade_code = $trade_code;
|
||||
} else {
|
||||
$trade_code = 'free_inner';
|
||||
}
|
||||
return self::$trade_code_list[$trade_code];
|
||||
}
|
||||
|
||||
private function send_request($method, $action, $params = [])
|
||||
{
|
||||
try{
|
||||
return $this->client->request($method, $action, $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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,263 +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);
|
||||
}
|
||||
}
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,230 +1,230 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
use Exception;
|
||||
|
||||
class namesilo implements DnsInterface
|
||||
{
|
||||
private $apikey;
|
||||
private $baseUrl = 'https://www.namesilo.com/api/';
|
||||
private $version = '1';
|
||||
private $error;
|
||||
private $domain;
|
||||
private $proxy;
|
||||
|
||||
function __construct($config)
|
||||
{
|
||||
$this->apikey = $config['sk'];
|
||||
$this->domain = $config['domain'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$param = ['page' => $PageNumber, 'pageSize' => $PageSize];
|
||||
$data = $this->send_reuqest('listDomains', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
if($data['domains']){
|
||||
foreach ($data['domains'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['domain'],
|
||||
'Domain' => $row['domain'],
|
||||
'RecordCount' => 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
return ['total' => $data['pager']['total'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
$param = ['domain' => $this->domain];
|
||||
$data = $this->send_reuqest('dnsListRecords', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['resource_record'] as $row) {
|
||||
$name = $row['host'] == $this->domain ? '@' : str_replace('.'.$this->domain, '', $row['host']);
|
||||
$list[] = [
|
||||
'RecordId' => $row['record_id'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $name,
|
||||
'Type' => $row['type'],
|
||||
'Value' => $row['value'],
|
||||
'Line' => 'default',
|
||||
'TTL' => $row['ttl'],
|
||||
'MX' => isset($row['distance']) ? $row['distance'] : null,
|
||||
'Status' => '1',
|
||||
'Weight' => null,
|
||||
'Remark' => null,
|
||||
'UpdateTime' => null,
|
||||
];
|
||||
}
|
||||
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;
|
||||
}));
|
||||
}
|
||||
}
|
||||
return ['total' => count($data['resource_record']), '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 ($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);
|
||||
return is_array($data) ? $data['record_id'] : false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
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);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$param = ['domain' => $this->domain, 'rrid' => $RecordId];
|
||||
$data = $this->send_reuqest('dnsDeleteRecord', $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
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)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function send_reuqest($operation, $param = null)
|
||||
{
|
||||
$url = $this->baseUrl . $operation;
|
||||
|
||||
$params = [
|
||||
'version' => $this->version,
|
||||
'type' => 'json',
|
||||
'key' => $this->apikey,
|
||||
];
|
||||
if($param){
|
||||
$params = array_merge($params, $param);
|
||||
}
|
||||
|
||||
$url .= '?' . http_build_query($params);
|
||||
|
||||
try{
|
||||
$response = http_request($url, null, null, null, null, $this->proxy);
|
||||
}catch(Exception $e){
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
$arr = json_decode($response['body'], true);
|
||||
if (isset($arr['reply']['code'])) {
|
||||
if ($arr['reply']['code'] == 300) {
|
||||
return $arr['reply'];
|
||||
} else {
|
||||
$this->setError(isset($arr['reply']['detail']) ? $arr['reply']['detail'] : '未知错误');
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->setError($response['body']);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function setError($message)
|
||||
{
|
||||
$this->error = $message;
|
||||
//file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
use Exception;
|
||||
|
||||
class namesilo implements DnsInterface
|
||||
{
|
||||
private $apikey;
|
||||
private $baseUrl = 'https://www.namesilo.com/api/';
|
||||
private $version = '1';
|
||||
private $error;
|
||||
private $domain;
|
||||
private $proxy;
|
||||
|
||||
function __construct($config)
|
||||
{
|
||||
$this->apikey = $config['sk'];
|
||||
$this->domain = $config['domain'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$param = ['page' => $PageNumber, 'pageSize' => $PageSize];
|
||||
$data = $this->send_reuqest('listDomains', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
if($data['domains']){
|
||||
foreach ($data['domains'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['domain'],
|
||||
'Domain' => $row['domain'],
|
||||
'RecordCount' => 0,
|
||||
];
|
||||
}
|
||||
}
|
||||
return ['total' => $data['pager']['total'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
$param = ['domain' => $this->domain];
|
||||
$data = $this->send_reuqest('dnsListRecords', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['resource_record'] as $row) {
|
||||
$name = $row['host'] == $this->domain ? '@' : str_replace('.'.$this->domain, '', $row['host']);
|
||||
$list[] = [
|
||||
'RecordId' => $row['record_id'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $name,
|
||||
'Type' => $row['type'],
|
||||
'Value' => $row['value'],
|
||||
'Line' => 'default',
|
||||
'TTL' => $row['ttl'],
|
||||
'MX' => isset($row['distance']) ? $row['distance'] : null,
|
||||
'Status' => '1',
|
||||
'Weight' => null,
|
||||
'Remark' => null,
|
||||
'UpdateTime' => null,
|
||||
];
|
||||
}
|
||||
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;
|
||||
}));
|
||||
}
|
||||
}
|
||||
return ['total' => count($data['resource_record']), '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 ($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);
|
||||
return is_array($data) ? $data['record_id'] : false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
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);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$param = ['domain' => $this->domain, 'rrid' => $RecordId];
|
||||
$data = $this->send_reuqest('dnsDeleteRecord', $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
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)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function send_reuqest($operation, $param = null)
|
||||
{
|
||||
$url = $this->baseUrl . $operation;
|
||||
|
||||
$params = [
|
||||
'version' => $this->version,
|
||||
'type' => 'json',
|
||||
'key' => $this->apikey,
|
||||
];
|
||||
if($param){
|
||||
$params = array_merge($params, $param);
|
||||
}
|
||||
|
||||
$url .= '?' . http_build_query($params);
|
||||
|
||||
try{
|
||||
$response = http_request($url, null, null, null, null, $this->proxy);
|
||||
}catch(Exception $e){
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
$arr = json_decode($response['body'], true);
|
||||
if (isset($arr['reply']['code'])) {
|
||||
if ($arr['reply']['code'] == 300) {
|
||||
return $arr['reply'];
|
||||
} else {
|
||||
$this->setError(isset($arr['reply']['detail']) ? $arr['reply']['detail'] : '未知错误');
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->setError($response['body']);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function setError($message)
|
||||
{
|
||||
$this->error = $message;
|
||||
//file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,425 +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;
|
||||
}
|
||||
}
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
|
||||
353
app/lib/dns/spaceship.php
Normal file
353
app/lib/dns/spaceship.php
Normal file
@@ -0,0 +1,353 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
|
||||
/**
|
||||
* @see https://docs.spaceship.dev/
|
||||
*/
|
||||
class spaceship implements DnsInterface
|
||||
{
|
||||
private $apiKey;
|
||||
private $apiSecret;
|
||||
private $baseUrl = 'https://spaceship.dev/api/v1';
|
||||
private $error;
|
||||
private $domain;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->apiKey = $config['ak'];
|
||||
$this->apiSecret = $config['sk'];
|
||||
$this->domain = $config['domain'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
|
||||
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 = 100)
|
||||
{
|
||||
$param = ['take' => $PageSize, 'skip' => ($PageNumber - 1) * $PageSize];
|
||||
$data = $this->send_reuqest('GET', '/domains', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['items'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['name'],
|
||||
'Domain' => $row['name'],
|
||||
'RecordCount' => 0,
|
||||
];
|
||||
}
|
||||
return ['total' => $data['total'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录列表
|
||||
|
||||
private function send_reuqest($method, $path, $params = null)
|
||||
{
|
||||
$url = $this->baseUrl . $path;
|
||||
|
||||
$headers = [
|
||||
'X-API-Key: ' . $this->apiKey,
|
||||
'X-API-Secret: ' . $this->apiSecret,
|
||||
];
|
||||
|
||||
$body = '';
|
||||
if ($method == 'GET') {
|
||||
if ($params) {
|
||||
$url .= '?' . http_build_query($params);
|
||||
}
|
||||
} else {
|
||||
$body = json_encode($params);
|
||||
$headers[] = 'Content-Type: application/json';
|
||||
}
|
||||
|
||||
$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, $headers);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
if ($method == 'POST') {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
} elseif ($method == 'PUT') {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
} elseif ($method == 'PATCH') {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
} elseif ($method == 'DELETE') {
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
}
|
||||
$response = curl_exec($ch);
|
||||
$errno = curl_errno($ch);
|
||||
|
||||
if ($errno) {
|
||||
$this->setError('Curl error: ' . curl_error($ch));
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
if ($errno) return false;
|
||||
|
||||
$arr = json_decode($response, true);
|
||||
if (!isset($arr['detail'])) {
|
||||
return $arr;
|
||||
} else {
|
||||
$this->setError($response['detail']);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//获取子域名解析记录列表
|
||||
|
||||
private function setError($message)
|
||||
{
|
||||
$this->error = $message;
|
||||
//file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
|
||||
}
|
||||
|
||||
//获取解析记录详细信息
|
||||
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null)
|
||||
{
|
||||
if ($SubDomain == '') $SubDomain = '@';
|
||||
return $this->getDomainRecords($PageNumber, $PageSize, null, $SubDomain, null, $Type, $Line);
|
||||
}
|
||||
|
||||
//添加解析记录
|
||||
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
$param = ['take' => $PageSize, 'skip' => ($PageNumber - 1) * $PageSize];
|
||||
if (!isNullOrEmpty(($SubDomain))) {
|
||||
$param['host'] = $SubDomain;
|
||||
}
|
||||
$data = $this->send_reuqest('GET', '/dns/records/' . $this->domain, $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['items'] as $row) {
|
||||
$type = $row['type'];
|
||||
$name = $row['name'];
|
||||
if ('MX' == $type) {
|
||||
$address = $row['exchange'];
|
||||
$mx = $row['preference'];
|
||||
} else if ('CNAME' == $type) {
|
||||
$address = $row['cname'];
|
||||
$mx = 0;
|
||||
} else if ('TXT' == $type) {
|
||||
$address = $row['value'];
|
||||
$mx = 0;
|
||||
} else if ('PTR' == $type) {
|
||||
$address = $row['pointer'];
|
||||
$mx = 0;
|
||||
} else if ('NS' == $type) {
|
||||
$address = $row['nameserver'];
|
||||
$mx = 0;
|
||||
} else if ('HTTPS' == $type) {
|
||||
$address = $row['targetName'] . $row['svcParams'] . '|' . $row['svcPriority'];
|
||||
$mx = 0;
|
||||
} else if ('CAA' == $type) {
|
||||
$address = $row['value'];
|
||||
$mx = 0;
|
||||
} else if ('TLSA' == $type) {
|
||||
$address = $row['associationData'];
|
||||
$mx = 0;
|
||||
} else if ('SVRB' == $type) {
|
||||
$address = $row['targetName'] . $row['svcParams'] . '|' . $row['svcPriority'];
|
||||
$mx = 0;
|
||||
} else if ('ALIAS' == $type) {
|
||||
$address = $row['aliasName'];
|
||||
$mx = 0;
|
||||
} else {
|
||||
$address = $row['address'];
|
||||
$mx = 0;
|
||||
}
|
||||
|
||||
$list[] = [
|
||||
'RecordId' => $row['type'] . '|' . $name . '|' . $address . '|' . $mx,
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $row['name'],
|
||||
'Type' => $row['type'],
|
||||
'Value' => $address,
|
||||
'TTL' => $row['ttl'],
|
||||
'Line' => 'default',
|
||||
'MX' => $mx,
|
||||
'Status' => '1',
|
||||
'Weight' => null,
|
||||
'Remark' => null,
|
||||
'UpdateTime' => null,
|
||||
];
|
||||
}
|
||||
return ['total' => $data['total'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
|
||||
public function getDomainRecordInfo($RecordId)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
|
||||
public function addDomainRecord($Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$param = [
|
||||
'force' => true,
|
||||
'items' => [
|
||||
[
|
||||
'type' => $this->convertType($Type),
|
||||
'name' => $Name,
|
||||
'address' => $Value,
|
||||
'ttl' => $TTL,
|
||||
]
|
||||
]
|
||||
];
|
||||
$data = $this->send_reuqest('PUT', '/dns/records/' . $this->domain, $param);
|
||||
return !isset($data);
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
|
||||
private function convertType($type)
|
||||
{
|
||||
return $type;
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$param = [
|
||||
'force' => true,
|
||||
'items' => [
|
||||
[
|
||||
'type' => $this->convertType($Type),
|
||||
'name' => $Name,
|
||||
'address' => $Value,
|
||||
'ttl' => $TTL,
|
||||
]
|
||||
]
|
||||
];
|
||||
$data = $this->send_reuqest('PUT', '/dns/records/' . $this->domain, $param);
|
||||
return !isset($data);
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$array = explode("|", $RecordId);
|
||||
$type = $array[0];
|
||||
$name = $array[1];
|
||||
$address = $array[2];
|
||||
$mx = $array[3];
|
||||
if ('MX' == $type) {
|
||||
$param = [
|
||||
[
|
||||
'type' => $type,
|
||||
'name' => $name,
|
||||
'exchange' => $address,
|
||||
'preference' => (int)$mx,
|
||||
]
|
||||
];
|
||||
} else if ('TXT' == $type) {
|
||||
$param = [
|
||||
[
|
||||
'type' => $type,
|
||||
'name' => $name,
|
||||
'value' => $address,
|
||||
]
|
||||
];
|
||||
} else if ('CNAME' == $type) {
|
||||
$param = [
|
||||
[
|
||||
'type' => $type,
|
||||
'name' => $name,
|
||||
'cname' => $address,
|
||||
]
|
||||
];
|
||||
} else if ('ALIAS' == $type) {
|
||||
$param = [
|
||||
[
|
||||
'type' => $type,
|
||||
'name' => $name,
|
||||
'aliasName' => $address,
|
||||
]
|
||||
];
|
||||
} else {
|
||||
$param = [
|
||||
[
|
||||
'type' => $type,
|
||||
'name' => $name,
|
||||
'address' => $address,
|
||||
]
|
||||
];
|
||||
}
|
||||
$data = $this->send_reuqest('DELETE', '/dns/records/' . $this->domain, $param);
|
||||
return !isset($data);
|
||||
}
|
||||
|
||||
//获取域名信息
|
||||
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public function getMinTTL()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,215 +1,215 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
|
||||
/**
|
||||
* @see http://apipost.west.cn/
|
||||
*/
|
||||
class west implements DnsInterface
|
||||
{
|
||||
private $username;
|
||||
private $api_password;
|
||||
private $baseUrl = 'https://api.west.cn/api/v2';
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->username = $config['ak'];
|
||||
$this->api_password = $config['sk'];
|
||||
$this->domain = $config['domain'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$param = ['act' => 'getdomains', 'page' => $PageNumber, 'limit' => $PageSize, 'domain' => $KeyWord];
|
||||
$data = $this->execute('/domain/', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['items'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['domain'],
|
||||
'Domain' => $row['domain'],
|
||||
'RecordCount' => 0,
|
||||
];
|
||||
}
|
||||
return ['total' => $data['total'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
$param = ['act' => 'getdnsrecord', 'domain' => $this->domain, 'type' => $Type, 'line' => $Line, 'host' => $KeyWord, 'value' => $Value, 'pageno' => $PageNumber, 'limit' => $PageSize];
|
||||
if (!isNullOrEmpty(($SubDomain))) {
|
||||
$param['host'] = $SubDomain;
|
||||
}
|
||||
$data = $this->execute('/domain/', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['items'] as $row) {
|
||||
$list[] = [
|
||||
'RecordId' => $row['id'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $row['item'],
|
||||
'Type' => $row['type'],
|
||||
'Value' => $row['value'],
|
||||
'Line' => $row['line'],
|
||||
'TTL' => $row['ttl'],
|
||||
'MX' => $row['level'],
|
||||
'Status' => $row['pause'] == 1 ? '0' : '1',
|
||||
'Weight' => null,
|
||||
'Remark' => null,
|
||||
'UpdateTime' => null,
|
||||
];
|
||||
}
|
||||
return ['total' => $data['total'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null)
|
||||
{
|
||||
if ($SubDomain == '') $SubDomain = '@';
|
||||
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)
|
||||
{
|
||||
$param = ['act' => 'adddnsrecord', 'domain' => $this->domain, 'host' => $Name, 'type' => $this->convertType($Type), 'value' => $Value, 'level' => $MX, 'ttl' => intval($TTL), 'line' => $Line];
|
||||
$data = $this->execute('/domain/', $param);
|
||||
return is_array($data) ? $data['id'] : false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$param = ['act' => 'moddnsrecord', 'domain' => $this->domain, 'id' => $RecordId, 'type' => $this->convertType($Type), 'value' => $Value, 'level' => $MX, 'ttl' => intval($TTL), 'line' => $Line];
|
||||
$data = $this->execute('/domain/', $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$param = ['act' => 'deldnsrecord', 'domain' => $this->domain, 'id' => $RecordId];
|
||||
$data = $this->execute('/domain/', $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$param = ['act' => 'pause', 'domain' => $this->domain, 'id' => $RecordId, 'val' => $Status == '1' ? '0' : '1'];
|
||||
$data = $this->execute('/domain/', $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
public function getRecordLine()
|
||||
{
|
||||
return [
|
||||
'' => ['name' => '默认', 'parent' => null],
|
||||
'LTEL' => ['name' => '电信', 'parent' => null],
|
||||
'LCNC' => ['name' => '联通', 'parent' => null],
|
||||
'LMOB' => ['name' => '移动', 'parent' => null],
|
||||
'LEDU' => ['name' => '教育网', 'parent' => null],
|
||||
'LSEO' => ['name' => '搜索引擎', 'parent' => null],
|
||||
'LFOR' => ['name' => '境外', 'parent' => null],
|
||||
];
|
||||
}
|
||||
|
||||
//获取域名信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function convertType($type)
|
||||
{
|
||||
return $type;
|
||||
}
|
||||
|
||||
private function execute($path, $params)
|
||||
{
|
||||
$params['username'] = $this->username;
|
||||
$params['time'] = getMillisecond();
|
||||
$params['token'] = md5($this->username.$this->api_password.$params['time']);
|
||||
try{
|
||||
$response = http_request($this->baseUrl . $path, http_build_query($params), null, null, null, $this->proxy);
|
||||
}catch(\Exception $e){
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
$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 {
|
||||
$this->setError($arr['msg']);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->setError('返回数据解析失败');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function setError($message)
|
||||
{
|
||||
$this->error = $message;
|
||||
//file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\lib\dns;
|
||||
|
||||
use app\lib\DnsInterface;
|
||||
|
||||
/**
|
||||
* @see http://apipost.west.cn/
|
||||
*/
|
||||
class west implements DnsInterface
|
||||
{
|
||||
private $username;
|
||||
private $api_password;
|
||||
private $baseUrl = 'https://api.west.cn/api/v2';
|
||||
private $error;
|
||||
private $domain;
|
||||
private $domainid;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->username = $config['ak'];
|
||||
$this->api_password = $config['sk'];
|
||||
$this->domain = $config['domain'];
|
||||
$this->proxy = isset($config['proxy']) ? $config['proxy'] == 1 : false;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$param = ['act' => 'getdomains', 'page' => $PageNumber, 'limit' => $PageSize, 'domain' => $KeyWord];
|
||||
$data = $this->execute('/domain/', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['items'] as $row) {
|
||||
$list[] = [
|
||||
'DomainId' => $row['domain'],
|
||||
'Domain' => $row['domain'],
|
||||
'RecordCount' => 0,
|
||||
];
|
||||
}
|
||||
return ['total' => $data['total'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析记录列表
|
||||
public function getDomainRecords($PageNumber = 1, $PageSize = 20, $KeyWord = null, $SubDomain = null, $Value = null, $Type = null, $Line = null, $Status = null)
|
||||
{
|
||||
$param = ['act' => 'getdnsrecord', 'domain' => $this->domain, 'type' => $Type, 'line' => $Line, 'host' => $KeyWord, 'value' => $Value, 'pageno' => $PageNumber, 'limit' => $PageSize];
|
||||
if (!isNullOrEmpty(($SubDomain))) {
|
||||
$param['host'] = $SubDomain;
|
||||
}
|
||||
$data = $this->execute('/domain/', $param);
|
||||
if ($data) {
|
||||
$list = [];
|
||||
foreach ($data['items'] as $row) {
|
||||
$list[] = [
|
||||
'RecordId' => $row['id'],
|
||||
'Domain' => $this->domain,
|
||||
'Name' => $row['item'],
|
||||
'Type' => $row['type'],
|
||||
'Value' => $row['value'],
|
||||
'Line' => $row['line'],
|
||||
'TTL' => $row['ttl'],
|
||||
'MX' => $row['level'],
|
||||
'Status' => $row['pause'] == 1 ? '0' : '1',
|
||||
'Weight' => null,
|
||||
'Remark' => null,
|
||||
'UpdateTime' => null,
|
||||
];
|
||||
}
|
||||
return ['total' => $data['total'], 'list' => $list];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取子域名解析记录列表
|
||||
public function getSubDomainRecords($SubDomain, $PageNumber = 1, $PageSize = 20, $Type = null, $Line = null)
|
||||
{
|
||||
if ($SubDomain == '') $SubDomain = '@';
|
||||
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)
|
||||
{
|
||||
$param = ['act' => 'adddnsrecord', 'domain' => $this->domain, 'host' => $Name, 'type' => $this->convertType($Type), 'value' => $Value, 'level' => $MX, 'ttl' => intval($TTL), 'line' => $Line];
|
||||
$data = $this->execute('/domain/', $param);
|
||||
return is_array($data) ? $data['id'] : false;
|
||||
}
|
||||
|
||||
//修改解析记录
|
||||
public function updateDomainRecord($RecordId, $Name, $Type, $Value, $Line = '0', $TTL = 600, $MX = 1, $Weight = null, $Remark = null)
|
||||
{
|
||||
$param = ['act' => 'moddnsrecord', 'domain' => $this->domain, 'id' => $RecordId, 'type' => $this->convertType($Type), 'value' => $Value, 'level' => $MX, 'ttl' => intval($TTL), 'line' => $Line];
|
||||
$data = $this->execute('/domain/', $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//修改解析记录备注
|
||||
public function updateDomainRecordRemark($RecordId, $Remark)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//删除解析记录
|
||||
public function deleteDomainRecord($RecordId)
|
||||
{
|
||||
$param = ['act' => 'deldnsrecord', 'domain' => $this->domain, 'id' => $RecordId];
|
||||
$data = $this->execute('/domain/', $param);
|
||||
return is_array($data);
|
||||
}
|
||||
|
||||
//设置解析记录状态
|
||||
public function setDomainRecordStatus($RecordId, $Status)
|
||||
{
|
||||
$param = ['act' => 'pause', 'domain' => $this->domain, 'id' => $RecordId, 'val' => $Status == '1' ? '0' : '1'];
|
||||
$data = $this->execute('/domain/', $param);
|
||||
return $data !== false;
|
||||
}
|
||||
|
||||
//获取解析记录操作日志
|
||||
public function getDomainRecordLog($PageNumber = 1, $PageSize = 20, $KeyWord = null, $StartDate = null, $endDate = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取解析线路列表
|
||||
public function getRecordLine()
|
||||
{
|
||||
return [
|
||||
'' => ['name' => '默认', 'parent' => null],
|
||||
'LTEL' => ['name' => '电信', 'parent' => null],
|
||||
'LCNC' => ['name' => '联通', 'parent' => null],
|
||||
'LMOB' => ['name' => '移动', 'parent' => null],
|
||||
'LEDU' => ['name' => '教育网', 'parent' => null],
|
||||
'LSEO' => ['name' => '搜索引擎', 'parent' => null],
|
||||
'LFOR' => ['name' => '境外', 'parent' => null],
|
||||
];
|
||||
}
|
||||
|
||||
//获取域名信息
|
||||
public function getDomainInfo()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//获取域名最低TTL
|
||||
public function getMinTTL()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addDomain($Domain)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function convertType($type)
|
||||
{
|
||||
return $type;
|
||||
}
|
||||
|
||||
private function execute($path, $params)
|
||||
{
|
||||
$params['username'] = $this->username;
|
||||
$params['time'] = getMillisecond();
|
||||
$params['token'] = md5($this->username.$this->api_password.$params['time']);
|
||||
try{
|
||||
$response = http_request($this->baseUrl . $path, http_build_query($params), null, null, null, $this->proxy);
|
||||
}catch(\Exception $e){
|
||||
$this->setError($e->getMessage());
|
||||
return false;
|
||||
}
|
||||
$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 {
|
||||
$this->setError($arr['msg']);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$this->setError('返回数据解析失败');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function setError($message)
|
||||
{
|
||||
$this->error = $message;
|
||||
//file_put_contents('logs.txt',date('H:i:s').' '.$message."\r\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use think\facade\Db;
|
||||
|
||||
class AuthApi
|
||||
{
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
$uid = input('post.uid/d');
|
||||
$timestamp = input('post.timestamp');
|
||||
$sign = input('post.sign');
|
||||
if (!$uid || empty($timestamp) || empty($sign)) {
|
||||
return json(['code' => -1, 'msg' => '认证参数不能为空'])->code(403);
|
||||
}
|
||||
if ($timestamp < time() - 300 || $timestamp > time() + 300) {
|
||||
return json(['code' => -1, 'msg' => '时间戳不合法'])->code(403);
|
||||
}
|
||||
$user = Db::name('user')->where('id', $uid)->find();
|
||||
if (!$user) {
|
||||
return json(['code' => -1, 'msg' => '用户不存在'])->code(403);
|
||||
}
|
||||
if ($user['status'] == 0) {
|
||||
return json(['code' => -1, 'msg' => '该用户已被封禁'])->code(403);
|
||||
}
|
||||
if ($user['is_api'] == 0) {
|
||||
return json(['code' => -1, 'msg' => '该用户未开启API权限'])->code(403);
|
||||
}
|
||||
if (md5($uid.$timestamp.$user['apikey']) !== $sign) {
|
||||
return json(['code' => -1, 'msg' => '签名错误'])->code(403);
|
||||
}
|
||||
|
||||
$user['type'] = 'user';
|
||||
$user['permission'] = [];
|
||||
if ($user['level'] == 1) {
|
||||
$user['permission'] = Db::name('permission')->where('uid', $uid)->column('domain');
|
||||
}
|
||||
|
||||
$request->islogin = true;
|
||||
$request->isApi = true;
|
||||
$request->user = $user;
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use think\facade\Db;
|
||||
|
||||
class AuthApi
|
||||
{
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
$uid = input('post.uid/d');
|
||||
$timestamp = input('post.timestamp');
|
||||
$sign = input('post.sign');
|
||||
if (!$uid || empty($timestamp) || empty($sign)) {
|
||||
return json(['code' => -1, 'msg' => '认证参数不能为空'])->code(403);
|
||||
}
|
||||
if ($timestamp < time() - 300 || $timestamp > time() + 300) {
|
||||
return json(['code' => -1, 'msg' => '时间戳不合法'])->code(403);
|
||||
}
|
||||
$user = Db::name('user')->where('id', $uid)->find();
|
||||
if (!$user) {
|
||||
return json(['code' => -1, 'msg' => '用户不存在'])->code(403);
|
||||
}
|
||||
if ($user['status'] == 0) {
|
||||
return json(['code' => -1, 'msg' => '该用户已被封禁'])->code(403);
|
||||
}
|
||||
if ($user['is_api'] == 0) {
|
||||
return json(['code' => -1, 'msg' => '该用户未开启API权限'])->code(403);
|
||||
}
|
||||
if (md5($uid.$timestamp.$user['apikey']) !== $sign) {
|
||||
return json(['code' => -1, 'msg' => '签名错误'])->code(403);
|
||||
}
|
||||
|
||||
$user['type'] = 'user';
|
||||
$user['permission'] = [];
|
||||
if ($user['level'] == 1) {
|
||||
$user['permission'] = Db::name('permission')->where('uid', $uid)->column('domain');
|
||||
}
|
||||
|
||||
$request->islogin = true;
|
||||
$request->isApi = true;
|
||||
$request->user = $user;
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use think\facade\Db;
|
||||
|
||||
class AuthUser
|
||||
{
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
$islogin = false;
|
||||
$cookie = cookie('user_token');
|
||||
$user = null;
|
||||
if ($cookie && config_get('sys_key')) {
|
||||
$token = authcode($cookie, 'DECODE', config_get('sys_key'));
|
||||
if ($token) {
|
||||
list($type, $uid, $sid, $expiretime) = explode("\t", $token);
|
||||
if ($type == 'user') {
|
||||
$user = Db::name('user')->where('id', $uid)->find();
|
||||
if ($user && $user['status'] == 1) {
|
||||
$session = md5($user['id'].$user['password']);
|
||||
if ($session === $sid && $expiretime > time()) {
|
||||
$islogin = true;
|
||||
}
|
||||
$user['type'] = 'user';
|
||||
$user['permission'] = [];
|
||||
if ($user['level'] == 1) {
|
||||
$user['permission'] = Db::name('permission')->where('uid', $uid)->column('domain');
|
||||
}
|
||||
}
|
||||
} elseif ($type == 'domain') {
|
||||
$user = Db::name('domain')->where('id', $uid)->find();
|
||||
if ($user && $user['is_sso'] == 1) {
|
||||
$session = md5($user['id'].$user['name']);
|
||||
if ($session === $sid && $expiretime > time()) {
|
||||
$islogin = true;
|
||||
}
|
||||
$user['username'] = $user['name'];
|
||||
$user['regtime'] = $user['addtime'];
|
||||
$user['type'] = 'domain';
|
||||
$user['level'] = 0;
|
||||
$user['permission'] = [$user['name']];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$request->islogin = $islogin;
|
||||
$request->user = $user;
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use think\facade\Db;
|
||||
|
||||
class AuthUser
|
||||
{
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
$islogin = false;
|
||||
$cookie = cookie('user_token');
|
||||
$user = null;
|
||||
if ($cookie && config_get('sys_key')) {
|
||||
$token = authcode($cookie, 'DECODE', config_get('sys_key'));
|
||||
if ($token) {
|
||||
list($type, $uid, $sid, $expiretime) = explode("\t", $token);
|
||||
if ($type == 'user') {
|
||||
$user = Db::name('user')->where('id', $uid)->find();
|
||||
if ($user && $user['status'] == 1) {
|
||||
$session = md5($user['id'].$user['password']);
|
||||
if ($session === $sid && $expiretime > time()) {
|
||||
$islogin = true;
|
||||
}
|
||||
$user['type'] = 'user';
|
||||
$user['permission'] = [];
|
||||
if ($user['level'] == 1) {
|
||||
$user['permission'] = Db::name('permission')->where('uid', $uid)->column('domain');
|
||||
}
|
||||
}
|
||||
} elseif ($type == 'domain') {
|
||||
$user = Db::name('domain')->where('id', $uid)->find();
|
||||
if ($user && $user['is_sso'] == 1) {
|
||||
$session = md5($user['id'].$user['name']);
|
||||
if ($session === $sid && $expiretime > time()) {
|
||||
$islogin = true;
|
||||
}
|
||||
$user['username'] = $user['name'];
|
||||
$user['regtime'] = $user['addtime'];
|
||||
$user['type'] = 'domain';
|
||||
$user['level'] = 0;
|
||||
$user['permission'] = [$user['name']];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$request->islogin = $islogin;
|
||||
$request->user = $user;
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
class CheckLogin
|
||||
{
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
if (!$request->islogin) {
|
||||
if ($request->isAjax() || !$request->isGet()) {
|
||||
return json(['code' => -1, 'msg' => '未登录'])->code(401);
|
||||
}
|
||||
return redirect((string)url('/login'));
|
||||
}
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
class CheckLogin
|
||||
{
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
if (!$request->islogin) {
|
||||
if ($request->isAjax() || !$request->isGet()) {
|
||||
return json(['code' => -1, 'msg' => '未登录'])->code(401);
|
||||
}
|
||||
return redirect((string)url('/login'));
|
||||
}
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use think\facade\View;
|
||||
|
||||
class RefererCheck
|
||||
{
|
||||
/**
|
||||
* 处理请求
|
||||
*
|
||||
* @param \think\Request $request
|
||||
* @param \Closure $next
|
||||
* @return Response
|
||||
*/
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
if (!checkRefererHost()) {
|
||||
return response('Access Denied', 403);
|
||||
}
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use think\facade\View;
|
||||
|
||||
class RefererCheck
|
||||
{
|
||||
/**
|
||||
* 处理请求
|
||||
*
|
||||
* @param \think\Request $request
|
||||
* @param \Closure $next
|
||||
* @return Response
|
||||
*/
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
if (!checkRefererHost()) {
|
||||
return response('Access Denied', 403);
|
||||
}
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use think\facade\View;
|
||||
|
||||
class ViewOutput
|
||||
{
|
||||
/**
|
||||
* 处理请求
|
||||
*
|
||||
* @param \think\Request $request
|
||||
* @param \Closure $next
|
||||
* @return Response
|
||||
*/
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
View::assign('islogin', $request->islogin);
|
||||
View::assign('user', $request->user);
|
||||
View::assign('cdnpublic', 'https://s4.zstatic.net/ajax/libs/');
|
||||
View::assign('skin', getAdminSkin());
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
declare (strict_types=1);
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use think\facade\View;
|
||||
|
||||
class ViewOutput
|
||||
{
|
||||
/**
|
||||
* 处理请求
|
||||
*
|
||||
* @param \think\Request $request
|
||||
* @param \Closure $next
|
||||
* @return Response
|
||||
*/
|
||||
public function handle($request, \Closure $next)
|
||||
{
|
||||
View::assign('islogin', $request->islogin);
|
||||
View::assign('user', $request->user);
|
||||
View::assign('skin', getAdminSkin());
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,148 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use Exception;
|
||||
use think\facade\Db;
|
||||
use app\lib\DeployHelper;
|
||||
|
||||
/**
|
||||
* SSL证书自动部署
|
||||
*/
|
||||
class CertDeployService
|
||||
{
|
||||
private static $retry_interval = [60, 300, 600, 1800, 3600];
|
||||
|
||||
private $client;
|
||||
private $aid;
|
||||
private $task;
|
||||
private $info;
|
||||
|
||||
//任务状态:0:待处理 1:已完成 -1:处理失败
|
||||
public function __construct($tid)
|
||||
{
|
||||
$task = Db::name('cert_deploy')->where('id', $tid)->find();
|
||||
if (!$task) throw new Exception('该自动部署任务不存在', 102);
|
||||
$this->task = $task;
|
||||
|
||||
$this->aid = $task['aid'];
|
||||
$this->client = DeployHelper::getModel($this->aid);
|
||||
if (!$this->client) throw new Exception('该自动部署任务类型不存在', 102);
|
||||
|
||||
$this->info = $task['info'] ? json_decode($task['info'], true) : [];
|
||||
}
|
||||
|
||||
public function process($isManual = false)
|
||||
{
|
||||
if ($this->task['status'] >= 1) return;
|
||||
if ($this->task['retry'] >= 6 && !$isManual) {
|
||||
throw new Exception('已超出最大重试次数('.$this->task['error'].')', 103);
|
||||
}
|
||||
|
||||
$order = Db::name('cert_order')->where('id', $this->task['oid'])->find();
|
||||
if(!$order) throw new Exception('SSL证书订单不存在', 102);
|
||||
if($order['status'] == 4) throw new Exception('SSL证书订单已吊销', 102);
|
||||
if($order['status'] != 3) throw new Exception('SSL证书订单未完成签发', 102);
|
||||
if(empty($order['fullchain']) || empty($order['privatekey'])) throw new Exception('SSL证书或私钥内容不存在', 102);
|
||||
|
||||
$this->lockTaskData();
|
||||
try {
|
||||
$this->deploy($order['fullchain'], $order['privatekey']);
|
||||
} finally {
|
||||
$this->unlockTaskData();
|
||||
}
|
||||
}
|
||||
|
||||
//部署证书
|
||||
public function deploy($fullchain, $privatekey)
|
||||
{
|
||||
$this->client->setLogger(function ($txt) {
|
||||
$this->saveLog($txt);
|
||||
});
|
||||
$this->saveLog(date('Y-m-d H:i:s'));
|
||||
$config = json_decode($this->task['config'], true);
|
||||
$config['domainList'] = Db::name('cert_domain')->where('oid', $this->task['oid'])->order('sort', 'asc')->column('domain');
|
||||
try {
|
||||
$this->client->deploy($fullchain, $privatekey, $config, $this->info);
|
||||
$this->saveResult(1);
|
||||
$this->saveLog('[Success] 证书部署成功');
|
||||
} catch (Exception $e) {
|
||||
$this->saveResult(-1, $e->getMessage(), date('Y-m-d H:i:s', time() + (array_key_exists($this->task['retry'], self::$retry_interval) ? self::$retry_interval[$this->task['retry']] : 3600)));
|
||||
throw $e;
|
||||
} finally {
|
||||
if($this->info){
|
||||
Db::name('cert_deploy')->where('id', $this->task['id'])->update(['info' => json_encode($this->info)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//重置任务
|
||||
public function reset()
|
||||
{
|
||||
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;
|
||||
$this->task['retry'] = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
$update['lasttime'] = date('Y-m-d H:i:s');
|
||||
}
|
||||
$res = Db::name('cert_deploy')->where('id', $this->task['id'])->data($update);
|
||||
if ($status < 0 || $retrytime) {
|
||||
$this->task['retry']++;
|
||||
$res->inc('retry');
|
||||
}
|
||||
$res->update();
|
||||
if ($error) {
|
||||
$this->saveLog('[Error] ' . $error);
|
||||
}
|
||||
}
|
||||
|
||||
private function lockTaskData()
|
||||
{
|
||||
Db::startTrans();
|
||||
try {
|
||||
$isLock = Db::name('cert_deploy')->where('id', $this->task['id'])->lock(true)->value('islock');
|
||||
if ($isLock == 1 && time() - strtotime($this->task['locktime']) < 3600) {
|
||||
throw new Exception('部署任务处理中,请稍后再试');
|
||||
}
|
||||
$update = ['islock' => 1, 'locktime' => date('Y-m-d H:i:s')];
|
||||
if (empty($this->task['processid'])) $this->task['processid'] = $update['processid'] = getSid();
|
||||
Db::name('cert_deploy')->where('id', $this->task['id'])->update($update);
|
||||
Db::commit();
|
||||
} catch (Exception $e) {
|
||||
Db::rollback();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function unlockTaskData()
|
||||
{
|
||||
Db::name('cert_deploy')->where('id', $this->task['id'])->update(['islock' => 0]);
|
||||
}
|
||||
|
||||
private function saveLog($txt)
|
||||
{
|
||||
if (empty($this->task['processid'])) return;
|
||||
if (!is_dir(app()->getRuntimePath() . 'log')) mkdir(app()->getRuntimePath() . 'log');
|
||||
$file_name = app()->getRuntimePath().'log/'.$this->task['processid'].'.log';
|
||||
$file_exists = file_exists($file_name);
|
||||
file_put_contents($file_name, $txt . PHP_EOL, FILE_APPEND);
|
||||
if (!$file_exists) {
|
||||
@chmod($file_name, 0777);
|
||||
}
|
||||
if(php_sapi_name() == 'cli'){
|
||||
echo $txt . PHP_EOL;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use Exception;
|
||||
use think\facade\Db;
|
||||
use app\lib\DeployHelper;
|
||||
|
||||
/**
|
||||
* SSL证书自动部署
|
||||
*/
|
||||
class CertDeployService
|
||||
{
|
||||
private static $retry_interval = [60, 300, 600, 1800, 3600];
|
||||
|
||||
private $client;
|
||||
private $aid;
|
||||
private $task;
|
||||
private $info;
|
||||
|
||||
//任务状态:0:待处理 1:已完成 -1:处理失败
|
||||
public function __construct($tid)
|
||||
{
|
||||
$task = Db::name('cert_deploy')->where('id', $tid)->find();
|
||||
if (!$task) throw new Exception('该自动部署任务不存在', 102);
|
||||
$this->task = $task;
|
||||
|
||||
$this->aid = $task['aid'];
|
||||
$this->client = DeployHelper::getModel($this->aid);
|
||||
if (!$this->client) throw new Exception('该自动部署任务类型不存在', 102);
|
||||
|
||||
$this->info = $task['info'] ? json_decode($task['info'], true) : [];
|
||||
}
|
||||
|
||||
public function process($isManual = false)
|
||||
{
|
||||
if ($this->task['status'] >= 1) return;
|
||||
if ($this->task['retry'] >= 6 && !$isManual) {
|
||||
throw new Exception('已超出最大重试次数('.$this->task['error'].')', 103);
|
||||
}
|
||||
|
||||
$order = Db::name('cert_order')->where('id', $this->task['oid'])->find();
|
||||
if(!$order) throw new Exception('SSL证书订单不存在', 102);
|
||||
if($order['status'] == 4) throw new Exception('SSL证书订单已吊销', 102);
|
||||
if($order['status'] != 3) throw new Exception('SSL证书订单未完成签发', 102);
|
||||
if(empty($order['fullchain']) || empty($order['privatekey'])) throw new Exception('SSL证书或私钥内容不存在', 102);
|
||||
|
||||
$this->lockTaskData();
|
||||
try {
|
||||
$this->deploy($order['fullchain'], $order['privatekey']);
|
||||
} finally {
|
||||
$this->unlockTaskData();
|
||||
}
|
||||
}
|
||||
|
||||
//部署证书
|
||||
public function deploy($fullchain, $privatekey)
|
||||
{
|
||||
$this->client->setLogger(function ($txt) {
|
||||
$this->saveLog($txt);
|
||||
});
|
||||
$this->saveLog(date('Y-m-d H:i:s'));
|
||||
$config = json_decode($this->task['config'], true);
|
||||
$config['domainList'] = Db::name('cert_domain')->where('oid', $this->task['oid'])->order('sort', 'asc')->column('domain');
|
||||
try {
|
||||
$this->client->deploy($fullchain, $privatekey, $config, $this->info);
|
||||
$this->saveResult(1);
|
||||
$this->saveLog('[Success] 证书部署成功');
|
||||
} catch (Exception $e) {
|
||||
$this->saveResult(-1, $e->getMessage(), date('Y-m-d H:i:s', time() + (array_key_exists($this->task['retry'], self::$retry_interval) ? self::$retry_interval[$this->task['retry']] : 3600)));
|
||||
throw $e;
|
||||
} finally {
|
||||
if($this->info){
|
||||
Db::name('cert_deploy')->where('id', $this->task['id'])->update(['info' => json_encode($this->info)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//重置任务
|
||||
public function reset()
|
||||
{
|
||||
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;
|
||||
$this->task['retry'] = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
$update['lasttime'] = date('Y-m-d H:i:s');
|
||||
}
|
||||
$res = Db::name('cert_deploy')->where('id', $this->task['id'])->data($update);
|
||||
if ($status < 0 || $retrytime) {
|
||||
$this->task['retry']++;
|
||||
$res->inc('retry');
|
||||
}
|
||||
$res->update();
|
||||
if ($error) {
|
||||
$this->saveLog('[Error] ' . $error);
|
||||
}
|
||||
}
|
||||
|
||||
private function lockTaskData()
|
||||
{
|
||||
Db::startTrans();
|
||||
try {
|
||||
$isLock = Db::name('cert_deploy')->where('id', $this->task['id'])->lock(true)->value('islock');
|
||||
if ($isLock == 1 && time() - strtotime($this->task['locktime']) < 3600) {
|
||||
throw new Exception('部署任务处理中,请稍后再试');
|
||||
}
|
||||
$update = ['islock' => 1, 'locktime' => date('Y-m-d H:i:s')];
|
||||
if (empty($this->task['processid'])) $this->task['processid'] = $update['processid'] = getSid();
|
||||
Db::name('cert_deploy')->where('id', $this->task['id'])->update($update);
|
||||
Db::commit();
|
||||
} catch (Exception $e) {
|
||||
Db::rollback();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function unlockTaskData()
|
||||
{
|
||||
Db::name('cert_deploy')->where('id', $this->task['id'])->update(['islock' => 0]);
|
||||
}
|
||||
|
||||
private function saveLog($txt)
|
||||
{
|
||||
if (empty($this->task['processid'])) return;
|
||||
if (!is_dir(app()->getRuntimePath() . 'log')) mkdir(app()->getRuntimePath() . 'log');
|
||||
$file_name = app()->getRuntimePath().'log/'.$this->task['processid'].'.log';
|
||||
$file_exists = file_exists($file_name);
|
||||
file_put_contents($file_name, $txt . PHP_EOL, FILE_APPEND);
|
||||
if (!$file_exists) {
|
||||
@chmod($file_name, 0777);
|
||||
}
|
||||
if(php_sapi_name() == 'cli'){
|
||||
echo $txt . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,441 +1,441 @@
|
||||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use Exception;
|
||||
use think\facade\Db;
|
||||
use app\lib\CertHelper;
|
||||
use app\utils\CertDnsUtils;
|
||||
|
||||
/**
|
||||
* SSL证书订单处理
|
||||
*/
|
||||
class CertOrderService
|
||||
{
|
||||
private static $retry_interval = [60, 180, 300, 600, 600];
|
||||
|
||||
private $client;
|
||||
private $aid;
|
||||
private $atype;
|
||||
private $order;
|
||||
private $info;
|
||||
private $dnsList;
|
||||
private $domainList;
|
||||
private $cnameDomainList = [];
|
||||
|
||||
// 订单状态:0:待提交 1:待验证 2:正在验证 3:已签发 4:已吊销 -1:购买证书失败 -2:创建订单失败 -3:添加DNS失败 -4:验证DNS失败 -5:验证订单失败 -6:订单验证未通过 -7:签发证书失败
|
||||
public function __construct($oid)
|
||||
{
|
||||
$order = Db::name('cert_order')->where('id', $oid)->find();
|
||||
if (!$order) throw new Exception('该证书订单不存在', 102);
|
||||
$this->order = $order;
|
||||
|
||||
$this->aid = $order['aid'];
|
||||
$account = Db::name('cert_account')->where('id', $this->aid)->find();
|
||||
if (!$account) throw new Exception('该证书账户不存在', 102);
|
||||
$config = json_decode($account['config'], true);
|
||||
$ext = $account['ext'] ? json_decode($account['ext'], true) : null;
|
||||
$this->atype = $account['type'];
|
||||
$this->client = CertHelper::getModel2($account['type'], $config, $ext);
|
||||
if (!$this->client) throw new Exception('该证书类型不存在', 102);
|
||||
|
||||
$domainList = Db::name('cert_domain')->where('oid', $oid)->order('sort', 'asc')->column('domain');
|
||||
if (!$domainList) throw new Exception('该证书订单没有绑定域名', 102);
|
||||
$this->domainList = $domainList;
|
||||
$this->info = $order['info'] ? json_decode($order['info'], true) : null;
|
||||
$this->dnsList = $order['dns'] ? json_decode($order['dns'], true) : null;
|
||||
}
|
||||
|
||||
//执行证书申请
|
||||
public function process($isManual = false)
|
||||
{
|
||||
if ($this->order['status'] >= 3) return 3;
|
||||
if ($this->order['retry2'] >= 3 && !$isManual) {
|
||||
throw new Exception('已超出最大重试次数('.$this->order['error'].')', 103);
|
||||
}
|
||||
if ($this->order['status'] != 1 && $this->order['status'] != 2 && $this->order['retry'] >= 3 && !$isManual) {
|
||||
if ($this->order['status'] == -2 || $this->order['status'] == -5 || $this->order['status'] == -6 || $this->order['status'] == -7) {
|
||||
$this->cancel();
|
||||
if($this->order['status'] <= -5) $this->delDns();
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->data(['status' => 0, 'retry' => 0, 'retrytime' => null, 'updatetime' => date('Y-m-d H:i:s')])->inc('retry2')->update();
|
||||
$this->order['status'] = 0;
|
||||
$this->order['retry'] = 0;
|
||||
} else {
|
||||
throw new Exception('已超出最大重试次数('.$this->order['error'].')', 103);
|
||||
}
|
||||
}
|
||||
|
||||
$cname = CertHelper::$cert_config[$this->atype]['cname'];
|
||||
foreach($this->domainList as $domain){
|
||||
$mainDomain = getMainDomain($domain);
|
||||
$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) {
|
||||
$errmsg = '域名'.$domain.'未在本系统添加';
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->data(['error'=>$errmsg]);
|
||||
throw new Exception($errmsg, 103);
|
||||
} else {
|
||||
$this->cnameDomainList[] = $cname_row['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->lockOrder();
|
||||
try {
|
||||
return $this->processOrder($isManual);
|
||||
} finally {
|
||||
$this->unlockOrder();
|
||||
if (($this->order['status'] == -2 || $this->order['status'] == -5 || $this->order['status'] == -6 || $this->order['status'] == -7) && $this->order['retry'] >= 3) {
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->data(['retrytime' => date('Y-m-d H:i:s', time() + 3600)])->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function processOrder($isManual = false)
|
||||
{
|
||||
$this->client->setLogger(function ($txt) {
|
||||
$this->saveLog($txt);
|
||||
});
|
||||
// step1: 购买证书
|
||||
if ($this->order['status'] == 0 || $this->order['status'] == -1) {
|
||||
$this->saveLog(date('Y-m-d H:i:s').' - 开始购买证书');
|
||||
$this->buyCert();
|
||||
}
|
||||
// step2: 创建订单
|
||||
if ($this->order['status'] == 0 || $this->order['status'] == -2) {
|
||||
$this->saveLog(date('Y-m-d H:i:s').' - 开始创建订单');
|
||||
$this->createOrder();
|
||||
}
|
||||
// step3: 添加DNS
|
||||
if ($isManual && $this->order['status'] == -3 && CertDnsUtils::verifyDns($this->dnsList)) {
|
||||
$this->saveResult(1);
|
||||
$this->saveLog('检测到DNS记录已添加成功');
|
||||
return 1;
|
||||
}
|
||||
if ($this->order['status'] == 0 || $this->order['status'] == -3) {
|
||||
$this->saveLog(date('Y-m-d H:i:s').' - 开始添加DNS记录');
|
||||
$this->addDns();
|
||||
$this->saveLog('添加DNS记录成功,请等待生效后进行验证...');
|
||||
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
|
||||
if ($this->order['status'] == 1 || $this->order['status'] == -4) {
|
||||
$this->verifyDns();
|
||||
}
|
||||
// step5: 验证订单
|
||||
if ($this->order['status'] == 1 || $this->order['status'] == -5) {
|
||||
$this->saveLog(date('Y-m-d H:i:s').' - 开始验证订单');
|
||||
$this->authOrder();
|
||||
}
|
||||
// step6: 查询验证结果
|
||||
if ($this->order['status'] == 2 || $this->order['status'] == -6) {
|
||||
$this->saveLog(date('Y-m-d H:i:s').' - 开始查询验证结果');
|
||||
$this->getAuthStatus();
|
||||
}
|
||||
// step7: 签发证书
|
||||
if ($this->order['status'] == 2 || $this->order['status'] == -7) {
|
||||
$this->saveLog(date('Y-m-d H:i:s').' - 开始签发证书');
|
||||
$this->finalizeOrder();
|
||||
}
|
||||
$this->delDns();
|
||||
$this->resetRetry2();
|
||||
$this->saveLog('[Success] 证书签发成功');
|
||||
Db::name('cert_deploy')->where('oid', $this->order['id'])->data(['status' => 0, 'retry' => 0, 'retrytime' => null, 'issend' => 0])->update();
|
||||
return 3;
|
||||
}
|
||||
|
||||
private function lockOrder()
|
||||
{
|
||||
Db::startTrans();
|
||||
try {
|
||||
$isLock = Db::name('cert_order')->where('id', $this->order['id'])->lock(true)->value('islock');
|
||||
if ($isLock == 1 && time() - strtotime($this->order['locktime']) < 3600) {
|
||||
throw new Exception('订单正在处理中,请稍后再试', 102);
|
||||
}
|
||||
$update = ['islock' => 1, 'locktime' => date('Y-m-d H:i:s')];
|
||||
if (empty($this->order['processid'])) $this->order['processid'] = $update['processid'] = getSid();
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->update($update);
|
||||
Db::commit();
|
||||
} catch (Exception $e) {
|
||||
Db::rollback();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function unlockOrder()
|
||||
{
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->update(['islock' => 0]);
|
||||
}
|
||||
|
||||
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) {
|
||||
$this->order['retry']++;
|
||||
$res->inc('retry');
|
||||
}
|
||||
$res->update();
|
||||
if ($error) {
|
||||
$this->saveLog('[Error] ' . $error);
|
||||
}
|
||||
}
|
||||
|
||||
private function resetRetry()
|
||||
{
|
||||
if ($this->order['retry'] > 0) {
|
||||
$this->order['retry'] = 0;
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->update(['retry' => 0, 'retrytime' => null]);
|
||||
}
|
||||
}
|
||||
|
||||
private function resetRetry2()
|
||||
{
|
||||
if ($this->order['retry2'] > 0) {
|
||||
$this->order['retry2'] = 0;
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->update(['retry2' => 0, 'retrytime' => null]);
|
||||
}
|
||||
}
|
||||
|
||||
//重置订单
|
||||
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, 'islock' => 0])->update();
|
||||
$file_name = app()->getRuntimePath().'log/'.$this->order['processid'].'.log';
|
||||
if (file_exists($file_name)) unlink($file_name);
|
||||
$this->order['status'] = 0;
|
||||
$this->order['retry'] = 0;
|
||||
$this->order['retry2'] = 0;
|
||||
$this->order['processid'] = null;
|
||||
}
|
||||
|
||||
//购买证书
|
||||
public function buyCert()
|
||||
{
|
||||
try {
|
||||
$this->client->buyCert($this->domainList, $this->info);
|
||||
} catch (Exception $e) {
|
||||
$this->saveResult(-1, $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
if($this->info){
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->update(['info' => json_encode($this->info)]);
|
||||
}
|
||||
$this->order['status'] = 0;
|
||||
$this->resetRetry();
|
||||
}
|
||||
|
||||
//创建订单
|
||||
public function createOrder()
|
||||
{
|
||||
try {
|
||||
if (!empty($this->cnameDomainList)) {
|
||||
foreach($this->cnameDomainList as $cnameId){
|
||||
$this->checkDomainCname($cnameId);
|
||||
}
|
||||
}
|
||||
try {
|
||||
$this->dnsList = $this->client->createOrder($this->domainList, $this->info, $this->order['keytype'], $this->order['keysize']);
|
||||
} catch (Exception $e) {
|
||||
if (strpos($e->getMessage(), 'KeyID header contained an invalid account URL') !== false) {
|
||||
$ext = $this->client->register();
|
||||
Db::name('cert_account')->where('id', $this->aid)->update(['ext' => json_encode($ext)]);
|
||||
$this->dnsList = $this->client->createOrder($this->domainList, $this->info, $this->order['keytype'], $this->order['keysize']);
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->saveResult(-2, $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->update(['info' => json_encode($this->info), 'dns' => json_encode($this->dnsList)]);
|
||||
|
||||
if (!empty($this->dnsList)) {
|
||||
$dns_txt = '需验证的DNS记录如下:';
|
||||
foreach ($this->dnsList as $mainDomain => $list) {
|
||||
foreach ($list as $row) {
|
||||
$domain = $row['name'] . '.' . $mainDomain;
|
||||
$dns_txt .= PHP_EOL.'主机记录: '.$domain.' 类型: '.$row['type'].' 记录值: '.$row['value'];
|
||||
}
|
||||
}
|
||||
$this->saveLog($dns_txt);
|
||||
}
|
||||
$this->order['status'] = 0;
|
||||
$this->resetRetry();
|
||||
}
|
||||
|
||||
//验证DNS记录
|
||||
public function verifyDns()
|
||||
{
|
||||
$verify = CertDnsUtils::verifyDns($this->dnsList);
|
||||
if (!$verify) {
|
||||
if ($this->order['retry'] >= 10) {
|
||||
$this->saveResult(-4, '未查询到DNS解析记录');
|
||||
} else {
|
||||
$this->saveLog('未查询到DNS解析记录(尝试第'.($this->order['retry']+1).'次)');
|
||||
$this->saveResult(1, null, date('Y-m-d H:i:s', time() + (array_key_exists($this->order['retry'], self::$retry_interval) ? self::$retry_interval[$this->order['retry']] : 1800)));
|
||||
}
|
||||
throw new Exception('未查询到DNS解析记录(尝试第'.($this->order['retry']).'次),请稍后再试');
|
||||
}
|
||||
if($this->order['retry'] == 0 && time() - strtotime($this->order['updatetime']) < 10){
|
||||
throw new Exception('请等待'.(10 - (time() - strtotime($this->order['updatetime']))).'秒后再试');
|
||||
}
|
||||
$this->order['status'] = 1;
|
||||
$this->resetRetry();
|
||||
}
|
||||
|
||||
//验证订单
|
||||
public function authOrder()
|
||||
{
|
||||
try {
|
||||
$this->client->authOrder($this->domainList, $this->info);
|
||||
} catch (Exception $e) {
|
||||
$this->saveResult(-5, $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
$this->saveResult(2);
|
||||
$this->resetRetry();
|
||||
}
|
||||
|
||||
//查询验证结果
|
||||
public function getAuthStatus()
|
||||
{
|
||||
try {
|
||||
$status = $this->client->getAuthStatus($this->domainList, $this->info);
|
||||
} catch (Exception $e) {
|
||||
$this->saveResult(-6, $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
if(!$status){
|
||||
if ($this->order['retry'] >= 10) {
|
||||
$this->saveResult(-6, '订单验证未通过');
|
||||
} else {
|
||||
$this->saveLog('订单验证未通过(尝试第'.($this->order['retry']+1).'次)');
|
||||
$this->saveResult(2, null, date('Y-m-d H:i:s', time() + (array_key_exists($this->order['retry'], self::$retry_interval) ? self::$retry_interval[$this->order['retry']] : 1800)));
|
||||
}
|
||||
throw new Exception('订单验证未通过(尝试第'.($this->order['retry']).'次),请稍后再试');
|
||||
}
|
||||
$this->order['status'] = 2;
|
||||
$this->resetRetry();
|
||||
}
|
||||
|
||||
//签发证书
|
||||
public function finalizeOrder()
|
||||
{
|
||||
try {
|
||||
$result = $this->client->finalizeOrder($this->domainList, $this->info, $this->order['keytype'], $this->order['keysize']);
|
||||
} catch (Exception $e) {
|
||||
$this->saveResult(-7, $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
$this->order['issuer'] = $result['issuer'];
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->update(['fullchain' => $result['fullchain'], 'privatekey' => $result['private_key'], 'issuer' => $result['issuer'], 'issuetime' => date('Y-m-d H:i:s', $result['validFrom']), 'expiretime' => date('Y-m-d H:i:s', $result['validTo'])]);
|
||||
$this->saveResult(3);
|
||||
$this->resetRetry();
|
||||
}
|
||||
|
||||
//吊销证书
|
||||
public function revoke()
|
||||
{
|
||||
$this->client->setLogger(function ($txt) {
|
||||
$this->saveLog($txt);
|
||||
});
|
||||
try {
|
||||
$this->client->revoke($this->info, $this->order['fullchain']);
|
||||
} catch (Exception $e) {
|
||||
throw $e;
|
||||
}
|
||||
$this->saveResult(4);
|
||||
}
|
||||
|
||||
//取消证书订单
|
||||
public function cancel(){
|
||||
$this->client->setLogger(function ($txt) {
|
||||
$this->saveLog($txt);
|
||||
});
|
||||
if($this->order['status'] == 1 || $this->order['status'] == 2 || $this->order['status'] < -2){
|
||||
try {
|
||||
$this->client->cancel($this->info);
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//添加DNS记录
|
||||
public function addDns()
|
||||
{
|
||||
if (empty($this->dnsList)) {
|
||||
$this->saveResult(1);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
CertDnsUtils::addDns($this->dnsList, function ($txt) {
|
||||
$this->saveLog($txt);
|
||||
}, !empty($this->cnameDomainList));
|
||||
} catch (Exception $e) {
|
||||
$this->saveResult(-3, $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
$this->saveResult(1);
|
||||
$this->resetRetry();
|
||||
}
|
||||
|
||||
//删除DNS记录
|
||||
public function delDns()
|
||||
{
|
||||
if (empty($this->dnsList)) return;
|
||||
try {
|
||||
CertDnsUtils::delDns($this->dnsList, function ($txt) {
|
||||
$this->saveLog($txt);
|
||||
}, true);
|
||||
} catch (Exception $e) {
|
||||
$this->saveLog('[Error] ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
//检查域名CNAME代理记录
|
||||
private function checkDomainCname($id)
|
||||
{
|
||||
$row = Db::name('cert_cname')->alias('A')->join('domain B', 'A.did = B.id')->where('A.id', $id)->field('A.*,B.name cnamedomain')->find();
|
||||
$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)) {
|
||||
$result = \app\utils\DnsQueryUtils::query_dns_doh($domain, 'CNAME');
|
||||
if (!$result || !in_array($record, $result)) {
|
||||
if ($row['status'] == 1) {
|
||||
Db::name('cert_cname')->where('id', $id)->update(['status' => 0]);
|
||||
}
|
||||
throw new Exception('域名' . $row['domain'] . '的CNAME代理记录未验证通过');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function saveLog($txt)
|
||||
{
|
||||
if (empty($this->order['processid'])) return;
|
||||
if (!is_dir(app()->getRuntimePath() . 'log')) mkdir(app()->getRuntimePath() . 'log');
|
||||
$file_name = app()->getRuntimePath().'log/'.$this->order['processid'].'.log';
|
||||
$file_exists = file_exists($file_name);
|
||||
file_put_contents($file_name, $txt . PHP_EOL, FILE_APPEND);
|
||||
if (!$file_exists) {
|
||||
@chmod($file_name, 0777);
|
||||
}
|
||||
if(php_sapi_name() == 'cli'){
|
||||
echo $txt . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use Exception;
|
||||
use think\facade\Db;
|
||||
use app\lib\CertHelper;
|
||||
use app\utils\CertDnsUtils;
|
||||
|
||||
/**
|
||||
* SSL证书订单处理
|
||||
*/
|
||||
class CertOrderService
|
||||
{
|
||||
private static $retry_interval = [60, 180, 300, 600, 600];
|
||||
|
||||
private $client;
|
||||
private $aid;
|
||||
private $atype;
|
||||
private $order;
|
||||
private $info;
|
||||
private $dnsList;
|
||||
private $domainList;
|
||||
private $cnameDomainList = [];
|
||||
|
||||
// 订单状态:0:待提交 1:待验证 2:正在验证 3:已签发 4:已吊销 -1:购买证书失败 -2:创建订单失败 -3:添加DNS失败 -4:验证DNS失败 -5:验证订单失败 -6:订单验证未通过 -7:签发证书失败
|
||||
public function __construct($oid)
|
||||
{
|
||||
$order = Db::name('cert_order')->where('id', $oid)->find();
|
||||
if (!$order) throw new Exception('该证书订单不存在', 102);
|
||||
$this->order = $order;
|
||||
|
||||
$this->aid = $order['aid'];
|
||||
$account = Db::name('cert_account')->where('id', $this->aid)->find();
|
||||
if (!$account) throw new Exception('该证书账户不存在', 102);
|
||||
$config = json_decode($account['config'], true);
|
||||
$ext = $account['ext'] ? json_decode($account['ext'], true) : null;
|
||||
$this->atype = $account['type'];
|
||||
$this->client = CertHelper::getModel2($account['type'], $config, $ext);
|
||||
if (!$this->client) throw new Exception('该证书类型不存在', 102);
|
||||
|
||||
$domainList = Db::name('cert_domain')->where('oid', $oid)->order('sort', 'asc')->column('domain');
|
||||
if (!$domainList) throw new Exception('该证书订单没有绑定域名', 102);
|
||||
$this->domainList = $domainList;
|
||||
$this->info = $order['info'] ? json_decode($order['info'], true) : null;
|
||||
$this->dnsList = $order['dns'] ? json_decode($order['dns'], true) : null;
|
||||
}
|
||||
|
||||
//执行证书申请
|
||||
public function process($isManual = false)
|
||||
{
|
||||
if ($this->order['status'] >= 3) return 3;
|
||||
if ($this->order['retry2'] >= 3 && !$isManual) {
|
||||
throw new Exception('已超出最大重试次数('.$this->order['error'].')', 103);
|
||||
}
|
||||
if ($this->order['status'] != 1 && $this->order['status'] != 2 && $this->order['retry'] >= 3 && !$isManual) {
|
||||
if ($this->order['status'] == -2 || $this->order['status'] == -5 || $this->order['status'] == -6 || $this->order['status'] == -7) {
|
||||
$this->cancel();
|
||||
if($this->order['status'] <= -5) $this->delDns();
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->data(['status' => 0, 'retry' => 0, 'retrytime' => null, 'updatetime' => date('Y-m-d H:i:s')])->inc('retry2')->update();
|
||||
$this->order['status'] = 0;
|
||||
$this->order['retry'] = 0;
|
||||
} else {
|
||||
throw new Exception('已超出最大重试次数('.$this->order['error'].')', 103);
|
||||
}
|
||||
}
|
||||
|
||||
$cname = CertHelper::$cert_config[$this->atype]['cname'];
|
||||
foreach($this->domainList as $domain){
|
||||
$mainDomain = getMainDomain($domain);
|
||||
$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) {
|
||||
$errmsg = '域名'.$domain.'未在本系统添加';
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->data(['error'=>$errmsg]);
|
||||
throw new Exception($errmsg, 103);
|
||||
} else {
|
||||
$this->cnameDomainList[] = $cname_row['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->lockOrder();
|
||||
try {
|
||||
return $this->processOrder($isManual);
|
||||
} finally {
|
||||
$this->unlockOrder();
|
||||
if (($this->order['status'] == -2 || $this->order['status'] == -5 || $this->order['status'] == -6 || $this->order['status'] == -7) && $this->order['retry'] >= 3) {
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->data(['retrytime' => date('Y-m-d H:i:s', time() + 3600)])->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function processOrder($isManual = false)
|
||||
{
|
||||
$this->client->setLogger(function ($txt) {
|
||||
$this->saveLog($txt);
|
||||
});
|
||||
// step1: 购买证书
|
||||
if ($this->order['status'] == 0 || $this->order['status'] == -1) {
|
||||
$this->saveLog(date('Y-m-d H:i:s').' - 开始购买证书');
|
||||
$this->buyCert();
|
||||
}
|
||||
// step2: 创建订单
|
||||
if ($this->order['status'] == 0 || $this->order['status'] == -2) {
|
||||
$this->saveLog(date('Y-m-d H:i:s').' - 开始创建订单');
|
||||
$this->createOrder();
|
||||
}
|
||||
// step3: 添加DNS
|
||||
if ($isManual && $this->order['status'] == -3 && CertDnsUtils::verifyDns($this->dnsList)) {
|
||||
$this->saveResult(1);
|
||||
$this->saveLog('检测到DNS记录已添加成功');
|
||||
return 1;
|
||||
}
|
||||
if ($this->order['status'] == 0 || $this->order['status'] == -3) {
|
||||
$this->saveLog(date('Y-m-d H:i:s').' - 开始添加DNS记录');
|
||||
$this->addDns();
|
||||
$this->saveLog('添加DNS记录成功,请等待生效后进行验证...');
|
||||
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
|
||||
if ($this->order['status'] == 1 || $this->order['status'] == -4) {
|
||||
$this->verifyDns();
|
||||
}
|
||||
// step5: 验证订单
|
||||
if ($this->order['status'] == 1 || $this->order['status'] == -5) {
|
||||
$this->saveLog(date('Y-m-d H:i:s').' - 开始验证订单');
|
||||
$this->authOrder();
|
||||
}
|
||||
// step6: 查询验证结果
|
||||
if ($this->order['status'] == 2 || $this->order['status'] == -6) {
|
||||
$this->saveLog(date('Y-m-d H:i:s').' - 开始查询验证结果');
|
||||
$this->getAuthStatus();
|
||||
}
|
||||
// step7: 签发证书
|
||||
if ($this->order['status'] == 2 || $this->order['status'] == -7) {
|
||||
$this->saveLog(date('Y-m-d H:i:s').' - 开始签发证书');
|
||||
$this->finalizeOrder();
|
||||
}
|
||||
$this->delDns();
|
||||
$this->resetRetry2();
|
||||
$this->saveLog('[Success] 证书签发成功');
|
||||
Db::name('cert_deploy')->where('oid', $this->order['id'])->data(['status' => 0, 'retry' => 0, 'retrytime' => null, 'issend' => 0])->update();
|
||||
return 3;
|
||||
}
|
||||
|
||||
private function lockOrder()
|
||||
{
|
||||
Db::startTrans();
|
||||
try {
|
||||
$isLock = Db::name('cert_order')->where('id', $this->order['id'])->lock(true)->value('islock');
|
||||
if ($isLock == 1 && time() - strtotime($this->order['locktime']) < 3600) {
|
||||
throw new Exception('订单正在处理中,请稍后再试', 102);
|
||||
}
|
||||
$update = ['islock' => 1, 'locktime' => date('Y-m-d H:i:s')];
|
||||
if (empty($this->order['processid'])) $this->order['processid'] = $update['processid'] = getSid();
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->update($update);
|
||||
Db::commit();
|
||||
} catch (Exception $e) {
|
||||
Db::rollback();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
private function unlockOrder()
|
||||
{
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->update(['islock' => 0]);
|
||||
}
|
||||
|
||||
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) {
|
||||
$this->order['retry']++;
|
||||
$res->inc('retry');
|
||||
}
|
||||
$res->update();
|
||||
if ($error) {
|
||||
$this->saveLog('[Error] ' . $error);
|
||||
}
|
||||
}
|
||||
|
||||
private function resetRetry()
|
||||
{
|
||||
if ($this->order['retry'] > 0) {
|
||||
$this->order['retry'] = 0;
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->update(['retry' => 0, 'retrytime' => null]);
|
||||
}
|
||||
}
|
||||
|
||||
private function resetRetry2()
|
||||
{
|
||||
if ($this->order['retry2'] > 0) {
|
||||
$this->order['retry2'] = 0;
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->update(['retry2' => 0, 'retrytime' => null]);
|
||||
}
|
||||
}
|
||||
|
||||
//重置订单
|
||||
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, 'islock' => 0])->update();
|
||||
$file_name = app()->getRuntimePath().'log/'.$this->order['processid'].'.log';
|
||||
if (file_exists($file_name)) unlink($file_name);
|
||||
$this->order['status'] = 0;
|
||||
$this->order['retry'] = 0;
|
||||
$this->order['retry2'] = 0;
|
||||
$this->order['processid'] = null;
|
||||
}
|
||||
|
||||
//购买证书
|
||||
public function buyCert()
|
||||
{
|
||||
try {
|
||||
$this->client->buyCert($this->domainList, $this->info);
|
||||
} catch (Exception $e) {
|
||||
$this->saveResult(-1, $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
if($this->info){
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->update(['info' => json_encode($this->info)]);
|
||||
}
|
||||
$this->order['status'] = 0;
|
||||
$this->resetRetry();
|
||||
}
|
||||
|
||||
//创建订单
|
||||
public function createOrder()
|
||||
{
|
||||
try {
|
||||
if (!empty($this->cnameDomainList)) {
|
||||
foreach($this->cnameDomainList as $cnameId){
|
||||
$this->checkDomainCname($cnameId);
|
||||
}
|
||||
}
|
||||
try {
|
||||
$this->dnsList = $this->client->createOrder($this->domainList, $this->info, $this->order['keytype'], $this->order['keysize']);
|
||||
} catch (Exception $e) {
|
||||
if (strpos($e->getMessage(), 'KeyID header contained an invalid account URL') !== false) {
|
||||
$ext = $this->client->register();
|
||||
Db::name('cert_account')->where('id', $this->aid)->update(['ext' => json_encode($ext)]);
|
||||
$this->dnsList = $this->client->createOrder($this->domainList, $this->info, $this->order['keytype'], $this->order['keysize']);
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->saveResult(-2, $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->update(['info' => json_encode($this->info), 'dns' => json_encode($this->dnsList)]);
|
||||
|
||||
if (!empty($this->dnsList)) {
|
||||
$dns_txt = '需验证的DNS记录如下:';
|
||||
foreach ($this->dnsList as $mainDomain => $list) {
|
||||
foreach ($list as $row) {
|
||||
$domain = $row['name'] . '.' . $mainDomain;
|
||||
$dns_txt .= PHP_EOL.'主机记录: '.$domain.' 类型: '.$row['type'].' 记录值: '.$row['value'];
|
||||
}
|
||||
}
|
||||
$this->saveLog($dns_txt);
|
||||
}
|
||||
$this->order['status'] = 0;
|
||||
$this->resetRetry();
|
||||
}
|
||||
|
||||
//验证DNS记录
|
||||
public function verifyDns()
|
||||
{
|
||||
$verify = CertDnsUtils::verifyDns($this->dnsList);
|
||||
if (!$verify) {
|
||||
if ($this->order['retry'] >= 10) {
|
||||
$this->saveResult(-4, '未查询到DNS解析记录');
|
||||
} else {
|
||||
$this->saveLog('未查询到DNS解析记录(尝试第'.($this->order['retry']+1).'次)');
|
||||
$this->saveResult(1, null, date('Y-m-d H:i:s', time() + (array_key_exists($this->order['retry'], self::$retry_interval) ? self::$retry_interval[$this->order['retry']] : 1800)));
|
||||
}
|
||||
throw new Exception('未查询到DNS解析记录(尝试第'.($this->order['retry']).'次),请稍后再试');
|
||||
}
|
||||
if($this->order['retry'] == 0 && time() - strtotime($this->order['updatetime']) < 10){
|
||||
throw new Exception('请等待'.(10 - (time() - strtotime($this->order['updatetime']))).'秒后再试');
|
||||
}
|
||||
$this->order['status'] = 1;
|
||||
$this->resetRetry();
|
||||
}
|
||||
|
||||
//验证订单
|
||||
public function authOrder()
|
||||
{
|
||||
try {
|
||||
$this->client->authOrder($this->domainList, $this->info);
|
||||
} catch (Exception $e) {
|
||||
$this->saveResult(-5, $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
$this->saveResult(2);
|
||||
$this->resetRetry();
|
||||
}
|
||||
|
||||
//查询验证结果
|
||||
public function getAuthStatus()
|
||||
{
|
||||
try {
|
||||
$status = $this->client->getAuthStatus($this->domainList, $this->info);
|
||||
} catch (Exception $e) {
|
||||
$this->saveResult(-6, $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
if(!$status){
|
||||
if ($this->order['retry'] >= 10) {
|
||||
$this->saveResult(-6, '订单验证未通过');
|
||||
} else {
|
||||
$this->saveLog('订单验证未通过(尝试第'.($this->order['retry']+1).'次)');
|
||||
$this->saveResult(2, null, date('Y-m-d H:i:s', time() + (array_key_exists($this->order['retry'], self::$retry_interval) ? self::$retry_interval[$this->order['retry']] : 1800)));
|
||||
}
|
||||
throw new Exception('订单验证未通过(尝试第'.($this->order['retry']).'次),请稍后再试');
|
||||
}
|
||||
$this->order['status'] = 2;
|
||||
$this->resetRetry();
|
||||
}
|
||||
|
||||
//签发证书
|
||||
public function finalizeOrder()
|
||||
{
|
||||
try {
|
||||
$result = $this->client->finalizeOrder($this->domainList, $this->info, $this->order['keytype'], $this->order['keysize']);
|
||||
} catch (Exception $e) {
|
||||
$this->saveResult(-7, $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
$this->order['issuer'] = $result['issuer'];
|
||||
Db::name('cert_order')->where('id', $this->order['id'])->update(['fullchain' => $result['fullchain'], 'privatekey' => $result['private_key'], 'issuer' => $result['issuer'], 'issuetime' => date('Y-m-d H:i:s', $result['validFrom']), 'expiretime' => date('Y-m-d H:i:s', $result['validTo'])]);
|
||||
$this->saveResult(3);
|
||||
$this->resetRetry();
|
||||
}
|
||||
|
||||
//吊销证书
|
||||
public function revoke()
|
||||
{
|
||||
$this->client->setLogger(function ($txt) {
|
||||
$this->saveLog($txt);
|
||||
});
|
||||
try {
|
||||
$this->client->revoke($this->info, $this->order['fullchain']);
|
||||
} catch (Exception $e) {
|
||||
throw $e;
|
||||
}
|
||||
$this->saveResult(4);
|
||||
}
|
||||
|
||||
//取消证书订单
|
||||
public function cancel(){
|
||||
$this->client->setLogger(function ($txt) {
|
||||
$this->saveLog($txt);
|
||||
});
|
||||
if($this->order['status'] == 1 || $this->order['status'] == 2 || $this->order['status'] < -2){
|
||||
try {
|
||||
$this->client->cancel($this->info);
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//添加DNS记录
|
||||
public function addDns()
|
||||
{
|
||||
if (empty($this->dnsList)) {
|
||||
$this->saveResult(1);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
CertDnsUtils::addDns($this->dnsList, function ($txt) {
|
||||
$this->saveLog($txt);
|
||||
}, !empty($this->cnameDomainList));
|
||||
} catch (Exception $e) {
|
||||
$this->saveResult(-3, $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
$this->saveResult(1);
|
||||
$this->resetRetry();
|
||||
}
|
||||
|
||||
//删除DNS记录
|
||||
public function delDns()
|
||||
{
|
||||
if (empty($this->dnsList)) return;
|
||||
try {
|
||||
CertDnsUtils::delDns($this->dnsList, function ($txt) {
|
||||
$this->saveLog($txt);
|
||||
}, true);
|
||||
} catch (Exception $e) {
|
||||
$this->saveLog('[Error] ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
//检查域名CNAME代理记录
|
||||
private function checkDomainCname($id)
|
||||
{
|
||||
$row = Db::name('cert_cname')->alias('A')->join('domain B', 'A.did = B.id')->where('A.id', $id)->field('A.*,B.name cnamedomain')->find();
|
||||
$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)) {
|
||||
$result = \app\utils\DnsQueryUtils::query_dns_doh($domain, 'CNAME');
|
||||
if (!$result || !in_array($record, $result)) {
|
||||
if ($row['status'] == 1) {
|
||||
Db::name('cert_cname')->where('id', $id)->update(['status' => 0]);
|
||||
}
|
||||
throw new Exception('域名' . $row['domain'] . '的CNAME代理记录未验证通过');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function saveLog($txt)
|
||||
{
|
||||
if (empty($this->order['processid'])) return;
|
||||
if (!is_dir(app()->getRuntimePath() . 'log')) mkdir(app()->getRuntimePath() . 'log');
|
||||
$file_name = app()->getRuntimePath().'log/'.$this->order['processid'].'.log';
|
||||
$file_exists = file_exists($file_name);
|
||||
file_put_contents($file_name, $txt . PHP_EOL, FILE_APPEND);
|
||||
if (!$file_exists) {
|
||||
@chmod($file_name, 0777);
|
||||
}
|
||||
if(php_sapi_name() == 'cli'){
|
||||
echo $txt . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,104 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use Exception;
|
||||
use think\facade\Db;
|
||||
use app\utils\MsgNotice;
|
||||
|
||||
class CertTaskService
|
||||
{
|
||||
|
||||
public function execute()
|
||||
{
|
||||
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,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) {
|
||||
$service->reset();
|
||||
}
|
||||
$retcode = $service->process();
|
||||
if ($retcode == 3) {
|
||||
echo 'ID:'.$row['id'].' 证书已签发成功!'.PHP_EOL;
|
||||
if($row['issend'] == 0) MsgNotice::cert_order_send($row['id'], true);
|
||||
} elseif ($retcode == 1) {
|
||||
echo 'ID:'.$row['id'].' 添加DNS记录成功!'.PHP_EOL;
|
||||
}
|
||||
break;
|
||||
} catch (Exception $e) {
|
||||
echo 'ID:'.$row['id'].' '.$e->getMessage().PHP_EOL;
|
||||
if ($e->getCode() == 102) {
|
||||
break;
|
||||
} elseif ($e->getCode() == 103) {
|
||||
if($row['issend'] == 0) MsgNotice::cert_order_send($row['id'], false);
|
||||
} else {
|
||||
$failcount++;
|
||||
}
|
||||
}
|
||||
if ($failcount >= 3) break;
|
||||
sleep(1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function execute_deploy()
|
||||
{
|
||||
$start = config_get('deploy_hour_start', 0);
|
||||
$end = config_get('deploy_hour_end', 23);
|
||||
$hour = date('H');
|
||||
if($start <= $end){
|
||||
if($hour < $start || $hour > $end){
|
||||
echo '不在部署任务运行时间范围内'.PHP_EOL; return false;
|
||||
}
|
||||
}else{
|
||||
if($hour < $start && $hour > $end){
|
||||
echo '不在部署任务运行时间范围内'.PHP_EOL; return false;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
try {
|
||||
$service = new CertDeployService($row['id']);
|
||||
$service->process();
|
||||
echo 'ID:'.$row['id'].' 部署任务执行成功!'.PHP_EOL;
|
||||
if($row['issend'] == 0) MsgNotice::cert_deploy_send($row['id'], true);
|
||||
$count++;
|
||||
} catch (Exception $e) {
|
||||
echo 'ID:'.$row['id'].' '.$e->getMessage().PHP_EOL;
|
||||
if ($e->getCode() == 102) {
|
||||
break;
|
||||
} elseif ($e->getCode() == 103) {
|
||||
if($row['issend'] == 0) MsgNotice::cert_deploy_send($row['id'], false);
|
||||
} else {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
if ($count >= 3) break;
|
||||
sleep(1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
<?php
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use Exception;
|
||||
use think\facade\Db;
|
||||
use app\utils\MsgNotice;
|
||||
|
||||
class CertTaskService
|
||||
{
|
||||
|
||||
public function execute()
|
||||
{
|
||||
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,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) {
|
||||
$service->reset();
|
||||
}
|
||||
$retcode = $service->process();
|
||||
if ($retcode == 3) {
|
||||
echo 'ID:'.$row['id'].' 证书已签发成功!'.PHP_EOL;
|
||||
if($row['issend'] == 0) MsgNotice::cert_order_send($row['id'], true);
|
||||
} elseif ($retcode == 1) {
|
||||
echo 'ID:'.$row['id'].' 添加DNS记录成功!'.PHP_EOL;
|
||||
}
|
||||
break;
|
||||
} catch (Exception $e) {
|
||||
echo 'ID:'.$row['id'].' '.$e->getMessage().PHP_EOL;
|
||||
if ($e->getCode() == 102) {
|
||||
break;
|
||||
} elseif ($e->getCode() == 103) {
|
||||
if($row['issend'] == 0) MsgNotice::cert_order_send($row['id'], false);
|
||||
} else {
|
||||
$failcount++;
|
||||
}
|
||||
}
|
||||
if ($failcount >= 3) break;
|
||||
sleep(1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function execute_deploy()
|
||||
{
|
||||
$start = config_get('deploy_hour_start', 0);
|
||||
$end = config_get('deploy_hour_end', 23);
|
||||
$hour = date('H');
|
||||
if($start <= $end){
|
||||
if($hour < $start || $hour > $end){
|
||||
echo '不在部署任务运行时间范围内'.PHP_EOL; return false;
|
||||
}
|
||||
}else{
|
||||
if($hour < $start && $hour > $end){
|
||||
echo '不在部署任务运行时间范围内'.PHP_EOL; return false;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
try {
|
||||
$service = new CertDeployService($row['id']);
|
||||
$service->process();
|
||||
echo 'ID:'.$row['id'].' 部署任务执行成功!'.PHP_EOL;
|
||||
if($row['issend'] == 0) MsgNotice::cert_deploy_send($row['id'], true);
|
||||
$count++;
|
||||
} catch (Exception $e) {
|
||||
echo 'ID:'.$row['id'].' '.$e->getMessage().PHP_EOL;
|
||||
if ($e->getCode() == 102) {
|
||||
break;
|
||||
} elseif ($e->getCode() == 103) {
|
||||
if($row['issend'] == 0) MsgNotice::cert_deploy_send($row['id'], false);
|
||||
} else {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
if ($count >= 3) break;
|
||||
sleep(1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,104 +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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user