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
@@ -770,7 +770,10 @@ export default {
oauthOnly: "OAuth2 Login Only", oauthOnly: "OAuth2 Login Only",
oauthOnlyHelper: "Whether to only allow OAuth2 login, disable password login", oauthOnlyHelper: "Whether to only allow OAuth2 login, disable password login",
enablePasskey: "Enable Passkey 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: { email: {
templates: "Email Templates", templates: "Email Templates",
templateType: "Template Type", templateType: "Template Type",
@@ -728,7 +728,7 @@ export default {
paymentSetting: "支付设置", paymentSetting: "支付设置",
captchaSetting: "验证码设置", captchaSetting: "验证码设置",
pipelineSetting: "流水线设置", pipelineSetting: "流水线设置",
oauthSetting: "登录设置", oauthSetting: "第三方登录",
networkSetting: "网络设置", networkSetting: "网络设置",
adminModeSetting: "管理模式", adminModeSetting: "管理模式",
adminModeHelper: "企业管理模式: 企业内部使用,通过项目来隔离权限,流水线、授权数据属于项目。\nsaas模式:供外部用户注册使用,各个用户之间数据隔离,流水线、授权数据属于用户。", adminModeHelper: "企业管理模式: 企业内部使用,通过项目来隔离权限,流水线、授权数据属于项目。\nsaas模式:供外部用户注册使用,各个用户之间数据隔离,流水线、授权数据属于用户。",
@@ -780,8 +780,9 @@ export default {
oauthAutoRedirectHelper: "是否自动跳转第三方登录(使用第一个已启用的第三方登录类型)", oauthAutoRedirectHelper: "是否自动跳转第三方登录(使用第一个已启用的第三方登录类型)",
oauthOnly: "仅使用第三方登录", oauthOnly: "仅使用第三方登录",
oauthOnlyHelper: "是否仅使用第三方登录,关闭密码登录(注意:请务必在测试第三方登录功能正常后再开启,否则会导致无法登录)\n 如果无法登录,请访问 http://你的certd地址/#/login?oauthOnly=false 来临时关闭此模式", oauthOnlyHelper: "是否仅使用第三方登录,关闭密码登录(注意:请务必在测试第三方登录功能正常后再开启,否则会导致无法登录)\n 如果无法登录,请访问 http://你的certd地址/#/login?oauthOnly=false 来临时关闭此模式",
enablePasskey: "启用Passkey", enablePasskey: "启用Passkey登录",
passkeyEnabledHelper: "是否启用Passkey登录", passkeyHostnameNotSame: "当前域名与主绑定域名不同",
passkeyEnabledHelper: "1、站点必须启用https \n2、域名不要变,否则会导致已注册的passkey失效 \n3、域名以主绑定域名为准,当前主域名:{0}",
email: { email: {
templates: "邮件模板", templates: "邮件模板",
templateType: "模板类型", templateType: "模板类型",
@@ -110,10 +110,10 @@ h6 {
flex: 0; flex: 0;
} }
.flex-col { // .flex-col {
display: flex; // display: flex;
flex-direction: column; // flex-direction: column;
} // }
.align-left { .align-left {
text-align: left; text-align: left;
@@ -45,79 +45,86 @@
</div> </div>
</div> </div>
<div v-if="settingStore.sysPublic.oauthEnabled && settingStore.isPlus" class="bindings-card md:rounded"> <div class="flex flex-wrap">
<div class="card-title"> <div class="w-full md:w-1/2 md:pr-2">
<fs-icon icon="ion:link-outline" class="title-icon" /> <div v-if="settingStore.sysPublic.oauthEnabled && settingStore.isPlus" class="bindings-card md:rounded">
<span>第三方账号绑定</span> <div class="card-title">
</div> <fs-icon icon="ion:link-outline" class="title-icon" />
<div class="bindings-list"> <span>第三方账号绑定</span>
<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> </div>
</template> <div class="bindings-list">
<div v-if="computedOauthBounds.length === 0" class="empty-text">暂无可用的第三方账号绑定</div> <template v-for="item in computedOauthBounds" :key="item.name">
</div> <div v-if="item.addonId" class="binding-item">
</div> <div class="binding-icon">
<fs-icon :icon="item.icon" class="icon" />
<div v-if="settingStore.sysPublic.passkeyEnabled && settingStore.isPlus" class="passkey-card md:rounded"> </div>
<div class="card-title"> <div class="binding-info">
<fs-icon icon="ion:finger-print" class="title-icon" /> <span class="binding-name">{{ item.title }}</span>
<span>Passkey 安全密钥</span> <span>
</div> <a-tag v-if="item.bound" color="green" class="bound-tag1">已绑定</a-tag>
<div class="passkey-list"> <a-tag v-else color="red" class="bound-tag1">未绑定</a-tag>
<div v-for="passkey in passkeys" :key="passkey.id" class="passkey-item"> </span>
<div class="passkey-icon"> </div>
<fs-icon icon="ion:finger-print" class="icon" /> <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>
<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> </div>
<div v-if="passkeys.length === 0" class="empty-state">
<fs-icon icon="ion:finger-print" class="empty-icon" /> <div class="w-full md:w-1/2 md:pl-2">
<p class="empty-text">暂无Passkey</p> <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>
<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> </div>
</fs-page> </fs-page>
@@ -485,7 +492,7 @@ onMounted(async () => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 20px; gap: 20px;
max-width: 1000px; // max-width: 1000px;
.profile-card, .profile-card,
.bindings-card, .bindings-card,
@@ -649,7 +656,7 @@ onMounted(async () => {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); background: linear-gradient(135deg, #ebefff 0%, #e5d4ff 100%);
border-radius: 10px; border-radius: 10px;
} }
@@ -4,7 +4,7 @@
<!-- <div class="login-title">登录</div>--> <!-- <div class="login-title">登录</div>-->
<template v-if="!isOauthOnly"> <template v-if="!isOauthOnly">
<a-tabs v-model:active-key="formState.loginType" :tab-bar-style="{ textAlign: 'center', borderBottom: 'unset' }"> <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'"> <template v-if="formState.loginType === 'password'">
<!-- <div class="login-title">登录</div>--> <!-- <div class="login-title">登录</div>-->
<a-form-item required has-feedback name="username" :rules="rules.username"> <a-form-item required has-feedback name="username" :rules="rules.username">
@@ -48,7 +48,7 @@
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="passkey" :tab="t('authentication.passkeyTab')"> <a-tab-pane key="passkey" :tab="t('authentication.passkeyTab')">
<template v-if="formState.loginType === 'passkey'"> <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") }} {{ t("authentication.passkeyNotSupported") }}
</div> </div>
</template> </template>
@@ -6,6 +6,10 @@
<a-switch v-model:checked="formState.public.passkeyEnabled" :disabled="!settingsStore.isPlus" :title="t('certd.plusFeature')" /> <a-switch v-model:checked="formState.public.passkeyEnabled" :disabled="!settingsStore.isPlus" :title="t('certd.plusFeature')" />
<vip-button class="ml-5" mode="button"></vip-button> <vip-button class="ml-5" mode="button"></vip-button>
</div> </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>
<a-form-item :label="t('certd.sys.setting.enableOauth')" :name="['public', 'oauthEnabled']"> <a-form-item :label="t('certd.sys.setting.enableOauth')" :name="['public', 'oauthEnabled']">
<div class="flex-o"> <div class="flex-o">
@@ -85,7 +89,7 @@
<script setup lang="tsx"> <script setup lang="tsx">
import { notification } from "ant-design-vue"; import { notification } from "ant-design-vue";
import { merge } from "lodash-es"; 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 AddonSelector from "../../../certd/addon/addon-selector/index.vue";
import { useSettingStore } from "/@/store/settings"; import { useSettingStore } from "/@/store/settings";
import * as api from "/@/views/sys/settings/api"; import * as api from "/@/views/sys/settings/api";
@@ -107,6 +111,16 @@ async function loadOauthProviders() {
oauthProviders.value = await api.GetOauthProviders(); 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) { function fillOauthProviders(form: any) {
const providers: any = {}; const providers: any = {};
for (const item of oauthProviders.value) { for (const item of oauthProviders.value) {
@@ -1,5 +1,5 @@
import { cache } from "@certd/basic"; 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 { isComm } from "@certd/plus-core";
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core"; import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { InjectEntityModel } from "@midwayjs/typeorm"; import { InjectEntityModel } from "@midwayjs/typeorm";
@@ -10,7 +10,7 @@ import { PasskeyEntity } from "../entity/passkey.js";
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Request, { allowDowngrade: true })
export class PasskeyService extends BaseService<PasskeyEntity> { export class PasskeyService extends BaseService<PasskeyEntity> {
@Inject() @Inject()
userService: UserService; userService: UserService;
@@ -24,25 +24,36 @@ export class PasskeyService extends BaseService<PasskeyEntity> {
return this.repository; return this.repository;
} }
async getRpInfo(){ async getRpInfo() {
let rpName = "Certd" let rpName = "Certd"
if(isComm()){ if (isComm()) {
const siteInfo = await this.sysSettingsService.getSetting<SysSiteInfo>(SysSiteInfo); const siteInfo = await this.sysSettingsService.getSetting<SysSiteInfo>(SysSiteInfo);
rpName = siteInfo.title || rpName; 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 { return {
rpName, rpName,
rpId,
origin,
} }
} }
async generateRegistrationOptions(userId: number, username: string, remoteIp: string, ctx: any) { async generateRegistrationOptions(userId: number, username: string, remoteIp: string, ctx: any) {
const { generateRegistrationOptions } = await import("@simplewebauthn/server"); const { generateRegistrationOptions } = await import("@simplewebauthn/server");
const user = await this.userService.info(userId); const user = await this.userService.info(userId);
const {rpName} = await this.getRpInfo(); const { rpName, rpId } = await this.getRpInfo();
const options = await generateRegistrationOptions({ const options = await generateRegistrationOptions({
rpName: rpName, rpName: rpName,
rpID: this.getRpId(ctx), rpID: rpId,
userID: new Uint8Array([userId]), userID: new Uint8Array([userId]),
userName: username, userName: username,
userDisplayName: user.nickName || username, userDisplayName: user.nickName || username,
@@ -67,17 +78,19 @@ export class PasskeyService extends BaseService<PasskeyEntity> {
ctx: any ctx: any
) { ) {
const { verifyRegistrationResponse } = await import("@simplewebauthn/server"); const { verifyRegistrationResponse } = await import("@simplewebauthn/server");
const storedUserId = cache.get(`passkey:registration:${challenge}`); const storedUserId = cache.get(`passkey:registration:${challenge}`);
if (!storedUserId || storedUserId !== userId) { if (!storedUserId || storedUserId !== userId) {
throw new AuthException("注册验证失败"); throw new AuthException("注册验证失败");
} }
const { rpId, origin } = await this.getRpInfo();
const verification = await verifyRegistrationResponse({ const verification = await verifyRegistrationResponse({
response, response,
expectedChallenge: challenge, expectedChallenge: challenge,
expectedOrigin: this.getOrigin(ctx), expectedOrigin: origin,
expectedRPID: this.getRpId(ctx), expectedRPID: rpId,
}); });
if (!verification.verified) { if (!verification.verified) {
@@ -94,9 +107,10 @@ export class PasskeyService extends BaseService<PasskeyEntity> {
} }
async generateAuthenticationOptions(ctx: any) { async generateAuthenticationOptions(ctx: any) {
const { rpId } = await this.getRpInfo();
const { generateAuthenticationOptions } = await import("@simplewebauthn/server"); const { generateAuthenticationOptions } = await import("@simplewebauthn/server");
const options = await generateAuthenticationOptions({ const options = await generateAuthenticationOptions({
rpID: this.getRpId(ctx), rpID: rpId,
timeout: 60000, timeout: 60000,
allowCredentials: [], allowCredentials: [],
}); });
@@ -116,22 +130,24 @@ export class PasskeyService extends BaseService<PasskeyEntity> {
ctx: any ctx: any
) { ) {
const { verifyAuthenticationResponse } = await import("@simplewebauthn/server"); const { verifyAuthenticationResponse } = await import("@simplewebauthn/server");
const passkey = await this.repository.findOne({ const passkey = await this.repository.findOne({
where: { where: {
passkeyId: credential.id, passkeyId: credential.id,
}, },
}); });
if (!passkey) { if (!passkey) {
throw new AuthException("Passkey不存在"); throw new AuthException("Passkey不存在");
} }
const { rpId, origin } = await this.getRpInfo();
const verification = await verifyAuthenticationResponse({ const verification = await verifyAuthenticationResponse({
response:credential, response: credential,
expectedChallenge: challenge, expectedChallenge: challenge,
expectedOrigin: this.getOrigin(ctx), expectedOrigin: origin,
expectedRPID: this.getRpId(ctx), expectedRPID: rpId,
credential: { credential: {
id: passkey.passkeyId, id: passkey.passkeyId,
publicKey: new Uint8Array(Buffer.from(passkey.publicKey, 'base64')), publicKey: new Uint8Array(Buffer.from(passkey.publicKey, 'base64')),
@@ -179,7 +195,7 @@ export class PasskeyService extends BaseService<PasskeyEntity> {
return { success: true }; return { success: true };
} }
async loginByPasskey( credential: any, challenge: string, ctx: any) { async loginByPasskey(credential: any, challenge: string, ctx: any) {
const verification = await this.verifyAuthenticationResponse( const verification = await this.verifyAuthenticationResponse(
credential, credential,
challenge, challenge,
@@ -191,7 +207,7 @@ export class PasskeyService extends BaseService<PasskeyEntity> {
passkeyId: verification.credentialId, passkeyId: verification.credentialId,
}, },
}); });
if (!passkey) { if (!passkey) {
throw new AuthException("Passkey不存在"); throw new AuthException("Passkey不存在");
} }
@@ -208,19 +224,19 @@ export class PasskeyService extends BaseService<PasskeyEntity> {
return user; return user;
} }
private getRpId(ctx: any): string { // private getRpId(ctx: any): string {
if (ctx && ctx.request && ctx.request.host) { // if (ctx && ctx.request && ctx.request.host) {
return ctx.request.host.split(':')[0]; // return ctx.request.host.split(':')[0];
} // }
return 'localhost'; // return 'localhost';
} // }
private getOrigin(ctx: any): string { // private getOrigin(ctx: any): string {
if (ctx && ctx.request) { // if (ctx && ctx.request) {
const protocol = ctx.request.protocol; // const protocol = ctx.request.protocol;
const host = ctx.request.host; // const host = ctx.request.host;
return `${protocol}://${host}`; // return `${protocol}://${host}`;
} // }
return 'https://localhost'; // return 'https://localhost';
} // }
} }
+50 -110
View File
@@ -49,7 +49,7 @@ importers:
packages/core/acme-client: packages/core/acme-client:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../basic version: link:../basic
'@peculiar/x509': '@peculiar/x509':
specifier: ^1.11.0 specifier: ^1.11.0
@@ -213,11 +213,11 @@ importers:
packages/core/pipeline: packages/core/pipeline:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../basic version: link:../basic
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.39.0 specifier: ^1.39.1
version: 1.39.0 version: link:../../pro/plus-core
dayjs: dayjs:
specifier: ^1.11.7 specifier: ^1.11.7
version: 1.11.13 version: 1.11.13
@@ -412,7 +412,7 @@ importers:
packages/libs/lib-k8s: packages/libs/lib-k8s:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../core/basic version: link:../../core/basic
'@kubernetes/client-node': '@kubernetes/client-node':
specifier: 0.21.0 specifier: 0.21.0
@@ -452,20 +452,20 @@ importers:
packages/libs/lib-server: packages/libs/lib-server:
dependencies: dependencies:
'@certd/acme-client': '@certd/acme-client':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../core/acme-client version: link:../../core/acme-client
'@certd/basic': '@certd/basic':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../core/basic version: link:../../core/basic
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plugin-lib': '@certd/plugin-lib':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../plugins/plugin-lib version: link:../../plugins/plugin-lib
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.39.0 specifier: ^1.39.1
version: 1.39.0 version: link:../../pro/plus-core
'@midwayjs/cache': '@midwayjs/cache':
specifier: 3.14.0 specifier: 3.14.0
version: 3.14.0 version: 3.14.0
@@ -610,16 +610,16 @@ importers:
packages/plugins/plugin-cert: packages/plugins/plugin-cert:
dependencies: dependencies:
'@certd/acme-client': '@certd/acme-client':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../core/acme-client version: link:../../core/acme-client
'@certd/basic': '@certd/basic':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../core/basic version: link:../../core/basic
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plugin-lib': '@certd/plugin-lib':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../plugin-lib version: link:../plugin-lib
psl: psl:
specifier: ^1.9.0 specifier: ^1.9.0
@@ -683,17 +683,17 @@ importers:
specifier: ^3.964.0 specifier: ^3.964.0
version: 3.964.0(aws-crt@1.26.2) version: 3.964.0(aws-crt@1.26.2)
'@certd/acme-client': '@certd/acme-client':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../core/acme-client version: link:../../core/acme-client
'@certd/basic': '@certd/basic':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../core/basic version: link:../../core/basic
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.39.0 specifier: ^1.39.1
version: 1.39.0 version: link:../../pro/plus-core
'@kubernetes/client-node': '@kubernetes/client-node':
specifier: 0.21.0 specifier: 0.21.0
version: 0.21.0 version: 0.21.0
@@ -783,16 +783,16 @@ importers:
packages/pro/commercial-core: packages/pro/commercial-core:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.38.12 specifier: ^1.39.1
version: link:../../core/basic version: link:../../core/basic
'@certd/lib-server': '@certd/lib-server':
specifier: ^1.38.12 specifier: ^1.39.1
version: link:../../libs/lib-server version: link:../../libs/lib-server
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.38.12 specifier: ^1.39.1
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.38.12 specifier: ^1.39.1
version: link:../plus-core version: link:../plus-core
'@midwayjs/core': '@midwayjs/core':
specifier: 3.20.11 specifier: 3.20.11
@@ -865,16 +865,16 @@ importers:
packages/pro/plugin-plus: packages/pro/plugin-plus:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.38.12 specifier: ^1.39.1
version: link:../../core/basic version: link:../../core/basic
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.38.12 specifier: ^1.39.1
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plugin-lib': '@certd/plugin-lib':
specifier: ^1.38.12 specifier: ^1.39.1
version: link:../../plugins/plugin-lib version: link:../../plugins/plugin-lib
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.38.12 specifier: ^1.39.1
version: link:../plus-core version: link:../plus-core
crypto-js: crypto-js:
specifier: ^4.2.0 specifier: ^4.2.0
@@ -950,7 +950,7 @@ importers:
packages/pro/plus-core: packages/pro/plus-core:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.38.12 specifier: ^1.39.1
version: link:../../core/basic version: link:../../core/basic
dayjs: dayjs:
specifier: ^1.11.7 specifier: ^1.11.7
@@ -1246,10 +1246,10 @@ importers:
version: 0.1.3(zod@3.24.4) version: 0.1.3(zod@3.24.4)
devDependencies: devDependencies:
'@certd/lib-iframe': '@certd/lib-iframe':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../libs/lib-iframe version: link:../../libs/lib-iframe
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../core/pipeline version: link:../../core/pipeline
'@rollup/plugin-commonjs': '@rollup/plugin-commonjs':
specifier: ^25.0.7 specifier: ^25.0.7
@@ -1444,47 +1444,47 @@ importers:
specifier: ^3.990.0 specifier: ^3.990.0
version: 3.990.0(aws-crt@1.26.2) version: 3.990.0(aws-crt@1.26.2)
'@certd/acme-client': '@certd/acme-client':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../core/acme-client version: link:../../core/acme-client
'@certd/basic': '@certd/basic':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../core/basic version: link:../../core/basic
'@certd/commercial-core': '@certd/commercial-core':
specifier: ^1.39.0 specifier: ^1.39.1
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)) version: link:../../pro/commercial-core
'@certd/cv4pve-api-javascript': '@certd/cv4pve-api-javascript':
specifier: ^8.4.2 specifier: ^8.4.2
version: 8.4.2 version: 8.4.2
'@certd/jdcloud': '@certd/jdcloud':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../libs/lib-jdcloud version: link:../../libs/lib-jdcloud
'@certd/lib-huawei': '@certd/lib-huawei':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../libs/lib-huawei version: link:../../libs/lib-huawei
'@certd/lib-k8s': '@certd/lib-k8s':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../libs/lib-k8s version: link:../../libs/lib-k8s
'@certd/lib-server': '@certd/lib-server':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../libs/lib-server version: link:../../libs/lib-server
'@certd/midway-flyway-js': '@certd/midway-flyway-js':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../libs/midway-flyway-js version: link:../../libs/midway-flyway-js
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plugin-cert': '@certd/plugin-cert':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../plugins/plugin-cert version: link:../../plugins/plugin-cert
'@certd/plugin-lib': '@certd/plugin-lib':
specifier: ^1.39.0 specifier: ^1.39.1
version: link:../../plugins/plugin-lib version: link:../../plugins/plugin-lib
'@certd/plugin-plus': '@certd/plugin-plus':
specifier: ^1.39.0 specifier: ^1.39.1
version: 1.39.0 version: link:../../pro/plugin-plus
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.39.0 specifier: ^1.39.1
version: 1.39.0 version: link:../../pro/plus-core
'@google-cloud/publicca': '@google-cloud/publicca':
specifier: ^1.3.0 specifier: ^1.3.0
version: 1.3.0(encoding@0.1.13) version: 1.3.0(encoding@0.1.13)
@@ -2826,18 +2826,9 @@ packages:
'@better-scroll/zoom@2.5.1': '@better-scroll/zoom@2.5.1':
resolution: {integrity: sha512-aGvFY5ooeZWS4RcxQLD+pGLpQHQxpPy0sMZV3yadcd2QK53PK9gS4Dp+BYfRv8lZ4/P2LoNEhr6Wq1DN6+uPlA==} 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': '@certd/cv4pve-api-javascript@8.4.2':
resolution: {integrity: sha512-udGce7ewrVl4DmZvX+17PjsnqsdDIHEDatr8QP0AVrY2p+8JkaSPW4mXCKiLGf82C9K2+GXgT+qNIqgW7tfF9Q==} 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': '@certd/vue-js-cron-core@6.0.3':
resolution: {integrity: sha512-kqzoAMhYz9j6FGNWEODRYtt4NpUEUwjpkU89z5WVg2tCtOcI5VhwyUGOd8AxiBCRfd6PtXvzuqw85PaOps9wrQ==} resolution: {integrity: sha512-kqzoAMhYz9j6FGNWEODRYtt4NpUEUwjpkU89z5WVg2tCtOcI5VhwyUGOd8AxiBCRfd6PtXvzuqw85PaOps9wrQ==}
@@ -15094,63 +15085,12 @@ snapshots:
dependencies: dependencies:
'@better-scroll/core': 2.5.1 '@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': '@certd/cv4pve-api-javascript@8.4.2':
dependencies: dependencies:
debug: 4.4.3(supports-color@8.1.1) debug: 4.4.3(supports-color@8.1.1)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - 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': '@certd/vue-js-cron-core@6.0.3':
dependencies: dependencies:
mustache: 4.2.0 mustache: 4.2.0
@@ -20829,13 +20769,13 @@ snapshots:
resolve: 1.22.10 resolve: 1.22.10
semver: 6.3.1 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: dependencies:
eslint: 7.32.0 eslint: 7.32.0
prettier: 2.8.8 prettier: 2.8.8
prettier-linter-helpers: 1.0.0 prettier-linter-helpers: 1.0.0
optionalDependencies: 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): 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: dependencies:
@@ -23239,7 +23179,7 @@ snapshots:
eslint: 7.32.0 eslint: 7.32.0
eslint-config-prettier: 8.10.0(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-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 execa: 5.1.1
inquirer: 7.3.3 inquirer: 7.3.3
json5: 2.2.3 json5: 2.2.3