mirror of
https://github.com/netcccyun/dnsmgr.git
synced 2026-02-21 23:37:22 +08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e25d5d76e9 | ||
|
|
c0e72908ab | ||
|
|
867785b774 | ||
|
|
d579ca07af | ||
|
|
7161caf0a5 | ||
|
|
c91b116466 | ||
|
|
b2d27b18a3 | ||
|
|
ee45ddd7ec | ||
|
|
9b66b020c9 | ||
|
|
ec16c3fc8b |
@ -198,7 +198,7 @@ class Domain extends BaseController
|
||||
if (!empty($id)) {
|
||||
$select->where('A.id', $id);
|
||||
} elseif (!empty($kw)) {
|
||||
$select->whereLike('name|A.remark', '%' . $kw . '%');
|
||||
$select->whereLike('A.name|A.remark', '%' . $kw . '%');
|
||||
}
|
||||
if (!empty($aid)) {
|
||||
$select->where('A.aid', $aid);
|
||||
|
||||
@ -85,8 +85,11 @@ class Optimizeip extends BaseController
|
||||
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 ($task['recordnum'] < 1) {
|
||||
return json(['code' => -1, 'msg' => '解析数量不能少于1个']);
|
||||
}
|
||||
if ($task['recordnum'] > 50) {
|
||||
return json(['code' => -1, 'msg' => '解析数量不能超过50个']);
|
||||
}
|
||||
if (Db::name('optimizeip')->where('did', $task['did'])->where('rr', $task['rr'])->find()) {
|
||||
return json(['code' => -1, 'msg' => '当前域名的优选IP任务已存在']);
|
||||
@ -109,8 +112,11 @@ class Optimizeip extends BaseController
|
||||
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 ($task['recordnum'] < 1) {
|
||||
return json(['code' => -1, 'msg' => '解析数量不能少于1个']);
|
||||
}
|
||||
if ($task['recordnum'] > 50) {
|
||||
return json(['code' => -1, 'msg' => '解析数量不能超过50个']);
|
||||
}
|
||||
if (Db::name('optimizeip')->where('did', $task['did'])->where('rr', $task['rr'])->where('id', '<>', $id)->find()) {
|
||||
return json(['code' => -1, 'msg' => '当前域名的优选IP任务已存在']);
|
||||
|
||||
@ -95,11 +95,12 @@ class System extends BaseController
|
||||
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'];
|
||||
$proxy_server = input('post.proxy_server', '', 'trim');
|
||||
$proxy_port = input('post.proxy_port/d', 0);
|
||||
$proxy_user = input('post.proxy_user', '', 'trim');
|
||||
$proxy_pwd = input('post.proxy_pwd', '', 'trim');
|
||||
$proxy_type = input('post.proxy_type', 'http', 'trim');
|
||||
|
||||
try {
|
||||
check_proxy('https://dl.amh.sh/ip.htm', $proxy_server, $proxy_port, $proxy_type, $proxy_user, $proxy_pwd);
|
||||
} catch (Exception $e) {
|
||||
|
||||
@ -257,7 +257,7 @@ location / {
|
||||
'wildcard' => false,
|
||||
'max_domains' => 1,
|
||||
'cname' => false,
|
||||
'note' => '每个自然年有20张免费证书额度,证书到期或吊销不释放额度。需要先进入阿里云控制台-<a href="https://yundun.console.aliyun.com/?p=cas#/certExtend/free/cn-hangzhou" target="_blank" rel="noreferrer">数字证书管理服务</a>,购买个人测试证书资源包。',
|
||||
'note' => '每个自然年有20张免费证书额度,证书到期或吊销不释放额度。需要先进入阿里云控制台-<a href="https://yundun.console.aliyun.com/?p=cas#/instance/TEST/cn-hangzhou" target="_blank" rel="noreferrer">数字证书管理服务</a>,购买个人测试证书资源包。',
|
||||
'inputs' => [
|
||||
'AccessKeyId' => [
|
||||
'name' => 'AccessKeyId',
|
||||
|
||||
@ -677,6 +677,65 @@ class DeployHelper
|
||||
],
|
||||
],
|
||||
],
|
||||
'acepanel' => [
|
||||
'name' => 'AcePanel',
|
||||
'class' => 1,
|
||||
'icon' => 'acepanel.svg',
|
||||
'desc' => '支持 AcePanel 3.0+ 版本使用',
|
||||
'note' => '支持 AcePanel 3.0+ 版本使用',
|
||||
'inputs' => [
|
||||
'url' => [
|
||||
'name' => '面板地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => 'AcePanel 地址',
|
||||
'note' => '填写规则如:https://192.168.1.100:8888/xxxxxx ,带访问入口但不要带其他后缀',
|
||||
'required' => true,
|
||||
],
|
||||
'id' => [
|
||||
'name' => '访问令牌ID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '1',
|
||||
'note' => 'AcePanel 设置->用户->访问令牌',
|
||||
'required' => true,
|
||||
],
|
||||
'token' => [
|
||||
'name' => '访问令牌',
|
||||
'type' => 'input',
|
||||
'note' => 'AcePanel 设置->用户->访问令牌',
|
||||
'placeholder' => '32位字符串',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'taskinputs' => [
|
||||
'type' => [
|
||||
'name' => '部署类型',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => 'AcePanel 网站的证书',
|
||||
'1' => 'AcePanel 本身的证书',
|
||||
],
|
||||
'value' => '0',
|
||||
'required' => true,
|
||||
],
|
||||
'sites' => [
|
||||
'name' => '网站名称列表',
|
||||
'type' => 'textarea',
|
||||
'placeholder' => '填写要部署证书的网站名称,每行一个',
|
||||
'note' => '填写创建网站时设置的网站唯一名称',
|
||||
'show' => 'type==0',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
'ratpanel' => [
|
||||
'name' => '耗子面板',
|
||||
'class' => 1,
|
||||
@ -777,6 +836,53 @@ class DeployHelper
|
||||
],
|
||||
],
|
||||
],
|
||||
'amh' => [
|
||||
'name' => 'AMH面板',
|
||||
'class' => 1,
|
||||
'icon' => 'amh.ico',
|
||||
'desc' => '',
|
||||
'note' => null,
|
||||
'tasknote' => '',
|
||||
'inputs' => [
|
||||
'url' => [
|
||||
'name' => '面板地址',
|
||||
'type' => 'input',
|
||||
'placeholder' => 'AMH面板地址',
|
||||
'note' => '填写规则如:http://192.168.1.100:8888 ,不要带其他后缀',
|
||||
'required' => true,
|
||||
],
|
||||
'apikey' => [
|
||||
'name' => 'API接口密钥',
|
||||
'type' => 'input',
|
||||
'placeholder' => '安装amapi软件后查看,是密钥不是私钥',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
'options' => [
|
||||
'0' => '否',
|
||||
'1' => '是',
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
],
|
||||
'taskinputs' => [
|
||||
'env_name' => [
|
||||
'name' => '环境名称',
|
||||
'type' => 'input',
|
||||
'placeholder' => '如:lnmp01',
|
||||
'required' => true,
|
||||
],
|
||||
'vhost_name' => [
|
||||
'name' => '网站名称列表',
|
||||
'type' => 'textarea',
|
||||
'placeholder' => '填写要部署证书的网站标识域名,每行一个',
|
||||
'note' => '网站标识域名一列的值,并非绑定域名',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
'synology' => [
|
||||
'name' => '群晖面板',
|
||||
'class' => 1,
|
||||
@ -1058,6 +1164,7 @@ ctrl+x 保存退出<br/>',
|
||||
['value'=>'vod', 'label'=>'视频点播'],
|
||||
['value'=>'fc', 'label'=>'函数计算3.0'],
|
||||
['value'=>'fc2', 'label'=>'函数计算2.0'],
|
||||
['value'=>'ga', 'label'=>'全球加速'],
|
||||
['value'=>'upload', 'label'=>'上传到证书管理'],
|
||||
],
|
||||
'value' => 'cdn',
|
||||
@ -1148,6 +1255,21 @@ ctrl+x 保存退出<br/>',
|
||||
'note' => '进入NLB实例详情->监听列表,复制监听ID(只支持TCPSSL监听协议)',
|
||||
'required' => true,
|
||||
],
|
||||
'ga_id' => [
|
||||
'name' => '全球加速实例ID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'show' => 'product==\'ga\'',
|
||||
'required' => true,
|
||||
],
|
||||
'ga_listener_id' => [
|
||||
'name' => '监听ID',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'show' => 'product==\'ga\'',
|
||||
'note' => '进入实例详情->监听列表,复制监听ID(只支持HTTPS监听协议)',
|
||||
'required' => true,
|
||||
],
|
||||
'deploy_type' => [
|
||||
'name' => '部署证书类型',
|
||||
'type' => 'select',
|
||||
@ -1156,21 +1278,21 @@ ctrl+x 保存退出<br/>',
|
||||
['value'=>'1', 'label'=>'扩展证书'],
|
||||
],
|
||||
'value' => '0',
|
||||
'show' => 'product==\'clb\'||product==\'alb\'||product==\'nlb\'',
|
||||
'show' => 'product==\'clb\'||product==\'alb\'||product==\'nlb\'||product==\'ga\'',
|
||||
'required' => true,
|
||||
],
|
||||
'clb_domain' => [
|
||||
'name' => '扩展域名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '多个域名可使用,分隔',
|
||||
'show' => 'product==\'clb\'&&deploy_type==1',
|
||||
'show' => 'product==\'clb\'&&deploy_type==1||product==\'ga\'&&deploy_type==1',
|
||||
'required' => true,
|
||||
],
|
||||
'domain' => [
|
||||
'name' => '绑定的域名',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'show' => 'product!=\'esa\'&&product!=\'clb\'&&product!=\'alb\'&&product!=\'nlb\'&&product!=\'upload\'',
|
||||
'show' => 'product!=\'esa\'&&product!=\'clb\'&&product!=\'alb\'&&product!=\'nlb\'&&product!=\'ga\'&&product!=\'upload\'',
|
||||
'required' => true,
|
||||
],
|
||||
],
|
||||
|
||||
@ -374,6 +374,12 @@ class DnsHelper
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'apikey' => [
|
||||
'name' => 'API密钥/令牌',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'auth' => [
|
||||
'name' => '认证方式',
|
||||
'type' => 'radio',
|
||||
@ -383,12 +389,6 @@ class DnsHelper
|
||||
],
|
||||
'value' => '0'
|
||||
],
|
||||
'apikey' => [
|
||||
'name' => 'API密钥/令牌',
|
||||
'type' => 'input',
|
||||
'placeholder' => '',
|
||||
'required' => true,
|
||||
],
|
||||
'proxy' => [
|
||||
'name' => '使用代理服务器',
|
||||
'type' => 'radio',
|
||||
|
||||
@ -62,10 +62,14 @@ class customacme implements CertInterface
|
||||
|
||||
$dnsList = [];
|
||||
if (!empty($order['challenges'])) {
|
||||
$keys = [];
|
||||
foreach ($order['challenges'] as $opts) {
|
||||
$key = $opts['key'] . '|' .$opts['value'];
|
||||
if (in_array($key, $keys)) continue;
|
||||
$mainDomain = getMainDomain($opts['domain']);
|
||||
$name = substr($opts['key'], 0, -(strlen($mainDomain) + 1));
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => 'TXT', 'value' => $opts['value']];
|
||||
$keys[] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -59,13 +59,17 @@ class litessl implements CertInterface
|
||||
|
||||
$dnsList = [];
|
||||
if (!empty($order['challenges'])) {
|
||||
$keys = [];
|
||||
foreach ($order['challenges'] as $opts) {
|
||||
$key = $opts['key'] . '|' .$opts['value'];
|
||||
if (in_array($key, $keys)) continue;
|
||||
$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 "litessl.cn"'];
|
||||
}*/
|
||||
$dnsList[$mainDomain][] = ['name' => $name, 'type' => 'TXT', 'value' => $opts['value']];
|
||||
$keys[] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
163
app/lib/deploy/acepanel.php
Normal file
163
app/lib/deploy/acepanel.php
Normal file
@ -0,0 +1,163 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class acepanel implements DeployInterface
|
||||
{
|
||||
private $logger;
|
||||
private $url;
|
||||
private $id;
|
||||
private $token;
|
||||
private $proxy;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->url = rtrim($config['url'], '/');
|
||||
$this->id = $config['id'];
|
||||
$this->token = $config['token'];
|
||||
$this->proxy = $config['proxy'] == 1;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (empty($this->url) || empty($this->id) || empty($this->token)) throw new Exception('请填写完整面板地址和访问令牌');
|
||||
|
||||
$response = $this->request('/user/info', null, 'GET');
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['msg']) && $result['msg'] == "success") {
|
||||
return true;
|
||||
} else {
|
||||
throw new Exception($result['msg'] ?? '面板地址无法连接');
|
||||
}
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if ($config['type'] == '1') {
|
||||
$this->deployPanel($fullchain, $privatekey);
|
||||
$this->log("面板证书部署成功");
|
||||
return;
|
||||
}
|
||||
$sites = explode("\n", $config['sites']);
|
||||
$success = 0;
|
||||
$errmsg = null;
|
||||
foreach ($sites as $site) {
|
||||
$site = trim($site);
|
||||
if (empty($site)) continue;
|
||||
try {
|
||||
$this->deploySite($site, $fullchain, $privatekey);
|
||||
$this->log("网站 {$site} 证书部署成功");
|
||||
$success++;
|
||||
} catch (Exception $e) {
|
||||
$errmsg = $e->getMessage();
|
||||
$this->log("网站 {$site} 证书部署失败:" . $errmsg);
|
||||
}
|
||||
}
|
||||
if ($success == 0) {
|
||||
throw new Exception($errmsg ?: '要部署的网站不存在');
|
||||
}
|
||||
}
|
||||
|
||||
private function deployPanel($fullchain, $privatekey)
|
||||
{
|
||||
$data = [
|
||||
'cert' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
];
|
||||
$response = $this->request('/setting/cert', $data);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['msg']) && $result['msg'] == "success") {
|
||||
return true;
|
||||
} elseif (isset($result['msg'])) {
|
||||
throw new Exception($result['msg']);
|
||||
} else {
|
||||
throw new Exception($response ?: '返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
private function deploySite($name, $fullchain, $privatekey)
|
||||
{
|
||||
$data = [
|
||||
'name' => $name,
|
||||
'cert' => $fullchain,
|
||||
'key' => $privatekey,
|
||||
];
|
||||
$response = $this->request('/website/cert', $data);
|
||||
$result = json_decode($response, true);
|
||||
if (isset($result['msg']) && $result['msg'] == "success") {
|
||||
return true;
|
||||
} elseif (isset($result['msg'])) {
|
||||
throw new Exception($result['msg']);
|
||||
} else {
|
||||
throw new Exception($response ?: '返回数据解析失败');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
private function request($path, $params, $method = 'POST')
|
||||
{
|
||||
$url = $this->url . '/api' . $path;
|
||||
$body = $method == 'GET' ? null : json_encode($params);
|
||||
$sign = $this->signRequest($method, $url, $body, $this->id, $this->token);
|
||||
$response = http_request($url, $body, null, null, [
|
||||
'Content-Type' => 'application/json',
|
||||
'X-Timestamp' => $sign['timestamp'],
|
||||
'Authorization' => 'HMAC-SHA256 Credential=' . $sign['id'] . ', Signature=' . $sign['signature']
|
||||
], $this->proxy, $method);
|
||||
return $response['body'];
|
||||
}
|
||||
|
||||
private function signRequest($method, $url, $body, $id, $token)
|
||||
{
|
||||
// 解析URL并获取路径
|
||||
$parsedUrl = parse_url($url);
|
||||
$path = $parsedUrl['path'];
|
||||
$query = $parsedUrl['query'] ?? '';
|
||||
|
||||
// 规范化路径
|
||||
$canonicalPath = $path;
|
||||
if (!str_starts_with($path, '/api')) {
|
||||
$apiPos = strpos($path, '/api');
|
||||
if ($apiPos !== false) {
|
||||
$canonicalPath = substr($path, $apiPos);
|
||||
}
|
||||
}
|
||||
|
||||
// 构造规范化请求
|
||||
$canonicalRequest = implode("\n", [
|
||||
$method,
|
||||
$canonicalPath,
|
||||
$query,
|
||||
hash('sha256', $body ?: '')
|
||||
]);
|
||||
|
||||
// 计算签名
|
||||
$timestamp = time();
|
||||
$stringToSign = implode("\n", [
|
||||
'HMAC-SHA256',
|
||||
$timestamp,
|
||||
hash('sha256', $canonicalRequest)
|
||||
]);
|
||||
$signature = hash_hmac('sha256', $stringToSign, $token);
|
||||
|
||||
return [
|
||||
'timestamp' => $timestamp,
|
||||
'signature' => $signature,
|
||||
'id' => $id
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -66,6 +66,8 @@ class aliyun implements DeployInterface
|
||||
$this->deploy_alb($cert_id, $config);
|
||||
} elseif ($config['product'] == 'nlb') {
|
||||
$this->deploy_nlb($cert_id, $config);
|
||||
} elseif ($config['product'] == 'ga') {
|
||||
$this->deploy_ga($cert_id, $config);
|
||||
} elseif ($config['product'] == 'upload') {
|
||||
} else {
|
||||
throw new Exception('未知的产品类型');
|
||||
@ -201,11 +203,11 @@ class aliyun implements DeployInterface
|
||||
}
|
||||
$this->log('ESA站点 ' . $sitename . ' 查询到' . $data['TotalCount'] . '个SSL证书');
|
||||
|
||||
$exist_cert_id = null;
|
||||
$exist_cert_name = null;
|
||||
$exist_cert_casid = null;
|
||||
$exist_cert = null;
|
||||
$oldest_cert = null;
|
||||
if ($data['TotalCount'] > 0) {
|
||||
foreach ($data['Result'] as $cert) {
|
||||
if ($cert['Type'] == 'free') continue;
|
||||
$domains = explode(',', $cert['SAN']);
|
||||
$flag = true;
|
||||
foreach ($domains as $domain) {
|
||||
@ -215,11 +217,40 @@ class aliyun implements DeployInterface
|
||||
}
|
||||
}
|
||||
if ($flag) {
|
||||
$exist_cert_id = $cert['Id'];
|
||||
$exist_cert_name = $cert['Name'];
|
||||
$exist_cert_casid = isset($cert['CasId']) ? $cert['CasId'] : null;
|
||||
$exist_cert = $cert;
|
||||
break;
|
||||
}
|
||||
if (!$oldest_cert) {
|
||||
$oldest_cert = $cert;
|
||||
} elseif (strtotime($cert['CreateTime']) < strtotime($oldest_cert['CreateTime'])) {
|
||||
$oldest_cert = $cert;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$exist_cert) { //新增证书时,若配额已满,则删除最旧的证书
|
||||
$param = [
|
||||
'Action' => 'ListInstanceQuotasWithUsage',
|
||||
'SiteId' => $site_id,
|
||||
'QuotaNames' => 'customHttpCert',
|
||||
];
|
||||
try {
|
||||
$data = $client->request($param, 'GET');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('查询ESA站点证书配额失败:' . $e->getMessage());
|
||||
}
|
||||
if (!empty($data['Quotas']) && intval($data['Quotas'][0]['Usage']) >= intval($data['Quotas'][0]['QuotaValue']) && $oldest_cert) {
|
||||
$param = [
|
||||
'Action' => 'DeleteCertificate',
|
||||
'SiteId' => $site_id,
|
||||
'Id' => $oldest_cert['Id'],
|
||||
];
|
||||
try {
|
||||
$client->request($param, 'GET');
|
||||
$this->log('ESA站点 ' . $sitename . ' 删除证书 ' . $oldest_cert['Name'] . ' 成功');
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('ESA站点 ' . $sitename . ' 删除证书' . $oldest_cert['Name'] . '失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,10 +263,10 @@ class aliyun implements DeployInterface
|
||||
'Region' => $config['region'],
|
||||
];
|
||||
|
||||
if ($exist_cert_id) {
|
||||
$param['Id'] = $exist_cert_id;
|
||||
if ($exist_cert) {
|
||||
$param['Id'] = $exist_cert['Id'];
|
||||
|
||||
if ($exist_cert_casid == $cas_id) {
|
||||
if (isset($exist_cert['CasId']) && $exist_cert['CasId'] == $cas_id) {
|
||||
$this->log('ESA站点 ' . $sitename . ' 证书已配置,无需重复操作');
|
||||
return;
|
||||
}
|
||||
@ -243,8 +274,8 @@ class aliyun implements DeployInterface
|
||||
|
||||
$client->request($param);
|
||||
|
||||
if ($exist_cert_name) {
|
||||
$this->log('ESA站点 ' . $sitename . ' 证书 ' . $exist_cert_name . ' 更新成功');
|
||||
if ($exist_cert) {
|
||||
$this->log('ESA站点 ' . $sitename . ' 证书 ' . $exist_cert['Name'] . ' 更新成功');
|
||||
} else {
|
||||
$this->log('ESA站点 ' . $sitename . ' 证书添加成功!');
|
||||
}
|
||||
@ -735,6 +766,84 @@ class aliyun implements DeployInterface
|
||||
}
|
||||
}
|
||||
|
||||
private function deploy_ga($cert_id, $config)
|
||||
{
|
||||
if (empty($config['ga_id'])) throw new Exception('全球加速实例ID不能为空');
|
||||
if (empty($config['ga_listener_id'])) throw new Exception('全球加速监听ID不能为空');
|
||||
|
||||
$client = new AliyunClient($this->AccessKeyId, $this->AccessKeySecret, 'ga.cn-hangzhou.aliyuncs.com', '2019-11-20', $this->proxy);
|
||||
$cert_id = $cert_id . '-cn-hangzhou';
|
||||
$deploy_type = isset($config['deploy_type']) ? intval($config['deploy_type']) : 0;
|
||||
|
||||
if ($deploy_type == 1) {
|
||||
if (empty($config['clb_domain'])) throw new Exception('扩展域名不能为空');
|
||||
$param = [
|
||||
'Action' => 'ListListenerCertificates',
|
||||
'RegionId' => 'cn-hangzhou',
|
||||
'AcceleratorId' => $config['ga_id'],
|
||||
'ListenerId' => $config['ga_listener_id'],
|
||||
];
|
||||
try {
|
||||
$data = $client->request($param);
|
||||
} catch (Exception $e) {
|
||||
throw new Exception('扩展域名列表查询失败:' . $e->getMessage());
|
||||
}
|
||||
$need_add = [];
|
||||
foreach (explode(',', $config['clb_domain']) as $domain) {
|
||||
$domainExists = false;
|
||||
$exist_cert_id = null;
|
||||
foreach ($data['Certificates'] as $cert) {
|
||||
if (isset($cert['Domain']) && $domain == $cert['Domain']) {
|
||||
$domainExists = true;
|
||||
$exist_cert_id = $cert['CertificateId'];
|
||||
}
|
||||
}
|
||||
if ($domainExists) {
|
||||
if ($exist_cert_id == $cert_id) {
|
||||
$this->log('全球加速实例监听扩展域名 ' . $domain . ' 证书已配置');
|
||||
continue;
|
||||
}
|
||||
$param = [
|
||||
'Action' => 'UpdateAdditionalCertificateWithListener',
|
||||
'RegionId' => 'cn-hangzhou',
|
||||
'AcceleratorId' => $config['ga_id'],
|
||||
'ListenerId' => $config['ga_listener_id'],
|
||||
'Domain' => $domain,
|
||||
'CertificateId' => $cert_id,
|
||||
];
|
||||
$client->request($param);
|
||||
$this->log('全球加速实例监听扩展域名 ' . $domain . ' 替换证书成功!');
|
||||
} else {
|
||||
$need_add[] = $domain;
|
||||
}
|
||||
}
|
||||
if (count($need_add) > 0) {
|
||||
$param = [
|
||||
'Action' => 'AssociateAdditionalCertificatesWithListener',
|
||||
'RegionId' => 'cn-hangzhou',
|
||||
'AcceleratorId' => $config['ga_id'],
|
||||
'ListenerId' => $config['ga_listener_id'],
|
||||
];
|
||||
foreach ($need_add as $index => $domain) {
|
||||
$param['Certificates.' . ($index + 1) . '.Id'] = $cert_id;
|
||||
$param['Certificates.' . ($index + 1) . '.Domain'] = $domain;
|
||||
}
|
||||
$client->request($param);
|
||||
$this->log('全球加速实例监听扩展域名 ' . implode(',', $need_add) . ' 绑定证书成功!');
|
||||
}
|
||||
} else {
|
||||
$param = [
|
||||
'Action' => 'UpdateListener',
|
||||
'RegionId' => 'cn-hangzhou',
|
||||
'AcceleratorId' => $config['ga_id'],
|
||||
'ListenerId' => $config['ga_listener_id'],
|
||||
'Certificates.1.Id' => $cert_id,
|
||||
];
|
||||
$client->request($param);
|
||||
$this->log('全球加速实例监听默认证书更新成功!');
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
|
||||
108
app/lib/deploy/amh.php
Normal file
108
app/lib/deploy/amh.php
Normal file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace app\lib\deploy;
|
||||
|
||||
use app\lib\DeployInterface;
|
||||
use Exception;
|
||||
|
||||
class amh 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('请填写面板地址和接口密钥');
|
||||
$this->login();
|
||||
return true;
|
||||
}
|
||||
|
||||
private function login()
|
||||
{
|
||||
$path = '/?c=amapi&a=login';
|
||||
$post_data = 'amapi_expires=' . time() + 120;
|
||||
$post_data .= '&amapi_sign=' . hash_hmac('sha256', $post_data, $this->apikey);
|
||||
$response = $this->request($path, $post_data);
|
||||
if ($response['code'] == 302 && strpos($response['redirect_url'], 'amh_token=') !== false) {
|
||||
if(preg_match('/amh_token=([A-Za-z0-9]+)/', $response['redirect_url'], $matches)) {
|
||||
return $matches[1];
|
||||
}else{
|
||||
throw new Exception('面板返回数据异常');
|
||||
}
|
||||
} elseif ($response['code'] == 200 && preg_match('/<p id="error".*?>(.*?)<\/p>/s', $response['body'], $matches)) {
|
||||
throw new Exception(strip_tags($matches[1]));
|
||||
} else {
|
||||
throw new Exception('面板地址无法连接');
|
||||
}
|
||||
}
|
||||
|
||||
public function deploy($fullchain, $privatekey, $config, &$info)
|
||||
{
|
||||
if (empty($config['env_name'])) throw new Exception('环境名称不能为空');
|
||||
if (empty($config['vhost_name'])) throw new Exception('网站标识域名不能为空');
|
||||
|
||||
$amh_token = $this->login();
|
||||
|
||||
foreach (explode("\n", $config['vhost_name']) as $vhost_name) {
|
||||
$vhost_name = trim($vhost_name);
|
||||
if (empty($vhost_name)) continue;
|
||||
|
||||
$path = '/?c=amssl&a=admin_amssl&envs_name=' . $config['env_name'] . '&vhost_name=' . $vhost_name . '&ModuleSort=app';
|
||||
$params = [
|
||||
'submit_key_crt' => 'y',
|
||||
'key_input1' => 'key_input1',
|
||||
'key_content1' => $privatekey,
|
||||
'crt_input1' => 'crt_input1',
|
||||
'crt_content1' => $fullchain,
|
||||
'amh_token' => $amh_token,
|
||||
];
|
||||
$response = $this->request($path, $params);
|
||||
if (strpos($response['body'], '<p id="success"') !== false) {
|
||||
$this->log("网站 {$vhost_name} 证书部署成功");
|
||||
} elseif (preg_match('/<p id="error".*?>(.*?)<\/p>/s', $response['body'], $matches)) {
|
||||
$errmsg = strip_tags($matches[1]);
|
||||
$this->log("网站 {$vhost_name} 证书部署失败:" . $errmsg);
|
||||
throw new Exception($errmsg);
|
||||
} elseif (preg_match('/<p id="error".*?>(.*?)<br \/>/s', $response['body'], $matches)) {
|
||||
$errmsg = $matches[1];
|
||||
if (strpos($errmsg, '<br />') !== false) {
|
||||
$errmsg = explode('<br />', $errmsg)[0];
|
||||
}
|
||||
$errmsg = strip_tags($errmsg);
|
||||
$this->log("网站 {$vhost_name} 证书部署失败:" . $errmsg);
|
||||
throw new Exception($errmsg);
|
||||
} else {
|
||||
throw new Exception("网站 {$vhost_name} 证书部署失败:未知错误");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function setLogger($func)
|
||||
{
|
||||
$this->logger = $func;
|
||||
}
|
||||
|
||||
private function log($txt)
|
||||
{
|
||||
if ($this->logger) {
|
||||
call_user_func($this->logger, $txt);
|
||||
}
|
||||
}
|
||||
|
||||
private function request($path, $post_data = null)
|
||||
{
|
||||
$url = $this->url . $path;
|
||||
$cookie = 'PHPSESSID=' . hash_hmac('md5', 'php_sessid=' . $this->apikey, $this->apikey);
|
||||
$response = http_request($url, $post_data, null, $cookie, null, $this->proxy);
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@ -159,14 +159,20 @@ class ssh implements DeployInterface
|
||||
file_put_contents($privateKeyPath, $this->config['privatekey']);
|
||||
file_put_contents($publicKeyPath, $publicKey);
|
||||
umask($umask);
|
||||
if (!empty($this->config['passphrase'])) {
|
||||
if (!ssh2_auth_pubkey_file($connection, $this->config['username'], $publicKeyPath, $privateKeyPath, $this->config['passphrase'])) {
|
||||
throw new Exception('私钥认证失败');
|
||||
}
|
||||
} else {
|
||||
if (!ssh2_auth_pubkey_file($connection, $this->config['username'], $publicKeyPath, $privateKeyPath)) {
|
||||
throw new Exception('私钥认证失败');
|
||||
|
||||
try {
|
||||
if (!empty($this->config['passphrase'])) {
|
||||
if (!ssh2_auth_pubkey_file($connection, $this->config['username'], $publicKeyPath, $privateKeyPath, $this->config['passphrase'])) {
|
||||
throw new Exception('私钥认证失败');
|
||||
}
|
||||
} else {
|
||||
if (!ssh2_auth_pubkey_file($connection, $this->config['username'], $publicKeyPath, $privateKeyPath)) {
|
||||
throw new Exception('私钥认证失败');
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
unlink($publicKeyPath);
|
||||
unlink($privateKeyPath);
|
||||
}
|
||||
} else {
|
||||
if (!ssh2_auth_password($connection, $this->config['username'], $this->config['password'])) {
|
||||
|
||||
@ -79,6 +79,7 @@ class cloudflare implements DnsInterface
|
||||
foreach ($data['result'] as $row) {
|
||||
$name = $this->domain == $row['name'] ? '@' : substr($row['name'], 0, -(strlen($this->domain) + 1));
|
||||
$status = str_ends_with($name, '_pause') ? '0' : '1';
|
||||
$name = $name == '__root__' ? '@' : $name;
|
||||
$name = $status == '0' ? substr($name, 0, -6) : $name;
|
||||
if ($row['type'] == 'SRV' && isset($row['priority'])) {
|
||||
$row['content'] = $row['priority'] . ' ' . $row['content'];
|
||||
@ -117,6 +118,7 @@ class cloudflare implements DnsInterface
|
||||
$name = $this->domain == $data['result']['name'] ? '@' : substr($data['result']['name'], 0, -(strlen($this->domain) + 1));
|
||||
$status = str_ends_with($name, '_pause') ? '0' : '1';
|
||||
$name = $status == '0' ? substr($name, 0, -6) : $name;
|
||||
$name = $name == '__root__' ? '@' : $name;
|
||||
if ($data['result']['type'] == 'SRV' && isset($data['result']['priority'])) {
|
||||
$data['result']['content'] = $data['result']['priority'] . ' ' . $data['result']['content'];
|
||||
}
|
||||
@ -182,6 +184,12 @@ class cloudflare implements DnsInterface
|
||||
{
|
||||
$info = $this->getDomainRecordInfo($RecordId);
|
||||
$Name = $Status == '1' ? str_replace('_pause', '', $info['Name']) : $info['Name'] . '_pause';
|
||||
// @ 作为特殊字符不能设置为解析, 故设置暂停解析的时候, 替换为 __root__
|
||||
if ($Name == '__root__') {
|
||||
$Name = '@';
|
||||
} elseif ($Name == '@_pause') {
|
||||
$Name = '__root___pause';
|
||||
}
|
||||
return $this->updateDomainRecord($RecordId, $Name, $info['Type'], $info['Value'], $info['Line'], $info['TTL'], $info['MX'], $info['Weight'], $info['Remark']);
|
||||
}
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@ class OptimizeService
|
||||
public static function get_license($api, $key)
|
||||
{
|
||||
if ($api == 2) {
|
||||
throw new Exception('当前接口暂不支持');
|
||||
throw new Exception('xingpingcn.top 接口免费使用,无需密钥,无积分限制');
|
||||
} elseif ($api == 1) {
|
||||
$url = 'https://api.hostmonit.com/get_license?license='.$key;
|
||||
} else {
|
||||
@ -39,7 +39,9 @@ class OptimizeService
|
||||
public function get_ip_address($cdn_type = 1, $ip_type = 'v4')
|
||||
{
|
||||
$api = config_get('optimize_ip_api', 0);
|
||||
if ($api == 1) {
|
||||
if ($api == 2) {
|
||||
return $this->get_ip_address_xingpingcn($ip_type);
|
||||
} elseif ($api == 1) {
|
||||
$url = 'https://api.hostmonit.com/get_optimization_ip';
|
||||
} else {
|
||||
$url = 'https://www.wetest.vip/api/cf2dns/';
|
||||
@ -70,6 +72,59 @@ class OptimizeService
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 xingpingcn.top 获取优选IP数据
|
||||
* @param string $ip_type IP类型 v4/v6
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
private function get_ip_address_xingpingcn($ip_type = 'v4')
|
||||
{
|
||||
if ($ip_type == 'v6') {
|
||||
throw new Exception('xingpingcn.top 接口暂不支持IPv6');
|
||||
}
|
||||
$proxy = config_get('optimize_ip_proxy', '');
|
||||
if (!empty($proxy)) {
|
||||
$proxy = trim($proxy);
|
||||
if (filter_var($proxy, FILTER_VALIDATE_URL) === false) {
|
||||
throw new Exception('无效的代理地址配置:URL 格式错误');
|
||||
}
|
||||
$scheme = parse_url($proxy, PHP_URL_SCHEME);
|
||||
if (!in_array($scheme, ['http', 'https'], true)) {
|
||||
throw new Exception('无效的代理地址配置:仅支持 http 和 https 协议');
|
||||
}
|
||||
$url = rtrim($proxy, '/') . '/xingpingcn/enhanced-FaaS-in-China/refs/heads/main/Cf.json';
|
||||
} else {
|
||||
$url = 'https://raw.githubusercontent.com/xingpingcn/enhanced-FaaS-in-China/refs/heads/main/Cf.json';
|
||||
}
|
||||
$response = get_curl($url);
|
||||
if ($response === '') {
|
||||
throw new Exception('获取优选IP数据失败,网络请求失败,请检查网络连接或代理地址');
|
||||
}
|
||||
$arr = json_decode($response, true);
|
||||
if (isset($arr['Cf']['result'])) {
|
||||
$result = $arr['Cf']['result'];
|
||||
$info = [];
|
||||
// 转换格式:dianxin->CT, liantong->CU, yidong->CM, default->DEF
|
||||
if (isset($result['dianxin']) && is_array($result['dianxin'])) {
|
||||
$info['CT'] = array_map(function($ip) { return ['ip' => $ip]; }, $result['dianxin']);
|
||||
}
|
||||
if (isset($result['liantong']) && is_array($result['liantong'])) {
|
||||
$info['CU'] = array_map(function($ip) { return ['ip' => $ip]; }, $result['liantong']);
|
||||
}
|
||||
if (isset($result['yidong']) && is_array($result['yidong'])) {
|
||||
$info['CM'] = array_map(function($ip) { return ['ip' => $ip]; }, $result['yidong']);
|
||||
}
|
||||
// 不使用他的默认线路数据, 因为这真的是默认. 由后续逻辑自己决定是否把CT线路当DEF来用
|
||||
// if (isset($result['default']) && is_array($result['default'])) {
|
||||
// $info['DEF'] = array_map(function($ip) { return ['ip' => $ip]; }, $result['default']);
|
||||
// }
|
||||
return $info;
|
||||
} else {
|
||||
throw new Exception('获取优选IP数据失败,接口返回数据格式错误');
|
||||
}
|
||||
}
|
||||
|
||||
public function get_ip_address2($cdn_type = 1, $ip_type = 'v4')
|
||||
{
|
||||
$key = $cdn_type.'_'.$ip_type;
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
<form onsubmit="return searchSubmit()" method="GET" class="form-inline" id="searchToolbar">
|
||||
<div class="form-group">
|
||||
<label>搜索</label>
|
||||
<input type="text" class="form-control" name="kw" placeholder="AccessKey或备注">
|
||||
<input type="text" class="form-control" name="kw" placeholder="账户名称或备注">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary"><i class="fa fa-search"></i> 搜索</button>
|
||||
<a href="javascript:searchClear()" class="btn btn-default" title="刷新域名账户列表"><i class="fa fa-refresh"></i> 刷新</a>
|
||||
@ -54,7 +54,7 @@ $(document).ready(function(){
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: 'AccessKey'
|
||||
title: '账户名称'
|
||||
},
|
||||
{
|
||||
field: 'remark',
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
<label class="col-sm-3 control-label no-padding-right" is-required>账户类型</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="type" class="form-control" v-model="set.type">
|
||||
<option v-for="(item, key) in typeList" :value="key">{{item.name}}</option>
|
||||
<option v-for="(item, key) in typeList" :value="key" :data-icon="item.icon">{{item.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -95,6 +95,8 @@
|
||||
{block name="script"}
|
||||
<script src="/static/js/vue-2.7.16.min.js"></script>
|
||||
<script src="/static/js/layer/layer.js"></script>
|
||||
<script src="/static/js/select2-4.0.13.min.js"></script>
|
||||
<script src="/static/js/select2-i18n-zh-CN-4.0.13.min.js"></script>
|
||||
<script src="/static/js/bootstrapValidator.min.js"></script>
|
||||
<script>
|
||||
var info = {$info|json_encode|raw};
|
||||
@ -163,8 +165,28 @@ new Vue({
|
||||
this.set.type = Object.keys(typeList)[0]
|
||||
}
|
||||
|
||||
var that = this;
|
||||
this.$nextTick(function () {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
function formatType(option) {
|
||||
if (!option.id) return option.text;
|
||||
var icon = $(option.element).data('icon');
|
||||
if (icon) {
|
||||
return $('<span><img src="/static/images/' + icon + '" class="type-logo" />' + option.text + '</span>');
|
||||
}
|
||||
return option.text;
|
||||
}
|
||||
$('select[name=type]').select2({
|
||||
templateResult: formatType,
|
||||
templateSelection: formatType,
|
||||
minimumResultsForSearch: Infinity,
|
||||
width: '100%'
|
||||
}).on('select2:select', function(e){
|
||||
that.set.type = e.params.data.id;
|
||||
});
|
||||
if(that.action == 'edit'){
|
||||
$('select[name=type]').val(that.set.type).trigger('change.select2');
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
|
||||
@ -54,14 +54,14 @@
|
||||
<label class="col-sm-3 control-label no-padding-right">解析IP类型<span class="tips" title="" data-toggle="tooltip" data-placement="bottom" data-original-title="同时开启IPv6&IPv4将会请求2次接口消耗双倍积分"><i class="fa fa-question-circle"></i></span></label>
|
||||
<div class="col-sm-6">
|
||||
<label class="checkbox-inline" v-for="option in iptypeList">
|
||||
<input type="checkbox" name="ip_type" :value="option.value" v-model="set.ip_type_select" required> {{option.label}}
|
||||
<input type="checkbox" name="ip_type" :value="option.value" v-model="set.ip_type_select" :disabled="option.value=='v6' && isXingpingcn" required> {{option.label}}<span v-if="option.value=='v6' && isXingpingcn" class="text-muted">(xingpingcn.top不支持)</span>
|
||||
</label><br/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label no-padding-right">每线路解析数量<span class="tips" title="" data-toggle="tooltip" data-placement="bottom" data-original-title="数量不要超过当前域名套餐允许的最大数量,否则会添加解析失败"><i class="fa fa-question-circle"></i></span></label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" name="recordnum" v-model="set.recordnum" placeholder="填写每线路解析数量" class="form-control" data-bv-integer="true" min="1" max="5" required>
|
||||
<input type="text" name="recordnum" v-model="set.recordnum" placeholder="填写每线路解析数量" class="form-control" data-bv-integer="true" min="1" max="50" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@ -110,6 +110,7 @@ new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
action: '{$action}',
|
||||
optimize_ip_api: '{:config_get("optimize_ip_api", 0)}',
|
||||
set: {
|
||||
id: '',
|
||||
remark: '',
|
||||
@ -132,8 +133,18 @@ new Vue({
|
||||
4:'EdgeOne'
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isXingpingcn: function() {
|
||||
return this.optimize_ip_api == '2';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'set.ip_type_select': function(val){
|
||||
// 如果使用xingpingcn.top接口,自动移除v6
|
||||
if(this.isXingpingcn && val.includes('v6')){
|
||||
this.set.ip_type_select = val.filter(v => v !== 'v6');
|
||||
return;
|
||||
}
|
||||
this.set.ip_type = val.join(',');
|
||||
}
|
||||
},
|
||||
@ -185,4 +196,4 @@ new Vue({
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{/block}
|
||||
{/block}
|
||||
|
||||
@ -14,8 +14,9 @@
|
||||
<div class="panel-heading"><h3 class="panel-title">使用说明</h3></div>
|
||||
<div class="panel-body">
|
||||
<p><li>不支持对CloudFlare里的域名添加优选,必须使用其他DNS服务商。需开通Cloudflare for SaaS,且域名使用CNAME的方式解析到CloudFlare。</li></p>
|
||||
<p><li>数据接口:<a href="https://www.wetest.vip/" target="_blank" rel="noreferrer">wetest.vip</a> 数据接口支持CloudFlare、CloudFront、EdgeOne;<a href="https://stock.hostmonit.com/" target="_blank" rel="noreferrer">HostMonit</a> 只支持CloudFlare。</li></p>
|
||||
<p><li>数据接口:<a href="https://www.wetest.vip/" target="_blank" rel="noreferrer">wetest.vip</a> 数据接口支持CloudFlare、CloudFront、EdgeOne;<a href="https://stock.hostmonit.com/" target="_blank" rel="noreferrer">HostMonit</a> 只支持CloudFlare;<a href="https://github.com/xingpingcn/enhanced-FaaS-in-China" target="_blank" rel="noreferrer">xingpingcn.top</a> 只支持CloudFlare(免费、无需密钥)。</li></p>
|
||||
<p><li>接口密钥:默认o1zrmHAF为免费KEY可永久免费使用。</li></p>
|
||||
<p><li>代理地址:如 https://ghfast.top/https://raw.githubusercontent.com/ ,留空则直接访问 https://raw.githubusercontent.com/。</li></p>
|
||||
<p><li>自动更新:可查看<a href="/system/cronset">计划任务设置</a></p>
|
||||
</div>
|
||||
</div>
|
||||
@ -24,19 +25,23 @@
|
||||
<div class="panel panel-info">
|
||||
<div class="panel-heading"><h3 class="panel-title">数据接口设置</h3></div>
|
||||
<div class="panel-body">
|
||||
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form">
|
||||
<form onsubmit="return saveSetting(this)" method="post" class="form-horizontal" role="form" id="apiSettingForm">
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 control-label">数据接口</label>
|
||||
<div class="col-sm-9"><select class="form-control" name="optimize_ip_api" default="{:config_get('optimize_ip_api')}"><option value="0">wetest.vip</option><option value="1">HostMonit</option></select></div>
|
||||
<div class="col-sm-9"><select class="form-control" name="optimize_ip_api" id="optimize_ip_api" default="{:config_get('optimize_ip_api')}"><option value="0">wetest.vip</option><option value="1">HostMonit</option><option value="2">xingpingcn.top</option></select></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-group" id="keyGroup">
|
||||
<label class="col-sm-3 control-label">接口密钥</label>
|
||||
<div class="col-sm-9"><input type="text" name="optimize_ip_key" value="{:config_get('optimize_ip_key', 'o1zrmHAF')}" class="form-control"/></div>
|
||||
</div>
|
||||
<div class="form-group" id="proxyGroup" style="display:none;">
|
||||
<label class="col-sm-3 control-label">代理地址</label>
|
||||
<div class="col-sm-9"><input type="text" name="optimize_ip_proxy" value="{:config_get('optimize_ip_proxy', '')}" class="form-control" placeholder="留空则直接访问GitHub"/></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-3 col-sm-9">
|
||||
<input type="submit" name="submit" value="保存" class="btn btn-primary btn-block"/>
|
||||
<a href="javascript:queryapi()" class="btn btn-default btn-block">查询积分</a>
|
||||
<a href="javascript:queryapi()" class="btn btn-default btn-block" id="queryBtn">查询积分</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@ -69,6 +74,25 @@ var items = $("select[default]");
|
||||
for (i = 0; i < items.length; i++) {
|
||||
$(items[i]).val($(items[i]).attr("default")||0);
|
||||
}
|
||||
// 切换接口时显示/隐藏对应设置项
|
||||
function toggleApiSettings(){
|
||||
var api = $("#optimize_ip_api").val();
|
||||
if(api == '2'){
|
||||
$("#keyGroup").hide();
|
||||
$("#proxyGroup").show();
|
||||
$("#queryBtn").hide();
|
||||
}else{
|
||||
$("#keyGroup").show();
|
||||
$("#proxyGroup").hide();
|
||||
$("#queryBtn").show();
|
||||
}
|
||||
}
|
||||
$("#optimize_ip_api").change(function(){
|
||||
toggleApiSettings();
|
||||
});
|
||||
// 页面加载时初始化
|
||||
toggleApiSettings();
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
function saveSetting(obj){
|
||||
var ii = layer.load(2, {shade:[0.1,'#fff']});
|
||||
$.ajax({
|
||||
@ -118,4 +142,4 @@ function queryapi(){
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{/block}
|
||||
{/block}
|
||||
|
||||
12
composer.lock
generated
12
composer.lock
generated
@ -8,16 +8,16 @@
|
||||
"packages": [
|
||||
{
|
||||
"name": "cccyun/php-whois",
|
||||
"version": "1.2",
|
||||
"version": "1.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/netcccyun/php-whois.git",
|
||||
"reference": "c631f1c5e26e7150501a14cd25a2380f8a077ca1"
|
||||
"reference": "f02627ba0bef005aa9e336d63541f9fd288675b5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/netcccyun/php-whois/zipball/c631f1c5e26e7150501a14cd25a2380f8a077ca1",
|
||||
"reference": "c631f1c5e26e7150501a14cd25a2380f8a077ca1",
|
||||
"url": "https://api.github.com/repos/netcccyun/php-whois/zipball/f02627ba0bef005aa9e336d63541f9fd288675b5",
|
||||
"reference": "f02627ba0bef005aa9e336d63541f9fd288675b5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -62,9 +62,9 @@
|
||||
"црщшы"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/netcccyun/php-whois/tree/1.2"
|
||||
"source": "https://github.com/netcccyun/php-whois/tree/1.3"
|
||||
},
|
||||
"time": "2025-06-25T06:54:23+00:00"
|
||||
"time": "2026-02-12T05:56:18+00:00"
|
||||
},
|
||||
{
|
||||
"name": "cccyun/think-captcha",
|
||||
|
||||
@ -31,7 +31,7 @@ return [
|
||||
'show_error_msg' => true,
|
||||
'exception_tmpl' => \think\facade\App::getAppPath() . 'view/exception.tpl',
|
||||
|
||||
'version' => '1046',
|
||||
'version' => '1047',
|
||||
|
||||
'dbversion' => '1045'
|
||||
];
|
||||
|
||||
6
public/static/images/acepanel.svg
Normal file
6
public/static/images/acepanel.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="812" height="812">
|
||||
<rect x="0" y="0" width="812" height="812" rx="80" ry="80" fill="#333333"/>
|
||||
<path d="M0,0 L136,0 L138,23 L141,47 L146,91 L152,146 L155,169 L157,188 L161,222 L163,243 L167,275 L169,294 L173,329 L176,355 L178,372 L178,377 L360,377 L354,399 L342,439 L333,470 L324,501 L321,509 L302,509 L307,504 L313,497 L313,494 L296,481 L293,481 L286,489 L277,494 L270,496 L261,496 L253,493 L248,487 L246,481 L246,474 L247,473 L276,470 L296,464 L307,458 L314,451 L318,443 L320,430 L318,420 L313,412 L306,406 L294,401 L290,400 L267,400 L254,404 L243,410 L233,419 L223,434 L217,450 L215,464 L215,478 L217,490 L222,502 L226,507 L226,509 L187,509 L193,501 L199,492 L199,488 L179,479 L176,478 L169,488 L164,493 L157,496 L150,496 L142,493 L138,488 L136,480 L138,464 L142,445 L146,435 L151,429 L157,426 L162,425 L169,425 L176,428 L181,436 L182,439 L187,438 L208,430 L207,422 L201,412 L193,406 L182,401 L175,400 L155,400 L143,404 L132,410 L122,419 L115,430 L110,440 L107,450 L105,468 L106,488 L111,500 L116,509 L86,509 L84,496 L81,460 L78,432 L74,385 L-27,385 L-22,373 L-7,342 L1,324 L9,308 L14,297 L67,297 L66,293 L62,236 L60,213 L57,169 L52,104 L52,95 L45,95 L43,101 L27,136 L11,171 L-7,210 L-26,251 L-42,285 L-50,304 L-60,326 L-68,342 L-76,359 L-84,375 L-96,400 L-114,437 L-125,460 L-137,485 L-148,508 L-149,509 L-261,509 L-259,503 L-245,475 L-235,456 L-212,411 L-202,392 L-189,366 L-179,347 L-159,308 L-145,281 L-137,266 L-129,250 L-105,203 L-91,176 L-82,159 L-74,143 L-51,98 L-34,65 L-19,36 L-1,1 Z " fill="#FEFEFE" transform="translate(372,151)"/>
|
||||
<path d="M0,0 L11,0 L17,3 L19,6 L19,15 L14,21 L4,25 L-14,28 L-20,28 L-18,17 L-13,9 L-5,2 Z " fill="#FBFBFB" transform="translate(642,575)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
BIN
public/static/images/amh.ico
Normal file
BIN
public/static/images/amh.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
Loading…
Reference in New Issue
Block a user