From 79df39acabab10ae7e1864dadcdc186bb007a3c5 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Wed, 16 Apr 2025 09:34:04 +0800 Subject: [PATCH 01/14] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E9=83=A8?= =?UTF-8?q?=E7=BD=B2=E5=88=B0=E5=8D=8E=E4=B8=BA=E4=BA=91CDN=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=85=88=E4=B8=8A=E4=BC=A0=E5=88=B0ccm?= =?UTF-8?q?=EF=BC=8C=E5=86=8D=E4=BD=BF=E7=94=A8=E8=AF=81=E4=B9=A6id?= =?UTF-8?q?=E9=83=A8=E7=BD=B2=EF=BC=8C=E4=BF=AE=E5=A4=8Doffline=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E4=B8=8B=E5=AF=BC=E8=87=B4=E9=83=A8=E7=BD=B2=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../plugins/deploy-to-cdn/index.ts | 48 +++++++++++++++---- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/deploy-to-cdn/index.ts b/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/deploy-to-cdn/index.ts index 5827b37b..32a916ef 100644 --- a/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/deploy-to-cdn/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/deploy-to-cdn/index.ts @@ -22,11 +22,11 @@ export class HauweiDeployCertToCDN extends AbstractTaskPlugin { helper: '请选择前置任务输出的域名证书', component: { name: 'output-selector', - from: [...CertApplyPluginNames], + from: [...CertApplyPluginNames,'HauweiUploadToCCM'], }, required: true, }) - cert!: CertInfo; + cert!: CertInfo | string; @TaskInput(createCertDomainGetterInputDefine({ props: { required: false } })) certDomains!: string[]; @@ -53,14 +53,25 @@ export class HauweiDeployCertToCDN extends AbstractTaskPlugin { domains!: string[]; async execute(): Promise { + if (!this.cert) { + throw new Error('域名证书不能为空'); + } this.logger.info('开始部署证书到华为云cdn'); const { cdn, client } = await this.getCdnClient(); - const httpsConfig = new cdn.HttpPutBody() + let httpsConfig = new cdn.HttpPutBody() .withHttpsStatus('on') .withCertificateType('server') - .withCertificateName(this.appendTimeSuffix('certd')) - .withCertificateValue(this.cert.crt) - .withPrivateKey(this.cert.key); + + if(typeof this.cert === 'object'){ + httpsConfig= httpsConfig.withCertificateSource(0) + .withCertificateName(this.appendTimeSuffix('certd')) + .withCertificateValue(this.cert.crt) + .withPrivateKey(this.cert.key); + }else{ + this.logger.info('使用已有域名证书:', this.cert); + httpsConfig= httpsConfig.withCertificateSource(2)//scm证书 + .withScmCertificateId(this.cert) + } const config = new cdn.Configs().withHttps(httpsConfig); const body = new cdn.ModifyDomainConfigRequestBody().withConfigs(config); @@ -70,9 +81,28 @@ export class HauweiDeployCertToCDN extends AbstractTaskPlugin { this.logger.info('部署域名:', JSON.stringify(this.domains)); for (const domain of this.domains) { this.logger.info('部署到域名:', domain); - const req = new cdn.UpdateDomainFullConfigRequest().withDomainName(domain).withBody(body); - await client.updateDomainFullConfig(req); - this.logger.info(`部署到域名${domain}完成:`); + + const queryReq = new cdn.ShowDomainDetailByNameRequest(domain); + const domainDetail = await client.showDomainDetailByName(queryReq); + //@ts-ignore + const status = domainDetail.domain.domainStatus || domainDetail.domain.domain_status + this.logger.info(`当前域名状态:`, status); + let ignoreError = false + if (status === 'offline') { + ignoreError = true + } + try{ + const req = new cdn.UpdateDomainFullConfigRequest().withDomainName(domain).withBody(body); + await client.updateDomainFullConfig(req); + this.logger.info(`部署到域名${domain}完成:`); + }catch (e) { + if (ignoreError){ + this.logger.warn(`部署到域名${domain}失败,由于其处于offline状态,忽略部署错误,继续执行:`, e); + }else{ + throw e + } + } + } this.logger.info('部署证书到华为云cdn完成'); From 0f82cf409bc60706ab07e4ca4f272b9a1ca7eecb Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Thu, 17 Apr 2025 00:06:49 +0800 Subject: [PATCH 02/14] =?UTF-8?q?perf:=20=E5=A4=9A=E9=87=8D=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/core/basic/src/utils/util.id.ts | 2 +- .../src/views/certd/settings/api.ts | 26 -- .../src/views/certd/settings/index.vue | 74 ------ .../views/certd/settings/two-factor/api.ts | 40 +++ .../views/certd/settings/two-factor/index.vue | 99 ++++++++ packages/ui/certd-server/package.json | 2 + .../mine/setting-two-factor-controller.ts | 61 +++++ .../user/mine/user-settings-controller.ts | 11 +- .../src/modules/mine/service/models.ts | 22 ++ .../mine/service/two-factor-service.ts | 62 +++++ .../mine/service/user-settings-service.ts | 62 ++++- pnpm-lock.yaml | 235 ++++++++---------- 12 files changed, 453 insertions(+), 243 deletions(-) delete mode 100644 packages/ui/certd-client/src/views/certd/settings/api.ts delete mode 100644 packages/ui/certd-client/src/views/certd/settings/index.vue create mode 100644 packages/ui/certd-client/src/views/certd/settings/two-factor/api.ts create mode 100644 packages/ui/certd-client/src/views/certd/settings/two-factor/index.vue create mode 100644 packages/ui/certd-server/src/controller/user/mine/setting-two-factor-controller.ts create mode 100644 packages/ui/certd-server/src/modules/mine/service/models.ts create mode 100644 packages/ui/certd-server/src/modules/mine/service/two-factor-service.ts diff --git a/packages/core/basic/src/utils/util.id.ts b/packages/core/basic/src/utils/util.id.ts index ba508752..4dd7d2f6 100644 --- a/packages/core/basic/src/utils/util.id.ts +++ b/packages/core/basic/src/utils/util.id.ts @@ -1,4 +1,4 @@ import { customAlphabet } from "nanoid"; export const randomNumber = customAlphabet("1234567890", 4); -export const simpleNanoId = customAlphabet("1234567890abcdefghijklmopqrstuvwxyz", 12); +export const simpleNanoId = customAlphabet("1234567890abcdefghijklmopqrstuvwxyzABCDEFGHIJKLMOPQRSTUVWXYZ", 12); diff --git a/packages/ui/certd-client/src/views/certd/settings/api.ts b/packages/ui/certd-client/src/views/certd/settings/api.ts deleted file mode 100644 index 295aee24..00000000 --- a/packages/ui/certd-client/src/views/certd/settings/api.ts +++ /dev/null @@ -1,26 +0,0 @@ -// @ts-ignore -import { request } from "/@/api/service"; -const apiPrefix = "/user/settings"; -export type UserSettings = { - defaultNotification?: number; - defaultCron?: string; -}; - -export async function UserSettingsGet() { - const res = await request({ - url: apiPrefix + "/getDefault", - method: "post", - }); - if (!res) { - return {}; - } - return res; -} - -export async function UserSettingsSave(setting: any) { - return await request({ - url: apiPrefix + "/saveDefault", - method: "post", - data: setting, - }); -} diff --git a/packages/ui/certd-client/src/views/certd/settings/index.vue b/packages/ui/certd-client/src/views/certd/settings/index.vue deleted file mode 100644 index f2344e22..00000000 --- a/packages/ui/certd-client/src/views/certd/settings/index.vue +++ /dev/null @@ -1,74 +0,0 @@ - - - - - diff --git a/packages/ui/certd-client/src/views/certd/settings/two-factor/api.ts b/packages/ui/certd-client/src/views/certd/settings/two-factor/api.ts new file mode 100644 index 00000000..2529af72 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/settings/two-factor/api.ts @@ -0,0 +1,40 @@ +// @ts-ignore +import { request } from "/@/api/service"; +const apiPrefix = "/user/settings"; +export type UserTwoFactorSetting = { + authenticator: { + enabled: boolean; + verified: boolean; + }; +}; + +export type AuthenticatorSaveReq = { + verifyCode?: string; +}; + +export async function TwoFactorSettingsGet() { + const res = await request({ + url: apiPrefix + "/twoFactor/get", + method: "post", + }); + if (!res) { + return {}; + } + return res as UserTwoFactorSetting; +} + +export async function TwoFactorAuthenticatorGet() { + const res = await request({ + url: apiPrefix + "/twoFactor/authenticator/qrcode", + method: "post", + }); + return res as string; //base64 +} + +export async function TwoFactorAuthenticatorSave(req: AuthenticatorSaveReq) { + return await request({ + url: apiPrefix + "/twoFactor/authenticator/save", + method: "post", + data: req, + }); +} diff --git a/packages/ui/certd-client/src/views/certd/settings/two-factor/index.vue b/packages/ui/certd-client/src/views/certd/settings/two-factor/index.vue new file mode 100644 index 00000000..eb699161 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/settings/two-factor/index.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/packages/ui/certd-server/package.json b/packages/ui/certd-server/package.json index 1e571479..f8c26796 100644 --- a/packages/ui/certd-server/package.json +++ b/packages/ui/certd-server/package.json @@ -97,9 +97,11 @@ "nanoid": "^5.0.7", "node-forge": "^1.3.1", "nodemailer": "^6.9.16", + "otplib": "^12.0.1", "pg": "^8.12.0", "psl": "^1.9.0", "qiniu": "^7.12.0", + "qrcode": "^1.5.4", "qs": "^6.13.1", "querystring": "^0.2.1", "reflect-metadata": "^0.2.2", diff --git a/packages/ui/certd-server/src/controller/user/mine/setting-two-factor-controller.ts b/packages/ui/certd-server/src/controller/user/mine/setting-two-factor-controller.ts new file mode 100644 index 00000000..0b30f97f --- /dev/null +++ b/packages/ui/certd-server/src/controller/user/mine/setting-two-factor-controller.ts @@ -0,0 +1,61 @@ +import { ALL, Body, Controller, Inject, Post, Provide } from "@midwayjs/core"; +import { BaseController, Constants } from "@certd/lib-server"; +import { UserSettingsService } from "../../../modules/mine/service/user-settings-service.js"; +import { UserTwoFactorSetting } from "../../../modules/mine/service/models.js"; +import { merge } from "lodash-es"; +import { TwoFactorService } from "../../../modules/mine/service/two-factor-service.js"; + +/** + */ +@Provide() +@Controller("/api/user/settings/twoFactor") +export class UserTwoFactorSettingController extends BaseController { + @Inject() + service: UserSettingsService; + + @Inject() + twoFactorService: TwoFactorService; + + + + @Post("/get", { summary: Constants.per.authOnly }) + async get() { + const userId = this.getUserId(); + const setting = await this.service.getSetting(userId, UserTwoFactorSetting); + return this.ok(setting); + } + + @Post("/save", { summary: Constants.per.authOnly }) + async save(@Body(ALL) bean: any) { + const userId = this.getUserId(); + const setting = new UserTwoFactorSetting(); + merge(setting, bean); + + // 禁用时清除 + if(!setting.authenticator.enabled){ + setting.authenticator.secret = null; + setting.authenticator.verified = false; + } + + await this.service.saveSetting(userId, setting); + return this.ok({}); + } + + @Post("/authenticator/qrcode", { summary: Constants.per.authOnly }) + async authenticatorQrcode() { + const userId = this.getUserId(); + const qrcode = await this.twoFactorService.getAuthenticatorQrCode(userId); + return this.ok(qrcode); + } + + @Post("/authenticator/save", { summary: Constants.per.authOnly }) + async authenticatorSave(@Body(ALL) bean: any) { + const userId = this.getUserId(); + await this.twoFactorService.saveAuthenticator({ + userId, + verifyCode: bean.verifyCode, + }); + return this.ok(); + } + +} diff --git a/packages/ui/certd-server/src/controller/user/mine/user-settings-controller.ts b/packages/ui/certd-server/src/controller/user/mine/user-settings-controller.ts index b4a53bdc..db2a3a29 100644 --- a/packages/ui/certd-server/src/controller/user/mine/user-settings-controller.ts +++ b/packages/ui/certd-server/src/controller/user/mine/user-settings-controller.ts @@ -1,8 +1,7 @@ -import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core'; -import { CrudController } from '@certd/lib-server'; -import { Constants } from '@certd/lib-server'; -import { UserSettingsService } from '../../../modules/mine/service/user-settings-service.js'; -import { UserSettingsEntity } from '../../../modules/mine/entity/user-settings.js'; +import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/core"; +import { Constants, CrudController } from "@certd/lib-server"; +import { UserSettingsService } from "../../../modules/mine/service/user-settings-service.js"; +import { UserSettingsEntity } from "../../../modules/mine/entity/user-settings.js"; /** */ @@ -66,4 +65,6 @@ export class UserSettingsController extends CrudController const entity = await this.service.getByKey(key, this.getUserId()); return this.ok(entity); } + + } diff --git a/packages/ui/certd-server/src/modules/mine/service/models.ts b/packages/ui/certd-server/src/modules/mine/service/models.ts new file mode 100644 index 00000000..d74b7eb4 --- /dev/null +++ b/packages/ui/certd-server/src/modules/mine/service/models.ts @@ -0,0 +1,22 @@ +import { BaseSettings } from "@certd/lib-server"; + +export type TwoFactorAuthenticator = { + enabled: boolean; + secret?: string; + type?: string; + verified?:boolean; +} + +export class UserTwoFactorSetting extends BaseSettings { + static __title__ = "用户多重认证设置"; + static __key__ = "user.two.factor"; + + authenticator: TwoFactorAuthenticator = { + enabled:false, + verified:false, + type: "totp" + }; + +} + + diff --git a/packages/ui/certd-server/src/modules/mine/service/two-factor-service.ts b/packages/ui/certd-server/src/modules/mine/service/two-factor-service.ts new file mode 100644 index 00000000..60075563 --- /dev/null +++ b/packages/ui/certd-server/src/modules/mine/service/two-factor-service.ts @@ -0,0 +1,62 @@ +import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core"; +import { UserSettingsService } from "./user-settings-service.js"; +import { UserTwoFactorSetting } from "./models.js"; +import { utils } from "@certd/basic"; +import { UserService } from "../../sys/authority/service/user-service.js"; + +/** + * 授权 + */ +@Provide() +@Scope(ScopeEnum.Request, { allowDowngrade: true }) +export class TwoFactorService { + @Inject() + userSettingsService: UserSettingsService; + @Inject() + userService: UserService; + + + async getAuthenticatorQrCode(userId: any) { + const setting = await this.userSettingsService.getSetting(userId, UserTwoFactorSetting); + + const authenticator = setting.authenticator; + if (!authenticator.secret) { + authenticator.secret = utils.id.simpleNanoId(16); + await this.userSettingsService.saveSetting(userId, setting); + } + + const user = await this.userService.info(userId); + const username = user.username; + const secret = authenticator.secret; + const qrcodeContent = `otpauth://totp/Certd:${username}?secret=${secret}&issuer=Certd`; + + //生成qrcode base64 + const qrcode = await import("qrcode"); + return await qrcode.toDataURL(qrcodeContent); + + } + + async saveAuthenticator(req: { userId: any; verifyCode: any }) { + const userId = req.userId; + const { authenticator } = await import("otplib"); + const tfSetting = await this.userSettingsService.getSetting(userId, UserTwoFactorSetting); + + const setting = tfSetting.authenticator; + if (!setting.secret) { + throw new Error("secret is required"); + } + const secret = setting.secret; + const token = req.verifyCode; + + const isValid = authenticator.verify({ token, secret }); + if (!isValid) { + throw new Error("authenticator 校验错误"); + } + + //校验成功,保存开启状态 + setting.enabled = true; + setting.verified = true; + + await this.userSettingsService.saveSetting(userId, setting); + } +} diff --git a/packages/ui/certd-server/src/modules/mine/service/user-settings-service.ts b/packages/ui/certd-server/src/modules/mine/service/user-settings-service.ts index 70fbbf5d..a61ebd5f 100644 --- a/packages/ui/certd-server/src/modules/mine/service/user-settings-service.ts +++ b/packages/ui/certd-server/src/modules/mine/service/user-settings-service.ts @@ -1,8 +1,9 @@ -import { Provide, Scope, ScopeEnum } from '@midwayjs/core'; -import { InjectEntityModel } from '@midwayjs/typeorm'; -import { Repository } from 'typeorm'; -import { BaseService } from '@certd/lib-server'; -import { UserSettingsEntity } from '../entity/user-settings.js'; +import { Provide, Scope, ScopeEnum } from "@midwayjs/core"; +import { InjectEntityModel } from "@midwayjs/typeorm"; +import { Repository } from "typeorm"; +import { BaseService, BaseSettings } from "@certd/lib-server"; +import { UserSettingsEntity } from "../entity/user-settings.js"; +import { merge } from "lodash-es"; /** * 授权 @@ -27,23 +28,29 @@ export class UserSettingsService extends BaseService { const setting = JSON.parse(entity.setting); return { id: entity.id, - ...setting, + ...setting }; } async getByKey(key: string, userId: number): Promise { + if(!userId){ + throw new Error('userId is required'); + } if (!key || !userId) { return null; } return await this.repository.findOne({ where: { key, - userId, - }, + userId + } }); } async getSettingByKey(key: string, userId: number): Promise { + if(!userId){ + throw new Error('userId is required'); + } const entity = await this.getByKey(key, userId); if (!entity) { return null; @@ -55,8 +62,8 @@ export class UserSettingsService extends BaseService { const entity = await this.repository.findOne({ where: { key: bean.key, - userId: bean.userId, - }, + userId: bean.userId + } }); if (entity) { entity.setting = bean.setting; @@ -66,4 +73,39 @@ export class UserSettingsService extends BaseService { await this.repository.save(bean); } } + + + async getSetting( userId: number,type: any): Promise { + if(!userId){ + throw new Error('userId is required'); + } + const key = type.__key__; + let newSetting: T = new type(); + const savedSettings = await this.getSettingByKey(key, userId); + newSetting = merge(newSetting, savedSettings); + return newSetting; + } + + async saveSetting(userId:number,bean: T) { + if(!userId){ + throw new Error('userId is required'); + } + const old = await this.getSetting(userId,bean.constructor) + bean = merge(old,bean) + + const type: any = bean.constructor; + const key = type.__key__; + const entity = await this.getByKey(key,userId); + const newEntity = new UserSettingsEntity(); + if (entity) { + newEntity.id = entity.id; + }else{ + newEntity.key = key; + newEntity.title = type.__title__; + newEntity.userId = userId; + } + entity.setting = JSON.stringify(bean); + await this.repository.save(entity); + } + } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d7f73fc7..ceafc151 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,7 +46,7 @@ importers: packages/core/acme-client: dependencies: '@certd/basic': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../basic '@peculiar/x509': specifier: ^1.11.0 @@ -204,11 +204,11 @@ importers: packages/core/pipeline: dependencies: '@certd/basic': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../basic '@certd/plus-core': - specifier: ^1.33.2 - version: 1.33.2 + specifier: ^1.33.4 + version: link:../../pro/plus-core dayjs: specifier: ^1.11.7 version: 1.11.13 @@ -412,7 +412,7 @@ importers: packages/libs/lib-k8s: dependencies: '@certd/basic': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../core/basic '@kubernetes/client-node': specifier: 0.21.0 @@ -452,17 +452,17 @@ importers: packages/libs/lib-server: dependencies: '@certd/acme-client': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../core/acme-client '@certd/basic': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../core/basic '@certd/pipeline': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../core/pipeline '@certd/plus-core': - specifier: ^1.33.2 - version: 1.33.2 + specifier: ^1.33.4 + version: link:../../pro/plus-core '@midwayjs/cache': specifier: ~3.14.0 version: 3.14.0 @@ -604,16 +604,16 @@ importers: packages/plugins/plugin-cert: dependencies: '@certd/acme-client': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../core/acme-client '@certd/basic': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../core/basic '@certd/pipeline': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../core/pipeline '@certd/plugin-lib': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../plugin-lib '@google-cloud/publicca': specifier: ^1.3.0 @@ -680,10 +680,10 @@ importers: specifier: ^1.7.10 version: 1.8.0 '@certd/basic': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../core/basic '@certd/pipeline': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../core/pipeline '@kubernetes/client-node': specifier: 0.21.0 @@ -771,19 +771,19 @@ importers: packages/pro/commercial-core: dependencies: '@certd/basic': - specifier: ^1.32.0 + specifier: ^1.33.4 version: link:../../core/basic '@certd/lib-server': - specifier: ^1.32.0 + specifier: ^1.33.4 version: link:../../libs/lib-server '@certd/pipeline': - specifier: ^1.32.0 + specifier: ^1.33.4 version: link:../../core/pipeline '@certd/plugin-plus': - specifier: ^1.32.0 + specifier: ^1.33.4 version: link:../plugin-plus '@certd/plus-core': - specifier: ^1.32.0 + specifier: ^1.33.4 version: link:../plus-core '@midwayjs/core': specifier: ~3.20.3 @@ -868,22 +868,22 @@ importers: specifier: ^1.0.2 version: 1.0.2 '@certd/basic': - specifier: ^1.32.0 + specifier: ^1.33.4 version: link:../../core/basic '@certd/lib-k8s': - specifier: ^1.32.0 + specifier: ^1.33.4 version: link:../../libs/lib-k8s '@certd/pipeline': - specifier: ^1.32.0 + specifier: ^1.33.4 version: link:../../core/pipeline '@certd/plugin-cert': - specifier: ^1.32.0 + specifier: ^1.33.4 version: link:../../plugins/plugin-cert '@certd/plugin-lib': - specifier: ^1.32.0 + specifier: ^1.33.4 version: link:../../plugins/plugin-lib '@certd/plus-core': - specifier: ^1.32.0 + specifier: ^1.33.4 version: link:../plus-core ali-oss: specifier: ^6.21.0 @@ -980,7 +980,7 @@ importers: packages/pro/plus-core: dependencies: '@certd/basic': - specifier: ^1.32.0 + specifier: ^1.33.4 version: link:../../core/basic dayjs: specifier: ^1.11.7 @@ -1270,10 +1270,10 @@ importers: version: 0.1.3(zod@3.24.2) devDependencies: '@certd/lib-iframe': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../libs/lib-iframe '@certd/pipeline': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../core/pipeline '@rollup/plugin-commonjs': specifier: ^25.0.7 @@ -1453,44 +1453,44 @@ importers: specifier: ^3.705.0 version: 3.758.0(aws-crt@1.25.3) '@certd/acme-client': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../core/acme-client '@certd/basic': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../core/basic '@certd/commercial-core': - specifier: ^1.33.2 - version: 1.33.2(better-sqlite3@11.8.1)(encoding@0.1.13)(mysql2@3.14.0)(pg@8.13.3)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@18.19.80)(typescript@5.8.2)) + specifier: ^1.33.4 + version: link:../../pro/commercial-core '@certd/jdcloud': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../libs/lib-jdcloud '@certd/lib-huawei': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../libs/lib-huawei '@certd/lib-k8s': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../libs/lib-k8s '@certd/lib-server': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../libs/lib-server '@certd/midway-flyway-js': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../libs/midway-flyway-js '@certd/pipeline': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../core/pipeline '@certd/plugin-cert': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../plugins/plugin-cert '@certd/plugin-lib': - specifier: ^1.33.2 + specifier: ^1.33.4 version: link:../../plugins/plugin-lib '@certd/plugin-plus': - specifier: ^1.33.2 - version: 1.33.2(encoding@0.1.13) + specifier: ^1.33.4 + version: link:../../pro/plugin-plus '@certd/plus-core': - specifier: ^1.33.2 - version: 1.33.2 + specifier: ^1.33.4 + version: link:../../pro/plus-core '@corsinvest/cv4pve-api-javascript': specifier: ^8.3.0 version: 8.3.0 @@ -1629,6 +1629,9 @@ importers: nodemailer: specifier: ^6.9.16 version: 6.10.0 + otplib: + specifier: ^12.0.1 + version: 12.0.1 pg: specifier: ^8.12.0 version: 8.13.3 @@ -1638,6 +1641,9 @@ importers: qiniu: specifier: ^7.12.0 version: 7.14.0 + qrcode: + specifier: ^1.5.4 + version: 1.5.4 qs: specifier: ^6.13.1 version: 6.14.0 @@ -2620,15 +2626,6 @@ packages: '@better-scroll/zoom@2.5.1': resolution: {integrity: sha512-aGvFY5ooeZWS4RcxQLD+pGLpQHQxpPy0sMZV3yadcd2QK53PK9gS4Dp+BYfRv8lZ4/P2LoNEhr6Wq1DN6+uPlA==} - '@certd/commercial-core@1.33.2': - resolution: {integrity: sha512-LTRvwRAkMEU+knG+/eA8QbyK3EE2Z2eyn4763ILadCY/qHLAWqxg7NUD+fwsuAoByvsr3l5qLPPOF73p5s1iEA==} - - '@certd/plugin-plus@1.33.2': - resolution: {integrity: sha512-x8qdJ1qtYfqVNBQ3uWJuFVYUHHnteoaH/3vnYKjgAnEAcosruZAz8h5RwwOjDhR+Vsdda/sSRdjlTCVDLidHZg==} - - '@certd/plus-core@1.33.2': - resolution: {integrity: sha512-CsD+/P3Ycne3KzxrZAkFXGpCO9ZQNY4p28WAA2XfKQ6sLIYLuuDBP18V/E7EygfJEZ/c9KJWMkYgx/4wGGvb6w==} - '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -3787,6 +3784,21 @@ packages: '@one-ini/wasm@0.1.1': resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + '@otplib/core@12.0.1': + resolution: {integrity: sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA==} + + '@otplib/plugin-crypto@12.0.1': + resolution: {integrity: sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g==} + + '@otplib/plugin-thirty-two@12.0.1': + resolution: {integrity: sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA==} + + '@otplib/preset-default@12.0.1': + resolution: {integrity: sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ==} + + '@otplib/preset-v11@12.0.1': + resolution: {integrity: sha512-9hSetMI7ECqbFiKICrNa4w70deTUfArtwXykPUvSHWOdzOlfa9ajglu7mNCntlvxycTiOAXkQGwjQCzzDEMRMg==} + '@panva/asn1.js@1.0.0': resolution: {integrity: sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==} engines: {node: '>=10.13.0'} @@ -9899,6 +9911,9 @@ packages: engines: {node: '>=0.10.0'} hasBin: true + otplib@12.0.1: + resolution: {integrity: sha512-xDGvUOQjop7RDgxTQ+o4pOol0/3xSZzawTiPKRrHnQWAy0WjhNs/5HdIDJCrqC4MBynmjXgULc6YfioaxZeFgg==} + output-file-sync@1.1.2: resolution: {integrity: sha512-uQLlclru4xpCi+tfs80l3QF24KL81X57ELNMy7W/dox+JTtxUf1bLyQ8968fFCmSqqbokjW0kn+WBIlO+rSkNg==} @@ -11985,6 +12000,10 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + thirty-two@1.0.2: + resolution: {integrity: sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA==} + engines: {node: '>=0.2.6'} + throttle-debounce@5.0.2: resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} engines: {node: '>=12.22'} @@ -14803,75 +14822,6 @@ snapshots: dependencies: '@better-scroll/core': 2.5.1 - '@certd/commercial-core@1.33.2(better-sqlite3@11.8.1)(encoding@0.1.13)(mysql2@3.14.0)(pg@8.13.3)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@18.19.80)(typescript@5.8.2))': - dependencies: - '@certd/basic': link:packages/core/basic - '@certd/lib-server': link:packages/libs/lib-server - '@certd/pipeline': link:packages/core/pipeline - '@certd/plugin-plus': 1.33.2(encoding@0.1.13) - '@certd/plus-core': 1.33.2 - '@midwayjs/core': 3.20.3 - '@midwayjs/koa': 3.20.3 - '@midwayjs/logger': 3.4.2 - '@midwayjs/typeorm': 3.20.3 - alipay-sdk: 4.13.0 - dayjs: 1.11.13 - typeorm: 0.3.21(better-sqlite3@11.8.1)(mysql2@3.14.0)(pg@8.13.3)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@18.19.80)(typescript@5.8.2)) - wechatpay-node-v3: 2.2.1 - transitivePeerDependencies: - - '@google-cloud/spanner' - - '@sap/hana-client' - - better-sqlite3 - - encoding - - hdb-pool - - ioredis - - mongodb - - mssql - - mysql2 - - oracledb - - pg - - pg-native - - pg-query-stream - - proxy-agent - - redis - - reflect-metadata - - sql.js - - sqlite3 - - supports-color - - ts-node - - typeorm-aurora-data-api-driver - - '@certd/plugin-plus@1.33.2(encoding@0.1.13)': - dependencies: - '@alicloud/pop-core': 1.8.0 - '@baiducloud/sdk': 1.0.2 - '@certd/basic': link:packages/core/basic - '@certd/lib-k8s': link:packages/libs/lib-k8s - '@certd/pipeline': link:packages/core/pipeline - '@certd/plugin-cert': link:packages/plugins/plugin-cert - '@certd/plugin-lib': link:packages/plugins/plugin-lib - '@certd/plus-core': 1.33.2 - ali-oss: 6.22.0 - baidu-aip-sdk: 4.16.16 - basic-ftp: 5.0.5 - cos-nodejs-sdk-v5: 2.14.6 - crypto-js: 4.2.0 - dayjs: 1.11.13 - form-data: 4.0.2 - https-proxy-agent: 7.0.6 - jsencrypt: 3.3.2 - qiniu: 7.14.0 - tencentcloud-sdk-nodejs: 4.0.1045(encoding@0.1.13) - transitivePeerDependencies: - - encoding - - proxy-agent - - supports-color - - '@certd/plus-core@1.33.2': - dependencies: - '@certd/basic': link:packages/core/basic - dayjs: 1.11.13 - '@colors/colors@1.5.0': optional: true @@ -16306,6 +16256,29 @@ snapshots: '@one-ini/wasm@0.1.1': {} + '@otplib/core@12.0.1': {} + + '@otplib/plugin-crypto@12.0.1': + dependencies: + '@otplib/core': 12.0.1 + + '@otplib/plugin-thirty-two@12.0.1': + dependencies: + '@otplib/core': 12.0.1 + thirty-two: 1.0.2 + + '@otplib/preset-default@12.0.1': + dependencies: + '@otplib/core': 12.0.1 + '@otplib/plugin-crypto': 12.0.1 + '@otplib/plugin-thirty-two': 12.0.1 + + '@otplib/preset-v11@12.0.1': + dependencies: + '@otplib/core': 12.0.1 + '@otplib/plugin-crypto': 12.0.1 + '@otplib/plugin-thirty-two': 12.0.1 + '@panva/asn1.js@1.0.0': {} '@peculiar/asn1-cms@2.3.15': @@ -20700,13 +20673,13 @@ snapshots: resolve: 1.22.10 semver: 6.3.1 - eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@7.32.0)(prettier@2.8.8): + eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8): dependencies: eslint: 7.32.0 prettier: 2.8.8 prettier-linter-helpers: 1.0.0 optionalDependencies: - eslint-config-prettier: 8.10.0(eslint@8.57.0) + eslint-config-prettier: 8.10.0(eslint@7.32.0) eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8): dependencies: @@ -23420,7 +23393,7 @@ snapshots: eslint: 7.32.0 eslint-config-prettier: 8.10.0(eslint@7.32.0) eslint-plugin-node: 11.1.0(eslint@7.32.0) - eslint-plugin-prettier: 3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@7.32.0)(prettier@2.8.8) + eslint-plugin-prettier: 3.4.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8) execa: 5.1.1 inquirer: 7.3.3 json5: 2.2.3 @@ -23853,6 +23826,12 @@ snapshots: dependencies: minimist: 1.2.8 + otplib@12.0.1: + dependencies: + '@otplib/core': 12.0.1 + '@otplib/preset-default': 12.0.1 + '@otplib/preset-v11': 12.0.1 + output-file-sync@1.1.2: dependencies: graceful-fs: 4.2.11 @@ -26255,6 +26234,8 @@ snapshots: dependencies: any-promise: 1.3.0 + thirty-two@1.0.2: {} + throttle-debounce@5.0.2: {} through2@2.0.5: From d5d54d4d3b7307b2fcf9194b2c04753f97ce0e1b Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Thu, 17 Apr 2025 01:15:55 +0800 Subject: [PATCH 03/14] chore: 2FA --- .../libs/lib-server/src/basic/constants.ts | 4 ++ .../src/basic/exception/auth-exception.ts | 10 ++- .../src/router/source/modules/certd.ts | 11 ++++ .../two-factor => mine/security}/api.ts | 7 ++ .../two-factor => mine/security}/index.vue | 66 +++++++++++++------ .../src/views/framework/login/index.vue | 48 +++++--------- .../controller/user/login/login-controller.ts | 28 ++++++-- .../mine/setting-two-factor-controller.ts | 7 ++ .../modules/login/service/login-service.ts | 47 +++++++++++-- .../src/modules/mine/service/models.ts | 1 - .../mine/service/two-factor-service.ts | 54 +++++++++++---- .../mine/service/user-settings-service.ts | 7 +- 12 files changed, 211 insertions(+), 79 deletions(-) rename packages/ui/certd-client/src/views/certd/{settings/two-factor => mine/security}/api.ts (84%) rename packages/ui/certd-client/src/views/certd/{settings/two-factor => mine/security}/index.vue (53%) diff --git a/packages/libs/lib-server/src/basic/constants.ts b/packages/libs/lib-server/src/basic/constants.ts index bfdaf507..8fdc4424 100644 --- a/packages/libs/lib-server/src/basic/constants.ts +++ b/packages/libs/lib-server/src/basic/constants.ts @@ -75,6 +75,10 @@ export const Constants = { code: 10010, message: '站点已关闭', }, + need2fa:{ + code: 10020, + message: '需要2FA认证', + }, openKeyError: { code: 20000, message: 'ApiToken错误', diff --git a/packages/libs/lib-server/src/basic/exception/auth-exception.ts b/packages/libs/lib-server/src/basic/exception/auth-exception.ts index 65a25591..6523a0ee 100644 --- a/packages/libs/lib-server/src/basic/exception/auth-exception.ts +++ b/packages/libs/lib-server/src/basic/exception/auth-exception.ts @@ -4,7 +4,15 @@ import { BaseException } from './base-exception.js'; * 授权异常 */ export class AuthException extends BaseException { - constructor(message) { + constructor(message?:string) { super('AuthException', Constants.res.auth.code, message ? message : Constants.res.auth.message); } } + + +export class Need2FAException extends BaseException { + constructor(message?:string) { + super('Need2FAException', Constants.res.need2fa.code, message ? message : Constants.res.need2fa.message); + } +} + diff --git a/packages/ui/certd-client/src/router/source/modules/certd.ts b/packages/ui/certd-client/src/router/source/modules/certd.ts index c524c01a..2c487474 100644 --- a/packages/ui/certd-client/src/router/source/modules/certd.ts +++ b/packages/ui/certd-client/src/router/source/modules/certd.ts @@ -143,6 +143,17 @@ export const certdResources = [ keepAlive: true, }, }, + { + title: "认证安全设置", + name: "UserSecurity", + path: "/certd/mine/security", + component: "/certd/mine/security/index.vue", + meta: { + icon: "ion:locked-outline", + auth: true, + isMenu: true, + }, + }, { title: "账号信息", name: "UserProfile", diff --git a/packages/ui/certd-client/src/views/certd/settings/two-factor/api.ts b/packages/ui/certd-client/src/views/certd/mine/security/api.ts similarity index 84% rename from packages/ui/certd-client/src/views/certd/settings/two-factor/api.ts rename to packages/ui/certd-client/src/views/certd/mine/security/api.ts index 2529af72..e401a80c 100644 --- a/packages/ui/certd-client/src/views/certd/settings/two-factor/api.ts +++ b/packages/ui/certd-client/src/views/certd/mine/security/api.ts @@ -38,3 +38,10 @@ export async function TwoFactorAuthenticatorSave(req: AuthenticatorSaveReq) { data: req, }); } + +export async function TwoFactorAuthenticatorOff() { + return await request({ + url: apiPrefix + "/twoFactor/authenticator/off", + method: "post", + }); +} diff --git a/packages/ui/certd-client/src/views/certd/settings/two-factor/index.vue b/packages/ui/certd-client/src/views/certd/mine/security/index.vue similarity index 53% rename from packages/ui/certd-client/src/views/certd/settings/two-factor/index.vue rename to packages/ui/certd-client/src/views/certd/mine/security/index.vue index eb699161..8c371f2e 100644 --- a/packages/ui/certd-client/src/views/certd/settings/two-factor/index.vue +++ b/packages/ui/certd-client/src/views/certd/mine/security/index.vue @@ -1,35 +1,39 @@