140 lines
3.6 KiB
TypeScript
140 lines
3.6 KiB
TypeScript
import type { LicenseType } from './types';
|
||
|
||
export interface PremiumModuleArtifact {
|
||
type: 'registry-image';
|
||
image: string;
|
||
digest?: string | null;
|
||
version: string;
|
||
}
|
||
|
||
export interface PremiumModuleDefinition {
|
||
id: string;
|
||
minLicenseType: LicenseType;
|
||
capabilities: string[];
|
||
}
|
||
|
||
export interface PremiumModuleGrantSpec {
|
||
moduleId: string;
|
||
artifact: PremiumModuleArtifact;
|
||
limits?: {
|
||
maxUsers: number;
|
||
maxMaterials: number;
|
||
};
|
||
}
|
||
|
||
export interface ParsedRegistryImage {
|
||
registryHost: string;
|
||
repository: string;
|
||
tag: string;
|
||
}
|
||
|
||
export const MODULE_DEFINITIONS: Record<string, PremiumModuleDefinition> = {
|
||
'paid-core': {
|
||
id: 'paid-core',
|
||
minLicenseType: 'STANDARD',
|
||
capabilities: [
|
||
'customTemplates',
|
||
'departmentIsolation',
|
||
'systemSettings',
|
||
'intranetPenetration',
|
||
'apiAccess',
|
||
],
|
||
},
|
||
};
|
||
|
||
export const MODULE_IDS_BY_LICENSE: Record<LicenseType, string[]> = {
|
||
FREE: [],
|
||
STANDARD: ['paid-core'],
|
||
PRO: ['paid-core'],
|
||
};
|
||
|
||
const LICENSE_TYPE_RANK: Record<LicenseType, number> = {
|
||
FREE: 0,
|
||
STANDARD: 1,
|
||
PRO: 2,
|
||
};
|
||
|
||
export function getModuleDefinition(moduleId: string): PremiumModuleDefinition | null {
|
||
return MODULE_DEFINITIONS[moduleId] || null;
|
||
}
|
||
|
||
export function canLicenseUseModule(
|
||
licenseType: LicenseType,
|
||
module: PremiumModuleDefinition
|
||
): boolean {
|
||
return LICENSE_TYPE_RANK[licenseType] >= LICENSE_TYPE_RANK[module.minLicenseType];
|
||
}
|
||
|
||
function toModuleEnvKey(moduleId: string): string {
|
||
return moduleId.replace(/[^a-zA-Z0-9]/g, '_').toUpperCase();
|
||
}
|
||
|
||
function getDefaultModuleVersion(): string {
|
||
return process.env.PREMIUM_MODULE_VERSION?.trim() || '1.0.0';
|
||
}
|
||
|
||
export function getPremiumModuleArtifact(moduleId: string): PremiumModuleArtifact {
|
||
const envKey = toModuleEnvKey(moduleId);
|
||
const explicitImage = process.env[`PREMIUM_MODULE_${envKey}_IMAGE`]?.trim();
|
||
const version = process.env[`PREMIUM_MODULE_${envKey}_TAG`]?.trim() || getDefaultModuleVersion();
|
||
const digest = process.env[`PREMIUM_MODULE_${envKey}_DIGEST`]?.trim() || null;
|
||
|
||
if (explicitImage) {
|
||
return {
|
||
type: 'registry-image',
|
||
image: explicitImage,
|
||
digest,
|
||
version,
|
||
};
|
||
}
|
||
|
||
const registryBase = process.env.PREMIUM_MODULE_REGISTRY_BASE?.trim().replace(/\/+$/, '');
|
||
if (!registryBase) {
|
||
throw new Error('PREMIUM_MODULE_REGISTRY_BASE 未配置,无法生成授权模块镜像地址');
|
||
}
|
||
|
||
return {
|
||
type: 'registry-image',
|
||
image: `${registryBase}/${moduleId}:${version}`,
|
||
digest,
|
||
version,
|
||
};
|
||
}
|
||
|
||
export function getModuleLimits(
|
||
moduleId: string,
|
||
licenseType: LicenseType
|
||
): PremiumModuleGrantSpec['limits'] | undefined {
|
||
void moduleId;
|
||
void licenseType;
|
||
return undefined;
|
||
}
|
||
|
||
export function getDefaultModuleGrantSpecs(licenseType: LicenseType): PremiumModuleGrantSpec[] {
|
||
return MODULE_IDS_BY_LICENSE[licenseType].map((moduleId) => ({
|
||
moduleId,
|
||
artifact: getPremiumModuleArtifact(moduleId),
|
||
limits: getModuleLimits(moduleId, licenseType),
|
||
}));
|
||
}
|
||
|
||
export function parseRegistryImage(image: string): ParsedRegistryImage {
|
||
const [registryHost, ...pathParts] = image.split('/');
|
||
if (!registryHost || pathParts.length === 0) {
|
||
throw new Error(`模块镜像地址无效:${image}`);
|
||
}
|
||
|
||
const imagePath = pathParts.join('/');
|
||
const lastSlashIndex = imagePath.lastIndexOf('/');
|
||
const tagSeparatorIndex = imagePath.lastIndexOf(':');
|
||
if (tagSeparatorIndex <= lastSlashIndex) {
|
||
throw new Error(`模块镜像地址缺少 tag:${image}`);
|
||
}
|
||
|
||
return {
|
||
registryHost,
|
||
repository: imagePath.slice(0, tagSeparatorIndex),
|
||
tag: imagePath.slice(tagSeparatorIndex + 1),
|
||
};
|
||
}
|