Compare commits

...

21 Commits

Author SHA1 Message Date
HINS 0f3f8519e0 perf: 优化 HiPM DNSMgr 插件,添加域名查询双层策略 (#744) @WUHINS
- 新增 getDomainId() 方法,首选 keyword 直接查询(O(1))
- 列表匹配作为降级方案(向后兼容)
- 性能提升 99%,减少 99% 数据传输
- 与 ddns-go hipmdnsmgr.go 实现保持一致
2026-06-09 23:10:15 +08:00
xiaojunnuo 61e3f5761c build: release 2026-06-06 03:06:48 +08:00
xiaojunnuo 2908569841 chore: 1 2026-06-06 03:02:47 +08:00
xiaojunnuo 775226b49f build: publish 2026-06-06 02:38:20 +08:00
xiaojunnuo e3dacb5b3f build: trigger build image 2026-06-06 02:38:08 +08:00
xiaojunnuo cdea411136 v1.41.1 2026-06-06 02:36:50 +08:00
xiaojunnuo fdb000ee7c build: prepare to build 2026-06-06 02:33:06 +08:00
xiaojunnuo 4a0be1c29d build: prepare to build 2026-06-06 02:31:59 +08:00
xiaojunnuo 892d22e225 chore: 1 2026-06-06 02:30:57 +08:00
xiaojunnuo 4958a48b92 build: prepare to build 2026-06-06 02:21:11 +08:00
xiaojunnuo 28bbea85f0 chore: 1 2026-06-06 02:20:09 +08:00
xiaojunnuo 73b3a29cfc build: prepare to build 2026-06-06 02:15:39 +08:00
xiaojunnuo 77b8024453 perf(volcengine-vke): 火山VKE集群证书支持两种类型的证书保密字典 2026-06-06 02:12:47 +08:00
xiaojunnuo 1175e1164b refactor(ui): 统一使用useMounted钩子简化页面初始化逻辑 2026-06-06 00:50:59 +08:00
xiaojunnuo 5546af518e perf: 优化列表页面请求两次的问题 2026-06-05 00:23:08 +08:00
xiaojunnuo 99fd3083f2 perf: 流水线、监控站点支持导出 2026-06-04 23:43:25 +08:00
xiaojunnuo c0df8be832 perf(settings): 新增NO_PROXY代理排除配置 2026-06-04 23:24:29 +08:00
xiaojunnuo 73cab6a6ee chore(auth): 清理已使用的邀请码缓存 2026-06-04 18:28:15 +08:00
xiaojunnuo 7a71e45799 perf: 优化邀请注册流程 2026-06-04 18:22:21 +08:00
xiaojunnuo fdb1d1e6dd build: publish 2026-06-04 12:32:41 +08:00
xiaojunnuo 6dd4d6adeb build: trigger build image 2026-06-04 12:32:29 +08:00
110 changed files with 1305 additions and 431 deletions
+6 -2
View File
@@ -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/
+79 -1
View File
@@ -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
View File
@@ -3,6 +3,16 @@
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
+35
View File
@@ -3,6 +3,41 @@
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
* 修复阿里云证书订单orderid 选择出错的问题 ([41254d1](https://github.com/certd/certd/commit/41254d10b748a2d3e6ba43c7e11411650c748d1b))
* **monitor:** 修复开放接口自动创建证书流水线重复触发和等待时间不足的问题 ([91d5c90](https://github.com/certd/certd/commit/91d5c90eb0eaf65c81dddbd2d4d4b404cb8b4d07))
* **pipeline:** 修复批量随机修改定时没有生效的bug ([2e19dda](https://github.com/certd/certd/commit/2e19dda72e70b525a7c269e18e963a5ee602f59f))
### Features
* 商业版支持邀请返佣功能 ([f9a310b](https://github.com/certd/certd/commit/f9a310b6c3bbf30f221482a0c59e9c30080bdfc8))
* 商业版支持邀请推广功能 ([f1d2a10](https://github.com/certd/certd/commit/f1d2a1033a0f8d3dbd91fc9793e07bd0b858b539))
* 新增管理员针对用户流水线和证书监控管理功能 ([0211552](https://github.com/certd/certd/commit/021155278e7375f8487b0531ed1b5ad52512f007))
* 新增套餐激活码功能,通过CDK兑换套餐 ([81d6289](https://github.com/certd/certd/commit/81d6289a8631b073b49f24dee4b14bb1c8f31071))
* 新增推广等级激励功能 ([5096df5](https://github.com/certd/certd/commit/5096df5cc0d8f0ad8aa327b8e2a900ba23714bd8))
* 新增证书申请参数模版管理,开放接口支持使用证书参数模版和指定证书申请参数 ([f8b71a0](https://github.com/certd/certd/commit/f8b71a0e612fad527cf49136335e0b46f0f379cd))
* 支持dns-persist-01持久化验证方式申请证书,优化Acme账号的存储方式 ([67b05e2](https://github.com/certd/certd/commit/67b05e2d75e96b9f855e1ca0b3d0d8d03b92d8e6))
### Performance Improvements
* 插件全局配置支持下拉选项自定义映射功能 ([c637985](https://github.com/certd/certd/commit/c637985575b09196b04cce37ac14fbe68c029bde))
* 商业版提现增加收款二维码上传 ([83a5a21](https://github.com/certd/certd/commit/83a5a21f956e50942541f1532f3a8dcaa5821d34))
* **aliyun-apig:** 优化阿里云API网关部署插件的查询及翻页 ([3e4b7f3](https://github.com/certd/certd/commit/3e4b7f30ac6f3c976c8274bdf256c69b8a2c46db))
* **trade:** 优化商品购买页面的规格展示和折扣计算,支持订单取消 ([6624769](https://github.com/certd/certd/commit/66247690326ce2789900fc9110c08b3502cea655))
## [1.40.5](https://github.com/certd/certd/compare/v1.40.4...v1.40.5) (2026-05-26)
### Bug Fixes
+1 -1
View File
@@ -9,5 +9,5 @@
}
},
"npmClient": "pnpm",
"version": "1.41.0"
"version": "1.41.1"
}
+4
View File
@@ -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 -3
View File
@@ -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": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
}
+6
View File
@@ -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
View File
@@ -1 +1 @@
12:22
02:33
+2 -2
View File
@@ -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": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"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);
});
});
+174 -28
View File
@@ -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) => {
+4
View File
@@ -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
+4 -4
View File
@@ -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": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
}
+4
View File
@@ -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
+2 -2
View File
@@ -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": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
}
+4
View File
@@ -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
+2 -2
View File
@@ -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": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
}
+4
View File
@@ -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
+2 -2
View File
@@ -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": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
}
+4
View File
@@ -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
+3 -3
View File
@@ -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": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
}
+6
View File
@@ -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
+7 -7
View File
@@ -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": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"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
+2 -2
View File
@@ -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": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"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
+6 -6
View File
@@ -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": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
}
+4
View File
@@ -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
+6 -6
View File
@@ -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": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
"gitHead": "cdea411136fdf56352699a6e278a403e0f53a94f"
}
+9
View File
@@ -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
+3 -3
View File
@@ -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优先",
@@ -107,6 +107,9 @@ function install(app: App, options: any = {}) {
scroll: {
x: 960,
},
rowSelection: {
fixed: "left",
},
size: "small",
pagination: false,
onResizeColumn: (w: number, col: any) => {
@@ -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,
},
},
{
@@ -389,7 +392,7 @@ export const certdResources = [
meta: {
show: () => {
const settingStore = useSettingStore();
return settingStore.isComm;
return settingStore.isInviteCommissionEnabled;
},
icon: "ion:gift-outline",
auth: 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;
@@ -15,6 +15,7 @@ import { resetAllStores, useAccessStore } from "/@/vben/stores";
import { useUserStore as vbenUserStore } from "/@/vben/stores/modules/user";
import { request } from "/@/api/service";
import { inviteUtils } from "/@/utils/util.invite";
interface UserState {
userInfo: Nullable<UserInfoRes>;
@@ -66,6 +67,9 @@ export const useUserStore = defineStore({
},
async register(user: RegisterReq) {
await UserApi.register(user);
if (user.inviteCode) {
inviteUtils.clear();
}
notification.success({
message: "注册成功,请登录",
});
@@ -85,6 +89,9 @@ export const useUserStore = defineStore({
let loginRes: any = null;
if (loginType === "sms") {
loginRes = await UserApi.loginBySms(params as SmsLoginReq);
if ((params as SmsLoginReq).inviteCode) {
inviteUtils.clear();
}
} else {
loginRes = await UserApi.login(params as LoginReq);
}
@@ -136,12 +143,12 @@ export const useUserStore = defineStore({
} catch (e) {
console.error("注销登录请求失败:", e);
}
// 第三方登录注销
this.oauthLogout();
}
this.resetState();
resetAllStores();
// 第三方登录注销
await this.oauthLogout();
goLogin && router.push("/login");
mitter.emit("app.logout");
},
@@ -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();
}
});
});
}
@@ -41,6 +41,10 @@ export const inviteUtils = {
}
},
clear() {
localStorage.removeItem(INVITE_STORAGE_KEY);
},
captureFromLocation() {
const hashQuery = window.location.hash?.split("?")[1] || "";
const search = window.location.search?.replace(/^\?/, "") || "";
@@ -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>
@@ -300,7 +300,7 @@ async function handleTabChange() {
}
async function refreshInvitePage(autoOpenAgreement = true) {
await settingStore.initOnce();
await settingStore.init();
enabled.value = settingStore.isInviteCommissionEnabled;
loaded.value = true;
if (!enabled.value) {
@@ -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();
@@ -26,13 +26,14 @@ export async function OauthToken(type: string, validationCode: string) {
});
}
export async function AutoRegister(type: string, code: string) {
export async function AutoRegister(type: string, code: string, inviteCode?: string) {
return await request({
url: apiPrefix + `/autoRegister`,
method: "post",
data: {
validationCode: code,
type,
inviteCode,
},
});
}
@@ -29,6 +29,7 @@ import { useRoute, useRouter } from "vue-router";
import { useUserStore } from "/@/store/user";
import { notification } from "ant-design-vue";
import { useSettingStore } from "/@/store/settings";
import { inviteUtils } from "/@/utils/util.invite";
const route = useRoute();
const router = useRouter();
@@ -99,7 +100,11 @@ async function goBindUser() {
async function autoRegister() {
//
const res = await api.AutoRegister(oauthType, bindCode.value);
const inviteCode = inviteUtils.get();
const res = await api.AutoRegister(oauthType, bindCode.value, inviteCode);
if (inviteCode) {
inviteUtils.clear();
}
//
userStore.onLoginSuccess(res);
//
@@ -215,6 +215,8 @@ export default defineComponent({
const handleFinish = async (values: any) => {
try {
//
userStore.resetState();
await userStore.register(
toRaw({
type: registerType.value,
@@ -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() {
// permissioncommonOptionsactionbarrowHandleshow
@@ -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({
// commonOptionsactionbarrowHandleshow
// ./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>
+8
View File
@@ -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
+14 -14
View File
@@ -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",
@@ -8,7 +8,6 @@ import { LoginService } from "../../../modules/login/service/login-service.js";
import { OauthBoundService } from "../../../modules/login/service/oauth-bound-service.js";
import { AddonGetterService } from "../../../modules/pipeline/service/addon-getter-service.js";
import { UserEntity } from "../../../modules/sys/authority/entity/user.js";
import { UserService } from "../../../modules/sys/authority/service/user-service.js";
import { IOauthProvider } from "../../../plugins/plugin-oauth/api.js";
type OauthProviderSetting = {
@@ -42,8 +41,6 @@ export class ConnectController extends BaseController {
loginService: LoginService;
@Inject()
codeService: CodeService;
@Inject()
userService: UserService;
@Inject()
oauthBoundService: OauthBoundService;
@@ -199,7 +196,7 @@ export class ConnectController extends BaseController {
}
@Post("/autoRegister", { description: Constants.per.guest })
public async autoRegister(@Body(ALL) body: { validationCode: string; type: string }) {
public async autoRegister(@Body(ALL) body: { validationCode: string; type: string; inviteCode?: string }) {
const validationValue = this.codeService.getValidationValue(body.validationCode);
if (!validationValue) {
throw new Error("第三方认证授权已过期");
@@ -212,7 +209,7 @@ export class ConnectController extends BaseController {
newUser.nickName = userInfo.nickName || simpleNanoId(6);
newUser.email = userInfo.email || "";
newUser = await this.userService.register("username", newUser, async txManager => {
newUser = await this.loginService.register("username", newUser, body.inviteCode, async txManager => {
const oauthBound: OauthBoundEntity = new OauthBoundEntity();
oauthBound.userId = newUser.id;
oauthBound.type = oauthType;
@@ -1,9 +1,9 @@
import { ALL, Body, Controller, Inject, Post, Provide, RequestIP } from "@midwayjs/core";
import { BaseController, Constants, SysSettingsService } from "@certd/lib-server";
import { RegisterType, UserService } from "../../../modules/sys/authority/service/user-service.js";
import { RegisterType } from "../../../modules/sys/authority/service/user-service.js";
import { CodeService } from "../../../modules/basic/service/code-service.js";
import { checkComm, checkPlus } from "@certd/plus-core";
import { InviteService } from "@certd/commercial-core";
import { LoginService } from "../../../modules/login/service/login-service.js";
export type RegisterReq = {
type: RegisterType;
@@ -24,16 +24,13 @@ export type RegisterReq = {
@Controller("/api/")
export class RegisterController extends BaseController {
@Inject()
userService: UserService;
loginService: LoginService;
@Inject()
codeService: CodeService;
@Inject()
sysSettingsService: SysSettingsService;
@Inject()
inviteService: InviteService;
@Post("/register", { description: Constants.per.guest })
public async register(
@Body(ALL)
@@ -62,9 +59,7 @@ export class RegisterController extends BaseController {
username: body.username,
password: body.password,
} as any;
const newUser = await this.userService.register(body.type, registerUser, async txManager => {
await this.inviteService.bindInvitee({ manager: txManager }, { inviteeUserId: registerUser.id, inviteCode: body.inviteCode });
});
const newUser = await this.loginService.register(body.type, registerUser, body.inviteCode);
return this.ok(newUser);
} else if (body.type === "mobile") {
if (sysPublicSettings.mobileRegisterEnabled === false) {
@@ -84,9 +79,7 @@ export class RegisterController extends BaseController {
mobile: body.mobile,
password: body.password,
} as any;
const newUser = await this.userService.register(body.type, registerUser, async txManager => {
await this.inviteService.bindInvitee({ manager: txManager }, { inviteeUserId: registerUser.id, inviteCode: body.inviteCode });
});
const newUser = await this.loginService.register(body.type, registerUser, body.inviteCode);
return this.ok(newUser);
} else if (body.type === "email") {
if (sysPublicSettings.emailRegisterEnabled === false) {
@@ -103,9 +96,7 @@ export class RegisterController extends BaseController {
email: body.email,
password: body.password,
} as any;
const newUser = await this.userService.register(body.type, registerUser, async txManager => {
await this.inviteService.bindInvitee({ manager: txManager }, { inviteeUserId: registerUser.id, inviteCode: body.inviteCode });
});
const newUser = await this.loginService.register(body.type, registerUser, body.inviteCode);
return this.ok(newUser);
}
}

Some files were not shown because too many files have changed in this diff Show More