flnxi-Wu/license-server/lib/premium-module-catalog.ts
2026-06-03 18:41:52 +08:00

140 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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),
};
}