mirror of
https://github.com/certd/certd.git
synced 2026-06-13 20:57:33 +08:00
Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev
This commit is contained in:
+6
-2
@@ -1,4 +1,4 @@
|
||||
./packages/core/lego
|
||||
./packages/core/lego
|
||||
# IntelliJ project files
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
@@ -34,4 +34,8 @@ test.js
|
||||
/logs
|
||||
.pnpm-lock.yaml
|
||||
pnpm-lock.yaml
|
||||
.studio/
|
||||
.studio/
|
||||
|
||||
# Certd 推广报告,仅本地使用
|
||||
/popularize/
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ version: 1.0.0
|
||||
## 实现流程
|
||||
|
||||
1. 先在 `packages/ui/certd-client/src/views` 下找 1-2 个相近 Fast Crud 页面,沿用它们的导入、布局、命名和权限写法。
|
||||
2. 在 `index.vue` 中使用 `fs-crud ref="crudRef" v-bind="crudBinding"`,并在 `onMounted` / `onActivated` 时调用 `crudExpose.doRefresh()`。
|
||||
2. 在 `index.vue` 中使用 `fs-crud ref="crudRef" v-bind="crudBinding"`,并在 `onMounted` 或 `onActivated` 时调用 `crudExpose.doRefresh()`;两个生命周期同时存在时只保留一个刷新入口,避免首次进入页面请求两次。
|
||||
3. 在 `crud.tsx` 中配置 `request.pageRequest`、`columns`、`search`、`form`、`rowHandle`、`actionbar`、`toolbar` 等,接口分页参数和返回值按现有页面适配。
|
||||
4. 操作按钮优先放在 Fast Crud 的 `rowHandle.buttons` 或 `actionbar.buttons` 中;审核、保存设置、批量操作等复杂交互可通过 `context` 调用 `index.vue` 中的方法。
|
||||
5. 金额、状态、时间、枚举等字段优先复用项目已有组件、字典和格式化工具;避免在模板里重复堆格式化逻辑。
|
||||
@@ -77,6 +77,84 @@ container:{}, //容器配置 ,对应fs-container
|
||||
- 有固定操作栏、统计区、说明区时,这些区域应 `flex: none`,把剩余空间交给表格区域。
|
||||
- 修改嵌入式 Fast Crud 页面后,要检查空数据、少量数据和多页数据时表格高度、分页器和空状态是否仍在预期区域内。
|
||||
|
||||
## 列表导出
|
||||
|
||||
- 列表需要导出时,优先使用 Fast Crud 工具栏导出能力,不要另写一套导出按钮或后端接口,除非数据必须跨权限、跨分页或异步生成文件。
|
||||
- 导出当前搜索条件下的数据时,在 `toolbar.export` 中设置 `dataFrom: "search"`,并显式打开导出按钮。
|
||||
- 导出列必须输出 Excel 可读的纯文本或数字;不要直接导出对象、数组、VNode、进度条组件、开关组件、时间戳毫秒值等。
|
||||
- 有隐藏但业务上需要导出的字段时,把字段定义为普通列并设置 `column.show: false`,再在 `columnFilter` 中对该字段返回 `true`。例如证书域名这类只用于导出的辅助列。
|
||||
- 嵌套字段可以使用 `lastVars.certDomains` 这类 key;导出格式化时用安全取值函数读取嵌套值。
|
||||
- `dataFormatter` 中统一格式化特殊字段:时间字段转 `YYYY-MM-DD HH:mm:ss`,日期类有效期转业务文案或 `YYYY-MM-DD`,枚举/开关转字典 label,数组转逗号分隔字符串,对象转明确的业务摘要。
|
||||
|
||||
```typescript
|
||||
import { ColumnProps, DataFormatterContext } from "@fast-crud/fast-crud";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
function getRecordValue(row: any, key: string) {
|
||||
return key.split(".").reduce((target, item) => target?.[item], row);
|
||||
}
|
||||
|
||||
function formatListValue(value: any) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.join(",");
|
||||
}
|
||||
return value ?? "";
|
||||
}
|
||||
|
||||
function exportColumnFilter(col: ColumnProps) {
|
||||
if (!col.key || ["_index", "_selection", "rowHandle"].includes(col.key)) {
|
||||
return false;
|
||||
}
|
||||
if (col.key === "lastVars.certDomains") {
|
||||
return true;
|
||||
}
|
||||
return col.show !== false;
|
||||
}
|
||||
|
||||
function exportDataFormatter(opts: DataFormatterContext) {
|
||||
const { row, originalRow, col, exportCol } = opts;
|
||||
const key = col.key;
|
||||
const value = getRecordValue(originalRow, key);
|
||||
|
||||
if (key === "lastVars.certDomains") {
|
||||
row[key] = formatListValue(value);
|
||||
} else if (key.includes("Time") && value) {
|
||||
row[key] = dayjs(value).format("YYYY-MM-DD HH:mm:ss");
|
||||
}
|
||||
|
||||
if (col.width) {
|
||||
exportCol.width = col.width / 10;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
toolbar: {
|
||||
buttons: {
|
||||
export: { show: true },
|
||||
},
|
||||
export: {
|
||||
dataFrom: "search",
|
||||
columnFilter: exportColumnFilter,
|
||||
dataFormatter: exportDataFormatter,
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
"lastVars.certDomains": {
|
||||
title: "证书域名",
|
||||
type: "text",
|
||||
column: {
|
||||
show: false,
|
||||
width: 260,
|
||||
ellipsis: true,
|
||||
},
|
||||
form: { show: false },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## 内置 CRUD 按钮
|
||||
|
||||
只要在 `request` 中配置了 `addRequest`、`editRequest`、`delRequest`,Fast Crud 会自动在 `rowHandle` 渲染新增、编辑、删除按钮并完成对应操作,**不需要手写 `openDeleteConfirm`、`openEditDialog` 等方法**。
|
||||
|
||||
+10
-1
@@ -1,9 +1,18 @@
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 流水线、监控站点支持导出 ([99fd308](https://github.com/certd/certd/commit/99fd3083f259cdb96fd656f04858dd708d1251c7))
|
||||
* 优化列表页面请求两次的问题 ([5546af5](https://github.com/certd/certd/commit/5546af518e92c765513787ccaf8e856be789bcf9))
|
||||
* 优化邀请注册流程 ([7a71e45](https://github.com/certd/certd/commit/7a71e45799d782d0691606fb42b4236f1d3009b0))
|
||||
* **settings:** 新增NO_PROXY代理排除配置 ([c0df8be](https://github.com/certd/certd/commit/c0df8be83237e323c2c9a5bd02507430a86a00cc))
|
||||
* **volcengine-vke:** 火山VKE集群证书支持两种类型的证书保密字典 ([77b8024](https://github.com/certd/certd/commit/77b802445322d576d54d194f7c505da49e0e824c))
|
||||
|
||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 流水线、监控站点支持导出 ([99fd308](https://github.com/certd/certd/commit/99fd3083f259cdb96fd656f04858dd708d1251c7))
|
||||
* 优化列表页面请求两次的问题 ([5546af5](https://github.com/certd/certd/commit/5546af518e92c765513787ccaf8e856be789bcf9))
|
||||
* 优化邀请注册流程 ([7a71e45](https://github.com/certd/certd/commit/7a71e45799d782d0691606fb42b4236f1d3009b0))
|
||||
* **settings:** 新增NO_PROXY代理排除配置 ([c0df8be](https://github.com/certd/certd/commit/c0df8be83237e323c2c9a5bd02507430a86a00cc))
|
||||
* **volcengine-vke:** 火山VKE集群证书支持两种类型的证书保密字典 ([77b8024](https://github.com/certd/certd/commit/77b802445322d576d54d194f7c505da49e0e824c))
|
||||
|
||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
+1
-1
@@ -9,5 +9,5 @@
|
||||
}
|
||||
},
|
||||
"npmClient": "pnpm",
|
||||
"version": "1.41.0"
|
||||
"version": "1.41.1"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.41.1](https://github.com/publishlab/node-acme-client/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
# [1.41.0](https://github.com/publishlab/node-acme-client/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "Simple and unopinionated ACME client",
|
||||
"private": false,
|
||||
"author": "nmorsman",
|
||||
"version": "1.41.0",
|
||||
"version": "1.41.1",
|
||||
"type": "module",
|
||||
"module": "./dist/index.js",
|
||||
"main": "./dist/index.js",
|
||||
@@ -18,7 +18,7 @@
|
||||
"types"
|
||||
],
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.41.0",
|
||||
"@certd/basic": "^1.41.1",
|
||||
"@peculiar/x509": "^1.11.0",
|
||||
"asn1js": "^3.0.5",
|
||||
"axios": "^1.9.0",
|
||||
@@ -76,5 +76,5 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||
},
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **settings:** 新增NO_PROXY代理排除配置 ([c0df8be](https://github.com/certd/certd/commit/c0df8be83237e323c2c9a5bd02507430a86a00cc))
|
||||
|
||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
@@ -1 +1 @@
|
||||
12:22
|
||||
02:33
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/basic",
|
||||
"private": false,
|
||||
"version": "1.41.0",
|
||||
"version": "1.41.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -52,5 +52,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { expect } from "chai";
|
||||
import { createAxiosService, HttpClient, setGlobalHeaders } from "./util.request.js";
|
||||
import { createAgent, createAxiosService, getGlobalAgents, HttpClient, isNoProxyMatched, setGlobalHeaders, setGlobalProxy } from "./util.request.js";
|
||||
import { ILogger } from "./util.log.js";
|
||||
|
||||
const testLogger = {
|
||||
debug() {},
|
||||
info() {},
|
||||
error() {},
|
||||
} as unknown as ILogger;
|
||||
@@ -10,6 +11,9 @@ const testLogger = {
|
||||
describe("util.request", () => {
|
||||
afterEach(() => {
|
||||
setGlobalHeaders({});
|
||||
setGlobalProxy({});
|
||||
delete process.env.NO_PROXY;
|
||||
delete process.env.no_proxy;
|
||||
});
|
||||
|
||||
it("should merge global headers without overriding request headers", async () => {
|
||||
@@ -50,4 +54,122 @@ describe("util.request", () => {
|
||||
request: "request",
|
||||
});
|
||||
});
|
||||
|
||||
it("should set no_proxy environment variables", () => {
|
||||
setGlobalProxy({
|
||||
httpProxy: "http://127.0.0.1:1080",
|
||||
httpsProxy: "http://127.0.0.1:1080",
|
||||
noProxy: "localhost,*.internal.example.com",
|
||||
});
|
||||
|
||||
expect(process.env.NO_PROXY).to.equal("localhost,*.internal.example.com");
|
||||
expect(process.env.no_proxy).to.equal("localhost,*.internal.example.com");
|
||||
});
|
||||
|
||||
it("should normalize multiline no_proxy environment variables", () => {
|
||||
setGlobalProxy({
|
||||
noProxy: "localhost\n127.0.0.1, 192.168.*\n*.internal.example.com",
|
||||
});
|
||||
|
||||
expect(process.env.NO_PROXY).to.equal("localhost,127.0.0.1,192.168.*,*.internal.example.com");
|
||||
expect(process.env.no_proxy).to.equal("localhost,127.0.0.1,192.168.*,*.internal.example.com");
|
||||
});
|
||||
|
||||
it("should not change environment variables when creating agents", () => {
|
||||
process.env.HTTP_PROXY = "http://old-http-proxy";
|
||||
process.env.HTTPS_PROXY = "http://old-https-proxy";
|
||||
process.env.NO_PROXY = "old.local";
|
||||
|
||||
createAgent({
|
||||
httpProxy: "http://127.0.0.1:1080",
|
||||
httpsProxy: "http://127.0.0.1:1081",
|
||||
});
|
||||
|
||||
expect(process.env.HTTP_PROXY).to.equal("http://old-http-proxy");
|
||||
expect(process.env.HTTPS_PROXY).to.equal("http://old-https-proxy");
|
||||
expect(process.env.NO_PROXY).to.equal("old.local");
|
||||
});
|
||||
|
||||
it("should bypass global proxy when request host matches no_proxy", async () => {
|
||||
setGlobalProxy({
|
||||
httpProxy: "http://127.0.0.1:1080",
|
||||
httpsProxy: "http://127.0.0.1:1080",
|
||||
noProxy: "localhost,.internal.example.com",
|
||||
});
|
||||
|
||||
const globalAgents = getGlobalAgents();
|
||||
const http = createAxiosService({ logger: testLogger }) as HttpClient;
|
||||
const res = await http.request({
|
||||
url: "https://api.internal.example.com",
|
||||
method: "get",
|
||||
logReq: false,
|
||||
logRes: false,
|
||||
adapter: async config => {
|
||||
return {
|
||||
config,
|
||||
data: {
|
||||
usesGlobalHttpAgent: config.httpAgent === globalAgents.httpAgent,
|
||||
usesGlobalHttpsAgent: config.httpsAgent === globalAgents.httpsAgent,
|
||||
},
|
||||
headers: {},
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
expect(res).to.deep.equal({
|
||||
usesGlobalHttpAgent: false,
|
||||
usesGlobalHttpsAgent: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("should bypass custom request proxy when request host matches no_proxy", async () => {
|
||||
setGlobalProxy({
|
||||
noProxy: ".internal.example.com",
|
||||
});
|
||||
|
||||
const http = createAxiosService({ logger: testLogger }) as HttpClient;
|
||||
const res = await http.request({
|
||||
url: "https://api.internal.example.com",
|
||||
method: "get",
|
||||
httpProxy: "http://127.0.0.1:1080",
|
||||
logReq: false,
|
||||
logRes: false,
|
||||
adapter: async config => {
|
||||
return {
|
||||
config,
|
||||
data: {
|
||||
httpAgent: config.httpAgent?.constructor?.name,
|
||||
httpsAgent: config.httpsAgent?.constructor?.name,
|
||||
},
|
||||
headers: {},
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
expect(res).to.deep.equal({
|
||||
httpAgent: "Agent",
|
||||
httpsAgent: "Agent",
|
||||
});
|
||||
});
|
||||
|
||||
it("should match no_proxy rules", () => {
|
||||
expect(isNoProxyMatched("*", { hostname: "api.example.com", port: "" })).to.equal(true);
|
||||
expect(isNoProxyMatched("api.example.com", { hostname: "api.example.com", port: "" })).to.equal(true);
|
||||
expect(isNoProxyMatched("example.com", { hostname: "api.example.com", port: "" })).to.equal(true);
|
||||
expect(isNoProxyMatched(".example.com", { hostname: "api.example.com", port: "" })).to.equal(true);
|
||||
expect(isNoProxyMatched("*.example.com", { hostname: "api.example.com", port: "" })).to.equal(true);
|
||||
expect(isNoProxyMatched("127.0.0.1", { hostname: "127.0.0.1", port: "" })).to.equal(true);
|
||||
expect(isNoProxyMatched("192.168.*", { hostname: "192.168.1.10", port: "" })).to.equal(true);
|
||||
expect(isNoProxyMatched("192.168.*", { hostname: "192.169.1.10", port: "" })).to.equal(false);
|
||||
expect(isNoProxyMatched("[::1]", { hostname: "::1", port: "" })).to.equal(true);
|
||||
expect(isNoProxyMatched("[::1]:8443", { hostname: "::1", port: "8443" })).to.equal(true);
|
||||
expect(isNoProxyMatched("api.example.com:8443", { hostname: "api.example.com", port: "8443" })).to.equal(true);
|
||||
expect(isNoProxyMatched("api.example.com:8443", { hostname: "api.example.com", port: "443" })).to.equal(false);
|
||||
expect(isNoProxyMatched("127.0.0.1", { hostname: "127.0.0.2", port: "" })).to.equal(false);
|
||||
expect(isNoProxyMatched(".example.com", { hostname: "example.org", port: "" })).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -82,11 +82,24 @@ export class HttpError extends Error {
|
||||
export const HttpCommonError = HttpError;
|
||||
|
||||
let defaultAgents = createAgent();
|
||||
const directAgents = createAgent();
|
||||
let defaultProxyOptions: GlobalProxyOptions = {};
|
||||
let defaultHeaders: Record<string, string> = {};
|
||||
|
||||
export function setGlobalProxy(opts: { httpProxy?: string; httpsProxy?: string }) {
|
||||
export type GlobalProxyOptions = {
|
||||
httpProxy?: string;
|
||||
httpsProxy?: string;
|
||||
noProxy?: string;
|
||||
};
|
||||
|
||||
export function setGlobalProxy(opts: GlobalProxyOptions) {
|
||||
logger.info("setGlobalProxy:", opts);
|
||||
defaultAgents = createAgent(opts);
|
||||
defaultProxyOptions = { ...opts };
|
||||
defaultAgents = createAgent({
|
||||
httpProxy: opts.httpProxy,
|
||||
httpsProxy: opts.httpsProxy,
|
||||
});
|
||||
setProxyEnvironment(opts);
|
||||
}
|
||||
|
||||
export function getGlobalAgents() {
|
||||
@@ -137,21 +150,25 @@ export function createAxiosService({ logger }: { logger: ILogger }) {
|
||||
if (config.timeout == null) {
|
||||
config.timeout = 15000;
|
||||
}
|
||||
let agents = defaultAgents;
|
||||
if (config.skipSslVerify || config.httpProxy) {
|
||||
let rejectUnauthorized = true;
|
||||
const bypassProxy = shouldBypassProxy(config, defaultProxyOptions.noProxy);
|
||||
const useCustomProxy = !!config.httpProxy && !bypassProxy;
|
||||
let agents = bypassProxy ? directAgents : defaultAgents;
|
||||
if (bypassProxy) {
|
||||
logger.info("命中no_proxy配置,跳过代理:", config.url);
|
||||
}
|
||||
if (config.skipSslVerify || useCustomProxy) {
|
||||
const agentOptions: any = {};
|
||||
if (config.skipSslVerify) {
|
||||
logger.info("忽略接口请求的SSL校验");
|
||||
rejectUnauthorized = false;
|
||||
agentOptions.rejectUnauthorized = false;
|
||||
}
|
||||
const proxy: any = {};
|
||||
if (config.httpProxy) {
|
||||
if (useCustomProxy) {
|
||||
logger.info("使用自定义http代理:", config.httpProxy);
|
||||
proxy.httpProxy = config.httpProxy;
|
||||
proxy.httpsProxy = config.httpProxy;
|
||||
agentOptions.httpProxy = config.httpProxy;
|
||||
agentOptions.httpsProxy = config.httpProxy;
|
||||
}
|
||||
|
||||
agents = createAgent({ rejectUnauthorized, ...proxy } as any);
|
||||
agents = createAgent(agentOptions);
|
||||
}
|
||||
|
||||
delete config.skipSslVerify;
|
||||
@@ -354,7 +371,7 @@ export type CreateAgentOptions = {
|
||||
httpsProxy?: string;
|
||||
} & nodeHttp.AgentOptions;
|
||||
export function createAgent(opts: CreateAgentOptions = {}) {
|
||||
opts = merge(
|
||||
const { httpProxy, httpsProxy, ...agentOptions } = merge(
|
||||
{
|
||||
autoSelectFamily: true,
|
||||
autoSelectFamilyAttemptTimeout: 1000,
|
||||
@@ -364,29 +381,19 @@ export function createAgent(opts: CreateAgentOptions = {}) {
|
||||
);
|
||||
|
||||
let httpAgent, httpsAgent;
|
||||
const httpProxy = opts.httpProxy;
|
||||
if (httpProxy) {
|
||||
process.env.HTTP_PROXY = httpProxy;
|
||||
process.env.http_proxy = httpProxy;
|
||||
logger.info("use httpProxy:", httpProxy);
|
||||
httpAgent = new HttpProxyAgent(httpProxy, opts as any);
|
||||
merge(httpAgent.options, opts);
|
||||
httpAgent = new HttpProxyAgent(httpProxy, agentOptions as any);
|
||||
merge(httpAgent.options, agentOptions);
|
||||
} else {
|
||||
process.env.HTTP_PROXY = "";
|
||||
process.env.http_proxy = "";
|
||||
httpAgent = new nodeHttp.Agent(opts);
|
||||
httpAgent = new nodeHttp.Agent(agentOptions);
|
||||
}
|
||||
const httpsProxy = opts.httpsProxy;
|
||||
if (httpsProxy) {
|
||||
process.env.HTTPS_PROXY = httpsProxy;
|
||||
process.env.https_proxy = httpsProxy;
|
||||
logger.info("use httpsProxy:", httpsProxy);
|
||||
httpsAgent = new HttpsProxyAgent(httpsProxy, opts as any);
|
||||
merge(httpsAgent.options, opts);
|
||||
httpsAgent = new HttpsProxyAgent(httpsProxy, agentOptions as any);
|
||||
merge(httpsAgent.options, agentOptions);
|
||||
} else {
|
||||
process.env.HTTPS_PROXY = "";
|
||||
process.env.https_proxy = "";
|
||||
httpsAgent = new https.Agent(opts);
|
||||
httpsAgent = new https.Agent(agentOptions);
|
||||
}
|
||||
return {
|
||||
httpAgent,
|
||||
@@ -394,6 +401,145 @@ export function createAgent(opts: CreateAgentOptions = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
function setProxyEnvironment(opts: GlobalProxyOptions = {}) {
|
||||
setEnvValue("HTTP_PROXY", opts.httpProxy);
|
||||
setEnvValue("http_proxy", opts.httpProxy);
|
||||
setEnvValue("HTTPS_PROXY", opts.httpsProxy);
|
||||
setEnvValue("https_proxy", opts.httpsProxy);
|
||||
const noProxy = normalizeNoProxyText(opts.noProxy);
|
||||
setEnvValue("NO_PROXY", noProxy);
|
||||
setEnvValue("no_proxy", noProxy);
|
||||
}
|
||||
|
||||
function setEnvValue(key: string, value?: string) {
|
||||
process.env[key] = value || "";
|
||||
}
|
||||
|
||||
function shouldBypassProxy(config: AxiosRequestConfig, noProxy?: string) {
|
||||
if (!noProxy) {
|
||||
return false;
|
||||
}
|
||||
const target = getRequestTarget(config);
|
||||
if (!target) {
|
||||
return false;
|
||||
}
|
||||
return splitNoProxyRules(noProxy).some(item => isNoProxyMatched(item, target));
|
||||
}
|
||||
|
||||
function getRequestTarget(config: AxiosRequestConfig) {
|
||||
try {
|
||||
const baseURL = config.baseURL || undefined;
|
||||
const url = new URL(config.url || "", baseURL);
|
||||
return {
|
||||
hostname: normalizeHost(url.hostname),
|
||||
port: url.port,
|
||||
};
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function isNoProxyMatched(rule: string, target: { hostname: string; port: string }) {
|
||||
if (rule === "*") {
|
||||
return true;
|
||||
}
|
||||
|
||||
const normalizedRule = normalizeNoProxyRule(rule);
|
||||
if (!normalizedRule.host) {
|
||||
return false;
|
||||
}
|
||||
if (normalizedRule.port && normalizedRule.port !== target.port) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const host = normalizeHost(target.hostname);
|
||||
if (normalizedRule.host.includes("*")) {
|
||||
return wildcardHostMatched(normalizedRule.host, host);
|
||||
}
|
||||
if (normalizedRule.host.startsWith("*.")) {
|
||||
const suffix = normalizedRule.host.substring(1);
|
||||
return host.endsWith(suffix);
|
||||
}
|
||||
if (normalizedRule.host.startsWith(".")) {
|
||||
return host === normalizedRule.host.substring(1) || host.endsWith(normalizedRule.host);
|
||||
}
|
||||
return host === normalizedRule.host || host.endsWith(`.${normalizedRule.host}`);
|
||||
}
|
||||
|
||||
function normalizeNoProxyRule(rule: string) {
|
||||
let value = rule.trim().toLowerCase();
|
||||
if (value.includes("://")) {
|
||||
try {
|
||||
const url = new URL(value);
|
||||
return {
|
||||
host: normalizeHost(url.hostname),
|
||||
port: url.port,
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
host: "",
|
||||
port: "",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let port = "";
|
||||
if (value.startsWith("[")) {
|
||||
const closeIndex = value.indexOf("]");
|
||||
const host = value.substring(1, closeIndex);
|
||||
const rest = value.substring(closeIndex + 1);
|
||||
if (rest.startsWith(":")) {
|
||||
port = rest.substring(1);
|
||||
}
|
||||
return {
|
||||
host: normalizeHost(host),
|
||||
port,
|
||||
};
|
||||
}
|
||||
|
||||
const colonCount = (value.match(/:/g) || []).length;
|
||||
const portIndex = value.lastIndexOf(":");
|
||||
if (colonCount === 1 && portIndex > -1) {
|
||||
port = value.substring(portIndex + 1);
|
||||
value = value.substring(0, portIndex);
|
||||
}
|
||||
return {
|
||||
host: normalizeHost(value),
|
||||
port,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeHost(host: string) {
|
||||
let value = host.trim().toLowerCase();
|
||||
if (value.startsWith("[") && value.endsWith("]")) {
|
||||
value = value.substring(1, value.length - 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function wildcardHostMatched(rule: string, host: string) {
|
||||
const pattern = rule.split("*").map(escapeRegExp).join(".*");
|
||||
return new RegExp(`^${pattern}$`).test(host);
|
||||
}
|
||||
|
||||
function escapeRegExp(value: string) {
|
||||
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
function normalizeNoProxyText(noProxy?: string) {
|
||||
return splitNoProxyRules(noProxy).join(",");
|
||||
}
|
||||
|
||||
function splitNoProxyRules(noProxy?: string) {
|
||||
if (!noProxy) {
|
||||
return [];
|
||||
}
|
||||
return noProxy
|
||||
.split(/[,\s]+/)
|
||||
.map(item => item.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
export async function download(req: { http: HttpClient; config: HttpRequestConfig; savePath: string; logger: ILogger }) {
|
||||
const { http, config, savePath, logger } = req;
|
||||
return safePromise((resolve, reject) => {
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/pipeline",
|
||||
"private": false,
|
||||
"version": "1.41.0",
|
||||
"version": "1.41.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -19,8 +19,8 @@
|
||||
"compile": "tsc --skipLibCheck --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.41.0",
|
||||
"@certd/plus-core": "^1.41.0",
|
||||
"@certd/basic": "^1.41.1",
|
||||
"@certd/plus-core": "^1.41.1",
|
||||
"dayjs": "^1.11.7",
|
||||
"lodash-es": "^4.17.21",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
@@ -49,5 +49,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-huawei",
|
||||
"private": false,
|
||||
"version": "1.41.0",
|
||||
"version": "1.41.1",
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/bundle.js",
|
||||
"types": "./dist/d/index.d.ts",
|
||||
@@ -27,5 +27,5 @@
|
||||
"prettier": "^2.8.8",
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-iframe",
|
||||
"private": false,
|
||||
"version": "1.41.0",
|
||||
"version": "1.41.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -34,5 +34,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/jdcloud",
|
||||
"version": "1.41.0",
|
||||
"version": "1.41.1",
|
||||
"description": "jdcloud openApi sdk",
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/bundle.js",
|
||||
@@ -59,5 +59,5 @@
|
||||
"fetch"
|
||||
]
|
||||
},
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-k8s",
|
||||
"private": false,
|
||||
"version": "1.41.0",
|
||||
"version": "1.41.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -19,7 +19,7 @@
|
||||
"compile": "tsc --skipLibCheck --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.41.0",
|
||||
"@certd/basic": "^1.41.1",
|
||||
"@kubernetes/client-node": "0.21.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -36,5 +36,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **settings:** 新增NO_PROXY代理排除配置 ([c0df8be](https://github.com/certd/certd/commit/c0df8be83237e323c2c9a5bd02507430a86a00cc))
|
||||
|
||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/lib-server",
|
||||
"version": "1.41.0",
|
||||
"version": "1.41.1",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -29,11 +29,11 @@
|
||||
],
|
||||
"license": "AGPL",
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.41.0",
|
||||
"@certd/basic": "^1.41.0",
|
||||
"@certd/pipeline": "^1.41.0",
|
||||
"@certd/plugin-lib": "^1.41.0",
|
||||
"@certd/plus-core": "^1.41.0",
|
||||
"@certd/acme-client": "^1.41.1",
|
||||
"@certd/basic": "^1.41.1",
|
||||
"@certd/pipeline": "^1.41.1",
|
||||
"@certd/plugin-lib": "^1.41.1",
|
||||
"@certd/plus-core": "^1.41.1",
|
||||
"@midwayjs/cache": "3.14.0",
|
||||
"@midwayjs/core": "3.20.11",
|
||||
"@midwayjs/i18n": "3.20.13",
|
||||
@@ -69,5 +69,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ export class SysPrivateSettings extends BaseSettings {
|
||||
|
||||
httpsProxy? = '';
|
||||
httpProxy? = '';
|
||||
noProxy? = '';
|
||||
commonHeaders?: string = '';
|
||||
|
||||
reverseProxies?: Record<string, string> = {};
|
||||
|
||||
@@ -165,6 +165,7 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
|
||||
const opts = {
|
||||
httpProxy: privateSetting.httpProxy,
|
||||
httpsProxy: privateSetting.httpsProxy,
|
||||
noProxy: privateSetting.noProxy,
|
||||
};
|
||||
setGlobalProxy(opts);
|
||||
setGlobalHeaders(this.parseKeyValueText(privateSetting.commonHeaders));
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/midway-flyway-js",
|
||||
"version": "1.41.0",
|
||||
"version": "1.41.1",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -49,5 +49,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-cert",
|
||||
"private": false,
|
||||
"version": "1.41.0",
|
||||
"version": "1.41.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -18,10 +18,10 @@
|
||||
"compile": "tsc --skipLibCheck --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.41.0",
|
||||
"@certd/basic": "^1.41.0",
|
||||
"@certd/pipeline": "^1.41.0",
|
||||
"@certd/plugin-lib": "^1.41.0",
|
||||
"@certd/acme-client": "^1.41.1",
|
||||
"@certd/basic": "^1.41.1",
|
||||
"@certd/pipeline": "^1.41.1",
|
||||
"@certd/plugin-lib": "^1.41.1",
|
||||
"psl": "^1.9.0",
|
||||
"punycode.js": "^2.3.1"
|
||||
},
|
||||
@@ -41,5 +41,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-lib",
|
||||
"private": false,
|
||||
"version": "1.41.0",
|
||||
"version": "1.41.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -23,10 +23,10 @@
|
||||
"@alicloud/pop-core": "^1.7.10",
|
||||
"@alicloud/tea-util": "^1.4.11",
|
||||
"@aws-sdk/client-s3": "^3.964.0",
|
||||
"@certd/acme-client": "^1.41.0",
|
||||
"@certd/basic": "^1.41.0",
|
||||
"@certd/pipeline": "^1.41.0",
|
||||
"@certd/plus-core": "^1.41.0",
|
||||
"@certd/acme-client": "^1.41.1",
|
||||
"@certd/basic": "^1.41.1",
|
||||
"@certd/pipeline": "^1.41.1",
|
||||
"@certd/plus-core": "^1.41.1",
|
||||
"@kubernetes/client-node": "0.21.0",
|
||||
"ali-oss": "^6.22.0",
|
||||
"basic-ftp": "^5.0.5",
|
||||
@@ -61,5 +61,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c"
|
||||
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,15 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 流水线、监控站点支持导出 ([99fd308](https://github.com/certd/certd/commit/99fd3083f259cdb96fd656f04858dd708d1251c7))
|
||||
* 优化列表页面请求两次的问题 ([5546af5](https://github.com/certd/certd/commit/5546af518e92c765513787ccaf8e856be789bcf9))
|
||||
* 优化邀请注册流程 ([7a71e45](https://github.com/certd/certd/commit/7a71e45799d782d0691606fb42b4236f1d3009b0))
|
||||
* **settings:** 新增NO_PROXY代理排除配置 ([c0df8be](https://github.com/certd/certd/commit/c0df8be83237e323c2c9a5bd02507430a86a00cc))
|
||||
|
||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-client",
|
||||
"version": "1.41.0",
|
||||
"version": "1.41.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --open",
|
||||
@@ -106,8 +106,8 @@
|
||||
"zod-defaults": "^0.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/lib-iframe": "^1.41.0",
|
||||
"@certd/pipeline": "^1.41.0",
|
||||
"@certd/lib-iframe": "^1.41.1",
|
||||
"@certd/pipeline": "^1.41.1",
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@types/chai": "^4.3.12",
|
||||
|
||||
@@ -82,6 +82,7 @@ export default {
|
||||
pipelineContent: "Pipeline Content",
|
||||
scheduledTaskCount: "Scheduled Task Count",
|
||||
deployTaskCount: "Deployment Task Count",
|
||||
certDomains: "Certificate Domains",
|
||||
remainingValidity: "Remaining Validity",
|
||||
effectiveTime: "Effective time",
|
||||
expiryTime: "Expiry Time",
|
||||
|
||||
@@ -41,6 +41,7 @@ export default {
|
||||
pi: {
|
||||
validTime: "Piepline Valid Time",
|
||||
validTimeHelper: "Not filled in means permanent validity",
|
||||
permanentValid: "Permanent",
|
||||
},
|
||||
types: {
|
||||
certApply: "Cert Apply",
|
||||
|
||||
@@ -111,6 +111,9 @@ export default {
|
||||
httpsProxyPlaceholder: "http://192.168.1.2:18010/",
|
||||
saveThenTestTitle: "Save first, then click test",
|
||||
httpsProxyHelper: "Usually both proxies are the same, save first then test",
|
||||
noProxy: "Proxy Bypass",
|
||||
noProxyPlaceholder: "localhost,127.0.0.1,.example.com,192.168.*",
|
||||
noProxyHelper: "Configure NO_PROXY. Separate entries with commas, spaces, or line breaks; matched requests bypass the proxy. \nExample: localhost,127.0.0.1,.example.com,192.168.*",
|
||||
dualStackNetwork: "Dual Stack Network",
|
||||
ipv4Priority: "IPv4 Priority",
|
||||
ipv6Priority: "IPv6 Priority",
|
||||
|
||||
@@ -86,6 +86,7 @@ export default {
|
||||
pipelineContent: "流水线内容",
|
||||
scheduledTaskCount: "定时任务数",
|
||||
deployTaskCount: "部署任务数",
|
||||
certDomains: "证书域名",
|
||||
remainingValidity: "到期剩余",
|
||||
effectiveTime: "生效时间",
|
||||
expiryTime: "过期时间",
|
||||
|
||||
@@ -41,6 +41,7 @@ export default {
|
||||
pi: {
|
||||
validTime: "流水线有效期",
|
||||
validTimeHelper: "不填则为永久有效",
|
||||
permanentValid: "永久有效",
|
||||
},
|
||||
types: {
|
||||
certApply: "证书申请",
|
||||
|
||||
@@ -108,6 +108,9 @@ export default {
|
||||
httpsProxyPlaceholder: "http://192.168.1.2:18010/",
|
||||
saveThenTestTitle: "保存后,再点击测试",
|
||||
httpsProxyHelper: "一般这两个代理填一样的,保存后再测试",
|
||||
noProxy: "代理排除",
|
||||
noProxyPlaceholder: "localhost,127.0.0.1,.example.com,192.168.*",
|
||||
noProxyHelper: "配置NO_PROXY,多个地址可用英文逗号、空格或换行分隔,命中的请求将不走代理\n例如:localhost,127.0.0.1,.example.com,192.168.*",
|
||||
dualStackNetwork: "双栈网络",
|
||||
ipv4Priority: "IPV4优先",
|
||||
ipv6Priority: "IPV6优先",
|
||||
|
||||
@@ -82,6 +82,7 @@ export const certdResources = [
|
||||
isMenu: true,
|
||||
icon: "ion:duplicate-outline",
|
||||
auth: true,
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -282,6 +283,7 @@ export const certdResources = [
|
||||
meta: {
|
||||
icon: "ion:barcode-outline",
|
||||
auth: true,
|
||||
keepAlive: true,
|
||||
isMenu: true,
|
||||
},
|
||||
},
|
||||
@@ -350,6 +352,7 @@ export const certdResources = [
|
||||
},
|
||||
icon: "ion:gift-outline",
|
||||
auth: true,
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -112,7 +112,7 @@ export const sysResources = [
|
||||
},
|
||||
{
|
||||
title: "certd.sysResources.headerMenus",
|
||||
name: "HeaderMenus",
|
||||
name: "SettingsHeaderMenus",
|
||||
path: "/sys/settings/header-menus",
|
||||
component: "/sys/settings/header-menus/index.vue",
|
||||
meta: {
|
||||
@@ -128,7 +128,7 @@ export const sysResources = [
|
||||
},
|
||||
{
|
||||
title: "certd.sysResources.sysAccess",
|
||||
name: "SysAccess",
|
||||
name: "SysAccessManager",
|
||||
path: "/sys/access",
|
||||
component: "/sys/access/index.vue",
|
||||
meta: {
|
||||
@@ -311,7 +311,7 @@ export const sysResources = [
|
||||
},
|
||||
icon: "ion:bag-check",
|
||||
permission: "sys:settings:edit",
|
||||
keepAlive: true,
|
||||
keepAlive: false,
|
||||
auth: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -105,6 +105,7 @@ export type InviteSetting = {
|
||||
export type SysPrivateSetting = {
|
||||
httpProxy?: string;
|
||||
httpsProxy?: string;
|
||||
noProxy?: string;
|
||||
commonHeaders?: string;
|
||||
reverseProxies?: any;
|
||||
dnsResultOrder?: string;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { onActivated, onMounted } from "vue";
|
||||
|
||||
/**
|
||||
* 可靠的页面刷新钩子:
|
||||
* - 如果组件实际被 KeepAlive 缓存命中,由 onActivated 触发 init;
|
||||
* - 如果没有被缓存,由 onMounted 兜底触发,避免不刷新也不触发 onActivated。
|
||||
*/
|
||||
export function useMounted(init: () => void | Promise<void>) {
|
||||
let activated = false;
|
||||
|
||||
onActivated(() => {
|
||||
activated = true;
|
||||
init();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
// 让 onActivated 有机会先执行;组件未被 KeepAlive 缓存时 onActivated 不会触发,由这里兜底。
|
||||
setTimeout(() => {
|
||||
if (!activated) {
|
||||
init();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -11,7 +11,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onActivated, onMounted } from "vue";
|
||||
import { defineComponent } from "vue";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { createAccessApi } from "/@/views/certd/access/api";
|
||||
@@ -23,14 +24,7 @@ export default defineComponent({
|
||||
const { t } = useI18n();
|
||||
const api = createAccessApi("user");
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { api, permission: { isProjectPermission: true } } });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onActivated, onMounted } from "vue";
|
||||
import { defineComponent } from "vue";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { createAddonApi } from "./api";
|
||||
@@ -26,14 +27,7 @@ export default defineComponent({
|
||||
createCrudOptions,
|
||||
context: { api, permission: { isProjectPermission: true } },
|
||||
});
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onActivated, onMounted } from "vue";
|
||||
import { defineComponent } from "vue";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
|
||||
@@ -24,14 +25,7 @@ export default defineComponent({
|
||||
permission: { isProjectPermission: true },
|
||||
},
|
||||
});
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
|
||||
@@ -109,7 +109,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
||||
},
|
||||
rowHandle: {
|
||||
fixed: "right",
|
||||
width: 120,
|
||||
width: 200,
|
||||
buttons: {
|
||||
edit: {
|
||||
click: ({ row }) => openForm(row),
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onActivated, onMounted } from "vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
|
||||
defineOptions({
|
||||
name: "CertApplyTemplate",
|
||||
@@ -25,12 +25,5 @@ const { crudBinding, crudRef, crudExpose } = useFs({
|
||||
permission: { isProjectPermission: true },
|
||||
},
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
</script>
|
||||
|
||||
@@ -25,7 +25,7 @@ const context: any = {
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
|
||||
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
// crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
|
||||
@@ -62,7 +62,7 @@ const handleBatchDelete = () => {
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
// crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
|
||||
@@ -62,7 +62,7 @@ const handleBatchDelete = () => {
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
// crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onActivated, onMounted } from "vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import { DeleteBatch } from "./api";
|
||||
import { useI18n } from "/src/locales";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useCrudPermission } from "/@/plugin/permission";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useI18n } from "/src/locales";
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -52,13 +52,6 @@ const handleBatchDelete = () => {
|
||||
message.error(t("certd.pleaseSelectRecords"));
|
||||
}
|
||||
};
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
</script>
|
||||
<style lang="less"></style>
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onActivated, onMounted } from "vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useI18n } from "/src/locales";
|
||||
|
||||
const { t } = useI18n();
|
||||
@@ -22,12 +22,5 @@ defineOptions({
|
||||
name: "CertStore",
|
||||
});
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { permission: { isProjectPermission: true } } });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
</script>
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import { onActivated, onMounted } from "vue";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useI18n } from "/src/locales";
|
||||
const { t } = useI18n();
|
||||
defineOptions({
|
||||
@@ -37,12 +37,5 @@ const context: any = {
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
|
||||
|
||||
const handleBatchDelete = context.handleBatchDelete;
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
</script>
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import { onActivated, onMounted } from "vue";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useI18n } from "/src/locales";
|
||||
const { t } = useI18n();
|
||||
defineOptions({
|
||||
@@ -42,12 +42,5 @@ const context: any = {
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
|
||||
|
||||
const handleBatchDelete = context.handleBatchDelete;
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
</script>
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onActivated, onMounted, ref, Ref } from "vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
|
||||
defineOptions({
|
||||
name: "SiteIpCertMonitor",
|
||||
@@ -21,12 +21,5 @@ const { crudBinding, crudRef, crudExpose } = useFs({
|
||||
props,
|
||||
},
|
||||
});
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
</script>
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onActivated, onMounted } from "vue";
|
||||
import { defineComponent } from "vue";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { createNotificationApi } from "./api";
|
||||
@@ -23,14 +24,7 @@ export default defineComponent({
|
||||
const api = createNotificationApi();
|
||||
notificationProvide(api);
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { api, permission: { isProjectPermission: true } } });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
|
||||
@@ -11,21 +11,14 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onActivated, onMounted } from "vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { OPEN_API_DOC } from "/@/views/certd/open/openkey/api";
|
||||
|
||||
defineOptions({
|
||||
name: "OpenKey",
|
||||
});
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { permission: { isProjectPermission: true } } });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
</script>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes, useUi } from "@fast-crud/fast-crud";
|
||||
import { AddReq, ColumnProps, CreateCrudOptionsProps, CreateCrudOptionsRet, DataFormatterContext, DelReq, dict, EditReq, UserPageQuery, UserPageRes, useUi } from "@fast-crud/fast-crud";
|
||||
import { Modal, notification } from "ant-design-vue";
|
||||
import dayjs from "dayjs";
|
||||
import { computed, ref } from "vue";
|
||||
@@ -75,6 +75,17 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply
|
||||
const projectStore = useProjectStore();
|
||||
const { myProjectDict } = useDicts();
|
||||
const DEFAULT_WILL_EXPIRE_DAYS = settingStore.sysPublic.defaultWillExpireDays || settingStore.sysPublic.defaultCertRenewDays || 15;
|
||||
const pipelineTypeDictData = [
|
||||
{ value: "cert", label: t("certd.types.certApply") },
|
||||
{ value: "cert_upload", label: t("certd.types.certUpload") },
|
||||
{ value: "custom", label: t("certd.types.custom") },
|
||||
{ value: "template", label: t("certd.types.template") },
|
||||
{ value: "cert_auto", label: t("certd.types.certApply") },
|
||||
];
|
||||
const disabledDictData = [
|
||||
{ value: false, label: t("certd.fields.enabledLabel") },
|
||||
{ value: true, label: t("certd.fields.disabledLabel") },
|
||||
];
|
||||
|
||||
function onDialogOpen(opt: any) {
|
||||
const searchForm = crudExpose.getSearchValidatedFormData();
|
||||
@@ -84,6 +95,79 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply
|
||||
};
|
||||
}
|
||||
|
||||
function getRecordValue(row: any, key: string) {
|
||||
return key.split(".").reduce((target, item) => target?.[item], row);
|
||||
}
|
||||
|
||||
function findDictLabel(data: any[], value: any) {
|
||||
return data.find(item => item.value === value)?.label ?? value;
|
||||
}
|
||||
|
||||
function formatValidTime(value: any) {
|
||||
if (!value || value <= 0) {
|
||||
return t("certd.pi.permanentValid");
|
||||
}
|
||||
if (value < Date.now()) {
|
||||
return t("certd.hasExpired");
|
||||
}
|
||||
return dayjs(value).format("YYYY-MM-DD");
|
||||
}
|
||||
|
||||
function formatRemainingValidity(lastVars: any) {
|
||||
const expiresTime = lastVars?.certExpiresTime;
|
||||
if (!expiresTime) {
|
||||
return "-";
|
||||
}
|
||||
const leftDays = dayjs(expiresTime).diff(dayjs(), "day");
|
||||
if (leftDays < 0) {
|
||||
return t("certd.hasExpired");
|
||||
}
|
||||
return `${leftDays}${t("certd.days")}`;
|
||||
}
|
||||
|
||||
function formatListValue(value: any) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.join(",");
|
||||
}
|
||||
return value ?? "";
|
||||
}
|
||||
|
||||
function exportColumnFilter(col: ColumnProps) {
|
||||
if (!col.key || ["_index", "_selection", "rowHandle"].includes(col.key)) {
|
||||
return false;
|
||||
}
|
||||
if (col.key === "lastVars.certDomains") {
|
||||
return true;
|
||||
}
|
||||
return col.show !== false;
|
||||
}
|
||||
|
||||
function exportDataFormatter(opts: DataFormatterContext) {
|
||||
const { row, originalRow, col, exportCol } = opts;
|
||||
const key = col.key;
|
||||
const value = getRecordValue(originalRow, key);
|
||||
|
||||
if (key === "validTime") {
|
||||
row[key] = formatValidTime(value);
|
||||
} else if (key === "lastVars") {
|
||||
row[key] = formatRemainingValidity(value);
|
||||
} else if (key === "lastVars.certDomains") {
|
||||
row[key] = formatListValue(value);
|
||||
} else if (key === "status") {
|
||||
row[key] = statusUtil.get(value)?.label ?? value;
|
||||
} else if (key === "disabled") {
|
||||
row[key] = findDictLabel(disabledDictData, value);
|
||||
} else if (key === "type") {
|
||||
row[key] = findDictLabel(pipelineTypeDictData, value);
|
||||
} else if (key.includes("Time") && value) {
|
||||
row[key] = dayjs(value).format("YYYY-MM-DD HH:mm:ss");
|
||||
}
|
||||
|
||||
if (col.width) {
|
||||
exportCol.width = col.width / 10;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
@@ -178,6 +262,18 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply
|
||||
confirmMessage: t("certd.table.confirmDeleteMessage"),
|
||||
},
|
||||
},
|
||||
toolbar: {
|
||||
buttons: {
|
||||
export: {
|
||||
show: true,
|
||||
},
|
||||
},
|
||||
export: {
|
||||
dataFrom: "search",
|
||||
columnFilter: exportColumnFilter,
|
||||
dataFormatter: exportDataFormatter,
|
||||
},
|
||||
},
|
||||
tabs: {
|
||||
name: "groupId",
|
||||
show: true,
|
||||
@@ -419,6 +515,19 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply
|
||||
width: 150,
|
||||
},
|
||||
},
|
||||
"lastVars.certDomains": {
|
||||
title: t("certd.fields.certDomains"),
|
||||
type: "text",
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
width: 260,
|
||||
show: false,
|
||||
ellipsis: true,
|
||||
showTitle: true,
|
||||
},
|
||||
},
|
||||
"lastVars.certEffectiveTime": {
|
||||
title: t("certd.fields.effectiveTime"),
|
||||
search: {
|
||||
@@ -503,10 +612,7 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply
|
||||
},
|
||||
},
|
||||
dict: dict({
|
||||
data: [
|
||||
{ value: false, label: t("certd.fields.enabledLabel") },
|
||||
{ value: true, label: t("certd.fields.disabledLabel") },
|
||||
],
|
||||
data: disabledDictData,
|
||||
}),
|
||||
form: {
|
||||
value: false,
|
||||
@@ -563,13 +669,7 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply
|
||||
col: { span: 2 },
|
||||
},
|
||||
dict: dict({
|
||||
data: [
|
||||
{ value: "cert", label: t("certd.types.certApply") },
|
||||
{ value: "cert_upload", label: t("certd.types.certUpload") },
|
||||
{ value: "custom", label: t("certd.types.custom") },
|
||||
{ value: "template", label: t("certd.types.template") },
|
||||
{ value: "cert_auto", label: t("certd.types.certApply") },
|
||||
],
|
||||
data: pipelineTypeDictData,
|
||||
}),
|
||||
form: {
|
||||
show: false,
|
||||
@@ -650,7 +750,7 @@ export default function ({ crudExpose, context: { selectedRowKeys, openCertApply
|
||||
align: "center",
|
||||
cellRender({ value }) {
|
||||
if (!value || value <= 0) {
|
||||
return "-";
|
||||
return t("certd.pi.permanentValid");
|
||||
}
|
||||
if (value < Date.now()) {
|
||||
return t("certd.hasExpired");
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onActivated, onMounted } from "vue";
|
||||
import { defineComponent } from "vue";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
|
||||
@@ -24,14 +25,7 @@ export default defineComponent({
|
||||
permission: { isProjectPermission: true },
|
||||
},
|
||||
});
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
|
||||
@@ -129,7 +129,7 @@ const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
// crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
onActivated(async () => {
|
||||
|
||||
@@ -37,7 +37,7 @@ import { useCrudPermission } from "/@/plugin/permission";
|
||||
const { t } = useI18n();
|
||||
|
||||
defineOptions({
|
||||
name: "CnameRecord",
|
||||
name: "SubDomain",
|
||||
});
|
||||
const context: any = {
|
||||
permission: {
|
||||
@@ -68,7 +68,7 @@ const handleBatchDelete = () => {
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
// crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onActivated, onMounted } from "vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useI18n } from "/src/locales";
|
||||
defineOptions({
|
||||
name: "PipelineTemplate",
|
||||
@@ -28,11 +28,5 @@ const { crudBinding, crudRef, crudExpose } = useFs({
|
||||
},
|
||||
});
|
||||
const { t } = useI18n();
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
</script>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onActivated, onMounted, Ref, ref } from "vue";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
@@ -117,13 +118,12 @@ onMounted(async () => {
|
||||
return;
|
||||
}
|
||||
await loadProjectDetail();
|
||||
await crudExpose.doRefresh();
|
||||
|
||||
if (migrate === "true") {
|
||||
openTransferDialog();
|
||||
}
|
||||
});
|
||||
onActivated(async () => {
|
||||
useMounted(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -53,7 +53,7 @@ const handleBatchDelete = () => {
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
// crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onActivated, onMounted, ref } from "vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import { computed, ref } from "vue";
|
||||
import createCrudOptions from "./crud";
|
||||
import { mySuiteApi, SuiteDetail } from "/@/views/certd/suite/mine/api";
|
||||
import SuiteCard from "/@/views/framework/home/dashboard/suite-card.vue";
|
||||
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
defineOptions({
|
||||
name: "MySuites",
|
||||
name: "MySuite",
|
||||
});
|
||||
const detail = ref<SuiteDetail>({});
|
||||
const currentSuite = computed(() => {
|
||||
@@ -39,11 +39,8 @@ async function loadSuiteDetail() {
|
||||
}
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(async () => {
|
||||
useMounted(async () => {
|
||||
await loadSuiteDetail();
|
||||
await crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -19,7 +19,7 @@ const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
// crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onActivated, onMounted } from "vue";
|
||||
import { defineComponent } from "vue";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "../../certd/access/crud";
|
||||
import { createAccessApi } from "/@/views/certd/access/api";
|
||||
@@ -21,15 +22,7 @@ export default defineComponent({
|
||||
setup() {
|
||||
const api = createAccessApi("sys");
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { api } });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
|
||||
@@ -22,7 +22,7 @@ import { useFs, useUi } from "@fast-crud/fast-crud";
|
||||
import { useI18n } from "/src/locales";
|
||||
|
||||
export default defineComponent({
|
||||
name: "AuthorityManager",
|
||||
name: "PermissionManager",
|
||||
components: { FsPermissionTree },
|
||||
setup() {
|
||||
// 此处传入permission进行通用按钮权限设置,会通过commonOptions去设置actionbar和rowHandle的按钮的show属性
|
||||
@@ -32,7 +32,7 @@ export default defineComponent({
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
// await crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
|
||||
@@ -11,14 +11,15 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onActivated, onMounted, ref } from "vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import * as permissionApi from "../permission/api";
|
||||
import * as api from "./api";
|
||||
import { message } from "ant-design-vue";
|
||||
import { defineComponent, ref } from "vue";
|
||||
import * as permissionApi from "../permission/api";
|
||||
import FsPermissionTree from "../permission/fs-permission-tree.vue";
|
||||
import * as api from "./api";
|
||||
import createCrudOptions from "./crud";
|
||||
import { UseCrudPermissionCompProps, UseCrudPermissionExtraProps } from "/@/plugin/permission";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useI18n } from "/src/locales";
|
||||
|
||||
function useAuthz() {
|
||||
@@ -104,15 +105,7 @@ export default defineComponent({
|
||||
// 更多关于按钮权限的源代码设置,请参考 ./src/plugin/fast-crud/index.js (75-77行)
|
||||
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { authz, permission } });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef,
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, onMounted, onActivated } from "vue";
|
||||
import { useCrud, useExpose, useFs } from "@fast-crud/fast-crud";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import { defineComponent } from "vue";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
export default defineComponent({
|
||||
name: "UserManager",
|
||||
setup() {
|
||||
@@ -18,14 +19,7 @@ export default defineComponent({
|
||||
// 此处传入权限前缀进行通用按钮权限设置,会通过commonOptions去设置actionbar和rowHandle的按钮的show属性
|
||||
// 更多关于按钮权限的源代码设置,请参考 ./src/plugin/fast-crud/index.js (75-77行)
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { permission: "sys:auth:user" } });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
|
||||
@@ -32,7 +32,7 @@ import { useI18n } from "/src/locales";
|
||||
const { t } = useI18n();
|
||||
|
||||
defineOptions({
|
||||
name: "CnameProvider",
|
||||
name: "CnameSetting",
|
||||
});
|
||||
const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions });
|
||||
|
||||
@@ -56,7 +56,7 @@ const handleBatchDelete = () => {
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
// crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
|
||||
@@ -56,7 +56,7 @@ const handleBatchDelete = () => {
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
// crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
|
||||
@@ -62,7 +62,7 @@ const handleBatchDelete = () => {
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
// crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
|
||||
@@ -53,7 +53,7 @@ const handleBatchDelete = () => {
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
// crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import { onActivated, onMounted } from "vue";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import createCrudOptions from "./crud";
|
||||
|
||||
defineOptions({
|
||||
@@ -25,11 +25,5 @@ defineOptions({
|
||||
const context: any = {};
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
|
||||
const handleBatchDelete = context.handleBatchDelete;
|
||||
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import createCrudOptionsUser from "/@/views/sys/authority/user/crud";
|
||||
import { CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
import { ColumnProps, CreateCrudOptionsProps, CreateCrudOptionsRet, DataFormatterContext, DelReq, dict, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
import { message, Modal } from "ant-design-vue";
|
||||
import dayjs from "dayjs";
|
||||
import { ref } from "vue";
|
||||
@@ -18,6 +18,77 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
};
|
||||
|
||||
const selectedRowKeys = ref<number[]>([]);
|
||||
const pipelineTypeDictData = [
|
||||
{ value: "cert", label: "证书申请" },
|
||||
{ value: "cert_upload", label: "证书上传" },
|
||||
{ value: "custom", label: "自定义" },
|
||||
{ value: "template", label: "模板" },
|
||||
{ value: "cert_auto", label: "证书申请" },
|
||||
];
|
||||
const disabledDictData = [
|
||||
{ label: "启用", value: false, color: "green" },
|
||||
{ label: "禁用", value: true, color: "red" },
|
||||
];
|
||||
|
||||
function findDictLabel(data: any[], value: any) {
|
||||
return data.find(item => item.value === value)?.label ?? value;
|
||||
}
|
||||
|
||||
function formatValidTime(value: any) {
|
||||
if (!value || value <= 0) {
|
||||
return "永久有效";
|
||||
}
|
||||
if (value < Date.now()) {
|
||||
return "已过期";
|
||||
}
|
||||
return dayjs(value).format("YYYY-MM-DD");
|
||||
}
|
||||
|
||||
function getRecordValue(row: any, key: string) {
|
||||
return key.split(".").reduce((target, item) => target?.[item], row);
|
||||
}
|
||||
|
||||
function formatListValue(value: any) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.join(",");
|
||||
}
|
||||
return value ?? "";
|
||||
}
|
||||
|
||||
function exportColumnFilter(col: ColumnProps) {
|
||||
if (!col.key || ["_index", "_selection", "rowHandle"].includes(col.key)) {
|
||||
return false;
|
||||
}
|
||||
if (col.key === "lastVars.certDomains") {
|
||||
return true;
|
||||
}
|
||||
return col.show !== false;
|
||||
}
|
||||
|
||||
function exportDataFormatter(opts: DataFormatterContext) {
|
||||
const { row, originalRow, col, exportCol } = opts;
|
||||
const key = col.key;
|
||||
const value = getRecordValue(originalRow, key);
|
||||
|
||||
if (key === "validTime") {
|
||||
row[key] = formatValidTime(value);
|
||||
} else if (key === "lastVars.certDomains") {
|
||||
row[key] = formatListValue(value);
|
||||
} else if (key === "status") {
|
||||
row[key] = statusUtil.get(value)?.label ?? value;
|
||||
} else if (key === "disabled") {
|
||||
row[key] = findDictLabel(disabledDictData, value);
|
||||
} else if (key === "type") {
|
||||
row[key] = findDictLabel(pipelineTypeDictData, value);
|
||||
} else if (key.includes("Time") && value) {
|
||||
row[key] = dayjs(value).format("YYYY-MM-DD HH:mm:ss");
|
||||
}
|
||||
|
||||
if (col.width) {
|
||||
exportCol.width = col.width / 10;
|
||||
}
|
||||
}
|
||||
|
||||
const handleBatchDelete = () => {
|
||||
if (!selectedRowKeys.value?.length) {
|
||||
message.error("请先选择要删除的记录");
|
||||
@@ -54,6 +125,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
},
|
||||
export: {
|
||||
dataFrom: "search",
|
||||
columnFilter: exportColumnFilter,
|
||||
dataFormatter: exportDataFormatter,
|
||||
},
|
||||
},
|
||||
pagination: {
|
||||
@@ -185,13 +258,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
},
|
||||
},
|
||||
dict: dict({
|
||||
data: [
|
||||
{ value: "cert", label: "证书申请" },
|
||||
{ value: "cert_upload", label: "证书上传" },
|
||||
{ value: "custom", label: "自定义" },
|
||||
{ value: "template", label: "模板" },
|
||||
{ value: "cert_auto", label: "证书申请" },
|
||||
],
|
||||
data: pipelineTypeDictData,
|
||||
}),
|
||||
column: {
|
||||
width: 110,
|
||||
@@ -236,10 +303,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
},
|
||||
},
|
||||
dict: dict({
|
||||
data: [
|
||||
{ label: "启用", value: false, color: "green" },
|
||||
{ label: "禁用", value: true, color: "red" },
|
||||
],
|
||||
data: disabledDictData,
|
||||
}),
|
||||
column: {
|
||||
width: 90,
|
||||
@@ -273,6 +337,18 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
"lastVars.certDomains": {
|
||||
title: "证书域名",
|
||||
type: "text",
|
||||
column: {
|
||||
width: 260,
|
||||
show: false,
|
||||
ellipsis: true,
|
||||
},
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
lastHistoryTime: {
|
||||
title: "最后执行时间",
|
||||
type: "datetime",
|
||||
@@ -306,7 +382,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
align: "center",
|
||||
cellRender({ value }) {
|
||||
if (!value || value <= 0) {
|
||||
return "-";
|
||||
return "永久有效";
|
||||
}
|
||||
if (value < Date.now()) {
|
||||
return <span style={{ color: "red" }}>已过期</span>;
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import { onActivated, onMounted } from "vue";
|
||||
import createCrudOptions from "./crud";
|
||||
|
||||
defineOptions({
|
||||
@@ -25,11 +25,5 @@ defineOptions({
|
||||
const context: any = {};
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
|
||||
const handleBatchDelete = context.handleBatchDelete;
|
||||
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
</script>
|
||||
|
||||
@@ -21,7 +21,7 @@ const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions
|
||||
const settingStore = useSettingStore();
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
// crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
|
||||
@@ -14,6 +14,11 @@
|
||||
<div class="helper">{{ t("certd.httpsProxyHelper") }}</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="t('certd.noProxy')" :name="['private', 'noProxy']">
|
||||
<a-textarea v-model:value="formState.private.noProxy" :placeholder="t('certd.noProxyPlaceholder')" rows="3" />
|
||||
<div class="helper">{{ t("certd.noProxyHelper") }}</div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item :label="t('certd.sys.setting.environmentVars')" :name="['private', 'environmentVars']">
|
||||
<a-textarea v-model:value="formState.private.environmentVars" :placeholder="environmentVarsExample" rows="4" />
|
||||
<div class="helper">{{ t("certd.sys.setting.environmentVarsHelper") }}</div>
|
||||
|
||||
@@ -123,4 +123,12 @@ const loginLogoCropperOptions = ref({
|
||||
border-radius: 0 !important;
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.page-sys-site {
|
||||
.sys-settings-form {
|
||||
width: 900px;
|
||||
max-width: 100%;
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -16,13 +16,13 @@ import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
|
||||
defineOptions({
|
||||
name: "ProductActivationCodeManager",
|
||||
name: "SysProductActivationCode",
|
||||
});
|
||||
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
// crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
|
||||
@@ -46,12 +46,13 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onActivated, onMounted } from "vue";
|
||||
import { Modal, notification } from "ant-design-vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud-level";
|
||||
import { Modal, notification } from "ant-design-vue";
|
||||
import { computed } from "vue";
|
||||
import * as api from "./api";
|
||||
import createCrudOptions from "./crud-level";
|
||||
import { util } from "/@/utils";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
|
||||
defineOptions({ name: "SysInviteLevel" });
|
||||
|
||||
@@ -92,13 +93,7 @@ function confirmRemove(opts: any) {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
||||
@@ -8,18 +8,12 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onActivated, onMounted } from "vue";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud-user-level";
|
||||
|
||||
defineOptions({ name: "SysInviteUserLevel" });
|
||||
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
</script>
|
||||
|
||||
@@ -8,18 +8,12 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onActivated, onMounted } from "vue";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud-withdraw";
|
||||
|
||||
defineOptions({ name: "SysInviteWithdraw" });
|
||||
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
</script>
|
||||
|
||||
@@ -11,21 +11,15 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onActivated, onMounted } from "vue";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
|
||||
defineOptions({
|
||||
name: "TradeManager",
|
||||
name: "OrderManager",
|
||||
});
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
</script>
|
||||
<style lang="less"></style>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onActivated, onMounted } from "vue";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
|
||||
@@ -19,12 +19,5 @@ defineOptions({
|
||||
name: "UserSuites",
|
||||
});
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: {} });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
onActivated(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
</script>
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 流水线、监控站点支持导出 ([99fd308](https://github.com/certd/certd/commit/99fd3083f259cdb96fd656f04858dd708d1251c7))
|
||||
* 优化邀请注册流程 ([7a71e45](https://github.com/certd/certd/commit/7a71e45799d782d0691606fb42b4236f1d3009b0))
|
||||
* **volcengine-vke:** 火山VKE集群证书支持两种类型的证书保密字典 ([77b8024](https://github.com/certd/certd/commit/77b802445322d576d54d194f7c505da49e0e824c))
|
||||
|
||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -7,6 +7,7 @@ title: 火山引擎-替换VKE证书
|
||||
icon: svg:icon-volcengine
|
||||
group: volcengine
|
||||
desc: 替换火山引擎VKE集群中的TLS Secret证书
|
||||
needPlus: true
|
||||
input:
|
||||
cert:
|
||||
title: 域名证书
|
||||
@@ -15,6 +16,7 @@ input:
|
||||
name: output-selector
|
||||
from:
|
||||
- ':cert:'
|
||||
- VolcengineUploadToCertCenter
|
||||
required: true
|
||||
order: 0
|
||||
certDomains:
|
||||
@@ -118,11 +120,11 @@ input:
|
||||
component:
|
||||
name: a-select
|
||||
options:
|
||||
- label: 按Ingress替换
|
||||
value: ingress
|
||||
- label: 按Secret替换
|
||||
value: secret
|
||||
value: ingress
|
||||
- label: 按Ingress替换
|
||||
value: ingress
|
||||
value: secret
|
||||
required: true
|
||||
order: 0
|
||||
ingressName:
|
||||
@@ -140,23 +142,37 @@ input:
|
||||
secretName:
|
||||
title: Secret名称
|
||||
required: true
|
||||
helper: 存储TLS证书的Secret名称,可填写多个
|
||||
helper: 选择要替换的Secret,可多选
|
||||
component:
|
||||
name: a-select
|
||||
name: remote-select
|
||||
vModel: value
|
||||
mode: tags
|
||||
open: false
|
||||
type: plugin
|
||||
action: onGetSecretList
|
||||
search: false
|
||||
pager: false
|
||||
single: false
|
||||
watches:
|
||||
- certDomains
|
||||
- accessId
|
||||
- regionId
|
||||
- clusterId
|
||||
- kubeconfigType
|
||||
- namespace
|
||||
mergeScript: |2-
|
||||
|
||||
return {
|
||||
show: ctx.compute(({form}) => form.targetType === 'secret'),
|
||||
required: ctx.compute(({form}) => form.targetType === 'secret')
|
||||
required: ctx.compute(({form}) => form.targetType === 'secret'),
|
||||
component: {
|
||||
form: ctx.compute(({form}) => form)
|
||||
}
|
||||
}
|
||||
|
||||
order: 0
|
||||
createOnNotFound:
|
||||
title: Secret自动创建
|
||||
helper: 如果Secret不存在,则创建kubernetes.io/tls类型Secret
|
||||
helper: 如果Secret不存在,则创建Opaque类型Secret
|
||||
value: false
|
||||
component:
|
||||
name: a-switch
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-server",
|
||||
"version": "1.41.0",
|
||||
"version": "1.41.1",
|
||||
"description": "fast-server base midway",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
@@ -54,20 +54,20 @@
|
||||
"@aws-sdk/client-sts": "^3.990.0",
|
||||
"@azure/arm-dns": "^5.1.0",
|
||||
"@azure/identity": "^4.13.1",
|
||||
"@certd/acme-client": "^1.41.0",
|
||||
"@certd/basic": "^1.41.0",
|
||||
"@certd/commercial-core": "^1.41.0",
|
||||
"@certd/acme-client": "^1.41.1",
|
||||
"@certd/basic": "^1.41.1",
|
||||
"@certd/commercial-core": "^1.41.1",
|
||||
"@certd/cv4pve-api-javascript": "^8.4.2",
|
||||
"@certd/jdcloud": "^1.41.0",
|
||||
"@certd/lib-huawei": "^1.41.0",
|
||||
"@certd/lib-k8s": "^1.41.0",
|
||||
"@certd/lib-server": "^1.41.0",
|
||||
"@certd/midway-flyway-js": "^1.41.0",
|
||||
"@certd/pipeline": "^1.41.0",
|
||||
"@certd/plugin-cert": "^1.41.0",
|
||||
"@certd/plugin-lib": "^1.41.0",
|
||||
"@certd/plugin-plus": "^1.41.0",
|
||||
"@certd/plus-core": "^1.41.0",
|
||||
"@certd/jdcloud": "^1.41.1",
|
||||
"@certd/lib-huawei": "^1.41.1",
|
||||
"@certd/lib-k8s": "^1.41.1",
|
||||
"@certd/lib-server": "^1.41.1",
|
||||
"@certd/midway-flyway-js": "^1.41.1",
|
||||
"@certd/pipeline": "^1.41.1",
|
||||
"@certd/plugin-cert": "^1.41.1",
|
||||
"@certd/plugin-lib": "^1.41.1",
|
||||
"@certd/plugin-plus": "^1.41.1",
|
||||
"@certd/plus-core": "^1.41.1",
|
||||
"@google-cloud/dns": "^5.3.1",
|
||||
"@google-cloud/publicca": "^1.3.0",
|
||||
"@huaweicloud/huaweicloud-sdk-cdn": "3.1.185",
|
||||
|
||||
@@ -30,16 +30,23 @@ export class AwsClient {
|
||||
},
|
||||
});
|
||||
|
||||
const cert = certInfo.crt.split("-----END CERTIFICATE-----")[0] + "-----END CERTIFICATE-----";
|
||||
// Split the full PEM chain: first block is the leaf cert, the rest is the intermediate chain
|
||||
const pemBlocks = certInfo.crt.split(/(?<=-----END CERTIFICATE-----)/);
|
||||
const cert = pemBlocks[0].trim();
|
||||
const chain = pemBlocks
|
||||
.slice(1)
|
||||
.join("")
|
||||
.trim();
|
||||
|
||||
// 构建上传参数
|
||||
const data = await acmClient.send(
|
||||
new ImportCertificateCommand({
|
||||
Certificate: Buffer.from(cert),
|
||||
PrivateKey: Buffer.from(certInfo.key),
|
||||
// CertificateChain: certificateChain, // 可选
|
||||
CertificateChain: chain ? Buffer.from(chain) : undefined,
|
||||
})
|
||||
);
|
||||
console.log("Upload successful:", data);
|
||||
this.logger.info(`Upload successful: ${data.CertificateArn}`);
|
||||
// 返回证书 ARN(Amazon Resource Name)
|
||||
return data.CertificateArn;
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
||||
|
||||
this._result.pipelineVars.certEffectiveTime = dayjs(certReader.detail.notBefore).valueOf();
|
||||
this._result.pipelineVars.certExpiresTime = dayjs(certReader.detail.notAfter).valueOf();
|
||||
this._result.pipelineVars.certDomains = certReader.getAllDomains();
|
||||
if (!this._result.pipelinePrivateVars) {
|
||||
this._result.pipelinePrivateVars = {};
|
||||
}
|
||||
|
||||
+62
@@ -3,6 +3,7 @@
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { VolcengineDeployToVKE } from "./plugin-deploy-to-vke.js";
|
||||
import { CertInfo } from "@certd/plugin-cert";
|
||||
|
||||
describe("VolcengineDeployToVKE", () => {
|
||||
it("uses a single-select cluster field", () => {
|
||||
@@ -86,4 +87,65 @@ describe("VolcengineDeployToVKE", () => {
|
||||
/当前命名空间可用Ingress:app-web,api-web/
|
||||
);
|
||||
});
|
||||
|
||||
it("creates Secret with cert_center format for new non-tls secrets", async () => {
|
||||
const plugin = new VolcengineDeployToVKE();
|
||||
plugin.namespace = "default";
|
||||
plugin.targetType = "secret";
|
||||
plugin.secretName = "test-tls";
|
||||
plugin.createOnNotFound = true;
|
||||
plugin.logger = { info: () => undefined } as any;
|
||||
plugin.appendTimeSuffix = (s: string) => s + "-test";
|
||||
|
||||
let secretBody: any;
|
||||
await (plugin as any).patchCertSecret({
|
||||
certId: "cert-abc123",
|
||||
k8sClient: {
|
||||
patchSecret: async (opts: any) => {
|
||||
secretBody = opts.body;
|
||||
return {};
|
||||
},
|
||||
client: {
|
||||
readNamespacedSecret: async () => {
|
||||
throw Object.assign(new Error("Not Found"), { response: { body: { code: 404 } } });
|
||||
},
|
||||
},
|
||||
},
|
||||
secretNames: ["test-tls"],
|
||||
});
|
||||
|
||||
assert.equal(secretBody.type, "Opaque");
|
||||
assert.equal(secretBody.data["cert_id"], Buffer.from("cert-abc123").toString("base64"));
|
||||
assert.equal(secretBody.data["cert_source"], Buffer.from("cert_center").toString("base64"));
|
||||
});
|
||||
|
||||
it("uses tls.crt/tls.key format for kubernetes.io/tls secrets", async () => {
|
||||
const plugin = new VolcengineDeployToVKE();
|
||||
plugin.namespace = "default";
|
||||
plugin.targetType = "secret";
|
||||
plugin.secretName = "test-tls";
|
||||
plugin.logger = { info: () => undefined } as any;
|
||||
plugin.appendTimeSuffix = (s: string) => s + "-test";
|
||||
plugin.cert = { crt: "MY_CRT", key: "MY_KEY" } as CertInfo;
|
||||
|
||||
let secretBody: any;
|
||||
await (plugin as any).patchCertSecret({
|
||||
certId: "cert-abc123",
|
||||
k8sClient: {
|
||||
patchSecret: async (opts: any) => {
|
||||
secretBody = opts.body;
|
||||
return {};
|
||||
},
|
||||
client: {
|
||||
readNamespacedSecret: async () => ({
|
||||
body: { type: "kubernetes.io/tls" },
|
||||
}),
|
||||
},
|
||||
},
|
||||
secretNames: ["test-tls"],
|
||||
});
|
||||
|
||||
assert.equal(secretBody.data["tls.crt"], Buffer.from("MY_CRT").toString("base64"));
|
||||
assert.equal(secretBody.data["tls.key"], Buffer.from("MY_KEY").toString("base64"));
|
||||
});
|
||||
});
|
||||
|
||||
+138
-31
@@ -1,9 +1,10 @@
|
||||
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
||||
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
|
||||
import { utils } from "@certd/basic";
|
||||
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
||||
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
|
||||
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
|
||||
import { AbstractPlusTaskPlugin } from "@certd/plugin-plus";
|
||||
import { VolcengineAccess } from "../access.js";
|
||||
import { VolcengineClient } from "../ve-client.js";
|
||||
import { utils } from "@certd/basic";
|
||||
|
||||
const regionOptions = [
|
||||
{ label: "北京", value: "cn-beijing" },
|
||||
@@ -20,23 +21,24 @@ const regionOptions = [
|
||||
icon: "svg:icon-volcengine",
|
||||
group: pluginGroups.volcengine.key,
|
||||
desc: "替换火山引擎VKE集群中的TLS Secret证书",
|
||||
needPlus:true,
|
||||
default: {
|
||||
strategy: {
|
||||
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||
},
|
||||
},
|
||||
})
|
||||
export class VolcengineDeployToVKE extends AbstractTaskPlugin {
|
||||
export class VolcengineDeployToVKE extends AbstractPlusTaskPlugin {
|
||||
@TaskInput({
|
||||
title: "域名证书",
|
||||
helper: "请选择前置任务输出的域名证书",
|
||||
component: {
|
||||
name: "output-selector",
|
||||
from: [...CertApplyPluginNames],
|
||||
from: [...CertApplyPluginNames, "VolcengineUploadToCertCenter"],
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
cert!: CertInfo;
|
||||
cert!: CertInfo | string;
|
||||
|
||||
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
|
||||
certDomains!: string[];
|
||||
@@ -107,11 +109,11 @@ export class VolcengineDeployToVKE extends AbstractTaskPlugin {
|
||||
component: {
|
||||
name: "a-select",
|
||||
options: [
|
||||
{ label: "按Ingress替换", value: "ingress" },
|
||||
{ label: "按Secret替换", value: "secret" },
|
||||
{ label: "按Ingress替换", value: "ingress" },
|
||||
],
|
||||
},
|
||||
value: "ingress",
|
||||
value: "secret",
|
||||
required: true,
|
||||
})
|
||||
targetType!: "ingress" | "secret";
|
||||
@@ -132,17 +134,25 @@ export class VolcengineDeployToVKE extends AbstractTaskPlugin {
|
||||
@TaskInput({
|
||||
title: "Secret名称",
|
||||
required: true,
|
||||
helper: "存储TLS证书的Secret名称,可填写多个",
|
||||
helper: "选择要替换的Secret,可多选",
|
||||
component: {
|
||||
name: "a-select",
|
||||
name: "remote-select",
|
||||
vModel: "value",
|
||||
mode: "tags",
|
||||
open: false,
|
||||
type: "plugin",
|
||||
action: "onGetSecretList",
|
||||
search: false,
|
||||
pager: false,
|
||||
single: false,
|
||||
watches: ["certDomains", "accessId", "regionId", "clusterId", "kubeconfigType", "namespace"],
|
||||
},
|
||||
mergeScript: `
|
||||
return {
|
||||
show: ctx.compute(({form}) => form.targetType === 'secret'),
|
||||
required: ctx.compute(({form}) => form.targetType === 'secret')
|
||||
required: ctx.compute(({form}) => form.targetType === 'secret'),
|
||||
component: {
|
||||
form: ctx.compute(({form}) => form)
|
||||
}
|
||||
}
|
||||
`,
|
||||
})
|
||||
@@ -150,7 +160,7 @@ export class VolcengineDeployToVKE extends AbstractTaskPlugin {
|
||||
|
||||
@TaskInput({
|
||||
title: "Secret自动创建",
|
||||
helper: "如果Secret不存在,则创建kubernetes.io/tls类型Secret",
|
||||
helper: "如果Secret不存在,则创建Opaque类型Secret",
|
||||
value: false,
|
||||
component: {
|
||||
name: "a-switch",
|
||||
@@ -181,6 +191,23 @@ export class VolcengineDeployToVKE extends AbstractTaskPlugin {
|
||||
this.logger.info("开始替换火山引擎VKE证书");
|
||||
const access = await this.getAccess<VolcengineAccess>(this.accessId);
|
||||
const vkeService = await this.getVkeService(access);
|
||||
|
||||
// 上传证书到证书中心
|
||||
let certId: string;
|
||||
if (typeof this.cert !== "string") {
|
||||
const certInfo = this.cert as CertInfo;
|
||||
this.logger.info("开始上传证书到证书中心");
|
||||
const certService = await this.getCertService(access);
|
||||
certId = await certService.ImportCertificate({
|
||||
certName: this.appendTimeSuffix("certd"),
|
||||
cert: certInfo,
|
||||
});
|
||||
this.logger.info("上传证书到证书中心成功:" + certId);
|
||||
} else {
|
||||
certId = this.cert;
|
||||
this.logger.info("使用已有证书中心ID:" + certId);
|
||||
}
|
||||
|
||||
const kubeconfigId = await this.createKubeconfig(vkeService);
|
||||
|
||||
try {
|
||||
@@ -191,7 +218,7 @@ export class VolcengineDeployToVKE extends AbstractTaskPlugin {
|
||||
skipTLSVerify: this.skipTLSVerify,
|
||||
});
|
||||
const secretNames = await this.getTargetSecretNames(k8sClient);
|
||||
await this.patchCertSecret({ cert: this.cert, k8sClient, secretNames });
|
||||
await this.patchCertSecret({ certId, k8sClient, secretNames });
|
||||
} catch (e) {
|
||||
if (e.response?.body) {
|
||||
throw new Error(this.formatK8sError(e.response.body));
|
||||
@@ -205,6 +232,15 @@ export class VolcengineDeployToVKE extends AbstractTaskPlugin {
|
||||
this.logger.info("VKE证书替换完成");
|
||||
}
|
||||
|
||||
private async getCertService(access: VolcengineAccess) {
|
||||
const client = new VolcengineClient({
|
||||
logger: this.logger,
|
||||
access,
|
||||
http: this.http,
|
||||
});
|
||||
return await client.getCertCenterService();
|
||||
}
|
||||
|
||||
private async getVkeService(access: VolcengineAccess) {
|
||||
const client = new VolcengineClient({
|
||||
logger: this.logger,
|
||||
@@ -293,6 +329,11 @@ export class VolcengineDeployToVKE extends AbstractTaskPlugin {
|
||||
}
|
||||
|
||||
private formatK8sError(body: any) {
|
||||
if (body?.code === 422 && body?.message?.includes("field is immutable")) {
|
||||
const secretName = body.details?.name || "未知";
|
||||
return `Secret类型不可变:Secret ${secretName} 已是kubernetes.io/tls类型,type字段不可修改。\n请删除该Secret后重试,或选择正确的Secret。\n原始错误:${JSON.stringify(body)}`;
|
||||
}
|
||||
|
||||
if (body?.code !== 403 || body?.reason !== "Forbidden") {
|
||||
return JSON.stringify(body);
|
||||
}
|
||||
@@ -334,34 +375,71 @@ export class VolcengineDeployToVKE extends AbstractTaskPlugin {
|
||||
return secretNames;
|
||||
}
|
||||
|
||||
private async patchCertSecret(options: { cert: CertInfo; k8sClient: any; secretNames: string[] }) {
|
||||
const { cert, k8sClient, secretNames } = options;
|
||||
private async patchCertSecret(options: { certId: string; k8sClient: any; secretNames: string[] }) {
|
||||
const { certId, k8sClient, secretNames } = options;
|
||||
if (!secretNames || secretNames.length === 0) {
|
||||
throw new Error("Secret名称不能为空");
|
||||
}
|
||||
|
||||
const body: any = {
|
||||
data: {
|
||||
"tls.crt": Buffer.from(cert.crt).toString("base64"),
|
||||
"tls.key": Buffer.from(cert.key).toString("base64"),
|
||||
},
|
||||
metadata: {
|
||||
labels: {
|
||||
certd: this.appendTimeSuffix("certd"),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
for (const secretName of secretNames) {
|
||||
let useTlsFormat = false;
|
||||
try {
|
||||
const res = await k8sClient.client.readNamespacedSecret(secretName, this.namespace);
|
||||
useTlsFormat = res.body?.type === "kubernetes.io/tls";
|
||||
} catch (e) {
|
||||
// Secret 不存在,将走创建逻辑
|
||||
}
|
||||
|
||||
let body: any;
|
||||
if (useTlsFormat) {
|
||||
let crt: string;
|
||||
let key: string;
|
||||
if (typeof this.cert === "string") {
|
||||
const access = await this.getAccess<VolcengineAccess>(this.accessId);
|
||||
const certService = await this.getCertService(access);
|
||||
const detail = await certService.GetCertificateDetail(this.cert);
|
||||
crt = detail.CertificateChain || "";
|
||||
key = detail.PrivateKey || "";
|
||||
this.logger.info("从证书中心获取证书详情成功");
|
||||
} else {
|
||||
crt = this.cert.crt;
|
||||
key = this.cert.key;
|
||||
}
|
||||
body = {
|
||||
data: {
|
||||
"tls.crt": Buffer.from(crt).toString("base64"),
|
||||
"tls.key": Buffer.from(key).toString("base64"),
|
||||
},
|
||||
metadata: {
|
||||
labels: {
|
||||
certd: this.appendTimeSuffix("certd"),
|
||||
},
|
||||
},
|
||||
};
|
||||
} else {
|
||||
body = {
|
||||
type: "Opaque",
|
||||
data: {
|
||||
cert_id: Buffer.from(certId).toString("base64"),
|
||||
cert_source: Buffer.from("cert_center").toString("base64"),
|
||||
},
|
||||
metadata: {
|
||||
labels: {
|
||||
certd: this.appendTimeSuffix("certd"),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
body.metadata.name = secretName;
|
||||
this.logger.info(`开始更新VKE Secret:${secretName}`);
|
||||
this.logger.info("开始更新VKE Secret:" + secretName);
|
||||
await k8sClient.patchSecret({
|
||||
namespace: this.namespace,
|
||||
secretName,
|
||||
body,
|
||||
createOnNotFound: this.createOnNotFound,
|
||||
});
|
||||
this.logger.info(`VKE Secret已更新:${secretName}`);
|
||||
this.logger.info("VKE Secret已更新:" + secretName);
|
||||
}
|
||||
|
||||
if (this.targetType === "ingress" && this.ingressName) {
|
||||
@@ -389,6 +467,35 @@ export class VolcengineDeployToVKE extends AbstractTaskPlugin {
|
||||
value: item.Id,
|
||||
}));
|
||||
}
|
||||
async onGetSecretList() {
|
||||
if (!this.accessId) {
|
||||
throw new Error("请选择Access授权");
|
||||
}
|
||||
if (!this.clusterId) {
|
||||
throw new Error("请选择VKE集群");
|
||||
}
|
||||
const access = await this.getAccess<VolcengineAccess>(this.accessId);
|
||||
const vkeService = await this.getVkeService(access);
|
||||
const kubeconfigId = await this.createKubeconfig(vkeService);
|
||||
|
||||
try {
|
||||
const kubeconfig = await this.getKubeconfig(vkeService, kubeconfigId);
|
||||
const k8sClient = new this.K8sClient({
|
||||
kubeConfigStr: kubeconfig,
|
||||
logger: this.logger,
|
||||
skipTLSVerify: this.skipTLSVerify,
|
||||
});
|
||||
const res = await k8sClient.getSecrets({ namespace: this.namespace || "default" });
|
||||
const list = res.body?.items || res.items || [];
|
||||
return list.map((item: any) => ({
|
||||
label: item.metadata.name,
|
||||
value: item.metadata.name,
|
||||
}));
|
||||
} finally {
|
||||
await this.deleteKubeconfig(vkeService, kubeconfigId);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
new VolcengineDeployToVKE();
|
||||
new VolcengineDeployToVKE();
|
||||
@@ -1,4 +1,4 @@
|
||||
import { VolcengineAccess } from "./access.js";
|
||||
import { VolcengineAccess } from "./access.js";
|
||||
import { HttpClient, ILogger } from "@certd/basic";
|
||||
|
||||
export type VolcengineOpts = {
|
||||
|
||||
@@ -1 +1 @@
|
||||
12:32
|
||||
02:38
|
||||
|
||||
@@ -1 +1 @@
|
||||
15:40
|
||||
03:06
|
||||
|
||||
Reference in New Issue
Block a user