mirror of
https://github.com/certd/certd.git
synced 2026-04-30 17:37:24 +08:00
chore: 1
This commit is contained in:
@@ -175,11 +175,11 @@ https://certd.handfree.work/
|
|||||||
| 功能 | 免费版 | 专业版 |
|
| 功能 | 免费版 | 专业版 |
|
||||||
|---------|---------------------------------------|--------------------------------|
|
|---------|---------------------------------------|--------------------------------|
|
||||||
| 免费证书申请 | 免费无限制 | 免费无限制 |
|
| 免费证书申请 | 免费无限制 | 免费无限制 |
|
||||||
| 域名数量 | 无限制 | 无限制 |
|
| 证书域名数量 | 无限制 | 无限制 |
|
||||||
| 证书流水线条数 | 无限制 | 无限制 |
|
| 证书流水线条数 | 无限制 | 无限制 |
|
||||||
| 站点证书监控 | 限制1条 | 无限制 |
|
|
||||||
| 自动部署插件 | 阿里云CDN、腾讯云、七牛CDN、主机部署、宝塔、1Panel等大部分插件 | 群晖、威联通、proxmox等 |
|
| 自动部署插件 | 阿里云CDN、腾讯云、七牛CDN、主机部署、宝塔、1Panel等大部分插件 | 群晖、威联通、proxmox等 |
|
||||||
| 通知 | 邮件通知、自定义webhook | 邮件免配置、企微、钉钉、飞书、anpush、server酱等 |
|
| 通知 | 邮件通知、自定义webhook | 邮件免配置、企微、钉钉、飞书、anpush、server酱等 |
|
||||||
|
| 站点监控 | 限制1条 | 无限制 |
|
||||||
| 批量操作 | 无 | 流水线模版,流水线复制,批量运行,批量设置通知、定时等 |
|
| 批量操作 | 无 | 流水线模版,流水线复制,批量运行,批量设置通知、定时等 |
|
||||||
| VIP群 | 无 | 可加,一对一技术支持,必要时可申请远程协助 |
|
| VIP群 | 无 | 可加,一对一技术支持,必要时可申请远程协助 |
|
||||||
|
|
||||||
|
|||||||
@@ -1,190 +0,0 @@
|
|||||||
import { SynologyAccess } from "./access.js";
|
|
||||||
import { HttpClient, ILogger } from "@certd/basic";
|
|
||||||
import qs from "querystring";
|
|
||||||
|
|
||||||
export type SynologyAccessToken = {
|
|
||||||
sid: string;
|
|
||||||
did?: string;
|
|
||||||
synotoken: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type SynologyRequest = {
|
|
||||||
method?: string;
|
|
||||||
apiParams: {
|
|
||||||
api: string;
|
|
||||||
version: number;
|
|
||||||
method: string;
|
|
||||||
};
|
|
||||||
params?: any;
|
|
||||||
data?: any;
|
|
||||||
form?: any;
|
|
||||||
headers?: any;
|
|
||||||
useSynoToken?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const device_name = "certd";
|
|
||||||
|
|
||||||
export class SynologyClient {
|
|
||||||
access: SynologyAccess;
|
|
||||||
http: HttpClient;
|
|
||||||
logger: ILogger;
|
|
||||||
skipSslVerify: boolean;
|
|
||||||
|
|
||||||
token: SynologyAccessToken;
|
|
||||||
constructor(access: SynologyAccess, http: HttpClient, logger: ILogger, skipSslVerify: boolean) {
|
|
||||||
this.access = access;
|
|
||||||
this.http = http;
|
|
||||||
this.logger = logger;
|
|
||||||
this.skipSslVerify = skipSslVerify;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 登录 DSM 的函数
|
|
||||||
async doLogin() {
|
|
||||||
const access = this.access;
|
|
||||||
if (access.otp && access.deviceId != null) {
|
|
||||||
this.logger.info("OTP登录");
|
|
||||||
return await this.doLoginWithDeviceId(access.deviceId);
|
|
||||||
}
|
|
||||||
this.logger.info("使用普通登录");
|
|
||||||
|
|
||||||
const loginUrl = this.getLoginUrl();
|
|
||||||
const res = await this.http.request({
|
|
||||||
url: loginUrl,
|
|
||||||
method: "GET",
|
|
||||||
params: {
|
|
||||||
api: "SYNO.API.Auth",
|
|
||||||
version: 6,
|
|
||||||
method: "login",
|
|
||||||
account: access.username,
|
|
||||||
passwd: access.password,
|
|
||||||
session: "Certd",
|
|
||||||
format: "sid",
|
|
||||||
enable_syno_token: "yes",
|
|
||||||
},
|
|
||||||
skipSslVerify: this.skipSslVerify ?? true,
|
|
||||||
timeout: this.access.timeout * 1000 || 120000,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!res.success) {
|
|
||||||
throw new Error(`登录失败: `, res.error);
|
|
||||||
}
|
|
||||||
this.logger.info("登录成功");
|
|
||||||
|
|
||||||
this.token = res.data as SynologyAccessToken;
|
|
||||||
return this.token;
|
|
||||||
}
|
|
||||||
|
|
||||||
async doLoginWithOTPCode(otpCode: string) {
|
|
||||||
const loginUrl = this.getLoginUrl();
|
|
||||||
const access = this.access;
|
|
||||||
const res = await this.http.request({
|
|
||||||
url: loginUrl,
|
|
||||||
method: "GET",
|
|
||||||
params: {
|
|
||||||
api: "SYNO.API.Auth",
|
|
||||||
version: 6,
|
|
||||||
method: "login",
|
|
||||||
account: access.username,
|
|
||||||
passwd: access.password,
|
|
||||||
otp_code: otpCode,
|
|
||||||
enable_device_token: "yes",
|
|
||||||
device_name,
|
|
||||||
},
|
|
||||||
timeout: this.access.timeout * 1000 || 30000,
|
|
||||||
skipSslVerify: this.skipSslVerify ?? true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!res.success) {
|
|
||||||
throw new Error(`登录失败: `, res.error);
|
|
||||||
}
|
|
||||||
this.logger.info("登录成功");
|
|
||||||
|
|
||||||
this.token = res.data as SynologyAccessToken;
|
|
||||||
return this.token;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getLoginUrl() {
|
|
||||||
const access = this.access;
|
|
||||||
const loginPath = access.version === "6" ? "auth.cgi" : "entry.cgi";
|
|
||||||
return `${access.baseUrl}/webapi/${loginPath}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
async doLoginWithDeviceId(device_id: string) {
|
|
||||||
const access = this.access;
|
|
||||||
const loginUrl = this.getLoginUrl();
|
|
||||||
const res = await this.http.request({
|
|
||||||
url: loginUrl,
|
|
||||||
method: "GET",
|
|
||||||
params: {
|
|
||||||
api: "SYNO.API.Auth",
|
|
||||||
version: 6,
|
|
||||||
method: "login",
|
|
||||||
account: access.username,
|
|
||||||
passwd: access.password,
|
|
||||||
device_name,
|
|
||||||
device_id,
|
|
||||||
session: "Certd",
|
|
||||||
format: "sid",
|
|
||||||
enable_syno_token: "yes",
|
|
||||||
},
|
|
||||||
timeout: this.access.timeout * 1000 || 30000,
|
|
||||||
skipSslVerify: this.skipSslVerify ?? true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!res.success) {
|
|
||||||
throw new Error(`登录失败: `, res.error);
|
|
||||||
}
|
|
||||||
this.logger.info("登录成功");
|
|
||||||
|
|
||||||
this.token = res.data as SynologyAccessToken;
|
|
||||||
return this.token;
|
|
||||||
}
|
|
||||||
|
|
||||||
async doRequest(req: SynologyRequest) {
|
|
||||||
const sid = this.token.sid;
|
|
||||||
const method = req.method || "POST";
|
|
||||||
const params = {
|
|
||||||
...req.apiParams,
|
|
||||||
_sid: sid, // 使用登录后获得的 session ID
|
|
||||||
...req.params,
|
|
||||||
SynoToken: this.token.synotoken,
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = await this.http.request({
|
|
||||||
url: `${this.access.baseUrl}/webapi/entry.cgi?${qs.stringify(params)}`,
|
|
||||||
method,
|
|
||||||
data: req.data,
|
|
||||||
headers: req.headers,
|
|
||||||
skipSslVerify: this.skipSslVerify ?? true,
|
|
||||||
timeout: this.access.timeout * 1000 || 30000,
|
|
||||||
});
|
|
||||||
if (!res.success) {
|
|
||||||
throw new Error(`API 调用失败: ${JSON.stringify(res.error)}`);
|
|
||||||
}
|
|
||||||
return res.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getCertList() {
|
|
||||||
this.logger.info("获取证书列表");
|
|
||||||
return await this.doRequest({
|
|
||||||
method: "GET",
|
|
||||||
apiParams: {
|
|
||||||
api: "SYNO.Core.Certificate.CRT",
|
|
||||||
version: 1,
|
|
||||||
method: "list",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async getInfo() {
|
|
||||||
this.logger.info("获取信息");
|
|
||||||
return await this.doRequest({
|
|
||||||
method: "GET",
|
|
||||||
apiParams: {
|
|
||||||
api: "SYNO.API.Info",
|
|
||||||
version: 1,
|
|
||||||
method: "query",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+5
-57
@@ -1,11 +1,9 @@
|
|||||||
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
||||||
import { CertInfo, CertReader } from "@certd/plugin-cert";
|
import { CertInfo } from "@certd/plugin-lib";
|
||||||
import { AbstractPlusTaskPlugin } from "@certd/plugin-lib";
|
import { AbstractPlusTaskPlugin } from "@certd/plugin-lib";
|
||||||
import { SynologyClient } from "../client.js";
|
import { SynologyClient } from "@certd/plugin-plus";
|
||||||
import fs from "fs";
|
|
||||||
import FormData from "form-data";
|
|
||||||
import { SynologyAccess } from "../access.js";
|
import { SynologyAccess } from "../access.js";
|
||||||
import { CertApplyPluginNames } from "@certd/plugin-cert";
|
import { CertApplyPluginNames } from "@certd/plugin-lib";
|
||||||
@IsTaskPlugin({
|
@IsTaskPlugin({
|
||||||
name: "SynologyDeployToPanel",
|
name: "SynologyDeployToPanel",
|
||||||
title: "群晖-部署证书到群晖面板",
|
title: "群晖-部署证书到群晖面板",
|
||||||
@@ -74,66 +72,16 @@ export class SynologyDeployToPanel extends AbstractPlusTaskPlugin {
|
|||||||
throw new Error(`未找到证书: ${this.certName}`);
|
throw new Error(`未找到证书: ${this.certName}`);
|
||||||
}
|
}
|
||||||
this.logger.info(`找到证书: ${certItem.id}`);
|
this.logger.info(`找到证书: ${certItem.id}`);
|
||||||
await this.updateCertToPanel(client, certItem);
|
await client.updateCertToPanel(certItem,this.cert);
|
||||||
} else {
|
} else {
|
||||||
this.logger.info("开始更新全部证书");
|
this.logger.info("开始更新全部证书");
|
||||||
for (const item of certListRes.certificates) {
|
for (const item of certListRes.certificates) {
|
||||||
this.logger.info(`更新证书: ${item.id}`);
|
this.logger.info(`更新证书: ${item.id}`);
|
||||||
await this.updateCertToPanel(client, item);
|
await client.updateCertToPanel(item,this.cert);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateCertToPanel(client: SynologyClient, certItem: any) {
|
|
||||||
/**
|
|
||||||
* query
|
|
||||||
* api: SYNO.Core.Certificate
|
|
||||||
* method: import
|
|
||||||
* version: 1
|
|
||||||
* SynoToken: Bvum9p7BNeSc6
|
|
||||||
*
|
|
||||||
* key: (二进制)
|
|
||||||
* cert: (二进制)
|
|
||||||
* inter_cert: (二进制)
|
|
||||||
* id: yxTtcC
|
|
||||||
* desc: certd
|
|
||||||
* as_default:
|
|
||||||
*/
|
|
||||||
this.logger.info(`更新证书:${certItem.id}`);
|
|
||||||
const certReader = new CertReader(this.cert);
|
|
||||||
|
|
||||||
return certReader.readCertFile({
|
|
||||||
logger: this.logger,
|
|
||||||
handle: async (ctx) => {
|
|
||||||
const form = new FormData();
|
|
||||||
const { tmpCrtPath, tmpKeyPath, tmpIcPath } = ctx;
|
|
||||||
this.logger.info(`上传证书:${tmpCrtPath},${tmpKeyPath}`);
|
|
||||||
form.append("key", fs.createReadStream(tmpKeyPath));
|
|
||||||
form.append("cert", fs.createReadStream(tmpCrtPath));
|
|
||||||
if (certReader.cert.ic) {
|
|
||||||
this.logger.info(`包含中间证书:${tmpIcPath}`);
|
|
||||||
form.append("inter_cert", fs.createReadStream(tmpIcPath));
|
|
||||||
}
|
|
||||||
form.append("id", certItem.id);
|
|
||||||
form.append("desc", certItem.desc);
|
|
||||||
// form传输必须是string,bool要改成string
|
|
||||||
// form.append("as_default", certItem.is_default + "");
|
|
||||||
|
|
||||||
console.log(JSON.stringify(form.getHeaders()));
|
|
||||||
return await client.doRequest({
|
|
||||||
method: "POST",
|
|
||||||
apiParams: {
|
|
||||||
api: "SYNO.Core.Certificate",
|
|
||||||
version: 1,
|
|
||||||
method: "import",
|
|
||||||
},
|
|
||||||
data: form,
|
|
||||||
headers: {
|
|
||||||
...form.getHeaders(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
new SynologyDeployToPanel();
|
new SynologyDeployToPanel();
|
||||||
|
|||||||
Reference in New Issue
Block a user