Merge remote-tracking branch 'origin/v2-dev' into v2-dev

This commit is contained in:
xiaojunnuo
2024-10-10 21:44:49 +08:00
45 changed files with 684 additions and 101 deletions

View File

@@ -0,0 +1,7 @@
FROM greper/certd:latest
ENV certd_agent_enabled=true
CMD ["npm", "run","start"]

View File

@@ -1,4 +1,5 @@
import { request } from "../service";
import { SiteEnv, SiteInfo } from "/@/store/modules/settings";
export type SysPublicSetting = {
registerEnabled: boolean;
@@ -24,14 +25,20 @@ export async function getInstallInfo(): Promise<SysInstallInfo> {
});
}
export async function getSiteInfo(): Promise<SysInstallInfo> {
export async function getSiteInfo(): Promise<SiteInfo> {
return await request({
url: "/basic/settings/siteInfo",
method: "get"
});
}
export async function getSiteEnv(): Promise<SiteEnv> {
return await request({
url: "/basic/settings/siteEnv",
method: "get"
});
}
export async function bindUrl(data): Promise<SysInstallInfo> {
export async function bindUrl(data: any): Promise<any> {
return await request({
url: "/sys/plus/bindUrl",
method: "post",

View File

@@ -47,7 +47,13 @@ function createService() {
return dataAxios.data;
default:
// 不是正确的 code
errorCreate(`${dataAxios.msg}: ${response.config.url}`);
const errorMessage = dataAxios.msg;
// @ts-ignore
if (response?.config?.onError) {
// @ts-ignore
response.config.onError(new Error(errorMessage));
}
errorCreate(`${errorMessage}: ${response.config.url}`);
return dataAxios;
}
}

View File

@@ -1,3 +1,8 @@
<template>
<a-select mode="tags" readonly :value="modelValue" />
<div>{{ errorRef }}</div>
</template>
<script setup lang="ts">
import { inject, ref, watch } from "vue";
@@ -57,9 +62,4 @@ watch(
);
</script>
<template>
<a-select mode="tags" readonly :value="modelValue" />
<div>{{ errorRef }}</div>
</template>
<style lang="less"></style>

View File

@@ -23,6 +23,7 @@ export default {
const pipeline = inject("pipeline") as Ref<any>;
const currentStageIndex = inject("currentStageIndex") as Ref<number>;
const currentTaskIndex = inject("currentTaskIndex") as Ref<number>;
const currentStepIndex = inject("currentStepIndex") as Ref<number>;
const currentTask = inject("currentTask") as Ref<any>;
@@ -32,6 +33,7 @@ export default {
options.value = pluginGroups.getPreStepOutputOptions({
pipeline: pipeline.value,
currentStageIndex: currentStageIndex.value,
currentTaskIndex: currentTaskIndex.value,
currentStepIndex: currentStepIndex.value,
currentTask: currentTask.value
});

View File

@@ -1,6 +1,28 @@
<template>
<div class="remote-select">
<div class="flex flex-row">
<a-select
class="remote-select-input"
show-search
:filter-option="filterOption"
:options="optionsRef"
:value="value"
v-bind="attrs"
@click="onClick"
@update:value="emit('update:value', $event)"
/>
<div class="ml-5">
<fs-button title="刷新选项" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
</div>
</div>
<div class="helper error">
{{ message }}
</div>
</div>
</template>
<script setup lang="ts">
import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
import { ref, watch } from "vue";
import { ref, useAttrs, watch } from "vue";
defineOptions({
name: "RemoteSelect"
@@ -16,6 +38,8 @@ const emit = defineEmits<{
"update:value": any;
}>();
const attrs = useAttrs();
const optionsRef = ref([]);
const message = ref("");
const getOptions = async () => {
@@ -27,8 +51,8 @@ const getOptions = async () => {
input: props.form
},
{
onError(err) {
message.value = err.message;
onError(err: any) {
message.value = `获取选项出错:${err.message}`;
}
}
);
@@ -44,6 +68,10 @@ async function onClick() {
return;
}
isFirst = false;
await refreshOptions();
}
async function refreshOptions() {
optionsRef.value = await getOptions();
}
@@ -64,21 +92,4 @@ watch(
);
</script>
<template>
<div>
<a-select
class="remote-select"
show-search
:filter-option="filterOption"
:options="optionsRef"
:value="value"
@click="onClick"
@update:value="emit('update:value', $event)"
/>
<div class="helper">
{{ message }}
</div>
</div>
</template>
<style lang="less"></style>

View File

@@ -14,7 +14,7 @@ export type RequestHandleReq<T = any> = {
input: T;
};
export async function doRequest(req: RequestHandleReq, opts?: any = {}) {
export async function doRequest(req: RequestHandleReq, opts: any = {}) {
const url = req.type === "access" ? "/pi/handle/access" : "/pi/handle/plugin";
const { typeName, action, data, input } = req;
const res = await request({

View File

@@ -1,7 +1,7 @@
<template>
<div>
<contextHolder />
<a-input v-bind="attrs" :value="value" :allow-clear="true" @update:value="emit('update:value', $event)">
<a-input :value="value" :allow-clear="true" v-bind="attrs" @update:value="emit('update:value', $event)">
<template #suffix>
<a-tag class="cursor-pointer" @click="getDeviceId">获取设备ID</a-tag>
</template>

View File

@@ -164,9 +164,27 @@ function openUpgrade() {
okText: "激活",
width: 900,
content: () => {
let activationCodeGetWay: any = null;
if (settingStore.siteEnv.agent.enabled != null) {
const agent = settingStore.siteEnv.agent;
if (agent.enabled === false) {
activationCodeGetWay = (
<a href="https://afdian.com/a/greper" target="_blank">
爱发电赞助VIP会员后获取
</a>
);
} else {
activationCodeGetWay = (
<a href={agent.contactLink} target="_blank">
{agent.contactText}
</a>
);
}
}
const vipLabel = settingStore.vipLabel;
const slots = [];
for (const key in vipTypeDefine) {
// @ts-ignore
const item = vipTypeDefine[key];
const vipBlockClass = `vip-block ${key === settingStore.plusInfo.vipType ? "current" : ""}`;
slots.push(
@@ -174,7 +192,7 @@ function openUpgrade() {
<div class={vipBlockClass}>
<h3 class="block-header">{item.title}</h3>
<ul>
{item.privilege.map((p) => (
{item.privilege.map((p: string) => (
<li>
<fs-icon class="color-green" icon="ion:checkmark-sharp" />
{p}
@@ -203,9 +221,7 @@ function openUpgrade() {
<div class="mt-10">
没有激活码
<a href="https://afdian.com/a/greper" target="_blank">
爱发电赞助VIP会员后获取
</a>
{activationCodeGetWay}
</div>
</div>
</div>

View File

@@ -31,7 +31,14 @@
<!-- >-->
<!-- Button-->
<!-- </button>-->
<fs-menu class="header-menu" mode="horizontal" :expand-selected="false" :selectable="false" :menus="headerMenus" />
<fs-menu
v-if="settingStore?.siteEnv?.agent?.enabled === false"
class="header-menu"
mode="horizontal"
:expand-selected="false"
:selectable="false"
:menus="headerMenus"
/>
<div class="header-btn">
<fs-locale />
</div>

View File

@@ -52,7 +52,7 @@ export const certdResources = [
path: "/certd/cname/record",
component: "/certd/cname/record/index.vue",
meta: {
icon: "ion:disc-outline",
icon: "ion:link-outline",
auth: true
}
},

View File

@@ -73,7 +73,7 @@ export const sysResources = [
path: "/sys/cname/provider",
component: "/sys/cname/provider/index.vue",
meta: {
icon: "ion:settings-outline",
icon: "ion:earth-outline",
permission: "sys:settings:view"
}
},

View File

@@ -35,8 +35,16 @@ export interface SettingState {
};
siteInfo: SiteInfo;
plusInfo?: PlusInfo;
siteEnv?: SiteEnv;
}
export type SiteEnv = {
agent?: {
enabled?: boolean;
contactText?: string;
contactLink?: string;
};
};
export type SiteInfo = {
title: string;
slogan: string;
@@ -94,7 +102,14 @@ export const useSettingStore = defineStore({
accountServerBaseUrl: "",
appKey: ""
},
siteInfo: defaultSiteInfo
siteInfo: defaultSiteInfo,
siteEnv: {
agent: {
enabled: undefined,
contactText: "",
contactLink: ""
}
}
}),
getters: {
getThemeConfig(): any {
@@ -132,6 +147,7 @@ export const useSettingStore = defineStore({
},
async loadSysSettings() {
await this.loadSysPublicSettings();
await this.loadSiteEnv();
await this.loadInstallInfo();
await this.loadPlusInfo();
await this.loadSiteInfo();
@@ -145,12 +161,16 @@ export const useSettingStore = defineStore({
const installInfo = await basicApi.getInstallInfo();
_.merge(this.installInfo, installInfo);
},
async loadSiteEnv() {
const siteEnv = await basicApi.getSiteEnv();
_.merge(this.siteEnv, siteEnv);
},
async loadPlusInfo() {
this.plusInfo = await basicApi.getPlusInfo();
},
async loadSiteInfo() {
const isComm = this.isComm;
let siteInfo = {};
let siteInfo: SiteInfo;
if (isComm) {
siteInfo = await basicApi.getSiteInfo();
if (siteInfo.logo) {

View File

@@ -203,9 +203,12 @@ h1, h2, h3, h4, h5, h6 {
}
.helper {
display: inline-block;
color: #aeaeae;
font-size: 12px;
&.error{
color: #ff4d4f;
}
}

View File

@@ -59,7 +59,10 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any) {
show: false
},
column: {
width: 120
width: 200,
component: {
color: "auto"
}
},
form: {
component: {

View File

@@ -51,7 +51,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
key: "id",
type: "number",
column: {
width: 50
width: 100
},
form: {
show: false
@@ -65,6 +65,9 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
},
form: {
rules: [{ required: true, message: "必填项" }]
},
column: {
width: 300
}
},
...commonColumnsDefine

View File

@@ -69,7 +69,7 @@ export default function (certPluginGroup: PluginGroup, formWrapperRef: any): Cre
return (
<ul>
<li>JS-ACME使便</li>
<li>Lego-ACMELego实现DNS提供商LEGO的用户可以使用</li>
<li>Lego-ACMELego实现DNS提供商LEGO的用户可以使用</li>
</ul>
);
}

View File

@@ -134,7 +134,7 @@
<a-tooltip>
<a-button type="dashed" shape="round" @click="taskAdd(stage, index)">
<fs-icon class="font-20" icon="ion:add-circle-outline"></fs-icon>
并行任务
添加任务
</a-button>
</a-tooltip>
</div>

View File

@@ -75,10 +75,11 @@ export class PluginGroups {
return this.map[name];
}
getPreStepOutputOptions({ pipeline, currentStageIndex, currentStepIndex, currentTask }: any) {
getPreStepOutputOptions({ pipeline, currentStageIndex, currentTaskIndex, currentStepIndex, currentTask }: any) {
const steps = this.collectionPreStepOutputs({
pipeline,
currentStageIndex,
currentTaskIndex,
currentStepIndex,
currentTask
});
@@ -96,7 +97,7 @@ export class PluginGroups {
return options;
}
collectionPreStepOutputs({ pipeline, currentStageIndex, currentStepIndex, currentTask }: any) {
collectionPreStepOutputs({ pipeline, currentStageIndex, currentTaskIndex, currentStepIndex, currentTask }: any) {
const steps: any[] = [];
// 开始放step
for (let i = 0; i < currentStageIndex; i++) {
@@ -107,6 +108,14 @@ export class PluginGroups {
}
}
}
//当前阶段之前的task
const currentStage = pipeline.stages[currentStageIndex];
for (let i = 0; i < currentTaskIndex; i++) {
const task = currentStage.tasks[i];
for (const step of task.steps) {
steps.push(step);
}
}
//放当前任务下的step
for (let i = 0; i < currentStepIndex; i++) {
const step = currentTask.steps[i];

View File

@@ -6,7 +6,7 @@
</div>
</div>
<p class="d2-page-cover__sub-title">{{ siteInfo.slogan }}</p>
<div v-if="siteInfo.warningOff !== true" class="warning">
<div v-if="siteInfo.warningOff !== true && settingStore.siteEnv?.agent?.enabled === false" class="warning">
<a-alert type="warning" show-icon>
<template #description>
<div class="flex">

View File

@@ -21,19 +21,19 @@ typeorm:
dataSource:
default:
database: './data/db-comm.sqlite'
#plus:
# server:
# baseUrls: ['https://api.ai.handsfree.work', 'https://api.ai.docmirror.cn']
#
#account:
# server:
# baseUrl: 'https://ai.handsfree.work/subject'
plus:
server:
baseUrls: ['http://127.0.0.1:11007']
baseUrls: ['https://api.ai.handsfree.work', 'https://api.ai.docmirror.cn']
account:
server:
baseUrl: 'http://127.0.0.1:1017/subject'
baseUrl: 'https://ai.handsfree.work/subject'
#plus:
# server:
# baseUrls: ['http://127.0.0.1:11007']
#
#account:
# server:
# baseUrl: 'http://127.0.0.1:1017/subject'

View File

@@ -37,6 +37,12 @@ const development = {
'/': '/index.html',
},
},
// '/index.html': {
// maxAge: 0,
// },
// '/': {
// maxAge: 0,
// },
},
},
cron: {
@@ -109,6 +115,11 @@ const development = {
// 仅在匹配路径到 /api/upload 的时候去解析 body 中的文件信息
match: /\/api\/basic\/file\/upload/,
},
agent: {
enabled: false,
contactText: '',
contactLink: '',
},
} as MidwayConfig;
mergeConfig(development, 'development');

View File

@@ -54,7 +54,11 @@ export class AutoInitSite {
}
// 授权许可
await this.plusService.verify();
try {
await this.plusService.verify();
} catch (e) {
logger.error('授权许可验证失败', e);
}
logger.info('初始化站点完成');
}

View File

@@ -1,5 +1,5 @@
import { ALL, Body, Config, Controller, Get, Inject, Provide } from '@midwayjs/core';
import { BaseController, Constants, SysInstallInfo, SysPublicSettings, SysSettingsService, SysSiteInfo } from '@certd/lib-server';
import { BaseController, Constants, SysInstallInfo, SysPublicSettings, SysSettingsService, SysSiteEnv, SysSiteInfo } from '@certd/lib-server';
import { AppKey, getPlusInfo } from '@certd/pipeline';
/**
@@ -12,6 +12,9 @@ export class BasicSettingsController extends BaseController {
@Config('account.server.baseUrl')
accountServerBaseUrl: any;
@Config('agent')
agentConfig: SysSiteEnv['agent'];
@Get('/public', { summary: Constants.per.guest })
public async getSysPublic() {
const settings = await this.sysSettingsService.getSetting(SysPublicSettings);
@@ -32,6 +35,14 @@ export class BasicSettingsController extends BaseController {
return this.ok(settings);
}
@Get('/siteEnv', { summary: Constants.per.guest })
public async getSiteEnv() {
const env: SysSiteEnv = {
agent: this.agentConfig,
};
return this.ok(env);
}
@Get('/plusInfo', { summary: Constants.per.guest })
async plusInfo(@Body(ALL) body: any) {
const info = getPlusInfo();

View File

@@ -62,9 +62,10 @@ export class PipelineController extends CrudController<PipelineService> {
@Post('/save', { summary: Constants.per.authOnly })
async save(@Body(ALL) bean: PipelineEntity) {
bean.userId = this.getUserId();
if (bean.id > 0) {
await this.authService.checkEntityUserId(this.ctx, this.getService(), bean.id);
} else {
bean.userId = this.getUserId();
}
await this.service.save(bean);
return this.ok(bean.id);

View File

@@ -118,10 +118,11 @@ export class PipelineService extends BaseService<PipelineEntity> {
const isUpdate = bean.id > 0 && old != null;
if (!isPlus()) {
let count = await this.repository.count();
if (isUpdate) {
count -= 1;
if (!isUpdate) {
//如果是添加要加1
count += 1;
}
if (count >= freeCount) {
if (count > freeCount) {
throw new NeedVIPException('免费版最多只能创建10个pipeline');
}
}

View File

@@ -10,3 +10,4 @@ export * from './plugin-west/index.js';
export * from './plugin-doge/index.js';
export * from './plugin-qiniu/index.js';
export * from './plugin-jdcloud/index.js';
export * from './plugin-woai/index.js';

View File

@@ -1,5 +1,5 @@
// @ts-ignore
import ssh2, { ConnectConfig } from 'ssh2';
import ssh2, { ConnectConfig, ExecOptions } from 'ssh2';
import path from 'path';
import * as _ from 'lodash-es';
import { ILogger } from '@certd/pipeline';
@@ -269,7 +269,7 @@ export class SshClient {
* Set-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\System32\cmd.exe"
* @param options
*/
async exec(options: { connectConf: SshAccess; script: string | Array<string> }) {
async exec(options: { connectConf: SshAccess; script: string | Array<string>; env?: any }): Promise<string[]> {
let { script } = options;
const { connectConf } = options;
@@ -278,14 +278,32 @@ export class SshClient {
connectConf,
callable: async (conn: AsyncSsh2Client) => {
let isWinCmd = false;
const isLinux = !connectConf.windows;
const envScripts = [];
if (connectConf.windows) {
isWinCmd = await this.isCmd(conn);
}
if (options.env) {
for (const key in options.env) {
if (isLinux) {
envScripts.push(`export ${key}=${options.env[key]}`);
} else if (isWinCmd) {
//win cmd
envScripts.push(`set ${key}=${options.env[key]}`);
} else {
//powershell
envScripts.push(`$env:${key}="${options.env[key]}"`);
}
}
}
if (isWinCmd) {
//组合成&&的形式
if (typeof script === 'string') {
script = script.split('\n');
}
script = envScripts.concat(script);
script = script as Array<string>;
script = script.join(' && ');
} else {
@@ -293,6 +311,9 @@ export class SshClient {
script = script as Array<string>;
script = script.join('\n');
}
if (envScripts.length > 0) {
script = envScripts.join('\n') + '\n' + script;
}
}
await conn.exec(script);
},

View File

@@ -3,6 +3,7 @@ import { SshClient } from '../../lib/ssh.js';
import { CertInfo, CertReader, CertReaderHandleContext } from '@certd/plugin-cert';
import * as fs from 'fs';
import { SshAccess } from '../../access/index.js';
import dayjs from 'dayjs';
@IsTaskPlugin({
name: 'uploadCertToHost',
@@ -106,6 +107,18 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
})
script!: string;
@TaskInput({
title: '注入环境变量',
value: false,
component: {
name: 'a-switch',
vModel: 'checked',
},
helper: '是否将证书域名、路径等信息注入脚本执行环境变量中,具体的变量名称,可以运行后从日志中查看',
required: false,
})
injectEnv!: string;
@TaskOutput({
title: '证书保存路径',
})
@@ -233,10 +246,28 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
const connectConf: SshAccess = await this.accessService.getById(accessId);
const sshClient = new SshClient(this.logger);
this.logger.info('执行脚本命令');
//环境变量
const env = {};
if (this.injectEnv) {
const domains = certReader.getAllDomains();
for (let i = 0; i < domains.length; i++) {
env[`CERT_DOMAIN_${i}`] = domains[i];
}
env['CERT_EXPIRES'] = dayjs(certReader.getCrtDetail().expires).unix();
env['HOST_CRT_PATH'] = this.hostCrtPath || '';
env['HOST_KEY_PATH'] = this.hostKeyPath || '';
env['HOST_IC_PATH'] = this.hostIcPath || '';
env['HOST_PFX_PATH'] = this.hostPfxPath || '';
env['HOST_DER_PATH'] = this.hostDerPath || '';
}
const scripts = this.script.split('\n');
await sshClient.exec({
connectConf,
script: scripts,
env,
});
}
}

View File

@@ -18,9 +18,17 @@ export class QiniuDeployCertToCDN extends AbstractTaskPlugin {
@TaskInput({
title: 'CDN加速域名',
helper: '你在七牛云上配置的CDN加速域名比如:certd.handsfree.work',
component: {
name: 'a-select',
vModel: 'value',
mode: 'tags',
open: false,
tokenSeparators: [',', ' ', '', '、', '|'],
},
rules: [{ type: 'domains' }],
required: true,
})
domainName!: string;
domainName!: string | string[];
@TaskInput({
title: '域名证书',
@@ -52,7 +60,7 @@ export class QiniuDeployCertToCDN extends AbstractTaskPlugin {
http: this.ctx.http,
access,
});
const url = `https://api.qiniu.com/domain/${this.domainName}/httpsconf`;
let certId = null;
if (typeof this.cert !== 'string') {
// 是证书id直接上传即可
@@ -62,13 +70,17 @@ export class QiniuDeployCertToCDN extends AbstractTaskPlugin {
certId = this.cert;
}
//开始修改证书
this.logger.info(`开始修改证书,certId:${certId},domain:${this.domainName}`);
const body = {
certID: certId,
};
await qiniuClient.doRequest(url, 'put', body);
const domains: string[] = typeof this.domainName === 'string' ? [this.domainName] : this.domainName;
for (const domain of domains) {
//开始修改证书
this.logger.info(`开始修改证书,certId:${certId},domain:${domain}`);
const body = {
certID: certId,
};
const url = `https://api.qiniu.com/domain/${domain}/httpsconf`;
await qiniuClient.doRequest(url, 'put', body);
this.logger.info(`修改证书成功,certId:${certId},domain:${domain}`);
}
this.logger.info('部署完成');
}

View File

@@ -0,0 +1,28 @@
import { AccessInput, BaseAccess, IsAccess } from '@certd/pipeline';
@IsAccess({
name: 'woai',
title: '我爱云授权',
desc: '我爱云CDN',
})
export class WoaiAccess extends BaseAccess {
@AccessInput({
title: '账号',
component: {
placeholder: '我爱云的账号',
},
required: true,
})
username = '';
@AccessInput({
title: '密码',
component: {
placeholder: '我爱云的密码',
},
required: true,
encrypt: true,
})
password = '';
}
new WoaiAccess();

View File

@@ -0,0 +1,2 @@
export * from './plugins/index.js';
export * from './access.js';

View File

@@ -0,0 +1 @@
export * from './plugin-deploy-to-cdn.js';

View File

@@ -0,0 +1,94 @@
import { AbstractTaskPlugin, HttpClient, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { WoaiAccess } from '../access.js';
@IsTaskPlugin({
name: 'WoaiCDN',
title: '部署证书到我爱云 CDN',
desc: '部署证书到我爱云CDN',
icon: 'clarity:plugin-line',
group: pluginGroups.cdn.key,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class WoaiCdnPlugin extends AbstractTaskPlugin {
@TaskInput({
title: '证书ID',
helper: '请填写 [证书列表](https://console.edge.51vs.club/site/certificate) 中的证书的ID',
component: { name: 'a-input' },
required: true,
})
certId!: string;
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: ['CertApply', 'CertApplyLego'],
},
required: true,
})
cert!: CertInfo;
@TaskInput({
title: 'Access授权',
helper: '我爱云的用户、密码授权',
component: {
name: 'access-selector',
type: 'woai',
},
required: true,
})
accessId!: string;
http!: HttpClient;
private readonly baseApi = 'https://console.edeg.51vs.club';
async onInstance() {
this.http = this.ctx.http;
}
private async doRequestApi(url: string, data: any = null, method = 'post', token: string | null = null) {
const headers = {
'Content-Type': 'application/json',
...(token ? { Token: token } : {}),
};
const res = await this.http.request<any, any>({
url,
method,
data,
headers,
});
if (res.code !== 200) {
throw new Error(`${JSON.stringify(res.message)}`);
}
return res;
}
async execute(): Promise<void> {
const { certId, cert, accessId } = this;
const access = (await this.accessService.getById(accessId)) as WoaiAccess;
// 登录获取token
const loginResponse = await this.doRequestApi(`${this.baseApi}/account/login`, {
username: access.username,
password: access.password,
});
const token = loginResponse.data.token;
this.logger.info('登录成功,获取到Token:', token);
// 更新证书
const editCertResponse = await this.doRequestApi(
`${this.baseApi}/certificate/edit`,
{
id: certId,
cert: cert.crt,
key: cert.key,
},
'post',
token
);
this.logger.info('证书更新成功:', editCertResponse.message);
}
}
new WoaiCdnPlugin();