|
|
import type { Permission, ServiceTokenEntry } from "@/lib/constants/auth";
|
|
|
import { forbidden, unauthorized } from "@/modules/auth/errors";
|
|
|
|
|
|
export type VerifiedServiceToken = {
|
|
|
customerId: string;
|
|
|
permissions: Permission[];
|
|
|
};
|
|
|
|
|
|
let cachedTokenMap: Map<string, ServiceTokenEntry> | null = null;
|
|
|
|
|
|
function loadTokenMap(): Map<string, ServiceTokenEntry> {
|
|
|
if (cachedTokenMap) {
|
|
|
return cachedTokenMap;
|
|
|
}
|
|
|
|
|
|
const raw = process.env.HOST_SERVICE_TOKENS;
|
|
|
if (!raw) {
|
|
|
cachedTokenMap = new Map();
|
|
|
return cachedTokenMap;
|
|
|
}
|
|
|
|
|
|
let parsed: Record<string, ServiceTokenEntry>;
|
|
|
try {
|
|
|
parsed = JSON.parse(raw) as Record<string, ServiceTokenEntry>;
|
|
|
} catch {
|
|
|
throw new Error("HOST_SERVICE_TOKENS 配置无效");
|
|
|
}
|
|
|
|
|
|
cachedTokenMap = new Map(Object.entries(parsed));
|
|
|
return cachedTokenMap;
|
|
|
}
|
|
|
|
|
|
/** 测试专用:重置缓存 */
|
|
|
export function resetServiceTokenCache(): void {
|
|
|
cachedTokenMap = null;
|
|
|
}
|
|
|
|
|
|
/** 校验宿主 Service Token 与 customer_id 租户绑定 */
|
|
|
export function verifyServiceToken(
|
|
|
token: string,
|
|
|
customerId: string,
|
|
|
): VerifiedServiceToken {
|
|
|
const entry = loadTokenMap().get(token);
|
|
|
if (!entry) {
|
|
|
throw unauthorized("Service Token 无效");
|
|
|
}
|
|
|
if (entry.customerId !== customerId) {
|
|
|
throw forbidden("customer_id 与令牌租户不匹配");
|
|
|
}
|
|
|
return {
|
|
|
customerId: entry.customerId,
|
|
|
permissions: entry.permissions ?? [],
|
|
|
};
|
|
|
}
|
|
|
|
|
|
/** 已注册宿主客户 ID 集合(markup 写操作校验用) */
|
|
|
export function listRegisteredCustomerIds(): string[] {
|
|
|
const ids = new Set<string>();
|
|
|
for (const entry of loadTokenMap().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();
|
|
|
}
|
|
|
|
|
|
export function isKnownCustomer(customerId: string): boolean {
|
|
|
return listRegisteredCustomerIds().includes(customerId);
|
|
|
}
|
|
|
|
|
|
/** 仅校验 token 存在(middleware 用,不含 customer 绑定) */
|
|
|
export function parseServiceToken(token: string): VerifiedServiceToken | null {
|
|
|
const entry = loadTokenMap().get(token);
|
|
|
if (!entry) {
|
|
|
return null;
|
|
|
}
|
|
|
return {
|
|
|
customerId: entry.customerId,
|
|
|
permissions: entry.permissions ?? [],
|
|
|
};
|
|
|
}
|