import "server-only"; import { PERMS } from "@/lib/permission-slugs"; import { authorizeHousekeeping } from "../../foundation/authorization"; import { satisfiesCapability } from "../../foundation/capability-context"; import { anyCapability, type CapabilityRequirement, fail, type HousekeepingCapabilityContext, type HousekeepingInboxSource, type HousekeepingWorkItem, ok, } from "../../foundation/contracts"; import { peopleModerationQuery } from "./queries/moderation"; import { peopleSupportQuery } from "./queries/support"; export const PEOPLE_INBOX_SOURCE_IDS = [ "people.tickets", "people.help-tickets", "people.cfh", "people.active-bans", ] as const; export interface PeopleInboxAdapters { tickets( context: HousekeepingCapabilityContext, signal: AbortSignal, ): Promise; helpTickets( context: HousekeepingCapabilityContext, signal: AbortSignal, ): Promise; cfh( context: HousekeepingCapabilityContext, signal: AbortSignal, ): Promise; activeBans( context: HousekeepingCapabilityContext, signal: AbortSignal, ): Promise; } function safeHref(href: string): boolean { return ( href.startsWith("/ase/") && !href.includes("\\") && !Array.from(href).some((character) => { const code = character.codePointAt(0) ?? 0; return code < 32 || code === 127; }) ); } function createSource( id: (typeof PEOPLE_INBOX_SOURCE_IDS)[number], capability: CapabilityRequirement, load: PeopleInboxAdapters[keyof PeopleInboxAdapters], ): HousekeepingInboxSource { return { id, owner: "people", capability, async getItems(context, signal) { const authorization = authorizeHousekeeping(context, capability); if (!authorization.ok) return authorization; try { const items = await load(context, signal); return ok( { availability: "available" as const, items: items .filter( (item) => safeHref(item.href) && satisfiesCapability(context, item.capability), ) .slice(0, 25), }, authorization.correlationId, ); } catch { return fail( "DEPENDENCY_UNAVAILABLE", "errors.housekeeping.dependencyUnavailable", authorization.correlationId, ); } }, }; } export function createPeopleInboxSources( adapters: PeopleInboxAdapters, ): readonly HousekeepingInboxSource[] { return [ createSource( "people.tickets", anyCapability(PERMS.TICKETS_VIEW, PERMS.MOD_TICKETS_VIEW), adapters.tickets, ), createSource( "people.help-tickets", anyCapability(PERMS.TICKETS_VIEW, PERMS.MOD_TICKETS_VIEW), adapters.helpTickets, ), createSource( "people.cfh", anyCapability(PERMS.MODERATION_VIEW, PERMS.MOD_CFH_VIEW), adapters.cfh, ), createSource( "people.active-bans", anyCapability(PERMS.BANS_VIEW, PERMS.MOD_BANS_VIEW), adapters.activeBans, ), ]; } function time(occurredAt: string | null) { if (occurredAt === null) return null; const timestamp = Date.parse(occurredAt); if (!Number.isFinite(timestamp)) return null; return { occurredAt, ageMs: Math.max(0, Date.now() - timestamp), freshness: Date.now() - timestamp > 86_400_000 ? ("stale" as const) : ("fresh" as const), }; } const productionAdapters: PeopleInboxAdapters = { async tickets(context) { const result = await peopleSupportQuery.run(context, { routeId: "people.support.tickets", list: { pageSize: 25, offset: 0, sort: "updatedAt", order: "desc" }, }); if (!result.ok || result.data.kind !== "tickets") throw new Error("tickets"); const capability = anyCapability(PERMS.TICKETS_VIEW, PERMS.MOD_TICKETS_VIEW); return result.data.page.items.flatMap((ticket) => { const date = time(ticket.updatedAt); return date === null ? [] : [{ sourceId: "people.tickets", itemId: String(ticket.id), deduplicationKey: `${ticket.source}-ticket:${ticket.id}`, domain: "people" as const, capability, severity: ticket.priority === "urgent" ? "critical" as const : "info" as const, priority: ticket.priority === "urgent" ? "critical" as const : "normal" as const, ...date, state: ticket.status, titleKey: "pages.housekeeping.items.ticket", context: { subject: ticket.subject }, href: ticket.href, actions: [], }]; }); }, async helpTickets(context) { const result = await peopleSupportQuery.run(context, { routeId: "people.support.help-tickets", list: { pageSize: 25, offset: 0, sort: "updatedAt", order: "desc" }, }); if (!result.ok || result.data.kind !== "help-tickets") throw new Error("help"); const capability = anyCapability(PERMS.TICKETS_VIEW, PERMS.MOD_TICKETS_VIEW); return result.data.page.items.flatMap((ticket) => { const date = time(ticket.updatedAt); return date === null ? [] : [{ sourceId: "people.help-tickets", itemId: ticket.id, deduplicationKey: `help-ticket:${ticket.id}`, domain: "people" as const, capability, severity: "info" as const, priority: "normal" as const, ...date, state: ticket.open ? "open" : "closed", titleKey: "pages.housekeeping.items.helpTicket", context: { title: ticket.title }, href: ticket.href, actions: [], }]; }); }, async cfh(context) { const result = await peopleModerationQuery.run(context, { routeId: "people.moderation.cfh", list: { pageSize: 25, offset: 0, sort: "createdAt", order: "desc" }, }); if (!result.ok || result.data.kind !== "cfh") throw new Error("cfh"); const capability = anyCapability(PERMS.MODERATION_VIEW, PERMS.MOD_CFH_VIEW); return result.data.page.items.flatMap((ticket) => { const date = time(ticket.createdAt); return date === null ? [] : [{ sourceId: "people.cfh", itemId: String(ticket.id), deduplicationKey: `cfh:${ticket.id}`, domain: "people" as const, capability, severity: "warning" as const, priority: "high" as const, ...date, state: String(ticket.state), titleKey: "pages.housekeeping.items.cfh", context: { issue: ticket.issue }, href: ticket.href, actions: [], }]; }); }, async activeBans(context) { const result = await peopleModerationQuery.run(context, { routeId: "people.moderation.bans", list: { pageSize: 25, offset: 0, sort: "createdAt", order: "desc" }, }); if (!result.ok || result.data.kind !== "bans") throw new Error("bans"); const capability = anyCapability(PERMS.BANS_VIEW, PERMS.MOD_BANS_VIEW); return result.data.page.items.flatMap((ban) => { const date = time(ban.createdAt); return date === null ? [] : [{ sourceId: "people.active-bans", itemId: String(ban.id), deduplicationKey: `ban:${ban.id}`, domain: "people" as const, capability, severity: "warning" as const, priority: "normal" as const, ...date, state: "active", titleKey: "pages.housekeeping.items.activeBan", context: { userId: ban.userId, reason: ban.reason }, href: `/ase/people/users/${ban.userId}` as const, actions: [], }]; }); }, }; export const PEOPLE_INBOX_SOURCES = createPeopleInboxSources(productionAdapters);