mirror of https://github.com/certd/certd
Merge remote-tracking branch 'origin/v2-dev' into v2-dev
commit
79f8e5bf47
|
@ -87,3 +87,15 @@ jobs:
|
||||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}-armv7
|
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}-armv7
|
||||||
greper/certd:armv7
|
greper/certd:armv7
|
||||||
greper/certd:${{steps.get_certd_version.outputs.result}}-armv7
|
greper/certd:${{steps.get_certd_version.outputs.result}}-armv7
|
||||||
|
|
||||||
|
- name: Build agent
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
push: true
|
||||||
|
context: ./packages/ui/agent/
|
||||||
|
tags: |
|
||||||
|
registry.cn-shenzhen.aliyuncs.com/handsfree/certd-agent:latest
|
||||||
|
registry.cn-shenzhen.aliyuncs.com/handsfree/certd-agent:${{steps.get_certd_version.outputs.result}}
|
||||||
|
greper/certd-agent:latest
|
||||||
|
greper/certd-agent:${{steps.get_certd_version.outputs.result}}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
## 1.本地调试运行
|
## 1.本地调试运行
|
||||||
|
|
||||||
安装依赖包:
|
### 克隆代码
|
||||||
```shell
|
```shell
|
||||||
|
|
||||||
# 克隆代码
|
# 克隆代码
|
||||||
|
@ -11,22 +11,38 @@ git clone https://github.com/certd/certd
|
||||||
|
|
||||||
#进入项目目录
|
#进入项目目录
|
||||||
cd certd
|
cd certd
|
||||||
|
```
|
||||||
|
|
||||||
|
### 修改pnpm-workspace.yaml文件
|
||||||
|
重要:否则无法正确加载专业版的access和plugin
|
||||||
|
```yaml
|
||||||
|
# pnpm-workspace.yaml
|
||||||
|
packages:
|
||||||
|
- 'packages/**' # <--------------注释掉这一行,PR时不要提交此修改
|
||||||
|
- 'packages/ui/**'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 安装依赖和初始化:
|
||||||
|
```shell
|
||||||
|
# 安装pnpm,如果提示npm命令不存在,就需要先安装nodejs
|
||||||
|
npm install -g pnpm@8.15.7 --registry=https://registry.npmmirror.com
|
||||||
|
|
||||||
|
# 使用国内镜像源,如果有代理,就不需要
|
||||||
|
pnpm config set registry https://registry.npmmirror.com
|
||||||
# 安装依赖
|
# 安装依赖
|
||||||
npm install -g pnpm@8.15.7
|
|
||||||
pnpm install
|
pnpm install
|
||||||
|
|
||||||
# 初始化构建
|
# 初始化构建
|
||||||
npm run init
|
npm run init
|
||||||
```
|
```
|
||||||
|
|
||||||
启动 server:
|
### 启动 server:
|
||||||
```shell
|
```shell
|
||||||
cd packages/ui/certd-server
|
cd packages/ui/certd-server
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
启动 client:
|
### 启动 client:
|
||||||
```shell
|
```shell
|
||||||
cd packages/ui/certd-client
|
cd packages/ui/certd-client
|
||||||
npm run dev
|
npm run dev
|
||||||
|
@ -48,7 +64,7 @@ npm run dev
|
||||||
这样用户就可以在`certd`后台中创建这种授权凭证了
|
这样用户就可以在`certd`后台中创建这种授权凭证了
|
||||||
|
|
||||||
### 3. dns-provider
|
### 3. dns-provider
|
||||||
如果域名是这个平台进行解析的,那么你需要实现dns-provider
|
如果域名是这个平台进行解析的,那么你需要实现dns-provider,(申请证书需要)
|
||||||
参考`plugin-cloudflare/dns-provider.ts` 修改为你要做的平台的`dns-provider`
|
参考`plugin-cloudflare/dns-provider.ts` 修改为你要做的平台的`dns-provider`
|
||||||
|
|
||||||
### 4. plugin-deploy
|
### 4. plugin-deploy
|
||||||
|
@ -66,7 +82,7 @@ export * from './plugins/plugin-deploy-to-xx'
|
||||||
在`./src/plugins/index.ts`中增加`import`
|
在`./src/plugins/index.ts`中增加`import`
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
export * from "./plugin-cloudflare"
|
export * from "./plugin-cloudflare.js"
|
||||||
```
|
```
|
||||||
|
|
||||||
## 重启服务进行调试
|
## 重启服务进行调试
|
||||||
|
|
|
@ -7,16 +7,18 @@ https://console.cloud.google.com/apis/library/publicca.googleapis.com
|
||||||
|
|
||||||
打开该链接后点击“启用”,随后等待右侧出现“API已启用”则可以关闭该页。
|
打开该链接后点击“启用”,随后等待右侧出现“API已启用”则可以关闭该页。
|
||||||
|
|
||||||
## 2、 申请Key
|
## 2、 获取授权
|
||||||
随后打开“Google Cloud Shell”(在右上角点击激活CloudShell图标)。
|
以下两种方式任选其一
|
||||||
|
### 2.1 直接获取EAB 【推荐】
|
||||||
|
|
||||||
|
|
||||||
|
1. 打开“Google Cloud Shell”(在右上角点击激活CloudShell图标)。
|
||||||
等待分配完成后在 Shell 窗口内输入如下命令:
|
等待分配完成后在 Shell 窗口内输入如下命令:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
gcloud beta publicca external-account-keys create
|
gcloud beta publicca external-account-keys create
|
||||||
```
|
```
|
||||||
此时会弹出“为 Cloud Shell 提供授权”,点击授权即可。
|
2. 此时会弹出“为 Cloud Shell 提供授权”,点击授权即可。
|
||||||
|
|
||||||
执行完成后会返回类似如下输出;注意不要在没有收到 Google 的邮件时执行该命令,会返回命令不存在。
|
执行完成后会返回类似如下输出;注意不要在没有收到 Google 的邮件时执行该命令,会返回命令不存在。
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
|
@ -24,14 +26,31 @@ Created an external account key
|
||||||
[b64MacKey: xxxxxxxxxxxxxxxx
|
[b64MacKey: xxxxxxxxxxxxxxxx
|
||||||
keyId: xxxxxxxxxxxxx]
|
keyId: xxxxxxxxxxxxx]
|
||||||
```
|
```
|
||||||
记录以上信息备用(注意keyId是不带中括号的)
|
|
||||||
|
3. 到Certd中,创建一条EAB授权记录,填写keyId(=kid) 和 b64MacKey 信息
|
||||||
|
注意:keyId没有`]`结尾,不要把`]`也复制了
|
||||||
|
|
||||||
|
注意:EAB授权使用过一次之后,会绑定邮箱,后续再次使用时,要使用相同的邮箱
|
||||||
|
否则会报错 `Unknown external account binding (EAB) key. This may be due to the EAB key expiring which occurs 7 days after creation`
|
||||||
|
|
||||||
|
### 2.2 通过服务账号获取EAB
|
||||||
|
|
||||||
|
此方式可以自动EAB,需要配置代理
|
||||||
|
|
||||||
|
1. 创建服务账号
|
||||||
|
https://console.cloud.google.com/projectselector2/iam-admin/serviceaccounts/create?walkthrough_id=iam--create-service-account&hl=zh-cn#step_index=1
|
||||||
|
|
||||||
|
2. 选择一个项目,进入创建服务账号页面
|
||||||
|
3. 给服务账号起一个名字,点击`创建并继续`
|
||||||
|
4. 向此服务账号授予对项目的访问权限: `选择角色`->`基本`->`Owner`
|
||||||
|
5. 点击完成
|
||||||
|
6. 点击服务账号,进入服务账号详情页面
|
||||||
|
7. 点击`添加密钥`->`创建新密钥`->`JSON`,下载密钥文件
|
||||||
|
8. 将json文件内容粘贴到 certd中 Google服务授权输入框中
|
||||||
|
|
||||||
|
|
||||||
## 3、 创建证书流水线
|
## 3、 创建证书流水线
|
||||||
选择证书提供商为google, 开启使用代理
|
选择证书提供商为google, 选择EAB授权 或 服务账号授权
|
||||||
|
|
||||||
## 4、 将key信息作为EAB授权信息
|
## 4、 其他就跟正常申请证书一样了
|
||||||
google证书需要EAB授权, 使用第二步中的 keyId 和 b64MacKey 信息创建一条EAB授权记录
|
|
||||||
注意:keyId没有`]`结尾,不要把`]`也复制了
|
|
||||||
## 5、 其他就跟正常申请证书一样了
|
|
||||||
|
|
||||||
|
|
|
@ -54,3 +54,11 @@ export class SysSiteInfo extends BaseSettings {
|
||||||
logo?: string;
|
logo?: string;
|
||||||
loginLogo?: string;
|
loginLogo?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SysSiteEnv {
|
||||||
|
agent?: {
|
||||||
|
enabled?: boolean;
|
||||||
|
contactText?: string;
|
||||||
|
contactLink?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -14,9 +14,10 @@
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.25.9",
|
|
||||||
"@certd/acme-client": "^1.25.9",
|
"@certd/acme-client": "^1.25.9",
|
||||||
|
"@certd/basic": "^1.25.9",
|
||||||
"@certd/pipeline": "^1.25.9",
|
"@certd/pipeline": "^1.25.9",
|
||||||
|
"@google-cloud/publicca": "^1.3.0",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"node-forge": "^0.10.0",
|
"node-forge": "^0.10.0",
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
import { IsAccess, AccessInput, BaseAccess } from "@certd/pipeline";
|
||||||
|
|
||||||
|
@IsAccess({
|
||||||
|
name: "google",
|
||||||
|
title: "google cloud",
|
||||||
|
desc: "谷歌云授权",
|
||||||
|
})
|
||||||
|
export class GoogleAccess extends BaseAccess {
|
||||||
|
@AccessInput({
|
||||||
|
title: "密钥类型",
|
||||||
|
value: "serviceAccount",
|
||||||
|
component: {
|
||||||
|
placeholder: "密钥类型",
|
||||||
|
name: "a-select",
|
||||||
|
vModel: "value",
|
||||||
|
options: [
|
||||||
|
{ value: "serviceAccount", label: "服务账号密钥" },
|
||||||
|
{ value: "apiKey", label: "ApiKey,暂不可用", disabled: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
helper: "密钥类型",
|
||||||
|
required: true,
|
||||||
|
encrypt: false,
|
||||||
|
})
|
||||||
|
type = "";
|
||||||
|
|
||||||
|
@AccessInput({
|
||||||
|
title: "项目ID",
|
||||||
|
component: {
|
||||||
|
placeholder: "ProjectId",
|
||||||
|
},
|
||||||
|
helper: "ProjectId",
|
||||||
|
required: true,
|
||||||
|
encrypt: false,
|
||||||
|
mergeScript: `
|
||||||
|
return {
|
||||||
|
show:ctx.compute(({form})=>{
|
||||||
|
return form.access.type === 'apiKey'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
projectId = "";
|
||||||
|
|
||||||
|
@AccessInput({
|
||||||
|
title: "ApiKey",
|
||||||
|
component: {
|
||||||
|
placeholder: "ApiKey",
|
||||||
|
},
|
||||||
|
helper: "不要选,目前没有用",
|
||||||
|
required: true,
|
||||||
|
encrypt: true,
|
||||||
|
mergeScript: `
|
||||||
|
return {
|
||||||
|
show:ctx.compute(({form})=>{
|
||||||
|
return form.access.type === 'apiKey'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
apiKey = "";
|
||||||
|
|
||||||
|
@AccessInput({
|
||||||
|
title: "服务账号密钥",
|
||||||
|
component: {
|
||||||
|
placeholder: "serviceAccountSecret",
|
||||||
|
name: "a-textarea",
|
||||||
|
vModel: "value",
|
||||||
|
rows: 4,
|
||||||
|
},
|
||||||
|
helper:
|
||||||
|
"[如何创建服务账号](https://cloud.google.com/iam/docs/service-accounts-create?hl=zh-CN) \n[获取密钥](https://console.cloud.google.com/iam-admin/serviceaccounts?hl=zh-cn),点击详情,点击创建密钥,将下载json文件,把内容填在此处",
|
||||||
|
required: true,
|
||||||
|
encrypt: true,
|
||||||
|
mergeScript: `
|
||||||
|
return {
|
||||||
|
show:ctx.compute(({form})=>{
|
||||||
|
return form.access.type === 'serviceAccount'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
serviceAccountSecret = "";
|
||||||
|
|
||||||
|
@AccessInput({
|
||||||
|
title: "https代理",
|
||||||
|
component: {
|
||||||
|
placeholder: "http://127.0.0.1:10811",
|
||||||
|
},
|
||||||
|
helper: "Google的请求需要走代理,如果不配置,则会使用环境变量中的全局HTTPS_PROXY配置\n或者服务器本身在海外,则不需要配置",
|
||||||
|
required: false,
|
||||||
|
encrypt: false,
|
||||||
|
})
|
||||||
|
httpsProxy = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
new GoogleAccess();
|
|
@ -1 +1,2 @@
|
||||||
export * from "./eab-access.js";
|
export * from "./eab-access.js";
|
||||||
|
export * from "./google-access.js";
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
import { EabAccess, GoogleAccess } from "../access/index.js";
|
||||||
|
import { ILogger } from "@certd/basic";
|
||||||
|
|
||||||
|
export class GoogleClient {
|
||||||
|
access: GoogleAccess;
|
||||||
|
logger: ILogger;
|
||||||
|
constructor(opts: { logger: ILogger; access: GoogleAccess }) {
|
||||||
|
this.access = opts.access;
|
||||||
|
this.logger = opts.logger;
|
||||||
|
}
|
||||||
|
async getEab() {
|
||||||
|
// https://cloud.google.com/docs/authentication/api-keys-use#using-with-client-libs
|
||||||
|
const { v1 } = await import("@google-cloud/publicca");
|
||||||
|
// process.env.HTTPS_PROXY = "http://127.0.0.1:10811";
|
||||||
|
const access = this.access;
|
||||||
|
if (!access.serviceAccountSecret) {
|
||||||
|
throw new Error("服务账号密钥 不能为空");
|
||||||
|
}
|
||||||
|
const credentials = JSON.parse(access.serviceAccountSecret);
|
||||||
|
|
||||||
|
const client = new v1.PublicCertificateAuthorityServiceClient({ credentials });
|
||||||
|
const parent = `projects/${credentials.project_id}/locations/global`;
|
||||||
|
const externalAccountKey = {};
|
||||||
|
const request = {
|
||||||
|
parent,
|
||||||
|
externalAccountKey,
|
||||||
|
};
|
||||||
|
|
||||||
|
let envHttpsProxy = "";
|
||||||
|
try {
|
||||||
|
if (this.access.httpsProxy) {
|
||||||
|
//设置临时使用代理
|
||||||
|
envHttpsProxy = process.env.HTTPS_PROXY;
|
||||||
|
process.env.HTTPS_PROXY = this.access.httpsProxy;
|
||||||
|
}
|
||||||
|
this.logger.info("开始获取google eab授权");
|
||||||
|
const response = await client.createExternalAccountKey(request);
|
||||||
|
const { keyId, b64MacKey } = response[0];
|
||||||
|
const eabAccess = new EabAccess();
|
||||||
|
eabAccess.kid = keyId;
|
||||||
|
eabAccess.hmacKey = b64MacKey.toString();
|
||||||
|
this.logger.info(`google eab授权获取成功,kid: ${eabAccess.kid}`);
|
||||||
|
return eabAccess;
|
||||||
|
} finally {
|
||||||
|
if (envHttpsProxy) {
|
||||||
|
process.env.HTTPS_PROXY = envHttpsProxy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// const access = new GoogleAccess();
|
||||||
|
// access.projectId = "hip-light-432411-d4";
|
||||||
|
// access.serviceAccountSecret = `
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// `;
|
||||||
|
// // process.env.HTTPS_PROXY = "http://127.0.0.1:10811";
|
||||||
|
// const client = new GoogleClient(access);
|
||||||
|
// client.getEab().catch((e) => {
|
||||||
|
// console.error(e);
|
||||||
|
// });
|
|
@ -40,6 +40,7 @@ type AcmeServiceOptions = {
|
||||||
eab?: ClientExternalAccountBindingOptions;
|
eab?: ClientExternalAccountBindingOptions;
|
||||||
skipLocalVerify?: boolean;
|
skipLocalVerify?: boolean;
|
||||||
useMappingProxy?: boolean;
|
useMappingProxy?: boolean;
|
||||||
|
reverseProxy?: string;
|
||||||
privateKeyType?: PrivateKeyType;
|
privateKeyType?: PrivateKeyType;
|
||||||
signal?: AbortSignal;
|
signal?: AbortSignal;
|
||||||
};
|
};
|
||||||
|
@ -91,8 +92,8 @@ export class AcmeService {
|
||||||
const urlMapping: UrlMapping = {
|
const urlMapping: UrlMapping = {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
mappings: {
|
mappings: {
|
||||||
"acme-v02.api.letsencrypt.org": "letsencrypt.proxy.handsfree.work",
|
"acme-v02.api.letsencrypt.org": this.options.reverseProxy || "letsencrypt.proxy.handsfree.work",
|
||||||
"dv.acme-v02.api.pki.goog": "google.proxy.handsfree.work",
|
"dv.acme-v02.api.pki.goog": this.options.reverseProxy || "google.proxy.handsfree.work",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const conf = await this.getAccountConfig(email, urlMapping);
|
const conf = await this.getAccountConfig(email, urlMapping);
|
||||||
|
|
|
@ -5,6 +5,7 @@ import _ from "lodash-es";
|
||||||
import { createDnsProvider, DnsProviderContext, IDnsProvider } from "../../dns-provider/index.js";
|
import { createDnsProvider, DnsProviderContext, IDnsProvider } from "../../dns-provider/index.js";
|
||||||
import { CertReader } from "./cert-reader.js";
|
import { CertReader } from "./cert-reader.js";
|
||||||
import { CertApplyBasePlugin } from "./base.js";
|
import { CertApplyBasePlugin } from "./base.js";
|
||||||
|
import { GoogleClient } from "../../libs/google.js";
|
||||||
|
|
||||||
export type { CertInfo };
|
export type { CertInfo };
|
||||||
export * from "./cert-reader.js";
|
export * from "./cert-reader.js";
|
||||||
|
@ -144,9 +145,9 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||||
type: "eab",
|
type: "eab",
|
||||||
},
|
},
|
||||||
maybeNeed: true,
|
maybeNeed: true,
|
||||||
required: true,
|
required: false,
|
||||||
helper:
|
helper:
|
||||||
"需要提供EAB授权\nZeroSSL:请前往[zerossl开发者中心](https://app.zerossl.com/developer),生成 'EAB Credentials' \n Google:请查看[google获取eab帮助文档](https://github.com/certd/certd/blob/v2/doc/google/google.md)",
|
"需要提供EAB授权\nZeroSSL:请前往[zerossl开发者中心](https://app.zerossl.com/developer),生成 'EAB Credentials'\n Google:请查看[google获取eab帮助文档](https://gitee.com/certd/certd/blob/v2/doc/google/google.md),用过一次后会绑定邮箱,后续复用EAB要用同一个邮箱",
|
||||||
mergeScript: `
|
mergeScript: `
|
||||||
return {
|
return {
|
||||||
show: ctx.compute(({form})=>{
|
show: ctx.compute(({form})=>{
|
||||||
|
@ -157,6 +158,26 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||||
})
|
})
|
||||||
eabAccessId!: number;
|
eabAccessId!: number;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "服务账号授权",
|
||||||
|
component: {
|
||||||
|
name: "access-selector",
|
||||||
|
type: "google",
|
||||||
|
},
|
||||||
|
maybeNeed: true,
|
||||||
|
required: false,
|
||||||
|
helper:
|
||||||
|
"google服务账号授权与EAB授权选填其中一个,[服务账号授权获取方法](https://gitee.com/certd/certd/blob/v2/doc/google/google.md)\n服务账号授权需要配置代理或者服务器本身在海外",
|
||||||
|
mergeScript: `
|
||||||
|
return {
|
||||||
|
show: ctx.compute(({form})=>{
|
||||||
|
return form.sslProvider === 'google'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
googleAccessId!: number;
|
||||||
|
|
||||||
@TaskInput({
|
@TaskInput({
|
||||||
title: "加密算法",
|
title: "加密算法",
|
||||||
value: "rsa_2048",
|
value: "rsa_2048",
|
||||||
|
@ -190,6 +211,15 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||||
})
|
})
|
||||||
useProxy = false;
|
useProxy = false;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "自定义反代地址",
|
||||||
|
component: {
|
||||||
|
placeholder: "google.yourproxy.com",
|
||||||
|
},
|
||||||
|
helper: "填写你的自定义反代地址,不要带http://\nletsencrypt反代目标:acme-v02.api.letsencrypt.org\ngoogle反代目标:dv.acme-v02.api.pki.goog",
|
||||||
|
})
|
||||||
|
reverseProxy = "";
|
||||||
|
|
||||||
@TaskInput({
|
@TaskInput({
|
||||||
title: "跳过本地校验DNS",
|
title: "跳过本地校验DNS",
|
||||||
value: false,
|
value: false,
|
||||||
|
@ -205,9 +235,32 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||||
|
|
||||||
async onInit() {
|
async onInit() {
|
||||||
let eab: any = null;
|
let eab: any = null;
|
||||||
if (this.eabAccessId) {
|
|
||||||
eab = await this.ctx.accessService.getById(this.eabAccessId);
|
if (this.sslProvider === "google") {
|
||||||
|
if (this.googleAccessId) {
|
||||||
|
this.logger.info("您正在使用google服务账号授权");
|
||||||
|
const googleAccess = await this.ctx.accessService.getById(this.googleAccessId);
|
||||||
|
const googleClient = new GoogleClient({
|
||||||
|
access: googleAccess,
|
||||||
|
logger: this.logger,
|
||||||
|
});
|
||||||
|
eab = await googleClient.getEab();
|
||||||
|
} else if (this.eabAccessId) {
|
||||||
|
this.logger.info("您正在使用google EAB授权");
|
||||||
|
eab = await this.ctx.accessService.getById(this.eabAccessId);
|
||||||
|
} else {
|
||||||
|
this.logger.error("google需要配置EAB授权或服务账号授权");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (this.sslProvider === "zerossl") {
|
||||||
|
if (this.eabAccessId) {
|
||||||
|
eab = await this.ctx.accessService.getById(this.eabAccessId);
|
||||||
|
} else {
|
||||||
|
this.logger.error("zerossl需要配置EAB授权");
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.acme = new AcmeService({
|
this.acme = new AcmeService({
|
||||||
userContext: this.userContext,
|
userContext: this.userContext,
|
||||||
logger: this.logger,
|
logger: this.logger,
|
||||||
|
@ -215,6 +268,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||||
eab,
|
eab,
|
||||||
skipLocalVerify: this.skipLocalVerify,
|
skipLocalVerify: this.skipLocalVerify,
|
||||||
useMappingProxy: this.useProxy,
|
useMappingProxy: this.useProxy,
|
||||||
|
reverseProxy: this.reverseProxy,
|
||||||
privateKeyType: this.privateKeyType,
|
privateKeyType: this.privateKeyType,
|
||||||
// cnameProxyService: this.ctx.cnameProxyService,
|
// cnameProxyService: this.ctx.cnameProxyService,
|
||||||
// dnsProviderCreator: this.createDnsProvider.bind(this),
|
// dnsProviderCreator: this.createDnsProvider.bind(this),
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
FROM greper/certd:latest
|
||||||
|
ENV certd_agent_enabled=true
|
||||||
|
CMD ["npm", "run","start"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { request } from "../service";
|
import { request } from "../service";
|
||||||
|
import { SiteEnv, SiteInfo } from "/@/store/modules/settings";
|
||||||
|
|
||||||
export type SysPublicSetting = {
|
export type SysPublicSetting = {
|
||||||
registerEnabled: boolean;
|
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({
|
return await request({
|
||||||
url: "/basic/settings/siteInfo",
|
url: "/basic/settings/siteInfo",
|
||||||
method: "get"
|
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({
|
return await request({
|
||||||
url: "/sys/plus/bindUrl",
|
url: "/sys/plus/bindUrl",
|
||||||
method: "post",
|
method: "post",
|
||||||
|
|
|
@ -47,7 +47,13 @@ function createService() {
|
||||||
return dataAxios.data;
|
return dataAxios.data;
|
||||||
default:
|
default:
|
||||||
// 不是正确的 code
|
// 不是正确的 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;
|
return dataAxios;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
<template>
|
||||||
|
<a-select mode="tags" readonly :value="modelValue" />
|
||||||
|
<div>{{ errorRef }}</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { inject, ref, watch } from "vue";
|
import { inject, ref, watch } from "vue";
|
||||||
|
|
||||||
|
@ -57,9 +62,4 @@ watch(
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
|
||||||
<a-select mode="tags" readonly :value="modelValue" />
|
|
||||||
<div>{{ errorRef }}</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style lang="less"></style>
|
<style lang="less"></style>
|
||||||
|
|
|
@ -23,6 +23,7 @@ export default {
|
||||||
|
|
||||||
const pipeline = inject("pipeline") as Ref<any>;
|
const pipeline = inject("pipeline") as Ref<any>;
|
||||||
const currentStageIndex = inject("currentStageIndex") as Ref<number>;
|
const currentStageIndex = inject("currentStageIndex") as Ref<number>;
|
||||||
|
const currentTaskIndex = inject("currentTaskIndex") as Ref<number>;
|
||||||
const currentStepIndex = inject("currentStepIndex") as Ref<number>;
|
const currentStepIndex = inject("currentStepIndex") as Ref<number>;
|
||||||
const currentTask = inject("currentTask") as Ref<any>;
|
const currentTask = inject("currentTask") as Ref<any>;
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ export default {
|
||||||
options.value = pluginGroups.getPreStepOutputOptions({
|
options.value = pluginGroups.getPreStepOutputOptions({
|
||||||
pipeline: pipeline.value,
|
pipeline: pipeline.value,
|
||||||
currentStageIndex: currentStageIndex.value,
|
currentStageIndex: currentStageIndex.value,
|
||||||
|
currentTaskIndex: currentTaskIndex.value,
|
||||||
currentStepIndex: currentStepIndex.value,
|
currentStepIndex: currentStepIndex.value,
|
||||||
currentTask: currentTask.value
|
currentTask: currentTask.value
|
||||||
});
|
});
|
||||||
|
|
|
@ -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">
|
<script setup lang="ts">
|
||||||
import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
|
import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
|
||||||
import { ref, watch } from "vue";
|
import { ref, useAttrs, watch } from "vue";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "RemoteSelect"
|
name: "RemoteSelect"
|
||||||
|
@ -16,6 +38,8 @@ const emit = defineEmits<{
|
||||||
"update:value": any;
|
"update:value": any;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
const attrs = useAttrs();
|
||||||
|
|
||||||
const optionsRef = ref([]);
|
const optionsRef = ref([]);
|
||||||
const message = ref("");
|
const message = ref("");
|
||||||
const getOptions = async () => {
|
const getOptions = async () => {
|
||||||
|
@ -27,8 +51,8 @@ const getOptions = async () => {
|
||||||
input: props.form
|
input: props.form
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onError(err) {
|
onError(err: any) {
|
||||||
message.value = err.message;
|
message.value = `获取选项出错:${err.message}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -44,6 +68,10 @@ async function onClick() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
|
await refreshOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshOptions() {
|
||||||
optionsRef.value = await getOptions();
|
optionsRef.value = await getOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,21 +92,4 @@ watch(
|
||||||
);
|
);
|
||||||
</script>
|
</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>
|
<style lang="less"></style>
|
||||||
|
|
|
@ -14,7 +14,7 @@ export type RequestHandleReq<T = any> = {
|
||||||
input: T;
|
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 url = req.type === "access" ? "/pi/handle/access" : "/pi/handle/plugin";
|
||||||
const { typeName, action, data, input } = req;
|
const { typeName, action, data, input } = req;
|
||||||
const res = await request({
|
const res = await request({
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<contextHolder />
|
<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>
|
<template #suffix>
|
||||||
<a-tag class="cursor-pointer" @click="getDeviceId">获取设备ID</a-tag>
|
<a-tag class="cursor-pointer" @click="getDeviceId">获取设备ID</a-tag>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -164,9 +164,27 @@ function openUpgrade() {
|
||||||
okText: "激活",
|
okText: "激活",
|
||||||
width: 900,
|
width: 900,
|
||||||
content: () => {
|
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 vipLabel = settingStore.vipLabel;
|
||||||
const slots = [];
|
const slots = [];
|
||||||
for (const key in vipTypeDefine) {
|
for (const key in vipTypeDefine) {
|
||||||
|
// @ts-ignore
|
||||||
const item = vipTypeDefine[key];
|
const item = vipTypeDefine[key];
|
||||||
const vipBlockClass = `vip-block ${key === settingStore.plusInfo.vipType ? "current" : ""}`;
|
const vipBlockClass = `vip-block ${key === settingStore.plusInfo.vipType ? "current" : ""}`;
|
||||||
slots.push(
|
slots.push(
|
||||||
|
@ -174,7 +192,7 @@ function openUpgrade() {
|
||||||
<div class={vipBlockClass}>
|
<div class={vipBlockClass}>
|
||||||
<h3 class="block-header">{item.title}</h3>
|
<h3 class="block-header">{item.title}</h3>
|
||||||
<ul>
|
<ul>
|
||||||
{item.privilege.map((p) => (
|
{item.privilege.map((p: string) => (
|
||||||
<li>
|
<li>
|
||||||
<fs-icon class="color-green" icon="ion:checkmark-sharp" />
|
<fs-icon class="color-green" icon="ion:checkmark-sharp" />
|
||||||
{p}
|
{p}
|
||||||
|
@ -203,9 +221,7 @@ function openUpgrade() {
|
||||||
|
|
||||||
<div class="mt-10">
|
<div class="mt-10">
|
||||||
没有激活码?
|
没有激活码?
|
||||||
<a href="https://afdian.com/a/greper" target="_blank">
|
{activationCodeGetWay}
|
||||||
爱发电赞助“VIP会员”后获取
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -31,7 +31,14 @@
|
||||||
<!-- >-->
|
<!-- >-->
|
||||||
<!-- Button-->
|
<!-- Button-->
|
||||||
<!-- </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">
|
<div class="header-btn">
|
||||||
<fs-locale />
|
<fs-locale />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -52,7 +52,7 @@ export const certdResources = [
|
||||||
path: "/certd/cname/record",
|
path: "/certd/cname/record",
|
||||||
component: "/certd/cname/record/index.vue",
|
component: "/certd/cname/record/index.vue",
|
||||||
meta: {
|
meta: {
|
||||||
icon: "ion:disc-outline",
|
icon: "ion:link-outline",
|
||||||
auth: true
|
auth: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -73,7 +73,7 @@ export const sysResources = [
|
||||||
path: "/sys/cname/provider",
|
path: "/sys/cname/provider",
|
||||||
component: "/sys/cname/provider/index.vue",
|
component: "/sys/cname/provider/index.vue",
|
||||||
meta: {
|
meta: {
|
||||||
icon: "ion:settings-outline",
|
icon: "ion:earth-outline",
|
||||||
permission: "sys:settings:view"
|
permission: "sys:settings:view"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -35,8 +35,16 @@ export interface SettingState {
|
||||||
};
|
};
|
||||||
siteInfo: SiteInfo;
|
siteInfo: SiteInfo;
|
||||||
plusInfo?: PlusInfo;
|
plusInfo?: PlusInfo;
|
||||||
|
siteEnv?: SiteEnv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SiteEnv = {
|
||||||
|
agent?: {
|
||||||
|
enabled?: boolean;
|
||||||
|
contactText?: string;
|
||||||
|
contactLink?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
export type SiteInfo = {
|
export type SiteInfo = {
|
||||||
title: string;
|
title: string;
|
||||||
slogan: string;
|
slogan: string;
|
||||||
|
@ -94,7 +102,14 @@ export const useSettingStore = defineStore({
|
||||||
accountServerBaseUrl: "",
|
accountServerBaseUrl: "",
|
||||||
appKey: ""
|
appKey: ""
|
||||||
},
|
},
|
||||||
siteInfo: defaultSiteInfo
|
siteInfo: defaultSiteInfo,
|
||||||
|
siteEnv: {
|
||||||
|
agent: {
|
||||||
|
enabled: undefined,
|
||||||
|
contactText: "",
|
||||||
|
contactLink: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
getters: {
|
getters: {
|
||||||
getThemeConfig(): any {
|
getThemeConfig(): any {
|
||||||
|
@ -132,6 +147,7 @@ export const useSettingStore = defineStore({
|
||||||
},
|
},
|
||||||
async loadSysSettings() {
|
async loadSysSettings() {
|
||||||
await this.loadSysPublicSettings();
|
await this.loadSysPublicSettings();
|
||||||
|
await this.loadSiteEnv();
|
||||||
await this.loadInstallInfo();
|
await this.loadInstallInfo();
|
||||||
await this.loadPlusInfo();
|
await this.loadPlusInfo();
|
||||||
await this.loadSiteInfo();
|
await this.loadSiteInfo();
|
||||||
|
@ -145,12 +161,16 @@ export const useSettingStore = defineStore({
|
||||||
const installInfo = await basicApi.getInstallInfo();
|
const installInfo = await basicApi.getInstallInfo();
|
||||||
_.merge(this.installInfo, installInfo);
|
_.merge(this.installInfo, installInfo);
|
||||||
},
|
},
|
||||||
|
async loadSiteEnv() {
|
||||||
|
const siteEnv = await basicApi.getSiteEnv();
|
||||||
|
_.merge(this.siteEnv, siteEnv);
|
||||||
|
},
|
||||||
async loadPlusInfo() {
|
async loadPlusInfo() {
|
||||||
this.plusInfo = await basicApi.getPlusInfo();
|
this.plusInfo = await basicApi.getPlusInfo();
|
||||||
},
|
},
|
||||||
async loadSiteInfo() {
|
async loadSiteInfo() {
|
||||||
const isComm = this.isComm;
|
const isComm = this.isComm;
|
||||||
let siteInfo = {};
|
let siteInfo: SiteInfo;
|
||||||
if (isComm) {
|
if (isComm) {
|
||||||
siteInfo = await basicApi.getSiteInfo();
|
siteInfo = await basicApi.getSiteInfo();
|
||||||
if (siteInfo.logo) {
|
if (siteInfo.logo) {
|
||||||
|
|
|
@ -203,9 +203,12 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
}
|
}
|
||||||
|
|
||||||
.helper {
|
.helper {
|
||||||
display: inline-block;
|
|
||||||
color: #aeaeae;
|
color: #aeaeae;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|
||||||
|
&.error{
|
||||||
|
color: #ff4d4f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,10 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any) {
|
||||||
show: false
|
show: false
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 120
|
width: 200,
|
||||||
|
component: {
|
||||||
|
color: "auto"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
|
|
|
@ -51,7 +51,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
||||||
key: "id",
|
key: "id",
|
||||||
type: "number",
|
type: "number",
|
||||||
column: {
|
column: {
|
||||||
width: 50
|
width: 100
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
show: false
|
show: false
|
||||||
|
@ -65,6 +65,9 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
rules: [{ required: true, message: "必填项" }]
|
rules: [{ required: true, message: "必填项" }]
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
width: 300
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
...commonColumnsDefine
|
...commonColumnsDefine
|
||||||
|
|
|
@ -69,7 +69,7 @@ export default function (certPluginGroup: PluginGroup, formWrapperRef: any): Cre
|
||||||
return (
|
return (
|
||||||
<ul>
|
<ul>
|
||||||
<li>JS-ACME:使用简单方便,功能强大【推荐】</li>
|
<li>JS-ACME:使用简单方便,功能强大【推荐】</li>
|
||||||
<li>Lego-ACME:基于Lego实现,支持海量DNS提供商,熟悉LEGO的用户可以使用【即将废弃】</li>
|
<li>Lego-ACME:基于Lego实现,支持海量DNS提供商,熟悉LEGO的用户可以使用</li>
|
||||||
</ul>
|
</ul>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,7 @@
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<a-button type="dashed" shape="round" @click="taskAdd(stage, index)">
|
<a-button type="dashed" shape="round" @click="taskAdd(stage, index)">
|
||||||
<fs-icon class="font-20" icon="ion:add-circle-outline"></fs-icon>
|
<fs-icon class="font-20" icon="ion:add-circle-outline"></fs-icon>
|
||||||
并行任务
|
添加任务
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -75,10 +75,11 @@ export class PluginGroups {
|
||||||
return this.map[name];
|
return this.map[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
getPreStepOutputOptions({ pipeline, currentStageIndex, currentStepIndex, currentTask }: any) {
|
getPreStepOutputOptions({ pipeline, currentStageIndex, currentTaskIndex, currentStepIndex, currentTask }: any) {
|
||||||
const steps = this.collectionPreStepOutputs({
|
const steps = this.collectionPreStepOutputs({
|
||||||
pipeline,
|
pipeline,
|
||||||
currentStageIndex,
|
currentStageIndex,
|
||||||
|
currentTaskIndex,
|
||||||
currentStepIndex,
|
currentStepIndex,
|
||||||
currentTask
|
currentTask
|
||||||
});
|
});
|
||||||
|
@ -96,7 +97,7 @@ export class PluginGroups {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
collectionPreStepOutputs({ pipeline, currentStageIndex, currentStepIndex, currentTask }: any) {
|
collectionPreStepOutputs({ pipeline, currentStageIndex, currentTaskIndex, currentStepIndex, currentTask }: any) {
|
||||||
const steps: any[] = [];
|
const steps: any[] = [];
|
||||||
// 开始放step
|
// 开始放step
|
||||||
for (let i = 0; i < currentStageIndex; i++) {
|
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
|
//放当前任务下的step
|
||||||
for (let i = 0; i < currentStepIndex; i++) {
|
for (let i = 0; i < currentStepIndex; i++) {
|
||||||
const step = currentTask.steps[i];
|
const step = currentTask.steps[i];
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="d2-page-cover__sub-title">{{ siteInfo.slogan }}</p>
|
<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>
|
<a-alert type="warning" show-icon>
|
||||||
<template #description>
|
<template #description>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
|
|
|
@ -21,19 +21,19 @@ typeorm:
|
||||||
dataSource:
|
dataSource:
|
||||||
default:
|
default:
|
||||||
database: './data/db-comm.sqlite'
|
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:
|
plus:
|
||||||
server:
|
server:
|
||||||
baseUrls: ['http://127.0.0.1:11007']
|
baseUrls: ['https://api.ai.handsfree.work', 'https://api.ai.docmirror.cn']
|
||||||
|
|
||||||
account:
|
account:
|
||||||
server:
|
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'
|
||||||
|
|
|
@ -37,6 +37,12 @@ const development = {
|
||||||
'/': '/index.html',
|
'/': '/index.html',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// '/index.html': {
|
||||||
|
// maxAge: 0,
|
||||||
|
// },
|
||||||
|
// '/': {
|
||||||
|
// maxAge: 0,
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cron: {
|
cron: {
|
||||||
|
@ -109,6 +115,11 @@ const development = {
|
||||||
// 仅在匹配路径到 /api/upload 的时候去解析 body 中的文件信息
|
// 仅在匹配路径到 /api/upload 的时候去解析 body 中的文件信息
|
||||||
match: /\/api\/basic\/file\/upload/,
|
match: /\/api\/basic\/file\/upload/,
|
||||||
},
|
},
|
||||||
|
agent: {
|
||||||
|
enabled: false,
|
||||||
|
contactText: '',
|
||||||
|
contactLink: '',
|
||||||
|
},
|
||||||
} as MidwayConfig;
|
} as MidwayConfig;
|
||||||
mergeConfig(development, 'development');
|
mergeConfig(development, 'development');
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,11 @@ export class AutoInitSite {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 授权许可
|
// 授权许可
|
||||||
await this.plusService.verify();
|
try {
|
||||||
|
await this.plusService.verify();
|
||||||
|
} catch (e) {
|
||||||
|
logger.error('授权许可验证失败', e);
|
||||||
|
}
|
||||||
|
|
||||||
logger.info('初始化站点完成');
|
logger.info('初始化站点完成');
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { ALL, Body, Config, Controller, Get, Inject, Provide } from '@midwayjs/core';
|
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';
|
import { AppKey, getPlusInfo } from '@certd/pipeline';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,6 +12,9 @@ export class BasicSettingsController extends BaseController {
|
||||||
@Config('account.server.baseUrl')
|
@Config('account.server.baseUrl')
|
||||||
accountServerBaseUrl: any;
|
accountServerBaseUrl: any;
|
||||||
|
|
||||||
|
@Config('agent')
|
||||||
|
agentConfig: SysSiteEnv['agent'];
|
||||||
|
|
||||||
@Get('/public', { summary: Constants.per.guest })
|
@Get('/public', { summary: Constants.per.guest })
|
||||||
public async getSysPublic() {
|
public async getSysPublic() {
|
||||||
const settings = await this.sysSettingsService.getSetting(SysPublicSettings);
|
const settings = await this.sysSettingsService.getSetting(SysPublicSettings);
|
||||||
|
@ -32,6 +35,14 @@ export class BasicSettingsController extends BaseController {
|
||||||
return this.ok(settings);
|
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 })
|
@Get('/plusInfo', { summary: Constants.per.guest })
|
||||||
async plusInfo(@Body(ALL) body: any) {
|
async plusInfo(@Body(ALL) body: any) {
|
||||||
const info = getPlusInfo();
|
const info = getPlusInfo();
|
||||||
|
|
|
@ -62,9 +62,10 @@ export class PipelineController extends CrudController<PipelineService> {
|
||||||
|
|
||||||
@Post('/save', { summary: Constants.per.authOnly })
|
@Post('/save', { summary: Constants.per.authOnly })
|
||||||
async save(@Body(ALL) bean: PipelineEntity) {
|
async save(@Body(ALL) bean: PipelineEntity) {
|
||||||
bean.userId = this.getUserId();
|
|
||||||
if (bean.id > 0) {
|
if (bean.id > 0) {
|
||||||
await this.authService.checkEntityUserId(this.ctx, this.getService(), bean.id);
|
await this.authService.checkEntityUserId(this.ctx, this.getService(), bean.id);
|
||||||
|
} else {
|
||||||
|
bean.userId = this.getUserId();
|
||||||
}
|
}
|
||||||
await this.service.save(bean);
|
await this.service.save(bean);
|
||||||
return this.ok(bean.id);
|
return this.ok(bean.id);
|
||||||
|
|
|
@ -118,10 +118,11 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||||
const isUpdate = bean.id > 0 && old != null;
|
const isUpdate = bean.id > 0 && old != null;
|
||||||
if (!isPlus()) {
|
if (!isPlus()) {
|
||||||
let count = await this.repository.count();
|
let count = await this.repository.count();
|
||||||
if (isUpdate) {
|
if (!isUpdate) {
|
||||||
count -= 1;
|
//如果是添加要加1
|
||||||
|
count += 1;
|
||||||
}
|
}
|
||||||
if (count >= freeCount) {
|
if (count > freeCount) {
|
||||||
throw new NeedVIPException('免费版最多只能创建10个pipeline');
|
throw new NeedVIPException('免费版最多只能创建10个pipeline');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,3 +10,4 @@ export * from './plugin-west/index.js';
|
||||||
export * from './plugin-doge/index.js';
|
export * from './plugin-doge/index.js';
|
||||||
export * from './plugin-qiniu/index.js';
|
export * from './plugin-qiniu/index.js';
|
||||||
export * from './plugin-jdcloud/index.js';
|
export * from './plugin-jdcloud/index.js';
|
||||||
|
export * from './plugin-woai/index.js';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import ssh2, { ConnectConfig } from 'ssh2';
|
import ssh2, { ConnectConfig, ExecOptions } from 'ssh2';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import * as _ from 'lodash-es';
|
import * as _ from 'lodash-es';
|
||||||
import { ILogger } from '@certd/pipeline';
|
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"
|
* Set-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\System32\cmd.exe"
|
||||||
* @param options
|
* @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;
|
let { script } = options;
|
||||||
const { connectConf } = options;
|
const { connectConf } = options;
|
||||||
|
|
||||||
|
@ -278,14 +278,32 @@ export class SshClient {
|
||||||
connectConf,
|
connectConf,
|
||||||
callable: async (conn: AsyncSsh2Client) => {
|
callable: async (conn: AsyncSsh2Client) => {
|
||||||
let isWinCmd = false;
|
let isWinCmd = false;
|
||||||
|
const isLinux = !connectConf.windows;
|
||||||
|
const envScripts = [];
|
||||||
if (connectConf.windows) {
|
if (connectConf.windows) {
|
||||||
isWinCmd = await this.isCmd(conn);
|
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 (isWinCmd) {
|
||||||
//组合成&&的形式
|
//组合成&&的形式
|
||||||
if (typeof script === 'string') {
|
if (typeof script === 'string') {
|
||||||
script = script.split('\n');
|
script = script.split('\n');
|
||||||
}
|
}
|
||||||
|
script = envScripts.concat(script);
|
||||||
script = script as Array<string>;
|
script = script as Array<string>;
|
||||||
script = script.join(' && ');
|
script = script.join(' && ');
|
||||||
} else {
|
} else {
|
||||||
|
@ -293,6 +311,9 @@ export class SshClient {
|
||||||
script = script as Array<string>;
|
script = script as Array<string>;
|
||||||
script = script.join('\n');
|
script = script.join('\n');
|
||||||
}
|
}
|
||||||
|
if (envScripts.length > 0) {
|
||||||
|
script = envScripts.join('\n') + '\n' + script;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
await conn.exec(script);
|
await conn.exec(script);
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { SshClient } from '../../lib/ssh.js';
|
||||||
import { CertInfo, CertReader, CertReaderHandleContext } from '@certd/plugin-cert';
|
import { CertInfo, CertReader, CertReaderHandleContext } from '@certd/plugin-cert';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import { SshAccess } from '../../access/index.js';
|
import { SshAccess } from '../../access/index.js';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
@IsTaskPlugin({
|
@IsTaskPlugin({
|
||||||
name: 'uploadCertToHost',
|
name: 'uploadCertToHost',
|
||||||
|
@ -106,6 +107,18 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
|
||||||
})
|
})
|
||||||
script!: string;
|
script!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: '注入环境变量',
|
||||||
|
value: false,
|
||||||
|
component: {
|
||||||
|
name: 'a-switch',
|
||||||
|
vModel: 'checked',
|
||||||
|
},
|
||||||
|
helper: '是否将证书域名、路径等信息注入脚本执行环境变量中,具体的变量名称,可以运行后从日志中查看',
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
injectEnv!: string;
|
||||||
|
|
||||||
@TaskOutput({
|
@TaskOutput({
|
||||||
title: '证书保存路径',
|
title: '证书保存路径',
|
||||||
})
|
})
|
||||||
|
@ -233,10 +246,28 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin {
|
||||||
const connectConf: SshAccess = await this.accessService.getById(accessId);
|
const connectConf: SshAccess = await this.accessService.getById(accessId);
|
||||||
const sshClient = new SshClient(this.logger);
|
const sshClient = new SshClient(this.logger);
|
||||||
this.logger.info('执行脚本命令');
|
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');
|
const scripts = this.script.split('\n');
|
||||||
await sshClient.exec({
|
await sshClient.exec({
|
||||||
connectConf,
|
connectConf,
|
||||||
script: scripts,
|
script: scripts,
|
||||||
|
env,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,17 @@ export class QiniuDeployCertToCDN extends AbstractTaskPlugin {
|
||||||
@TaskInput({
|
@TaskInput({
|
||||||
title: 'CDN加速域名',
|
title: 'CDN加速域名',
|
||||||
helper: '你在七牛云上配置的CDN加速域名,比如:certd.handsfree.work',
|
helper: '你在七牛云上配置的CDN加速域名,比如:certd.handsfree.work',
|
||||||
|
component: {
|
||||||
|
name: 'a-select',
|
||||||
|
vModel: 'value',
|
||||||
|
mode: 'tags',
|
||||||
|
open: false,
|
||||||
|
tokenSeparators: [',', ' ', ',', '、', '|'],
|
||||||
|
},
|
||||||
|
rules: [{ type: 'domains' }],
|
||||||
required: true,
|
required: true,
|
||||||
})
|
})
|
||||||
domainName!: string;
|
domainName!: string | string[];
|
||||||
|
|
||||||
@TaskInput({
|
@TaskInput({
|
||||||
title: '域名证书',
|
title: '域名证书',
|
||||||
|
@ -52,7 +60,7 @@ export class QiniuDeployCertToCDN extends AbstractTaskPlugin {
|
||||||
http: this.ctx.http,
|
http: this.ctx.http,
|
||||||
access,
|
access,
|
||||||
});
|
});
|
||||||
const url = `https://api.qiniu.com/domain/${this.domainName}/httpsconf`;
|
|
||||||
let certId = null;
|
let certId = null;
|
||||||
if (typeof this.cert !== 'string') {
|
if (typeof this.cert !== 'string') {
|
||||||
// 是证书id,直接上传即可
|
// 是证书id,直接上传即可
|
||||||
|
@ -62,13 +70,17 @@ export class QiniuDeployCertToCDN extends AbstractTaskPlugin {
|
||||||
certId = this.cert;
|
certId = this.cert;
|
||||||
}
|
}
|
||||||
|
|
||||||
//开始修改证书
|
const domains: string[] = typeof this.domainName === 'string' ? [this.domainName] : this.domainName;
|
||||||
this.logger.info(`开始修改证书,certId:${certId},domain:${this.domainName}`);
|
for (const domain of domains) {
|
||||||
const body = {
|
//开始修改证书
|
||||||
certID: certId,
|
this.logger.info(`开始修改证书,certId:${certId},domain:${domain}`);
|
||||||
};
|
const body = {
|
||||||
|
certID: certId,
|
||||||
await qiniuClient.doRequest(url, 'put', body);
|
};
|
||||||
|
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('部署完成');
|
this.logger.info('部署完成');
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from './plugins/index.js';
|
||||||
|
export * from './access.js';
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './plugin-deploy-to-cdn.js';
|
|
@ -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();
|
|
@ -1,5 +1,5 @@
|
||||||
packages:
|
packages:
|
||||||
# all packages in subdirs of packages/ and components/
|
- 'packages/**' # <--------------开发插件请注释掉这一行,PR时本修改不要提交
|
||||||
- 'packages/**'
|
- 'packages/ui/**'
|
||||||
# exclude packages that are inside test directories
|
|
||||||
- '!**/test/**'
|
- '!**/test/**'
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue