diff --git a/AGENTS.md b/AGENTS.md index f3c87f037..efb9fd375 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -59,12 +59,11 @@ Certd 是可私有化部署的 SSL/TLS 证书自动化管理平台,提供 Web ## 常用验证 -- 后端聚焦单测:`corepack pnpm --dir packages\ui\certd-server test:unit`。 -- 后端完整测试:`corepack pnpm --dir packages\ui\certd-server test`。 - 前端改动文件格式化:`packages\ui\certd-client\node_modules\.bin\prettier.cmd --write `。 - 前端改动文件 ESLint 修复:`packages\ui\certd-client\node_modules\.bin\eslint.cmd --fix `。 -- 后端改动文件 lint fix:`corepack pnpm --dir packages\ui\certd-server run lint`。 -- 其他package lint fix:`corepack pnpm --dir packages\xxx\xxxx run lint`。 +- 后端单元测试:`cd packages\ui\certd-server && npm run unit`。 +- 后端改动文件 lint fix:`cd packages\ui\certd-server && npm run lint`。 +- 其他package lint fix:`cd packages\xxx\xxxx && npm run lint`。 ## 通用工作规则 @@ -164,7 +163,6 @@ Certd 是可私有化部署的 SSL/TLS 证书自动化管理平台,提供 Web - 插件依赖的第三方 SDK 可能通过 runtime-deps 动态安装到后端运行目录 `./data/.runtime-deps`。分析阿里云、腾讯云等 SDK 行为时,需要进入该目录阅读实际安装版本代码。 - 修改证书申请、验证、部署或通知行为时,先判断归属:ACME client、pipeline 核心、后端 module/service/entity/controller、具体插件、前端 view/form/schema。 - 单个服务商或部署目标的问题,不要轻易修改共享 pipeline/core;只有可复用公共语义或跨插件一致行为才上移到 `packages/core/pipeline` 或 `packages/plugins/plugin-lib`。 -- ACME / EAB:公共 EAB 可能只能创建一次账号;跨用户复用公共 EAB 时,应保存并复用同一个 ACME account private key。 - `newAccount({ onlyReturnExisting: true })` 可用同一个 account private key 取回已创建账号 URL,且不会再次消费 EAB。 - 修改 EAB `kid` 后,应重新生成绑定该 `kid` 的 account private key;否则应阻止继续申请并提示刷新账号私钥。 - 插件开发前先读对应技能:`.trae/skills/dns-provider-dev/SKILL.md`、`.trae/skills/task-plugin-dev/SKILL.md`、`.trae/skills/access-plugin-dev/SKILL.md`、`.trae/skills/plugin-converter/SKILL.md`。 @@ -206,5 +204,5 @@ Certd 是可私有化部署的 SSL/TLS 证书自动化管理平台,提供 Web - 后端纯单测放在 `src/**/*.test.ts`,尽量与被测文件相邻;`test:unit` 只跑这些文件,构建/打包应排除 `*.test.ts`。 - 单测需要 mock ESM 静态 import 时,优先使用 `esmock`,不要为了测试改业务代码结构。 - 各包 `test:unit` 脚本应显式设置 `NODE_ENV=unittest`。 -- 单包单测优先用 `corepack pnpm --dir <包目录> test:unit`,例如 `corepack pnpm --dir packages\ui\certd-server test:unit`。 +- 单包单测优先用 `cd <包目录> && npm run test:unit`,例如 `cd packages\ui\certd-server && npm run test:unit`。 - 优先对改动包运行聚焦测试或格式化/ESLint;只有跨包影响明显时再考虑更大范围构建。 diff --git a/README.md b/README.md index 93caa2453..999d42294 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,8 @@ https://certd.handfree.work/ 3. 获得专业版功能 -> [50元专业版优惠券限时领取](https://app.handfree.work/subject/#/app/certd/product) https://app.handfree.work/subject/#/app/certd/product +> [50元专业版优惠券限时领取](https://app.handfree.work/subject/#/app/certd/product) +> https://app.handfree.work/subject/#/app/certd/product > app.handfree.work是Certd官方激活码购买平台 diff --git a/packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.test.ts b/packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.test.ts index ad017b065..7c722403d 100644 --- a/packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.test.ts +++ b/packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.test.ts @@ -52,7 +52,7 @@ describe("RuntimeDepsService", () => { } as any; const plugins: RuntimeDependencyPluginDefine[] = [{ name: "a", dependPackages: { foo: "^1.0.0" } }]; - const result = await service.ensureInstalled(plugins); + const result = await service.ensureInstalled({ plugins }); assert.equal(result.registryUrl, "https://registry.npmmirror.com"); assert.ok(fs.existsSync(path.join(rootDir, "package.json"))); @@ -78,7 +78,7 @@ describe("RuntimeDepsService", () => { }, } as any; - await service.ensureDependencies({ directPkg: "^1.0.0" }); + await service.ensureDependencies({ dependencies: { directPkg: "^1.0.0" } }); const manifest = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8")); assert.deepEqual(manifest.dependencies, { directPkg: "^1.0.0" }); @@ -238,8 +238,8 @@ describe("RuntimeDepsService", () => { }, } as any; - await service.ensureInstalled([{ name: "a", pluginType: "deploy", dependPackages: { foo: "^1.0.0" } }]); - await service.ensureInstalled([{ name: "b", pluginType: "deploy", dependPackages: { bar: "^2.0.0" } }]); + await service.ensureInstalled({ plugins: [{ name: "a", pluginType: "deploy", dependPackages: { foo: "^1.0.0" } }] }); + await service.ensureInstalled({ plugins: [{ name: "b", pluginType: "deploy", dependPackages: { bar: "^2.0.0" } }] }); const manifest = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8")); assert.deepEqual(manifest.dependencies, { @@ -399,7 +399,7 @@ describe("RuntimeDepsService", () => { }, } as any; - await service.ensureInstalled([{ name: "a", dependPackages: { foo: "^1.0.0" } }]); + await service.ensureInstalled({ plugins: [{ name: "a", dependPackages: { foo: "^1.0.0" } }] }); const state = JSON.parse(fs.readFileSync(path.join(rootDir, "install-state.json"), "utf8")); assert.equal(state.nodeVersion, process.version); @@ -436,7 +436,7 @@ describe("RuntimeDepsService", () => { serviceA.commandRunner = commandRunner as any; serviceB.commandRunner = commandRunner as any; - await Promise.all([serviceA.ensureInstalled([{ name: "a", dependPackages: { foo: "^1.0.0" } }]), serviceB.ensureInstalled([{ name: "a", dependPackages: { foo: "^1.0.0" } }])]); + await Promise.all([serviceA.ensureInstalled({ plugins: [{ name: "a", dependPackages: { foo: "^1.0.0" } }] }), serviceB.ensureInstalled({ plugins: [{ name: "a", dependPackages: { foo: "^1.0.0" } }] })]); assert.equal(installCount, 1); }); @@ -468,7 +468,7 @@ describe("RuntimeDepsService", () => { }, } as any; - await service.ensureInstalled([{ name: "a", dependPackages: { foo: "^1.0.0" } }]); + await service.ensureInstalled({ plugins: [{ name: "a", dependPackages: { foo: "^1.0.0" } }] }); } finally { if (oldNodeOptions == null) { delete process.env.NODE_OPTIONS; diff --git a/packages/ui/certd-server/src/plugins/plugin-plus/baota/plugins/plugin-deploy-automatch.ts b/packages/ui/certd-server/src/plugins/plugin-plus/baota/plugins/plugin-deploy-automatch.ts index 7feca89c7..9fc1a2064 100644 --- a/packages/ui/certd-server/src/plugins/plugin-plus/baota/plugins/plugin-deploy-automatch.ts +++ b/packages/ui/certd-server/src/plugins/plugin-plus/baota/plugins/plugin-deploy-automatch.ts @@ -1,9 +1,10 @@ import { HttpClient } from "@certd/basic"; -import { AbstractTaskPlugin, CertTargetItem, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline"; +import { CertTargetItem, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline"; import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert"; import { createCertDomainGetterInputDefine } from "@certd/plugin-lib"; -import { BaotaClient } from "../lib/client.js"; +import { AbstractPlusTaskPlugin } from "@certd/plugin-plus"; import { BaotaAccess } from "../access.js"; +import { BaotaClient } from "../lib/client.js"; /** * 宝塔-全自动部署插件 @@ -17,8 +18,9 @@ import { BaotaAccess } from "../access.js"; group: pluginGroups.panel.key, desc: "根据证书域名自动匹配宝塔站点,全自动部署SSL证书。新增加速域名自动感知,自动新增部署", runStrategy: RunStrategy.AlwaysRun, + needPlus: true, }) -export class BaotaAutoDeploySiteCert extends AbstractTaskPlugin { +export class BaotaAutoDeploySiteCert extends AbstractPlusTaskPlugin { /** 域名证书 */ @TaskInput({ title: "域名证书", @@ -37,7 +39,7 @@ export class BaotaAutoDeploySiteCert extends AbstractTaskPlugin { /** 宝塔授权 */ @TaskInput({ title: "宝塔授权", - helper: "baota的接口密钥", + helper: "将自动查找证书匹配的站点,检查证书即将过期的站点并更新", component: { name: "access-selector", type: "baota", @@ -55,7 +57,7 @@ export class BaotaAutoDeploySiteCert extends AbstractTaskPlugin { async onInstance() {} async execute(): Promise { - this.logger.info("开始宝塔全自动部署证书"); + this.logger.info(`开始宝塔全自动部署证书: ${this.certDomains.join(",")}`); const access = await this.getAccess(this.accessId); const http: HttpClient = this.ctx.http; const client = new BaotaClient(access, http); diff --git a/packages/ui/certd-server/test/controller/api.test.ts b/packages/ui/certd-server/test/controller/api.test.ts deleted file mode 100755 index ffc54359a..000000000 --- a/packages/ui/certd-server/test/controller/api.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { createHttpRequest } from "@midwayjs/mock"; -import { Application } from "@midwayjs/koa"; -import assert from "assert"; -import { getApp } from "../setup.js"; - -describe("test/controller/home.test.ts", () => { - it("should POST /api/get_user", async function (this: any) { - const app: Application = getApp(); - // make request - const result = await createHttpRequest(app).get("/api/get_user").query({ uid: 123 }); - - // use expect by jest - assert(result.status === 200); - assert(result.body.message === "OK"); - }); -}); diff --git a/packages/ui/certd-server/test/controller/home.test.ts b/packages/ui/certd-server/test/controller/home.test.ts deleted file mode 100755 index 81d20c390..000000000 --- a/packages/ui/certd-server/test/controller/home.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { createHttpRequest } from "@midwayjs/mock"; -import { Application } from "@midwayjs/koa"; -import assert from "assert"; -import { getApp } from "../setup.js"; - -describe("test/controller/home.test.ts", () => { - it("should GET /", async () => { - const app: Application = getApp(); - // make request - const result = await createHttpRequest(app).get("/"); - - // use expect by jest - assert(result.status === 200); - assert(result.text === "Hello Midwayjs!"); - }); -}); diff --git a/packages/ui/certd-server/test/plugins/51dns.test.mjs b/packages/ui/certd-server/test/plugins/51dns.test.mjs deleted file mode 100644 index 56241d75a..000000000 --- a/packages/ui/certd-server/test/plugins/51dns.test.mjs +++ /dev/null @@ -1,136 +0,0 @@ -import CryptoJS from 'crypto-js' - -function aes(val) { - var k = CryptoJS.enc.Utf8.parse('1234567890abcDEF'); - var iv = CryptoJS.enc.Utf8.parse('1234567890abcDEF'); - const enc = CryptoJS.AES.encrypt(val, k, { - iv: iv, - mode: CryptoJS.mode.CBC, - padding: CryptoJS.pad.ZeroPadding - }).toString(); - return enc; -} - - -import axios from 'axios' - -const instance = axios.create({ - baseURL: 'https://www.51dns.com', - timeout: 5000, - withCredentials: true, - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } -}) - -async function login() { - - const res = await instance.request({ - url: 'https://www.51dns.com/login.html', - method: 'get', - headers: { - // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36', - 'Origin': 'https://www.51dns.com', - 'Referer': 'https://www.51dns.com', - } - }) - - //提取 var csrfToken = "ieOfM21eDd9nWJv3OZtMJF6ogDsnPKQHJ17dlMck"; - const _token = res.data.match(/var csrfToken = "(.*?)"/)[1] - console.log(_token) - console.log(res.headers) - - - const setCookie = res.headers['set-cookie'] - const cookie = setCookie.map(item => { - return item.split(';')[0] - }).join(';') - - - var obj = { - 'email_or_phone': aes(""), - 'password': aes(""), - 'type': aes('account'), - 'redirectTo': 'https://www.51dns.com/domain', - '_token': _token - } - // console.log(JSON.stringify(obj, null, 2)) // Avoid logging sensitive data - const res2 = await instance.request({ - url: 'https://www.51dns.com/login', - method: 'post', - data: { - ...obj - }, - headers: { - /** - * Origin: - * https://www.51dns.com - * Referer: - * https://www.51dns.com/login.html - * User-Agent: - * Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36 - // __root_domain_v=.51dns.com; - */ - - // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36', - 'Origin': 'https://www.51dns.com', - 'Referer': 'https://www.51dns.com/login.html', - 'Content-Type': 'application/x-www-form-urlencoded', - 'Cookie': cookie, - //X-Requested-With: - // XMLHttpRequest - 'X-Requested-With': 'XMLHttpRequest' - } - }) - - console.log(res2.headers) - if (res2.data.code == 0) { - console.log("登录成功") - } - - const setCookie2 = res2.headers['set-cookie'] - const cookie2 = setCookie2.map(item => { - return item.split(';')[0] - }).join(';') - - // - // // console.log(res2.data) - // // 提取 182****43522
- // console.log(res2.data.match(/(.*?)<\/span>/)[1]) - // const success1 = res2.data.includes('DNS解析') - // console.log("success", success1) - - - const res3 = await instance.request({ - url: 'https://www.51dns.com/domain', - method: 'get', - withCredentials: true, - headers: { - // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36', - 'Origin': 'https://www.51dns.com', - 'Referer': 'https://www.51dns.com/login.html', - 'Cookie': cookie2, - } - }) - - console.log(res3.statusText) - console.log(res3.headers) - const success2 = res3.data.includes('DNS解析') - console.log("success", success2) - - - /** - * certd.top - - */ - //上面文本中间有换行,需要提取 193341603 部分,必须有certd.top,使用 new Regexp, .号要能匹配换行符,非贪婪模式 - const regExp = new RegExp(']*>certd\\.top<\\/a>',"i"); - - const domainId = res3.data.match(regExp)[1] - - - console.log("domainId", domainId) -} - -login() diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f888727ff..b55f326ec 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,5 @@ packages: - - 'packages/**' # <--------------开发插件请注释掉这一行,PR时本修改不要提交 + - 'packages/**' # <--------------开发插件请注释掉这一行,PR时本修改不要提交 - 'packages/ui/**' - '!**/test/**' - '!packages/libs/libs/lib-huawei' @@ -8,5 +8,19 @@ packages: # - '!packages/libs/libs/lib-k8s' # k8s引用了basic - '!packages/libs/libs/midway-flyway-js' +# 禁止放开这里的注释,不要配置allowBuilds +# allowBuilds: +# aws-crt: set this to true or false +# better-sqlite3: set this to true or false +# core-js: set this to true or false +# cos-js-sdk-v5: set this to true or false +# cpu-features: set this to true or false +# es5-ext: set this to true or false +# esbuild: set this to true or false +# ssh2: set this to true or false +# unrs-resolver: set this to true or false +# vue-demi: set this to true or false + + linkWorkspacePackages: deep -preferWorkspacePackages: true \ No newline at end of file +preferWorkspacePackages: true