chore: 优化passkey

This commit is contained in:
xiaojunnuo
2026-03-15 02:20:39 +08:00
parent bbef854c02
commit f642e42eea
8 changed files with 204 additions and 223 deletions

View File

@@ -770,7 +770,10 @@ export default {
oauthOnly: "OAuth2 Login Only",
oauthOnlyHelper: "Whether to only allow OAuth2 login, disable password login",
enablePasskey: "Enable Passkey Login",
passkeyEnabledHelper: "Whether to enable Passkey login",
passkeyHostnameNotSame: "Passkey hostname must be the same as the main domain",
passkeyEnabledHelper:
"1、Site must enable https \n2、Domain name must not change, otherwise the registered passkey will be invalid \n3、Domain name must be the same as the main domain, otherwise the registered passkey will be invalid",
email: {
templates: "Email Templates",
templateType: "Template Type",

View File

@@ -728,7 +728,7 @@ export default {
paymentSetting: "支付设置",
captchaSetting: "验证码设置",
pipelineSetting: "流水线设置",
oauthSetting: "登录设置",
oauthSetting: "第三方登录",
networkSetting: "网络设置",
adminModeSetting: "管理模式",
adminModeHelper: "企业管理模式: 企业内部使用,通过项目来隔离权限,流水线、授权数据属于项目。\nsaas模式供外部用户注册使用各个用户之间数据隔离流水线、授权数据属于用户。",
@@ -780,8 +780,9 @@ export default {
oauthAutoRedirectHelper: "是否自动跳转第三方登录(使用第一个已启用的第三方登录类型)",
oauthOnly: "仅使用第三方登录",
oauthOnlyHelper: "是否仅使用第三方登录,关闭密码登录(注意:请务必在测试第三方登录功能正常后再开启,否则会导致无法登录)\n 如果无法登录,请访问 http://你的certd地址/#/login?oauthOnly=false 来临时关闭此模式",
enablePasskey: "启用Passkey",
passkeyEnabledHelper: "是否启用Passkey登录",
enablePasskey: "启用Passkey登录",
passkeyHostnameNotSame: "当前域名与主绑定域名不同",
passkeyEnabledHelper: "1、站点必须启用https \n2、域名不要变否则会导致已注册的passkey失效 \n3、域名以主绑定域名为准当前主域名:{0}",
email: {
templates: "邮件模板",
templateType: "模板类型",

View File

@@ -110,10 +110,10 @@ h6 {
flex: 0;
}
.flex-col {
display: flex;
flex-direction: column;
}
// .flex-col {
// display: flex;
// flex-direction: column;
// }
.align-left {
text-align: left;

View File

@@ -45,79 +45,86 @@
</div>
</div>
<div v-if="settingStore.sysPublic.oauthEnabled && settingStore.isPlus" class="bindings-card md:rounded">
<div class="card-title">
<fs-icon icon="ion:link-outline" class="title-icon" />
<span>第三方账号绑定</span>
</div>
<div class="bindings-list">
<template v-for="item in computedOauthBounds" :key="item.name">
<div v-if="item.addonId" class="binding-item">
<div class="binding-icon">
<fs-icon :icon="item.icon" class="icon" />
</div>
<div class="binding-info">
<span class="binding-name">{{ item.title }}</span>
<span class="binding-status" :class="item.bound ? 'bound' : 'unbound'">
{{ item.bound ? "已绑定" : "未绑定" }}
</span>
</div>
<a-button v-if="item.bound" type="primary" danger class="action-btn" @click="unbind(item.name)">
<template #icon><fs-icon icon="ion:unlink-outline" /></template>
解绑
</a-button>
<a-button v-else type="primary" class="action-btn" @click="bind(item.name)">
<template #icon><fs-icon icon="ion:link-outline" /></template>
绑定
</a-button>
<div class="flex flex-wrap">
<div class="w-full md:w-1/2 md:pr-2">
<div v-if="settingStore.sysPublic.oauthEnabled && settingStore.isPlus" class="bindings-card md:rounded">
<div class="card-title">
<fs-icon icon="ion:link-outline" class="title-icon" />
<span>第三方账号绑定</span>
</div>
</template>
<div v-if="computedOauthBounds.length === 0" class="empty-text">暂无可用的第三方账号绑定</div>
</div>
</div>
<div v-if="settingStore.sysPublic.passkeyEnabled && settingStore.isPlus" class="passkey-card md:rounded">
<div class="card-title">
<fs-icon icon="ion:finger-print" class="title-icon" />
<span>Passkey 安全密钥</span>
</div>
<div class="passkey-list">
<div v-for="passkey in passkeys" :key="passkey.id" class="passkey-item">
<div class="passkey-icon">
<fs-icon icon="ion:finger-print" class="icon" />
<div class="bindings-list">
<template v-for="item in computedOauthBounds" :key="item.name">
<div v-if="item.addonId" class="binding-item">
<div class="binding-icon">
<fs-icon :icon="item.icon" class="icon" />
</div>
<div class="binding-info">
<span class="binding-name">{{ item.title }}</span>
<span>
<a-tag v-if="item.bound" color="green" class="bound-tag1">已绑定</a-tag>
<a-tag v-else color="red" class="bound-tag1">未绑定</a-tag>
</span>
</div>
<a-button v-if="item.bound" type="primary" danger class="action-btn" @click="unbind(item.name)">
<template #icon><fs-icon icon="ion:unlink-outline" /></template>
解绑
</a-button>
<a-button v-else type="primary" class="action-btn" @click="bind(item.name)">
<template #icon><fs-icon icon="ion:link-outline" /></template>
绑定
</a-button>
</div>
</template>
<div v-if="computedOauthBounds.length === 0" class="empty-text">暂无可用的第三方账号绑定</div>
</div>
<div class="passkey-info">
<div class="passkey-name">{{ passkey.deviceName }}</div>
<div class="passkey-meta flex items-center">
<span class="meta-item flex items-center">
<fs-icon icon="ion:calendar-outline" class="meta-icon" />
{{ formatDate(passkey.registeredAt) }}
</span>
<span class="meta-item flex items-center">
<fs-icon icon="ion:time-outline" class="meta-icon" />
最近使用<fs-time-humanize :model-value="passkey.updateTime" />
</span>
</div>
</div>
<a-button type="primary" danger class="remove-btn" @click="unbindPasskey(passkey.id)">
<template #icon><fs-icon icon="ion:trash-outline" /></template>
移除
</a-button>
</div>
</div>
<div v-if="passkeys.length === 0" class="empty-state">
<fs-icon icon="ion:finger-print" class="empty-icon" />
<p class="empty-text">暂无Passkey</p>
<div class="w-full md:w-1/2 md:pl-2">
<div v-if="settingStore.sysPublic.passkeyEnabled && settingStore.isPlus" class="passkey-card md:rounded">
<div class="card-title">
<fs-icon icon="ion:finger-print" class="title-icon" />
<span>Passkey 安全密钥</span>
</div>
<div class="passkey-list">
<div v-for="passkey in passkeys" :key="passkey.id" class="passkey-item">
<div class="passkey-icon">
<fs-icon icon="ion:finger-print" class="icon" />
</div>
<div class="passkey-info">
<div class="passkey-name">{{ passkey.deviceName }}</div>
<div class="passkey-meta flex items-center">
<span class="meta-item flex items-center">
<fs-icon icon="ion:calendar-outline" class="meta-icon" />
{{ formatDate(passkey.registeredAt) }}
</span>
<span class="meta-item flex items-center">
<fs-icon icon="ion:time-outline" class="meta-icon" />
最近使用<fs-time-humanize :model-value="passkey.updateTime" />
</span>
</div>
</div>
<a-button type="primary" danger class="remove-btn" @click="unbindPasskey(passkey.id)">
<template #icon><fs-icon icon="ion:trash-outline" /></template>
移除
</a-button>
</div>
</div>
<div v-if="passkeys.length === 0" class="empty-state">
<fs-icon icon="ion:finger-print" class="empty-icon" />
<p class="empty-text">暂无Passkey</p>
</div>
<div v-if="!passkeySupported" class="warning-box">
<fs-icon icon="ion:warning-outline" class="warning-icon" />
<span>{{ t("authentication.passkeyNotSupported") }}</span>
</div>
<a-button v-if="passkeySupported" type="primary" class="add-btn" @click="registerPasskey">
<template #icon><fs-icon icon="ion:add-circle-outline" /></template>
注册新的Passkey
</a-button>
<pre class="helper">{{ t("authentication.passkeyRegisterHelper") }}</pre>
</div>
</div>
<div v-if="!passkeySupported" class="warning-box">
<fs-icon icon="ion:warning-outline" class="warning-icon" />
<span>{{ t("authentication.passkeyNotSupported") }}</span>
</div>
<a-button v-if="passkeySupported" type="primary" class="add-btn" @click="registerPasskey">
<template #icon><fs-icon icon="ion:add-circle-outline" /></template>
注册新的Passkey
</a-button>
<pre class="helper">{{ t("authentication.passkeyRegisterHelper") }}</pre>
</div>
</div>
</fs-page>
@@ -485,7 +492,7 @@ onMounted(async () => {
display: flex;
flex-direction: column;
gap: 20px;
max-width: 1000px;
// max-width: 1000px;
.profile-card,
.bindings-card,
@@ -649,7 +656,7 @@ onMounted(async () => {
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
background: linear-gradient(135deg, #ebefff 0%, #e5d4ff 100%);
border-radius: 10px;
}

View File

@@ -4,7 +4,7 @@
<!-- <div class="login-title">登录</div>-->
<template v-if="!isOauthOnly">
<a-tabs v-model:active-key="formState.loginType" :tab-bar-style="{ textAlign: 'center', borderBottom: 'unset' }">
<a-tab-pane key="password" :tab="t('authentication.passwordTab')" :disabled="sysPublicSettings.passwordLoginEnabled !== true">
<a-tab-pane key="password" :tab="t('authentication.passwordTab')">
<template v-if="formState.loginType === 'password'">
<!-- <div class="login-title">登录</div>-->
<a-form-item required has-feedback name="username" :rules="rules.username">
@@ -48,7 +48,7 @@
</a-tab-pane>
<a-tab-pane key="passkey" :tab="t('authentication.passkeyTab')">
<template v-if="formState.loginType === 'passkey'">
<div v-if="!passkeySupported" class="text-red-500 text-sm mt-2">
<div v-if="!passkeySupported" class="text-red-500 text-sm mt-2 text-center mb-10">
{{ t("authentication.passkeyNotSupported") }}
</div>
</template>

View File

@@ -6,6 +6,10 @@
<a-switch v-model:checked="formState.public.passkeyEnabled" :disabled="!settingsStore.isPlus" :title="t('certd.plusFeature')" />
<vip-button class="ml-5" mode="button"></vip-button>
</div>
<pre class="helper">{{ t("certd.sys.setting.passkeyEnabledHelper", [bindDomain]) }}</pre>
<div v-if="!bindDomainIsSame" class="text-red-500 text-sm mt-2">
{{ t("certd.sys.setting.passkeyHostnameNotSame") }}
</div>
</a-form-item>
<a-form-item :label="t('certd.sys.setting.enableOauth')" :name="['public', 'oauthEnabled']">
<div class="flex-o">
@@ -85,7 +89,7 @@
<script setup lang="tsx">
import { notification } from "ant-design-vue";
import { merge } from "lodash-es";
import { reactive, ref, Ref } from "vue";
import { computed, reactive, ref, Ref } from "vue";
import AddonSelector from "../../../certd/addon/addon-selector/index.vue";
import { useSettingStore } from "/@/store/settings";
import * as api from "/@/views/sys/settings/api";
@@ -107,6 +111,16 @@ async function loadOauthProviders() {
oauthProviders.value = await api.GetOauthProviders();
}
const bindDomain = computed(() => {
const uri = new URL(settingsStore.installInfo.bindUrl);
return uri.hostname;
});
const bindDomainIsSame = computed(() => {
const currentHostname = window.location.hostname;
return bindDomain.value === currentHostname;
});
function fillOauthProviders(form: any) {
const providers: any = {};
for (const item of oauthProviders.value) {

View File

@@ -1,5 +1,5 @@
import { cache } from "@certd/basic";
import { AuthException, BaseService, SysSettingsService, SysSiteInfo } from "@certd/lib-server";
import { AuthException, BaseService, SysInstallInfo, SysSettingsService, SysSiteInfo } from "@certd/lib-server";
import { isComm } from "@certd/plus-core";
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { InjectEntityModel } from "@midwayjs/typeorm";
@@ -10,7 +10,7 @@ import { PasskeyEntity } from "../entity/passkey.js";
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class PasskeyService extends BaseService<PasskeyEntity> {
@Inject()
userService: UserService;
@@ -24,25 +24,36 @@ export class PasskeyService extends BaseService<PasskeyEntity> {
return this.repository;
}
async getRpInfo(){
async getRpInfo() {
let rpName = "Certd"
if(isComm()){
if (isComm()) {
const siteInfo = await this.sysSettingsService.getSetting<SysSiteInfo>(SysSiteInfo);
rpName = siteInfo.title || rpName;
}
const installInfo = await this.sysSettingsService.getSetting<SysInstallInfo>(SysInstallInfo);
const url = installInfo.bindUrl || "http://localhost:7001";
const uri = new URL(url);
const rpId = uri.hostname;
const origin = uri.origin;
return {
rpName,
rpId,
origin,
}
}
async generateRegistrationOptions(userId: number, username: string, remoteIp: string, ctx: any) {
const { generateRegistrationOptions } = await import("@simplewebauthn/server");
const user = await this.userService.info(userId);
const {rpName} = await this.getRpInfo();
const { rpName, rpId } = await this.getRpInfo();
const options = await generateRegistrationOptions({
rpName: rpName,
rpID: this.getRpId(ctx),
rpID: rpId,
userID: new Uint8Array([userId]),
userName: username,
userDisplayName: user.nickName || username,
@@ -67,17 +78,19 @@ export class PasskeyService extends BaseService<PasskeyEntity> {
ctx: any
) {
const { verifyRegistrationResponse } = await import("@simplewebauthn/server");
const storedUserId = cache.get(`passkey:registration:${challenge}`);
if (!storedUserId || storedUserId !== userId) {
throw new AuthException("注册验证失败");
}
const { rpId, origin } = await this.getRpInfo();
const verification = await verifyRegistrationResponse({
response,
expectedChallenge: challenge,
expectedOrigin: this.getOrigin(ctx),
expectedRPID: this.getRpId(ctx),
expectedOrigin: origin,
expectedRPID: rpId,
});
if (!verification.verified) {
@@ -94,9 +107,10 @@ export class PasskeyService extends BaseService<PasskeyEntity> {
}
async generateAuthenticationOptions(ctx: any) {
const { rpId } = await this.getRpInfo();
const { generateAuthenticationOptions } = await import("@simplewebauthn/server");
const options = await generateAuthenticationOptions({
rpID: this.getRpId(ctx),
rpID: rpId,
timeout: 60000,
allowCredentials: [],
});
@@ -116,22 +130,24 @@ export class PasskeyService extends BaseService<PasskeyEntity> {
ctx: any
) {
const { verifyAuthenticationResponse } = await import("@simplewebauthn/server");
const passkey = await this.repository.findOne({
where: {
passkeyId: credential.id,
},
});
if (!passkey) {
throw new AuthException("Passkey不存在");
}
const { rpId, origin } = await this.getRpInfo();
const verification = await verifyAuthenticationResponse({
response:credential,
response: credential,
expectedChallenge: challenge,
expectedOrigin: this.getOrigin(ctx),
expectedRPID: this.getRpId(ctx),
expectedOrigin: origin,
expectedRPID: rpId,
credential: {
id: passkey.passkeyId,
publicKey: new Uint8Array(Buffer.from(passkey.publicKey, 'base64')),
@@ -179,7 +195,7 @@ export class PasskeyService extends BaseService<PasskeyEntity> {
return { success: true };
}
async loginByPasskey( credential: any, challenge: string, ctx: any) {
async loginByPasskey(credential: any, challenge: string, ctx: any) {
const verification = await this.verifyAuthenticationResponse(
credential,
challenge,
@@ -191,7 +207,7 @@ export class PasskeyService extends BaseService<PasskeyEntity> {
passkeyId: verification.credentialId,
},
});
if (!passkey) {
throw new AuthException("Passkey不存在");
}
@@ -208,19 +224,19 @@ export class PasskeyService extends BaseService<PasskeyEntity> {
return user;
}
private getRpId(ctx: any): string {
if (ctx && ctx.request && ctx.request.host) {
return ctx.request.host.split(':')[0];
}
return 'localhost';
}
// private getRpId(ctx: any): string {
// if (ctx && ctx.request && ctx.request.host) {
// return ctx.request.host.split(':')[0];
// }
// return 'localhost';
// }
private getOrigin(ctx: any): string {
if (ctx && ctx.request) {
const protocol = ctx.request.protocol;
const host = ctx.request.host;
return `${protocol}://${host}`;
}
return 'https://localhost';
}
// private getOrigin(ctx: any): string {
// if (ctx && ctx.request) {
// const protocol = ctx.request.protocol;
// const host = ctx.request.host;
// return `${protocol}://${host}`;
// }
// return 'https://localhost';
// }
}

160
pnpm-lock.yaml generated
View File

@@ -49,7 +49,7 @@ importers:
packages/core/acme-client:
dependencies:
'@certd/basic':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../basic
'@peculiar/x509':
specifier: ^1.11.0
@@ -213,11 +213,11 @@ importers:
packages/core/pipeline:
dependencies:
'@certd/basic':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../basic
'@certd/plus-core':
specifier: ^1.39.0
version: 1.39.0
specifier: ^1.39.1
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.39.0
specifier: ^1.39.1
version: link:../../core/basic
'@kubernetes/client-node':
specifier: 0.21.0
@@ -452,20 +452,20 @@ importers:
packages/libs/lib-server:
dependencies:
'@certd/acme-client':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../core/acme-client
'@certd/basic':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../core/basic
'@certd/pipeline':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../core/pipeline
'@certd/plugin-lib':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../plugins/plugin-lib
'@certd/plus-core':
specifier: ^1.39.0
version: 1.39.0
specifier: ^1.39.1
version: link:../../pro/plus-core
'@midwayjs/cache':
specifier: 3.14.0
version: 3.14.0
@@ -610,16 +610,16 @@ importers:
packages/plugins/plugin-cert:
dependencies:
'@certd/acme-client':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../core/acme-client
'@certd/basic':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../core/basic
'@certd/pipeline':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../core/pipeline
'@certd/plugin-lib':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../plugin-lib
psl:
specifier: ^1.9.0
@@ -683,17 +683,17 @@ importers:
specifier: ^3.964.0
version: 3.964.0(aws-crt@1.26.2)
'@certd/acme-client':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../core/acme-client
'@certd/basic':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../core/basic
'@certd/pipeline':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../core/pipeline
'@certd/plus-core':
specifier: ^1.39.0
version: 1.39.0
specifier: ^1.39.1
version: link:../../pro/plus-core
'@kubernetes/client-node':
specifier: 0.21.0
version: 0.21.0
@@ -783,16 +783,16 @@ importers:
packages/pro/commercial-core:
dependencies:
'@certd/basic':
specifier: ^1.38.12
specifier: ^1.39.1
version: link:../../core/basic
'@certd/lib-server':
specifier: ^1.38.12
specifier: ^1.39.1
version: link:../../libs/lib-server
'@certd/pipeline':
specifier: ^1.38.12
specifier: ^1.39.1
version: link:../../core/pipeline
'@certd/plus-core':
specifier: ^1.38.12
specifier: ^1.39.1
version: link:../plus-core
'@midwayjs/core':
specifier: 3.20.11
@@ -865,16 +865,16 @@ importers:
packages/pro/plugin-plus:
dependencies:
'@certd/basic':
specifier: ^1.38.12
specifier: ^1.39.1
version: link:../../core/basic
'@certd/pipeline':
specifier: ^1.38.12
specifier: ^1.39.1
version: link:../../core/pipeline
'@certd/plugin-lib':
specifier: ^1.38.12
specifier: ^1.39.1
version: link:../../plugins/plugin-lib
'@certd/plus-core':
specifier: ^1.38.12
specifier: ^1.39.1
version: link:../plus-core
crypto-js:
specifier: ^4.2.0
@@ -950,7 +950,7 @@ importers:
packages/pro/plus-core:
dependencies:
'@certd/basic':
specifier: ^1.38.12
specifier: ^1.39.1
version: link:../../core/basic
dayjs:
specifier: ^1.11.7
@@ -1246,10 +1246,10 @@ importers:
version: 0.1.3(zod@3.24.4)
devDependencies:
'@certd/lib-iframe':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../libs/lib-iframe
'@certd/pipeline':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../core/pipeline
'@rollup/plugin-commonjs':
specifier: ^25.0.7
@@ -1444,47 +1444,47 @@ importers:
specifier: ^3.990.0
version: 3.990.0(aws-crt@1.26.2)
'@certd/acme-client':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../core/acme-client
'@certd/basic':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../core/basic
'@certd/commercial-core':
specifier: ^1.39.0
version: 1.39.0(better-sqlite3@11.10.0)(mysql2@3.14.1)(pg@8.16.0)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@18.19.100)(typescript@5.9.3))
specifier: ^1.39.1
version: link:../../pro/commercial-core
'@certd/cv4pve-api-javascript':
specifier: ^8.4.2
version: 8.4.2
'@certd/jdcloud':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../libs/lib-jdcloud
'@certd/lib-huawei':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../libs/lib-huawei
'@certd/lib-k8s':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../libs/lib-k8s
'@certd/lib-server':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../libs/lib-server
'@certd/midway-flyway-js':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../libs/midway-flyway-js
'@certd/pipeline':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../core/pipeline
'@certd/plugin-cert':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../plugins/plugin-cert
'@certd/plugin-lib':
specifier: ^1.39.0
specifier: ^1.39.1
version: link:../../plugins/plugin-lib
'@certd/plugin-plus':
specifier: ^1.39.0
version: 1.39.0
specifier: ^1.39.1
version: link:../../pro/plugin-plus
'@certd/plus-core':
specifier: ^1.39.0
version: 1.39.0
specifier: ^1.39.1
version: link:../../pro/plus-core
'@google-cloud/publicca':
specifier: ^1.3.0
version: 1.3.0(encoding@0.1.13)
@@ -2826,18 +2826,9 @@ packages:
'@better-scroll/zoom@2.5.1':
resolution: {integrity: sha512-aGvFY5ooeZWS4RcxQLD+pGLpQHQxpPy0sMZV3yadcd2QK53PK9gS4Dp+BYfRv8lZ4/P2LoNEhr6Wq1DN6+uPlA==}
'@certd/commercial-core@1.39.0':
resolution: {integrity: sha512-amnJsyLdNMTUU+v67exGMHe6igU5m0vaCJaWjq6c+CQN7PKkM9Bt7H7f7FlbHRon8q6AngtpIgQNhIx7473Dmg==}
'@certd/cv4pve-api-javascript@8.4.2':
resolution: {integrity: sha512-udGce7ewrVl4DmZvX+17PjsnqsdDIHEDatr8QP0AVrY2p+8JkaSPW4mXCKiLGf82C9K2+GXgT+qNIqgW7tfF9Q==}
'@certd/plugin-plus@1.39.0':
resolution: {integrity: sha512-/cS9XYwjgP6JNgysHKz5tN93D8+CIOviBtLdnkkaZ6nXluLueRSBqNNvCqQM8dE1GndS7sEYj3RnkX7ver/Q3A==}
'@certd/plus-core@1.39.0':
resolution: {integrity: sha512-wLIMH38oBCtPBu3xxG2JGBiFchT0ko9Q/iPIoF1YJnKooGYS69qjtqEwGoGo5nHFqrRJB4RGmd880wj7wCb5cg==}
'@certd/vue-js-cron-core@6.0.3':
resolution: {integrity: sha512-kqzoAMhYz9j6FGNWEODRYtt4NpUEUwjpkU89z5WVg2tCtOcI5VhwyUGOd8AxiBCRfd6PtXvzuqw85PaOps9wrQ==}
@@ -15094,63 +15085,12 @@ snapshots:
dependencies:
'@better-scroll/core': 2.5.1
'@certd/commercial-core@1.39.0(better-sqlite3@11.10.0)(mysql2@3.14.1)(pg@8.16.0)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@18.19.100)(typescript@5.9.3))':
dependencies:
'@certd/basic': link:packages/core/basic
'@certd/lib-server': link:packages/libs/lib-server
'@certd/pipeline': link:packages/core/pipeline
'@certd/plus-core': 1.39.0
'@midwayjs/core': 3.20.11
'@midwayjs/koa': 3.20.13
'@midwayjs/logger': 3.4.2
'@midwayjs/typeorm': 3.20.11
dayjs: 1.11.13
typeorm: 0.3.24(better-sqlite3@11.10.0)(mysql2@3.14.1)(pg@8.16.0)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@18.19.100)(typescript@5.9.3))
transitivePeerDependencies:
- '@google-cloud/spanner'
- '@sap/hana-client'
- babel-plugin-macros
- better-sqlite3
- hdb-pool
- ioredis
- mongodb
- mssql
- mysql2
- oracledb
- pg
- pg-native
- pg-query-stream
- redis
- reflect-metadata
- sql.js
- sqlite3
- supports-color
- ts-node
- typeorm-aurora-data-api-driver
'@certd/cv4pve-api-javascript@8.4.2':
dependencies:
debug: 4.4.3(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
'@certd/plugin-plus@1.39.0':
dependencies:
'@certd/basic': link:packages/core/basic
'@certd/pipeline': link:packages/core/pipeline
'@certd/plugin-lib': link:packages/plugins/plugin-lib
'@certd/plus-core': 1.39.0
crypto-js: 4.2.0
dayjs: 1.11.13
form-data: 4.0.2
jsrsasign: 11.1.0
querystring: 0.2.1
'@certd/plus-core@1.39.0':
dependencies:
'@certd/basic': link:packages/core/basic
dayjs: 1.11.13
'@certd/vue-js-cron-core@6.0.3':
dependencies:
mustache: 4.2.0
@@ -20829,13 +20769,13 @@ snapshots:
resolve: 1.22.10
semver: 6.3.1
eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8):
eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.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@7.32.0)
eslint-config-prettier: 8.10.0(eslint@8.57.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:
@@ -23239,7 +23179,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@7.32.0))(eslint@7.32.0)(prettier@2.8.8)
eslint-plugin-prettier: 3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@7.32.0)(prettier@2.8.8)
execa: 5.1.1
inquirer: 7.3.3
json5: 2.2.3