mirror of https://github.com/certd/certd
perf: 支持部署证书到网宿CDN
parent
98da4e1791
commit
c3da026b33
|
@ -26,11 +26,11 @@ process.on('uncaughtException', error => {
|
||||||
});
|
});
|
||||||
|
|
||||||
@Configuration({
|
@Configuration({
|
||||||
// detectorOptions: {
|
detectorOptions: {
|
||||||
// ignore: [
|
ignore: [
|
||||||
// '**/plugins/**'
|
'**/plugins/**'
|
||||||
// ]
|
]
|
||||||
// },
|
},
|
||||||
imports: [
|
imports: [
|
||||||
koa,
|
koa,
|
||||||
orm,
|
orm,
|
||||||
|
|
|
@ -25,3 +25,4 @@ export * from './plugin-flex/index.js'
|
||||||
export * from './plugin-farcdn/index.js'
|
export * from './plugin-farcdn/index.js'
|
||||||
export * from './plugin-fnos/index.js'
|
export * from './plugin-fnos/index.js'
|
||||||
export * from './plugin-rainyun/index.js'
|
export * from './plugin-rainyun/index.js'
|
||||||
|
export * from './plugin-wangsu/index.js'
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
import { AccessInput, BaseAccess, IsAccess } from "@certd/pipeline";
|
||||||
|
import { HttpRequestConfig } from "@certd/basic";
|
||||||
|
import { CertInfo } from "@certd/plugin-cert";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
@IsAccess({
|
||||||
|
name: "wangsu",
|
||||||
|
title: "网宿授权",
|
||||||
|
desc: "",
|
||||||
|
icon: "svg:icon-lucky"
|
||||||
|
})
|
||||||
|
export class WangsuAccess extends BaseAccess {
|
||||||
|
|
||||||
|
@AccessInput({
|
||||||
|
title: "accessKeyId",
|
||||||
|
component: {
|
||||||
|
placeholder: "accessKeyId",
|
||||||
|
component: {
|
||||||
|
name: "a-input",
|
||||||
|
vModel: "value"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
helper: "[点击前往获取AccessKey](https://console.wangsu.com/account/accessKey?rsr=ws)",
|
||||||
|
encrypt: false,
|
||||||
|
required: true
|
||||||
|
})
|
||||||
|
accessKeyId!: string;
|
||||||
|
|
||||||
|
@AccessInput({
|
||||||
|
title: "accessKeySecret",
|
||||||
|
component: {
|
||||||
|
placeholder: "accessKeySecret",
|
||||||
|
component: {
|
||||||
|
name: "a-input",
|
||||||
|
vModel: "value"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
encrypt: true,
|
||||||
|
required: true
|
||||||
|
})
|
||||||
|
accessKeySecret!: string;
|
||||||
|
|
||||||
|
|
||||||
|
@AccessInput({
|
||||||
|
title: "测试",
|
||||||
|
component: {
|
||||||
|
name: "api-test",
|
||||||
|
action: "TestRequest"
|
||||||
|
},
|
||||||
|
helper: "点击测试接口是否正常"
|
||||||
|
})
|
||||||
|
testRequest = true;
|
||||||
|
|
||||||
|
async onTestRequest() {
|
||||||
|
await this.getCertList({ });
|
||||||
|
return "ok";
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCertList(req: { }) {
|
||||||
|
/**
|
||||||
|
* certificate-id
|
||||||
|
* name
|
||||||
|
* dns-names
|
||||||
|
*/
|
||||||
|
const res = await this.doRequest({
|
||||||
|
url: "/api/ssl/certificate",
|
||||||
|
method: "GET",
|
||||||
|
});
|
||||||
|
|
||||||
|
return res["ssl-certificate"]
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCertInfo(req:{certId:string}){
|
||||||
|
return await this.doRequest({
|
||||||
|
url: `/api/certificate/${req.certId}`,
|
||||||
|
method:"GET",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateCert(req: {
|
||||||
|
certId: string,
|
||||||
|
cert: CertInfo,
|
||||||
|
}) {
|
||||||
|
|
||||||
|
const certInfo= await this.getCertInfo({certId:req.certId});
|
||||||
|
|
||||||
|
const name = certInfo.name;
|
||||||
|
const {cert,certId} = req;
|
||||||
|
return await this.doRequest({
|
||||||
|
url: `/api/certificate/${certId}`,
|
||||||
|
method:"PUT",
|
||||||
|
data: {
|
||||||
|
/**
|
||||||
|
* name: string;
|
||||||
|
* certificate?: string;
|
||||||
|
* privateKey?: string;
|
||||||
|
* autoRenew?: string;
|
||||||
|
* isNeedAlarm?: string;
|
||||||
|
* csrId?: number;
|
||||||
|
* comment?: string;
|
||||||
|
*/
|
||||||
|
name:name,
|
||||||
|
certificate: cert.crt,
|
||||||
|
privateKey: cert.key,
|
||||||
|
autoRenew:"false",
|
||||||
|
isNeedAlarm:"false",
|
||||||
|
comment: "certd"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async doRequest(req: HttpRequestConfig) {
|
||||||
|
|
||||||
|
const data: any = req.data;
|
||||||
|
|
||||||
|
const {AkSkConfig,AkSkAuth} = await import("./lib/index.js");
|
||||||
|
|
||||||
|
const akskConfig = new AkSkConfig();
|
||||||
|
akskConfig.accessKey = this.accessKeyId;
|
||||||
|
akskConfig.secretKey = this.accessKeySecret;
|
||||||
|
akskConfig.endPoint = "open.chinanetcenter.com";
|
||||||
|
akskConfig.uri = req.url;
|
||||||
|
akskConfig.method = req.method;
|
||||||
|
|
||||||
|
const requestMsg = AkSkAuth.transferHttpRequestMsg(akskConfig,data?JSON.stringify(data):"");
|
||||||
|
AkSkAuth.getAuthAndSetHeaders(requestMsg, akskConfig.accessKey, akskConfig.secretKey);
|
||||||
|
|
||||||
|
let response = undefined
|
||||||
|
try{
|
||||||
|
response = await this.ctx.http.request({
|
||||||
|
method: requestMsg.method,
|
||||||
|
url: requestMsg.url,
|
||||||
|
headers: requestMsg.headers,
|
||||||
|
data: requestMsg.body
|
||||||
|
});
|
||||||
|
}catch (e) {
|
||||||
|
if (e.response?.data?.result) {
|
||||||
|
throw new Error(e.response?.data?.result);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.code != null && response.code != 0){
|
||||||
|
throw new Error(response.message);
|
||||||
|
}
|
||||||
|
if (response.data != null && response.code!==null){
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
new WangsuAccess();
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from "./plugins/index.js";
|
||||||
|
export * from "./access.js";
|
|
@ -0,0 +1,87 @@
|
||||||
|
import { HttpRequestMsg } from "../model/HttpRequestMsg.js";
|
||||||
|
import { AkSkConfig } from "../model/AkSkConfig.js";
|
||||||
|
import { CryptoUtils } from "../util/CryptoUtils.js";
|
||||||
|
import { HttpUtils } from "../util/HttpUtils.js";
|
||||||
|
import { Constant } from "../common/Constant.js";
|
||||||
|
|
||||||
|
export class AkSkAuth {
|
||||||
|
|
||||||
|
public static invoke(akSkConfig: AkSkConfig, jsonBody: string): Promise<string | null> {
|
||||||
|
const requestMsg = AkSkAuth.transferHttpRequestMsg(akSkConfig, jsonBody);
|
||||||
|
AkSkAuth.getAuthAndSetHeaders(requestMsg, akSkConfig.accessKey, akSkConfig.secretKey);
|
||||||
|
return HttpUtils.call(requestMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static transferHttpRequestMsg(akSkConfig: AkSkConfig, jsonBody: string): HttpRequestMsg {
|
||||||
|
const requestMsg = new HttpRequestMsg();
|
||||||
|
requestMsg.uri = akSkConfig.uri;
|
||||||
|
if (akSkConfig.endPoint && akSkConfig.endPoint !== Constant.END_POINT) {
|
||||||
|
requestMsg.host = akSkConfig.endPoint;
|
||||||
|
requestMsg.url = `${Constant.HTTPS_REQUEST_PREFIX}${akSkConfig.endPoint}${requestMsg.uri}`;
|
||||||
|
} else {
|
||||||
|
requestMsg.host = Constant.HTTP_DOMAIN;
|
||||||
|
requestMsg.url = `${Constant.HTTP_REQUEST_PREFIX}${requestMsg.uri}`;
|
||||||
|
}
|
||||||
|
requestMsg.method = akSkConfig.method;
|
||||||
|
requestMsg.signedHeaders = AkSkAuth.getSignedHeaders(akSkConfig.signedHeaders);
|
||||||
|
if (['POST', 'PUT', 'PATCH', 'DELETE'].indexOf(akSkConfig.method) !== -1) {
|
||||||
|
requestMsg.body = jsonBody;
|
||||||
|
}
|
||||||
|
return requestMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getAuthAndSetHeaders(requestMsg: HttpRequestMsg, accessKey: string, secretKey: string): void {
|
||||||
|
const timeStamp = (Date.now() / 1000 | 0).toString();
|
||||||
|
requestMsg.headers['Host'] = requestMsg.host;
|
||||||
|
requestMsg.headers[Constant.HEAD_SIGN_ACCESS_KEY] = accessKey;
|
||||||
|
requestMsg.headers[Constant.HEAD_SIGN_TIMESTAMP] = timeStamp;
|
||||||
|
requestMsg.headers["Accept"] = Constant.APPLICATION_JSON;
|
||||||
|
const signature = AkSkAuth.getSignature(requestMsg, secretKey, timeStamp);
|
||||||
|
requestMsg.headers['Authorization'] = AkSkAuth.genAuthorization(accessKey, AkSkAuth.getSignedHeaders(requestMsg.signedHeaders), signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static genAuthorization(accessKey: string, signedHeaders: string, signature: string): string {
|
||||||
|
return `${Constant.HEAD_SIGN_ALGORITHM} Credential=${accessKey}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getSignature(requestMsg: HttpRequestMsg, secretKey: string, timestamp: string): string {
|
||||||
|
let bodyStr = requestMsg.body || "";
|
||||||
|
const hashedRequestPayload = CryptoUtils.sha256Hex(bodyStr);
|
||||||
|
const canonicalRequest = `${requestMsg.method}\n${requestMsg.uri.split("?")[0]}\n${decodeURIComponent(requestMsg.getQueryString())}\n${AkSkAuth.getCanonicalHeaders(requestMsg.headers, AkSkAuth.getSignedHeaders(requestMsg.signedHeaders))}\n${AkSkAuth.getSignedHeaders(requestMsg.signedHeaders)}\n${hashedRequestPayload}`;
|
||||||
|
const stringToSign = `${Constant.HEAD_SIGN_ALGORITHM}\n${timestamp}\n${CryptoUtils.sha256Hex(canonicalRequest)}`;
|
||||||
|
return CryptoUtils.hmac256(secretKey, stringToSign).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getCanonicalHeaders(headers: Record<string, string>, signedHeaders: string): string {
|
||||||
|
const headerNames = signedHeaders.split(";");
|
||||||
|
let canonicalHeaders = "";
|
||||||
|
for (const headerName of headerNames) {
|
||||||
|
const headerValue = AkSkAuth.getValueByHeader(headerName, headers);
|
||||||
|
if (headerValue !== null) {
|
||||||
|
canonicalHeaders += `${headerName}:${headerValue.toLowerCase()}\n`;
|
||||||
|
} else {
|
||||||
|
// Handle missing headers if necessary, e.g., log a warning or skip
|
||||||
|
console.warn(`Header ${headerName} not found in provided headers.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return canonicalHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static getSignedHeaders(signedHeaders: string): string {
|
||||||
|
if (!signedHeaders) {
|
||||||
|
return "content-type;host";
|
||||||
|
}
|
||||||
|
const headers = signedHeaders.split(";");
|
||||||
|
return headers.map(header => header.toLowerCase()).sort().join(";");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getValueByHeader(name: string, customHeaderMap: { [key: string]: string }): string | null {
|
||||||
|
for (const key in customHeaderMap) {
|
||||||
|
if (key.toLowerCase() === name.toLowerCase()) {
|
||||||
|
return customHeaderMap[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
export class Constant {
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
|
public static readonly HTTP_REQUEST_PREFIX: string = "https://open.chinanetcenter.com";
|
||||||
|
public static readonly HTTPS_REQUEST_PREFIX: string = "https://";
|
||||||
|
public static readonly HTTP_DOMAIN: string = "open.chinanetcenter.com";
|
||||||
|
|
||||||
|
public static readonly APPLICATION_JSON: string = "application/json";
|
||||||
|
|
||||||
|
public static readonly HEAD_SIGN_ACCESS_KEY: string = "x-cnc-accessKey";
|
||||||
|
public static readonly HEAD_SIGN_TIMESTAMP: string = "x-cnc-timestamp";
|
||||||
|
public static readonly HEAD_SIGN_ALGORITHM: string = "CNC-HMAC-SHA256";
|
||||||
|
|
||||||
|
public static readonly X_CNC_AUTH_METHOD: string = "x-cnc-auth-method";
|
||||||
|
|
||||||
|
public static readonly AUTH_METHOD: string = "AKSK";
|
||||||
|
|
||||||
|
public static readonly END_POINT: string = "{endPoint}";
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
export class ApiAuthException extends Error {
|
||||||
|
public cause?: any;
|
||||||
|
|
||||||
|
constructor(message: string, cause?: any) {
|
||||||
|
super(message);
|
||||||
|
this.cause = cause;
|
||||||
|
this.name = 'ApiAuthException';
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
import { AkSkConfig } from "./model/AkSkConfig.js";
|
||||||
|
import { AkSkAuth } from "./auth/AkSkAuth.js";
|
||||||
|
|
||||||
|
export { AkSkAuth, AkSkConfig}
|
|
@ -0,0 +1,56 @@
|
||||||
|
export class AkSkConfig {
|
||||||
|
private _accessKey: string | undefined;
|
||||||
|
private _secretKey: string | undefined;
|
||||||
|
private _uri: string | undefined;
|
||||||
|
private _endPoint: string | undefined;
|
||||||
|
private _method: string | undefined;
|
||||||
|
private _signedHeaders: string | undefined;
|
||||||
|
|
||||||
|
public get accessKey(): string {
|
||||||
|
return this._accessKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set accessKey(value: string) {
|
||||||
|
this._accessKey = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get secretKey(): string {
|
||||||
|
return this._secretKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set secretKey(value: string) {
|
||||||
|
this._secretKey = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get uri(): string {
|
||||||
|
return this._uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set uri(value: string) {
|
||||||
|
this._uri = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get endPoint(): string {
|
||||||
|
return this._endPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set endPoint(value: string) {
|
||||||
|
this._endPoint = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get method(): string {
|
||||||
|
return this._method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set method(value: string) {
|
||||||
|
this._method = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get signedHeaders(): string {
|
||||||
|
return this._signedHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set signedHeaders(value: string) {
|
||||||
|
this._signedHeaders = value;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { Constant } from '../common/Constant.js'; // Assuming you have a TypeScript version of this
|
||||||
|
|
||||||
|
export class HttpRequestMsg {
|
||||||
|
uri: string ;
|
||||||
|
url: string;
|
||||||
|
host: string;
|
||||||
|
method: string;
|
||||||
|
protocol: string;
|
||||||
|
params: Record<string, string>;
|
||||||
|
headers: Record<string, string>;
|
||||||
|
body: string;
|
||||||
|
signedHeaders: string;
|
||||||
|
msg: any;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.params = {};
|
||||||
|
this.headers = {};
|
||||||
|
this.putHeader('Content-Type', Constant.APPLICATION_JSON);
|
||||||
|
this.putHeader(Constant.X_CNC_AUTH_METHOD, Constant.AUTH_METHOD);
|
||||||
|
}
|
||||||
|
|
||||||
|
putParam(name: string, value: string): void {
|
||||||
|
this.params[name] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
getParam(name: string): string | null {
|
||||||
|
const value = this.params[name];
|
||||||
|
return value && value.trim() !== '' ? value : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getQueryString(): string {
|
||||||
|
if(this.uri == undefined)
|
||||||
|
return "";
|
||||||
|
const index = this.uri.indexOf("?");
|
||||||
|
if (this.method === 'POST' || index === -1) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return this.uri.substring(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
putHeader(name: string, value: string): void {
|
||||||
|
this.headers[name] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeader(name: string): string | null {
|
||||||
|
for (const key in this.headers) {
|
||||||
|
if (key.toLowerCase() === name.toLowerCase()) {
|
||||||
|
return this.headers[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeaderByNames(...names: string[]): string | null {
|
||||||
|
for (const name of names) {
|
||||||
|
const value = this.getHeader(name);
|
||||||
|
if (value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeHeader(name: string): void {
|
||||||
|
for (const key in this.headers) {
|
||||||
|
if (key.toLowerCase() === name.toLowerCase()) {
|
||||||
|
delete this.headers[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setJsonBody(object: any): void {
|
||||||
|
this.body = JSON.stringify(object);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import CryptoJS from 'crypto-js';
|
||||||
|
|
||||||
|
export class CryptoUtils {
|
||||||
|
private constructor() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hmac+sha256+hex
|
||||||
|
*/
|
||||||
|
public static sha256Hex(s: string): string {
|
||||||
|
const hash = CryptoJS.SHA256(s);
|
||||||
|
return hash.toString(CryptoJS.enc.Hex).toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hmac+sha256
|
||||||
|
*/
|
||||||
|
public static hmac256(secretKey: string, message: string): string {
|
||||||
|
const keyWordArray = CryptoJS.enc.Utf8.parse(secretKey);
|
||||||
|
const messageWordArray = CryptoJS.enc.Utf8.parse(message);
|
||||||
|
const hash = CryptoJS.HmacSHA256(messageWordArray, keyWordArray);
|
||||||
|
return hash.toString(CryptoJS.enc.Hex).toLowerCase();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { HttpRequestMsg } from '../model/HttpRequestMsg.js'; // Assuming you have a TypeScript version of this
|
||||||
|
import { ApiAuthException } from '../exception/ApiAuthException.js'; // Assuming you have a TypeScript version of this
|
||||||
|
import axios, { AxiosError } from 'axios';
|
||||||
|
|
||||||
|
export class HttpUtils {
|
||||||
|
private constructor() { }
|
||||||
|
public static async call(requestMsg: HttpRequestMsg): Promise<string | null> {
|
||||||
|
var response;
|
||||||
|
try {
|
||||||
|
response = await axios({
|
||||||
|
method: requestMsg.method,
|
||||||
|
url: requestMsg.url,
|
||||||
|
headers: requestMsg.headers,
|
||||||
|
data: requestMsg.body
|
||||||
|
});
|
||||||
|
console.info("API invoke success. Response:", response.data);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
// Handle AxiosError specifically
|
||||||
|
console.error('API invoke failed. Response:', error.response.data);
|
||||||
|
return error.response.data;
|
||||||
|
} else {
|
||||||
|
// Handle other types of errors
|
||||||
|
console.error('API invoke failed.', error);
|
||||||
|
}
|
||||||
|
throw new ApiAuthException('API invoke failed.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./plugin-refresh-cert.js";
|
|
@ -0,0 +1,121 @@
|
||||||
|
import {
|
||||||
|
AbstractTaskPlugin,
|
||||||
|
IsTaskPlugin,
|
||||||
|
PageSearch,
|
||||||
|
pluginGroups,
|
||||||
|
RunStrategy,
|
||||||
|
TaskInput
|
||||||
|
} from "@certd/pipeline";
|
||||||
|
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
|
||||||
|
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
|
||||||
|
import { WangsuAccess } from "../access.js";
|
||||||
|
|
||||||
|
@IsTaskPlugin({
|
||||||
|
//命名规范,插件类型+功能(就是目录plugin-demo中的demo),大写字母开头,驼峰命名
|
||||||
|
name: "WangsuRefreshCert",
|
||||||
|
title: "网宿-更新证书",
|
||||||
|
desc: "网宿证书自动更新",
|
||||||
|
icon: "svg:icon-lucky",
|
||||||
|
//插件分组
|
||||||
|
group: pluginGroups.cdn.key,
|
||||||
|
needPlus: false,
|
||||||
|
default: {
|
||||||
|
//默认值配置照抄即可
|
||||||
|
strategy: {
|
||||||
|
runStrategy: RunStrategy.SkipWhenSucceed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
//类名规范,跟上面插件名称(name)一致
|
||||||
|
export class WangsuRefreshCert extends AbstractTaskPlugin {
|
||||||
|
//证书选择,此项必须要有
|
||||||
|
@TaskInput({
|
||||||
|
title: "域名证书",
|
||||||
|
helper: "请选择前置任务输出的域名证书",
|
||||||
|
component: {
|
||||||
|
name: "output-selector",
|
||||||
|
from: [...CertApplyPluginNames]
|
||||||
|
}
|
||||||
|
// required: true, // 必填
|
||||||
|
})
|
||||||
|
cert!: CertInfo;
|
||||||
|
|
||||||
|
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
|
||||||
|
certDomains!: string[];
|
||||||
|
|
||||||
|
//授权选择框
|
||||||
|
@TaskInput({
|
||||||
|
title: "网宿授权",
|
||||||
|
component: {
|
||||||
|
name: "access-selector",
|
||||||
|
type: "wangsu" //固定授权类型
|
||||||
|
},
|
||||||
|
required: true //必填
|
||||||
|
})
|
||||||
|
accessId!: string;
|
||||||
|
//
|
||||||
|
|
||||||
|
@TaskInput(
|
||||||
|
createRemoteSelectInputDefine({
|
||||||
|
title: "证书Id",
|
||||||
|
helper: "要更新的网宿证书id",
|
||||||
|
action: WangsuRefreshCert.prototype.onGetCertList.name,
|
||||||
|
pager: false,
|
||||||
|
search: false
|
||||||
|
})
|
||||||
|
)
|
||||||
|
certList!: string[];
|
||||||
|
|
||||||
|
//插件实例化时执行的方法
|
||||||
|
async onInstance() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//插件执行方法
|
||||||
|
async execute(): Promise<void> {
|
||||||
|
const access = await this.getAccess<WangsuAccess>(this.accessId);
|
||||||
|
|
||||||
|
for (const item of this.certList) {
|
||||||
|
this.logger.info(`----------- 开始更新证书:${item}`);
|
||||||
|
await access.updateCert({
|
||||||
|
certId: item,
|
||||||
|
cert: this.cert
|
||||||
|
});
|
||||||
|
this.logger.info(`----------- 更新证书${item}成功`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.info("部署完成");
|
||||||
|
}
|
||||||
|
|
||||||
|
async onGetCertList(data: PageSearch = {}) {
|
||||||
|
const access = await this.getAccess<WangsuAccess>(this.accessId);
|
||||||
|
|
||||||
|
const list = await access.getCertList({});
|
||||||
|
if (!list || list.length === 0) {
|
||||||
|
throw new Error("没有找到证书,请先在控制台上传一次证书且关联域名");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* certificate-id
|
||||||
|
* name
|
||||||
|
* dns-names
|
||||||
|
*/
|
||||||
|
const options = list.map((item: any) => {
|
||||||
|
const domains = item["dns-names"]
|
||||||
|
const certId = item["certificate-id"];
|
||||||
|
return {
|
||||||
|
label: `${item.name}<${certId}-${domains[0]}>`,
|
||||||
|
value: certId,
|
||||||
|
domain: item["dns-names"]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
list: this.ctx.utils.options.buildGroupOptions(options, this.certDomains),
|
||||||
|
total: list.length,
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: list.length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//实例化一下,注册插件
|
||||||
|
new WangsuRefreshCert();
|
Loading…
Reference in New Issue