|
|
import type { Permission, ServiceTokenEntry } from "@/lib/constants/auth";
|
|
|
import { forbidden, unauthorized } from "@/modules/auth/errors";
|
|
|
|
|
|
export type VerifiedServiceToken = {
|
|
|
customerId: string;
|
|
|
permissions: Permission[];
|
|
|
};
|
|
|
|
|
|
let cachedEnvTokenMap: Map<string, ServiceTokenEntry> | null = null;
|
|
|
|
|
|
function loadEnvTokenMap(): Map<string, ServiceTokenEntry> {
|
|
|
if (cachedEnvTokenMap) {
|
|
|
return cachedEnvTokenMap;
|
|
|
}
|
|
|
|
|
|
const raw = process.env.HOST_SERVICE_TOKENS;
|
|
|
if (!raw) {
|
|
|
cachedEnvTokenMap = new Map();
|
|
|
return cachedEnvTokenMap;
|
|
|
}
|
|
|
|
|
|
let parsed: Record<string, ServiceTokenEntry>;
|
|
|
try {
|
|
|
parsed = JSON.parse(raw) as Record<string, ServiceTokenEntry>;
|
|
|
} catch {
|
|
|
throw new Error("HOST_SERVICE_TOKENS 配置无效");
|
|
|
}
|
|
|
|
|
|
cachedEnvTokenMap = new Map(Object.entries(parsed));
|
|
|
return cachedEnvTokenMap;
|
|
|
}
|
|
|
|
|
|
/** 测试专用:重置 env 缓存 */
|
|
|
export function resetServiceTokenEnvCache(): void {
|
|
|
cachedEnvTokenMap = null;
|
|
|
}
|
|
|
|
|
|
function verifyFromEnvMap(
|
|
|
token: string,
|
|
|
customerId?: string,
|
|
|
): VerifiedServiceToken | null {
|
|
|
const entry = loadEnvTokenMap().get(token);
|
|
|
if (!entry) {
|
|
|
return null;
|
|
|
}
|
|
|
if (customerId && entry.customerId !== customerId) {
|
|
|
throw forbidden("customer_id 与令牌租户不匹配");
|
|
|
}
|
|
|
return {
|
|
|
customerId: entry.customerId,
|
|
|
permissions: entry.permissions ?? [],
|
|
|
};
|
|
|
}
|
|
|
|
|
|
/** env 命中则返回,未命中返回 null(不抛错) */
|
|
|
export function resolveEnvServiceToken(
|
|
|
token: string,
|
|
|
customerId?: string,
|
|
|
): VerifiedServiceToken | null {
|
|
|
return verifyFromEnvMap(token, customerId);
|
|
|
}
|
|
|
|
|
|
/** env 中的客户 ID(同步) */
|
|
|
export function listEnvCustomerIds(): string[] {
|
|
|
const ids = new Set<string>();
|
|
|
for (const entry of loadEnvTokenMap().values()) {
|
|
|
ids.add(entry.customerId);
|
|
|
}
|
|
|
const registry = process.env.CUSTOMER_REGISTRY;
|
|
|
if (registry) {
|
|
|
for (const part of registry.split(",")) {
|
|
|
const id = part.trim();
|
|
|
if (id) {
|
|
|
ids.add(id);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return [...ids].sort();
|
|
|
}
|
|
|
|
|
|
/** 同步:仅 env + CUSTOMER_REGISTRY */
|
|
|
export function listRegisteredCustomerIds(): string[] {
|
|
|
return listEnvCustomerIds();
|
|
|
}
|
|
|
|
|
|
export function isKnownCustomer(customerId: string): boolean {
|
|
|
return listEnvCustomerIds().includes(customerId);
|
|
|
}
|
|
|
|
|
|
/** 同步:仅校验 env 中配置的 Token(middleware 快路径) */
|
|
|
export function verifyServiceTokenFromEnv(
|
|
|
token: string,
|
|
|
customerId: string,
|
|
|
): VerifiedServiceToken {
|
|
|
const verified = verifyFromEnvMap(token, customerId);
|
|
|
if (!verified) {
|
|
|
throw unauthorized("Service Token 无效");
|
|
|
}
|
|
|
return verified;
|
|
|
}
|
|
|
|
|
|
/** 同步:env token 解析(无 customer_id 头时) */
|
|
|
export function parseServiceTokenFromEnv(
|
|
|
token: string,
|
|
|
): VerifiedServiceToken | null {
|
|
|
return verifyFromEnvMap(token);
|
|
|
}
|
|
|
|
|
|
/** @deprecated 使用 verifyServiceTokenFromEnv */
|
|
|
export function verifyServiceToken(
|
|
|
token: string,
|
|
|
customerId: string,
|
|
|
): VerifiedServiceToken {
|
|
|
return verifyServiceTokenFromEnv(token, customerId);
|
|
|
}
|
|
|
|
|
|
/** @deprecated 使用 parseServiceTokenFromEnv */
|
|
|
export function parseServiceToken(token: string): VerifiedServiceToken | null {
|
|
|
return parseServiceTokenFromEnv(token);
|
|
|
}
|