Files
EpicNext-Cms/src/features/housekeeping/domains/operations/inbox.ts
T
Simo 85ad0452d3
CI / check (pull_request) Successful in 37s
CI / release (pull_request) Skipped
CI / deploy (pull_request) Skipped
feat(housekeeping): compose operations workspace
2026-08-30 17:23:55 +02:00

98 lines
2.7 KiB
TypeScript

import "server-only";
import { PERMS } from "@/lib/permission-slugs";
import { authorizeHousekeeping } from "../../foundation/authorization";
import { satisfiesCapability } from "../../foundation/capability-context";
import {
anyCapability,
fail,
type HousekeepingCapabilityContext,
type HousekeepingInboxSource,
ok,
} from "../../foundation/contracts";
const capability = anyCapability(PERMS.ADMIN_DASHBOARD);
export interface OperationsAccessSummary {
readonly accessibleDomains: number;
}
export interface OperationsInboxAdapters {
accessSummary(
context: HousekeepingCapabilityContext,
signal: AbortSignal,
): Promise<OperationsAccessSummary>;
}
export function createOperationsInboxSources(
adapters: OperationsInboxAdapters,
): readonly HousekeepingInboxSource[] {
return [
{
id: "operations.access-summary",
owner: "operations",
capability,
async getItems(context, signal) {
const authorization = authorizeHousekeeping(context, capability);
if (!authorization.ok) return authorization;
try {
const summary = await adapters.accessSummary(context, signal);
const occurredAt = new Date().toISOString();
return ok(
{
availability: "available" as const,
items:
summary.accessibleDomains === 0
? [
{
sourceId: "operations.access-summary",
itemId: "limited-access",
deduplicationKey:
"operations.access-summary:limited-access",
domain: "operations" as const,
capability,
severity: "warning" as const,
priority: "high" as const,
ageMs: 0,
state: "limited-access",
occurredAt,
titleKey:
"pages.housekeeping.items.operationsLimitedAccess",
href: "/ase" as const,
freshness: "fresh" as const,
actions: [],
},
]
: [],
},
authorization.correlationId,
);
} catch {
return fail(
"DEPENDENCY_UNAVAILABLE",
"errors.housekeeping.dependencyUnavailable",
authorization.correlationId,
);
}
},
},
];
}
export const OPERATIONS_INBOX_SOURCES = createOperationsInboxSources({
async accessSummary(context, signal) {
if (signal.aborted) throw new Error("aborted operations access summary");
const { HOUSEKEEPING_MANIFESTS } = await import("../../manifests");
return {
accessibleDomains: HOUSEKEEPING_MANIFESTS.filter(
(domain) =>
domain.id !== "operations" &&
satisfiesCapability(context, domain.capability) &&
domain.routes.some((route) =>
satisfiesCapability(context, route.capability),
),
).length,
};
},
});