Files
curriculum-project-hub/hub/src/database/filelib/groupResolverHttp.ts
T

50 lines
1.9 KiB
TypeScript

/**
* GroupResolver 的 HTTP 实现(契约 C2,Group 团队服务到位后启用,
* 经 HUB_GROUP_SERVICE_URL 配置)。
*
* 语义红线:
* - 失败 → FileLibError(503, group_unavailable)。依赖故障不是"无权限",
* 绝不伪装成 404/403(计划 D13)。
* - 我方绝不自己推祖先:返回什么用什么,不在本地补逻辑。
*/
import { FileLibError } from "./model.js";
import type { GroupResolver } from "./groupResolver.js";
export interface HttpGroupResolverConfig {
readonly baseUrl: string;
readonly timeoutMs?: number;
/** 测试可注入假 fetch;生产用全局 fetch。 */
readonly fetchFn?: typeof fetch;
}
export function createHttpGroupResolver(config: HttpGroupResolverConfig): GroupResolver {
const timeoutMs = config.timeoutMs ?? 2_000;
const fetchFn = config.fetchFn ?? fetch;
return {
async resolveMemberGroupIds(userId) {
const url = `${config.baseUrl.replace(/\/$/, "")}/groups/resolve-member-groups?userId=${encodeURIComponent(userId)}`;
let response: Response;
try {
response = await fetchFn(url, { signal: AbortSignal.timeout(timeoutMs) });
} catch (error) {
throw new FileLibError(503, "group_unavailable", `group service unreachable: ${String(error)}`);
}
if (!response.ok) {
throw new FileLibError(503, "group_unavailable", `group service returned ${response.status}`);
}
let body: unknown;
try {
body = await response.json();
} catch {
throw new FileLibError(503, "group_unavailable", "group service returned malformed JSON");
}
const groupIds = (body as { groupIds?: unknown }).groupIds;
if (!Array.isArray(groupIds) || groupIds.some((id) => typeof id !== "string")) {
throw new FileLibError(503, "group_unavailable", "group service returned malformed payload");
}
return groupIds as readonly string[];
},
};
}