mirror of
https://github.com/certd/certd.git
synced 2026-07-06 03:47:34 +08:00
perf: 商业版支持限制泛域名数量
This commit is contained in:
@@ -4,7 +4,7 @@ import { AutoLoadPlugins } from "./auto-load-plugins.js";
|
||||
import { AutoCron } from "./auto-cron.js";
|
||||
import { AutoMitterRegister } from "./auto-mitter-register.js";
|
||||
import { AutoPipelineEmitterRegister } from "./auto-pipeline-emitter-register.js";
|
||||
import { AutoFix } from "./auto-fix.js";
|
||||
import { AutoFix } from "./fix/auto-fix.js";
|
||||
import { AutoPrint } from "./auto-print.js";
|
||||
|
||||
@Autoload()
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
import assert from "assert";
|
||||
import esmock from "esmock";
|
||||
import { AutoFix, buildEabAccountKeyValue, buildLegacyGoogleAccountConfigWhere, buildOauthBoundType, parseStorageValue } from "./auto-fix.js";
|
||||
|
||||
function createAutoFix(options: { pluginConfigService?: any; accessService?: any; storageService?: any; sysSettingsService?: any; oauthBoundService?: any }) {
|
||||
const autoFix = new AutoFix();
|
||||
autoFix.pluginConfigService = options.pluginConfigService;
|
||||
autoFix.accessService = options.accessService;
|
||||
autoFix.storageService = options.storageService;
|
||||
autoFix.sysSettingsService = options.sysSettingsService;
|
||||
autoFix.oauthBoundService = options.oauthBoundService;
|
||||
return autoFix;
|
||||
}
|
||||
|
||||
describe("AutoFix", () => {
|
||||
it("parses legacy storage values", () => {
|
||||
const config = parseStorageValue(
|
||||
JSON.stringify({
|
||||
value: {
|
||||
key: "legacy-private-key",
|
||||
accountUrl: "https://example.com/acct/1",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
assert.equal(config.key, "legacy-private-key");
|
||||
});
|
||||
|
||||
it("builds the EAB account key payload", () => {
|
||||
const payload = JSON.parse(buildEabAccountKeyValue("kid-1", "private-key"));
|
||||
|
||||
assert.deepEqual(payload, {
|
||||
kid: "kid-1",
|
||||
privateKey: "private-key",
|
||||
});
|
||||
});
|
||||
|
||||
it("builds legacy Google account config query by exact email key only", () => {
|
||||
assert.deepEqual(buildLegacyGoogleAccountConfigWhere("user@example.com"), {
|
||||
userId: 1,
|
||||
scope: "user",
|
||||
namespace: "1",
|
||||
key: "acme.config.google.user@example.com",
|
||||
});
|
||||
});
|
||||
|
||||
it("builds OAuth subtype bound type", () => {
|
||||
assert.equal(buildOauthBoundType("clogin", "alipay"), "clogin:alipay");
|
||||
assert.equal(buildOauthBoundType("github"), "github");
|
||||
});
|
||||
|
||||
it("finds legacy Google account config by exact email key only", async () => {
|
||||
let findOneWhere: any;
|
||||
let findCalled = false;
|
||||
const autoFix = createAutoFix({
|
||||
pluginConfigService: null as any,
|
||||
accessService: null as any,
|
||||
storageService: {
|
||||
getRepository() {
|
||||
return {
|
||||
async findOne(options: any) {
|
||||
findOneWhere = options.where;
|
||||
return {
|
||||
value: JSON.stringify({
|
||||
value: {
|
||||
privateKey: "legacy-private-key",
|
||||
},
|
||||
}),
|
||||
};
|
||||
},
|
||||
async find() {
|
||||
findCalled = true;
|
||||
return [];
|
||||
},
|
||||
};
|
||||
},
|
||||
} as any,
|
||||
});
|
||||
|
||||
const config = await autoFix.getLegacyGoogleAccountConfig("user@example.com");
|
||||
|
||||
assert.equal(config.privateKey, "legacy-private-key");
|
||||
assert.deepEqual(findOneWhere, buildLegacyGoogleAccountConfigWhere("user@example.com"));
|
||||
assert.equal(findCalled, false);
|
||||
});
|
||||
|
||||
it("does not query legacy Google account config without email", async () => {
|
||||
let repositoryCalled = false;
|
||||
const autoFix = createAutoFix({
|
||||
pluginConfigService: null as any,
|
||||
accessService: null as any,
|
||||
storageService: {
|
||||
getRepository() {
|
||||
repositoryCalled = true;
|
||||
return {};
|
||||
},
|
||||
} as any,
|
||||
});
|
||||
|
||||
const config = await autoFix.getLegacyGoogleAccountConfig();
|
||||
|
||||
assert.equal(config, null);
|
||||
assert.equal(repositoryCalled, false);
|
||||
});
|
||||
|
||||
it("skips Google common EAB account key fix outside commercial edition", async () => {
|
||||
let pluginConfigCalled = false;
|
||||
const autoFix = createAutoFix({
|
||||
pluginConfigService: {
|
||||
async getPluginConfig() {
|
||||
pluginConfigCalled = true;
|
||||
return null;
|
||||
},
|
||||
} as any,
|
||||
accessService: null as any,
|
||||
storageService: null as any,
|
||||
sysSettingsService: {
|
||||
async getPublicSettings() {
|
||||
return {
|
||||
oauthProviders: {},
|
||||
};
|
||||
},
|
||||
},
|
||||
oauthBoundService: {
|
||||
async transaction(callback: any) {
|
||||
return await callback({
|
||||
async findOne() {
|
||||
return null;
|
||||
},
|
||||
async update() {
|
||||
return { affected: 0 };
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await autoFix.init();
|
||||
|
||||
assert.equal(pluginConfigCalled, false);
|
||||
});
|
||||
|
||||
it("fixes Google common EAB account key in commercial edition", async () => {
|
||||
const { AutoFix: MockedAutoFix } = await esmock("./auto-fix.js", {
|
||||
"@certd/plus-core": {
|
||||
isComm: () => true,
|
||||
},
|
||||
});
|
||||
let getAccessByIdArgs: any[] = [];
|
||||
let findOneWhere: any;
|
||||
let updateAccessParam: any;
|
||||
const autoFix = new MockedAutoFix();
|
||||
autoFix.pluginConfigService = {
|
||||
async getPluginConfig(options: any) {
|
||||
assert.deepEqual(options, {
|
||||
name: "CertApply",
|
||||
type: "builtIn",
|
||||
});
|
||||
return {
|
||||
sysSetting: {
|
||||
input: {
|
||||
googleCommonEabAccessId: 12,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
autoFix.accessService = {
|
||||
async getAccessById(...args: any[]) {
|
||||
getAccessByIdArgs = args;
|
||||
return {
|
||||
kid: "kid-1",
|
||||
email: "user@example.com",
|
||||
};
|
||||
},
|
||||
async updateAccess(param: any) {
|
||||
updateAccessParam = param;
|
||||
},
|
||||
};
|
||||
autoFix.storageService = {
|
||||
getRepository() {
|
||||
return {
|
||||
async findOne(options: any) {
|
||||
findOneWhere = options.where;
|
||||
return {
|
||||
value: JSON.stringify({
|
||||
value: {
|
||||
privateKey: "legacy-private-key",
|
||||
},
|
||||
}),
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
await autoFix.fixGoogleCommonEabAccountKey();
|
||||
|
||||
assert.deepEqual(getAccessByIdArgs, [12, false]);
|
||||
assert.deepEqual(findOneWhere, buildLegacyGoogleAccountConfigWhere("user@example.com"));
|
||||
assert.deepEqual(updateAccessParam, {
|
||||
id: 12,
|
||||
eabType: "google",
|
||||
accountKey: buildEabAccountKeyValue("kid-1", "legacy-private-key"),
|
||||
});
|
||||
});
|
||||
|
||||
it("fixes legacy OAuth bound type from string addon loginType and converts loginType to array", async () => {
|
||||
const updates: any[] = [];
|
||||
const autoFix = createAutoFix({
|
||||
pluginConfigService: null as any,
|
||||
accessService: null as any,
|
||||
storageService: null as any,
|
||||
sysSettingsService: {
|
||||
async getPublicSettings() {
|
||||
return {
|
||||
oauthProviders: {
|
||||
clogin: {
|
||||
addonId: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
oauthBoundService: {
|
||||
async transaction(callback: any) {
|
||||
return await callback({
|
||||
async findOne(entity: any, options: any) {
|
||||
assert.equal(entity.name, "AddonEntity");
|
||||
assert.deepEqual(options, { where: { id: 1 } });
|
||||
return {
|
||||
id: 1,
|
||||
setting: JSON.stringify({
|
||||
loginType: "alipay",
|
||||
}),
|
||||
};
|
||||
},
|
||||
async update(entity: any, where: any, value: any) {
|
||||
updates.push({ entity: entity.name, where, value });
|
||||
return { affected: entity.name === "OauthBoundEntity" ? 1 : 0 };
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await autoFix.fixOauthSubtypeBoundType();
|
||||
|
||||
assert.deepEqual(updates[0], {
|
||||
entity: "OauthBoundEntity",
|
||||
where: { type: "clogin" },
|
||||
value: { type: "clogin:alipay" },
|
||||
});
|
||||
assert.equal(updates[1].entity, "AddonEntity");
|
||||
assert.deepEqual(updates[1].where, { id: 1 });
|
||||
assert.deepEqual(JSON.parse(updates[1].value.setting).loginType, ["alipay"]);
|
||||
});
|
||||
|
||||
it("skips OAuth subtype fix when addon loginType is already not legacy string", async () => {
|
||||
let updateCalled = false;
|
||||
const autoFix = createAutoFix({
|
||||
pluginConfigService: null as any,
|
||||
accessService: null as any,
|
||||
storageService: null as any,
|
||||
sysSettingsService: {
|
||||
async getPublicSettings() {
|
||||
return {
|
||||
oauthProviders: {
|
||||
clogin: {
|
||||
addonId: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
oauthBoundService: {
|
||||
async transaction(callback: any) {
|
||||
return await callback({
|
||||
async findOne() {
|
||||
return {
|
||||
id: 1,
|
||||
setting: JSON.stringify({
|
||||
loginType: ["alipay", "github"],
|
||||
}),
|
||||
};
|
||||
},
|
||||
async update() {
|
||||
updateCalled = true;
|
||||
return { affected: 0 };
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await autoFix.fixOauthSubtypeBoundType();
|
||||
|
||||
assert.equal(updateCalled, false);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
import assert from "assert";
|
||||
import { AutoFix } from "./auto-fix.js";
|
||||
|
||||
describe("AutoFix", () => {
|
||||
it("runs fix tasks in order", async () => {
|
||||
const calls: string[] = [];
|
||||
const autoFix = new AutoFix();
|
||||
autoFix.googleCommonEabAccountKeyFix = {
|
||||
async init() {
|
||||
calls.push("google");
|
||||
},
|
||||
} as any;
|
||||
autoFix.oauthSubtypeBoundTypeFix = {
|
||||
async init() {
|
||||
calls.push("oauth");
|
||||
},
|
||||
} as any;
|
||||
autoFix.certInfoWildcardDomainCountFix = {
|
||||
async init() {
|
||||
calls.push("cert");
|
||||
},
|
||||
} as any;
|
||||
autoFix.suiteContentWildcardDomainCountFix = {
|
||||
async init() {
|
||||
calls.push("suite");
|
||||
},
|
||||
} as any;
|
||||
|
||||
await autoFix.init();
|
||||
|
||||
assert.deepEqual(calls, ["google", "oauth", "cert", "suite"]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { GoogleCommonEabAccountKeyFix } from "./google-common-eab-account-key-fix.js";
|
||||
import { OauthSubtypeBoundTypeFix } from "./oauth-subtype-bound-type-fix.js";
|
||||
import { CertInfoWildcardDomainCountFix } from "./cert-info-wildcard-domain-count-fix.js";
|
||||
import { SuiteContentWildcardDomainCountFix } from "./suite-content-wildcard-domain-count-fix.js";
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class AutoFix {
|
||||
@Inject()
|
||||
googleCommonEabAccountKeyFix: GoogleCommonEabAccountKeyFix;
|
||||
|
||||
@Inject()
|
||||
oauthSubtypeBoundTypeFix: OauthSubtypeBoundTypeFix;
|
||||
|
||||
@Inject()
|
||||
certInfoWildcardDomainCountFix: CertInfoWildcardDomainCountFix;
|
||||
|
||||
@Inject()
|
||||
suiteContentWildcardDomainCountFix: SuiteContentWildcardDomainCountFix;
|
||||
|
||||
async init() {
|
||||
await this.googleCommonEabAccountKeyFix.init();
|
||||
await this.oauthSubtypeBoundTypeFix.init();
|
||||
await this.certInfoWildcardDomainCountFix.init();
|
||||
await this.suiteContentWildcardDomainCountFix.init();
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
import assert from "assert";
|
||||
import { CertInfoWildcardDomainCountFix } from "./cert-info-wildcard-domain-count-fix.js";
|
||||
|
||||
describe("CertInfoWildcardDomainCountFix", () => {
|
||||
it("fixes cert info wildcard domain count only when value changed", async () => {
|
||||
const updated: any[] = [];
|
||||
const rows = [
|
||||
{ id: 1, domains: "*.a.com,a.com, *.b.com ", wildcardDomainCount: 0 },
|
||||
{ id: 2, domains: "c.com", wildcardDomainCount: 0 },
|
||||
{ id: 3, domains: "*.d.com", wildcardDomainCount: 1 },
|
||||
];
|
||||
const fix = new CertInfoWildcardDomainCountFix();
|
||||
fix.certInfoService = {
|
||||
countWildcardDomains(domains: string[]) {
|
||||
return domains.filter(item => item.trim().toLowerCase().startsWith("*.")).length;
|
||||
},
|
||||
async find() {
|
||||
return rows;
|
||||
},
|
||||
async update(value: any) {
|
||||
updated.push(value);
|
||||
const row = rows.find(item => item.id === value.id);
|
||||
Object.assign(row, value);
|
||||
},
|
||||
} as any;
|
||||
|
||||
await fix.init();
|
||||
await fix.init();
|
||||
|
||||
assert.deepEqual(updated, [
|
||||
{
|
||||
id: 1,
|
||||
wildcardDomainCount: 2,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { logger } from "@certd/basic";
|
||||
import { CertInfoService } from "../../monitor/service/cert-info-service.js";
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class CertInfoWildcardDomainCountFix {
|
||||
@Inject()
|
||||
certInfoService: CertInfoService;
|
||||
|
||||
async init() {
|
||||
if (!this.certInfoService) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const list = await this.certInfoService.find({
|
||||
select: {
|
||||
id: true,
|
||||
domains: true,
|
||||
wildcardDomainCount: true,
|
||||
},
|
||||
});
|
||||
let fixedCount = 0;
|
||||
for (const item of list) {
|
||||
if (!item.domains) {
|
||||
continue;
|
||||
}
|
||||
const wildcardDomainCount = this.certInfoService.countWildcardDomains(item.domains.split(","));
|
||||
if ((item.wildcardDomainCount ?? 0) === wildcardDomainCount) {
|
||||
continue;
|
||||
}
|
||||
await this.certInfoService.update({
|
||||
id: item.id,
|
||||
wildcardDomainCount,
|
||||
});
|
||||
fixedCount++;
|
||||
}
|
||||
if (fixedCount > 0) {
|
||||
logger.info(`已修复证书泛域名数量历史数据,数量=${fixedCount}`);
|
||||
}
|
||||
} catch (e: any) {
|
||||
logger.error("修复证书泛域名数量历史数据失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
import assert from "assert";
|
||||
import esmock from "esmock";
|
||||
import { buildEabAccountKeyValue, buildLegacyGoogleAccountConfigWhere, GoogleCommonEabAccountKeyFix, parseStorageValue } from "./google-common-eab-account-key-fix.js";
|
||||
|
||||
describe("GoogleCommonEabAccountKeyFix", () => {
|
||||
it("parses legacy storage values", () => {
|
||||
const config = parseStorageValue(
|
||||
JSON.stringify({
|
||||
value: {
|
||||
key: "legacy-private-key",
|
||||
accountUrl: "https://example.com/acct/1",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
assert.equal(config.key, "legacy-private-key");
|
||||
});
|
||||
|
||||
it("builds the EAB account key payload", () => {
|
||||
const payload = JSON.parse(buildEabAccountKeyValue("kid-1", "private-key"));
|
||||
|
||||
assert.deepEqual(payload, {
|
||||
kid: "kid-1",
|
||||
privateKey: "private-key",
|
||||
});
|
||||
});
|
||||
|
||||
it("builds legacy Google account config query by exact email key only", () => {
|
||||
assert.deepEqual(buildLegacyGoogleAccountConfigWhere("user@example.com"), {
|
||||
userId: 1,
|
||||
scope: "user",
|
||||
namespace: "1",
|
||||
key: "acme.config.google.user@example.com",
|
||||
});
|
||||
});
|
||||
|
||||
it("finds legacy Google account config by exact email key only", async () => {
|
||||
let findOneWhere: any;
|
||||
let findCalled = false;
|
||||
const fix = new GoogleCommonEabAccountKeyFix();
|
||||
fix.storageService = {
|
||||
getRepository() {
|
||||
return {
|
||||
async findOne(options: any) {
|
||||
findOneWhere = options.where;
|
||||
return {
|
||||
value: JSON.stringify({
|
||||
value: {
|
||||
privateKey: "legacy-private-key",
|
||||
},
|
||||
}),
|
||||
};
|
||||
},
|
||||
async find() {
|
||||
findCalled = true;
|
||||
return [];
|
||||
},
|
||||
};
|
||||
},
|
||||
} as any;
|
||||
|
||||
const config = await fix.getLegacyGoogleAccountConfig("user@example.com");
|
||||
|
||||
assert.equal(config.privateKey, "legacy-private-key");
|
||||
assert.deepEqual(findOneWhere, buildLegacyGoogleAccountConfigWhere("user@example.com"));
|
||||
assert.equal(findCalled, false);
|
||||
});
|
||||
|
||||
it("does not query legacy Google account config without email", async () => {
|
||||
let repositoryCalled = false;
|
||||
const fix = new GoogleCommonEabAccountKeyFix();
|
||||
fix.storageService = {
|
||||
getRepository() {
|
||||
repositoryCalled = true;
|
||||
return {};
|
||||
},
|
||||
} as any;
|
||||
|
||||
const config = await fix.getLegacyGoogleAccountConfig();
|
||||
|
||||
assert.equal(config, null);
|
||||
assert.equal(repositoryCalled, false);
|
||||
});
|
||||
|
||||
it("skips Google common EAB account key fix outside commercial edition", async () => {
|
||||
const { GoogleCommonEabAccountKeyFix: MockedFix } = await esmock("./google-common-eab-account-key-fix.js", {
|
||||
"@certd/plus-core": {
|
||||
isComm: () => false,
|
||||
},
|
||||
});
|
||||
let pluginConfigCalled = false;
|
||||
const fix = new MockedFix();
|
||||
fix.pluginConfigService = {
|
||||
async getPluginConfig() {
|
||||
pluginConfigCalled = true;
|
||||
return null;
|
||||
},
|
||||
};
|
||||
|
||||
await fix.init();
|
||||
|
||||
assert.equal(pluginConfigCalled, false);
|
||||
});
|
||||
|
||||
it("fixes Google common EAB account key in commercial edition", async () => {
|
||||
const { GoogleCommonEabAccountKeyFix: MockedFix } = await esmock("./google-common-eab-account-key-fix.js", {
|
||||
"@certd/plus-core": {
|
||||
isComm: () => true,
|
||||
},
|
||||
});
|
||||
let getAccessByIdArgs: any[] = [];
|
||||
let findOneWhere: any;
|
||||
let updateAccessParam: any;
|
||||
const fix = new MockedFix();
|
||||
fix.pluginConfigService = {
|
||||
async getPluginConfig(options: any) {
|
||||
assert.deepEqual(options, {
|
||||
name: "CertApply",
|
||||
type: "builtIn",
|
||||
});
|
||||
return {
|
||||
sysSetting: {
|
||||
input: {
|
||||
googleCommonEabAccessId: 12,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
fix.accessService = {
|
||||
async getAccessById(...args: any[]) {
|
||||
getAccessByIdArgs = args;
|
||||
return {
|
||||
kid: "kid-1",
|
||||
email: "user@example.com",
|
||||
};
|
||||
},
|
||||
async updateAccess(param: any) {
|
||||
updateAccessParam = param;
|
||||
},
|
||||
};
|
||||
fix.storageService = {
|
||||
getRepository() {
|
||||
return {
|
||||
async findOne(options: any) {
|
||||
findOneWhere = options.where;
|
||||
return {
|
||||
value: JSON.stringify({
|
||||
value: {
|
||||
privateKey: "legacy-private-key",
|
||||
},
|
||||
}),
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
await fix.init();
|
||||
|
||||
assert.deepEqual(getAccessByIdArgs, [12, false]);
|
||||
assert.deepEqual(findOneWhere, buildLegacyGoogleAccountConfigWhere("user@example.com"));
|
||||
assert.deepEqual(updateAccessParam, {
|
||||
id: 12,
|
||||
eabType: "google",
|
||||
accountKey: buildEabAccountKeyValue("kid-1", "legacy-private-key"),
|
||||
});
|
||||
});
|
||||
});
|
||||
+5
-71
@@ -1,11 +1,9 @@
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { logger } from "@certd/basic";
|
||||
import { AccessService, AddonEntity, SysSettingsService } from "@certd/lib-server";
|
||||
import { AccessService } from "@certd/lib-server";
|
||||
import { isComm } from "@certd/plus-core";
|
||||
import { PluginConfigService } from "../plugin/service/plugin-config-service.js";
|
||||
import { StorageService } from "../pipeline/service/storage-service.js";
|
||||
import { OauthBoundService } from "../login/service/oauth-bound-service.js";
|
||||
import { OauthBoundEntity } from "../login/entity/oauth-bound.js";
|
||||
import { PluginConfigService } from "../../plugin/service/plugin-config-service.js";
|
||||
import { StorageService } from "../../pipeline/service/storage-service.js";
|
||||
|
||||
export function parseStorageValue(value?: string) {
|
||||
if (!value) {
|
||||
@@ -35,13 +33,9 @@ export function buildLegacyGoogleAccountConfigWhere(email: string) {
|
||||
};
|
||||
}
|
||||
|
||||
export function buildOauthBoundType(type: string, subtype?: string) {
|
||||
return subtype ? `${type}:${subtype}` : type;
|
||||
}
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class AutoFix {
|
||||
export class GoogleCommonEabAccountKeyFix {
|
||||
@Inject()
|
||||
pluginConfigService: PluginConfigService;
|
||||
|
||||
@@ -51,67 +45,7 @@ export class AutoFix {
|
||||
@Inject()
|
||||
storageService: StorageService;
|
||||
|
||||
@Inject()
|
||||
sysSettingsService: SysSettingsService;
|
||||
|
||||
@Inject()
|
||||
oauthBoundService: OauthBoundService;
|
||||
|
||||
async init() {
|
||||
await this.fixGoogleCommonEabAccountKey();
|
||||
await this.fixOauthSubtypeBoundType();
|
||||
}
|
||||
|
||||
async fixOauthSubtypeBoundType() {
|
||||
try {
|
||||
const publicSettings = await this.sysSettingsService.getPublicSettings();
|
||||
const oauthProviders = publicSettings.oauthProviders || {};
|
||||
await this.oauthBoundService.transaction(async manager => {
|
||||
for (const [type, provider] of Object.entries(oauthProviders)) {
|
||||
if (!provider.addonId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const addonEntity = await manager.findOne(AddonEntity, { where: { id: provider.addonId } });
|
||||
const legacyLoginType = this.getLegacyAddonLoginType(addonEntity?.setting);
|
||||
if (!legacyLoginType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const newType = buildOauthBoundType(type, legacyLoginType);
|
||||
const res = await manager.update(OauthBoundEntity, { type }, { type: newType });
|
||||
if (res.affected) {
|
||||
logger.info(`已修复OAuth绑定历史数据,${type} -> ${newType},数量=${res.affected}`);
|
||||
}
|
||||
await this.convertLegacyAddonLoginTypeToArray(addonEntity, legacyLoginType, manager);
|
||||
}
|
||||
});
|
||||
} catch (e: any) {
|
||||
logger.error("修复OAuth subtype绑定历史数据失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private getLegacyAddonLoginType(settingValue?: string) {
|
||||
if (!settingValue) {
|
||||
return null;
|
||||
}
|
||||
const setting = JSON.parse(settingValue);
|
||||
return typeof setting.loginType === "string" && setting.loginType ? setting.loginType : null;
|
||||
}
|
||||
|
||||
private async convertLegacyAddonLoginTypeToArray(addonEntity: AddonEntity | null, loginType: string, manager: any) {
|
||||
if (!addonEntity?.setting) {
|
||||
return;
|
||||
}
|
||||
const setting = JSON.parse(addonEntity.setting);
|
||||
if (typeof setting.loginType !== "string") {
|
||||
return;
|
||||
}
|
||||
setting.loginType = [loginType];
|
||||
await manager.update(AddonEntity, { id: addonEntity.id }, { setting: JSON.stringify(setting) });
|
||||
}
|
||||
|
||||
async fixGoogleCommonEabAccountKey() {
|
||||
if (!isComm()) {
|
||||
return;
|
||||
}
|
||||
@@ -163,7 +97,7 @@ export class AutoFix {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
parseStorageValue(value?: string) {
|
||||
return parseStorageValue(value);
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import assert from "assert";
|
||||
import { buildOauthBoundType, OauthSubtypeBoundTypeFix } from "./oauth-subtype-bound-type-fix.js";
|
||||
|
||||
describe("OauthSubtypeBoundTypeFix", () => {
|
||||
it("builds OAuth subtype bound type", () => {
|
||||
assert.equal(buildOauthBoundType("clogin", "alipay"), "clogin:alipay");
|
||||
assert.equal(buildOauthBoundType("github"), "github");
|
||||
});
|
||||
|
||||
it("fixes legacy OAuth bound type from string addon loginType and converts loginType to array", async () => {
|
||||
const updates: any[] = [];
|
||||
const fix = new OauthSubtypeBoundTypeFix();
|
||||
fix.sysSettingsService = {
|
||||
async getPublicSettings() {
|
||||
return {
|
||||
oauthProviders: {
|
||||
clogin: {
|
||||
addonId: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
} as any;
|
||||
fix.oauthBoundService = {
|
||||
async transaction(callback: any) {
|
||||
return await callback({
|
||||
async findOne(entity: any, options: any) {
|
||||
assert.equal(entity.name, "AddonEntity");
|
||||
assert.deepEqual(options, { where: { id: 1 } });
|
||||
return {
|
||||
id: 1,
|
||||
setting: JSON.stringify({
|
||||
loginType: "alipay",
|
||||
}),
|
||||
};
|
||||
},
|
||||
async update(entity: any, where: any, value: any) {
|
||||
updates.push({ entity: entity.name, where, value });
|
||||
return { affected: entity.name === "OauthBoundEntity" ? 1 : 0 };
|
||||
},
|
||||
});
|
||||
},
|
||||
} as any;
|
||||
|
||||
await fix.init();
|
||||
|
||||
assert.deepEqual(updates[0], {
|
||||
entity: "OauthBoundEntity",
|
||||
where: { type: "clogin" },
|
||||
value: { type: "clogin:alipay" },
|
||||
});
|
||||
assert.equal(updates[1].entity, "AddonEntity");
|
||||
assert.deepEqual(updates[1].where, { id: 1 });
|
||||
assert.deepEqual(JSON.parse(updates[1].value.setting).loginType, ["alipay"]);
|
||||
});
|
||||
|
||||
it("skips OAuth subtype fix when addon loginType is already not legacy string", async () => {
|
||||
let updateCalled = false;
|
||||
const fix = new OauthSubtypeBoundTypeFix();
|
||||
fix.sysSettingsService = {
|
||||
async getPublicSettings() {
|
||||
return {
|
||||
oauthProviders: {
|
||||
clogin: {
|
||||
addonId: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
} as any;
|
||||
fix.oauthBoundService = {
|
||||
async transaction(callback: any) {
|
||||
return await callback({
|
||||
async findOne() {
|
||||
return {
|
||||
id: 1,
|
||||
setting: JSON.stringify({
|
||||
loginType: ["alipay", "github"],
|
||||
}),
|
||||
};
|
||||
},
|
||||
async update() {
|
||||
updateCalled = true;
|
||||
return { affected: 0 };
|
||||
},
|
||||
});
|
||||
},
|
||||
} as any;
|
||||
|
||||
await fix.init();
|
||||
|
||||
assert.equal(updateCalled, false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { logger } from "@certd/basic";
|
||||
import { AddonEntity, SysSettingsService } from "@certd/lib-server";
|
||||
import { OauthBoundService } from "../../login/service/oauth-bound-service.js";
|
||||
import { OauthBoundEntity } from "../../login/entity/oauth-bound.js";
|
||||
|
||||
export function buildOauthBoundType(type: string, subtype?: string) {
|
||||
return subtype ? `${type}:${subtype}` : type;
|
||||
}
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class OauthSubtypeBoundTypeFix {
|
||||
@Inject()
|
||||
sysSettingsService: SysSettingsService;
|
||||
|
||||
@Inject()
|
||||
oauthBoundService: OauthBoundService;
|
||||
|
||||
async init() {
|
||||
try {
|
||||
const publicSettings = await this.sysSettingsService.getPublicSettings();
|
||||
const oauthProviders = publicSettings.oauthProviders || {};
|
||||
await this.oauthBoundService.transaction(async manager => {
|
||||
for (const [type, provider] of Object.entries(oauthProviders)) {
|
||||
if (!provider.addonId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const addonEntity = await manager.findOne(AddonEntity, { where: { id: provider.addonId } });
|
||||
const legacyLoginType = this.getLegacyAddonLoginType(addonEntity?.setting);
|
||||
if (!legacyLoginType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const newType = buildOauthBoundType(type, legacyLoginType);
|
||||
const res = await manager.update(OauthBoundEntity, { type }, { type: newType });
|
||||
if (res.affected) {
|
||||
logger.info(`已修复OAuth绑定历史数据,${type} -> ${newType},数量=${res.affected}`);
|
||||
}
|
||||
await this.convertLegacyAddonLoginTypeToArray(addonEntity, legacyLoginType, manager);
|
||||
}
|
||||
});
|
||||
} catch (e: any) {
|
||||
logger.error("修复OAuth subtype绑定历史数据失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private getLegacyAddonLoginType(settingValue?: string) {
|
||||
if (!settingValue) {
|
||||
return null;
|
||||
}
|
||||
const setting = JSON.parse(settingValue);
|
||||
return typeof setting.loginType === "string" && setting.loginType ? setting.loginType : null;
|
||||
}
|
||||
|
||||
private async convertLegacyAddonLoginTypeToArray(addonEntity: AddonEntity | null, loginType: string, manager: any) {
|
||||
if (!addonEntity?.setting) {
|
||||
return;
|
||||
}
|
||||
const setting = JSON.parse(addonEntity.setting);
|
||||
if (typeof setting.loginType !== "string") {
|
||||
return;
|
||||
}
|
||||
setting.loginType = [loginType];
|
||||
await manager.update(AddonEntity, { id: addonEntity.id }, { setting: JSON.stringify(setting) });
|
||||
}
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
import assert from "assert";
|
||||
import { fixSuiteContentWildcardDomainCount, SuiteContentWildcardDomainCountFix } from "./suite-content-wildcard-domain-count-fix.js";
|
||||
|
||||
describe("SuiteContentWildcardDomainCountFix", () => {
|
||||
it("fills missing suite wildcard domain count from total domain count", () => {
|
||||
const fixed = fixSuiteContentWildcardDomainCount(
|
||||
JSON.stringify({
|
||||
maxDomainCount: 10,
|
||||
})
|
||||
);
|
||||
|
||||
assert.equal(JSON.parse(fixed).maxWildcardDomainCount, 10);
|
||||
assert.equal(
|
||||
JSON.parse(
|
||||
fixSuiteContentWildcardDomainCount(
|
||||
JSON.stringify({
|
||||
maxDomainCount: -1,
|
||||
})
|
||||
)
|
||||
).maxWildcardDomainCount,
|
||||
-1
|
||||
);
|
||||
assert.equal(
|
||||
fixSuiteContentWildcardDomainCount(
|
||||
JSON.stringify({
|
||||
maxDomainCount: 10,
|
||||
maxWildcardDomainCount: 3,
|
||||
})
|
||||
),
|
||||
null
|
||||
);
|
||||
});
|
||||
|
||||
it("fixes suite content wildcard domain count in product and user suite tables", async () => {
|
||||
const rows = {
|
||||
cd_product: [
|
||||
{ id: 1, content: JSON.stringify({ maxDomainCount: 1 }) },
|
||||
{ id: 2, content: JSON.stringify({ maxDomainCount: 1, maxWildcardDomainCount: 0 }) },
|
||||
],
|
||||
cd_user_suite: [{ id: 3, content: JSON.stringify({ maxDomainCount: 2 }) }],
|
||||
};
|
||||
const updates: any[] = [];
|
||||
const entityManager = {
|
||||
async query(sql: string) {
|
||||
const table = sql.includes("cd_product") ? "cd_product" : "cd_user_suite";
|
||||
return rows[table];
|
||||
},
|
||||
async update(tableName: string, where: any, value: any) {
|
||||
updates.push({ tableName, where, value });
|
||||
const row = rows[tableName].find((item: any) => item.id === where.id);
|
||||
Object.assign(row, value);
|
||||
},
|
||||
};
|
||||
const fix = new SuiteContentWildcardDomainCountFix();
|
||||
fix.dataSourceManager = {
|
||||
getDataSource() {
|
||||
return {
|
||||
manager: entityManager,
|
||||
};
|
||||
},
|
||||
} as any;
|
||||
|
||||
await fix.init();
|
||||
await fix.init();
|
||||
|
||||
assert.equal(updates.length, 2);
|
||||
assert.equal(updates[0].tableName, "cd_product");
|
||||
assert.equal(JSON.parse(updates[0].value.content).maxWildcardDomainCount, 1);
|
||||
assert.equal(updates[1].tableName, "cd_user_suite");
|
||||
assert.equal(JSON.parse(updates[1].value.content).maxWildcardDomainCount, 2);
|
||||
});
|
||||
});
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { logger } from "@certd/basic";
|
||||
import { TypeORMDataSourceManager } from "@midwayjs/typeorm";
|
||||
|
||||
export function fixSuiteContentWildcardDomainCount(contentValue?: string) {
|
||||
if (!contentValue) {
|
||||
return null;
|
||||
}
|
||||
const content = JSON.parse(contentValue);
|
||||
if (content.maxWildcardDomainCount != null) {
|
||||
return null;
|
||||
}
|
||||
content.maxWildcardDomainCount = content.maxDomainCount == null || content.maxDomainCount === -1 ? -1 : content.maxDomainCount;
|
||||
return JSON.stringify(content);
|
||||
}
|
||||
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class SuiteContentWildcardDomainCountFix {
|
||||
@Inject()
|
||||
dataSourceManager: TypeORMDataSourceManager;
|
||||
|
||||
async init() {
|
||||
if (!this.dataSourceManager) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const entityManager = this.dataSourceManager.getDataSource("default").manager;
|
||||
let fixedCount = 0;
|
||||
fixedCount += await this.fixSuiteContentWildcardDomainCountByTable(entityManager, "cd_product");
|
||||
fixedCount += await this.fixSuiteContentWildcardDomainCountByTable(entityManager, "cd_user_suite");
|
||||
if (fixedCount > 0) {
|
||||
logger.info(`已修复套餐最大泛域名数量历史数据,数量=${fixedCount}`);
|
||||
}
|
||||
} catch (e: any) {
|
||||
logger.error("修复套餐最大泛域名数量历史数据失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
private async fixSuiteContentWildcardDomainCountByTable(entityManager: any, tableName: string) {
|
||||
const list = await entityManager.query(`select id, content from ${tableName}`);
|
||||
let fixedCount = 0;
|
||||
for (const item of list) {
|
||||
const content = fixSuiteContentWildcardDomainCount(item.content);
|
||||
if (!content) {
|
||||
continue;
|
||||
}
|
||||
await entityManager.update(tableName, { id: item.id }, { content });
|
||||
fixedCount++;
|
||||
}
|
||||
return fixedCount;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user