feat(housekeeping): integrate studio operations
This commit is contained in:
1 parent
020c06f172
commit
2e95a106e0
36 files changed
+2296
-60
No files matched your search
@@ -0,0 +1,126 @@
|
||||
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";
|
||||
import {
|
||||
type StudioOperationRecord,
|
||||
studioOperationRepository,
|
||||
} from "./queries/studio";
|
||||
|
||||
export const HOTEL_INBOX_SOURCE_IDS = ["hotel.studio.operations"] as const;
|
||||
|
||||
type HotelOperationLoader = (
|
||||
context: HousekeepingCapabilityContext,
|
||||
signal: AbortSignal,
|
||||
) => Promise<readonly StudioOperationRecord[]>;
|
||||
|
||||
export interface HotelInboxAdapters {
|
||||
readonly operations: HotelOperationLoader;
|
||||
}
|
||||
|
||||
export function createHotelInboxSources(
|
||||
adapters: HotelInboxAdapters,
|
||||
): readonly HousekeepingInboxSource[] {
|
||||
const capability = anyCapability(PERMS.ASSETS_IMPORT, PERMS.CATALOG_EDIT);
|
||||
return [
|
||||
{
|
||||
id: "hotel.studio.operations",
|
||||
owner: "hotel",
|
||||
capability,
|
||||
async getItems(context, signal) {
|
||||
const authorization = authorizeHousekeeping(context, capability);
|
||||
if (!authorization.ok) return authorization;
|
||||
try {
|
||||
if (signal.aborted) throw new Error("aborted Hotel inbox");
|
||||
const operations = await adapters.operations(context, signal);
|
||||
return ok(
|
||||
{
|
||||
availability: "available" as const,
|
||||
items: operations
|
||||
.filter((operation) => operation.phase !== "completed")
|
||||
.filter((operation) =>
|
||||
satisfiesCapability(
|
||||
context,
|
||||
operation.kind === "maintenance"
|
||||
? anyCapability(PERMS.CATALOG_EDIT)
|
||||
: anyCapability(PERMS.ASSETS_IMPORT),
|
||||
),
|
||||
)
|
||||
.slice(0, 25)
|
||||
.map((operation) => {
|
||||
const timestamp = Date.parse(operation.updatedAt);
|
||||
const ageMs = Number.isFinite(timestamp)
|
||||
? Math.max(0, Date.now() - timestamp)
|
||||
: 0;
|
||||
return {
|
||||
sourceId: "hotel.studio.operations",
|
||||
itemId: operation.operationId,
|
||||
deduplicationKey: `hotel.studio.operations:${operation.operationId}`,
|
||||
domain: "hotel" as const,
|
||||
capability:
|
||||
operation.kind === "maintenance"
|
||||
? anyCapability(PERMS.CATALOG_EDIT)
|
||||
: anyCapability(PERMS.ASSETS_IMPORT),
|
||||
severity:
|
||||
operation.phase === "failed"
|
||||
? ("critical" as const)
|
||||
: operation.phase === "partial"
|
||||
? ("warning" as const)
|
||||
: ("info" as const),
|
||||
priority:
|
||||
operation.phase === "failed"
|
||||
? ("high" as const)
|
||||
: ("normal" as const),
|
||||
ageMs,
|
||||
state: operation.phase,
|
||||
occurredAt: operation.updatedAt,
|
||||
titleKey: "pages.housekeeping.items.hotelStudioOperation",
|
||||
context: {
|
||||
kind: operation.kind,
|
||||
completed: operation.completed,
|
||||
total: operation.total,
|
||||
},
|
||||
href: `/ase/hotel/studio/${
|
||||
operation.kind === "badge"
|
||||
? "badges"
|
||||
: operation.kind === "effect"
|
||||
? "effects"
|
||||
: operation.kind === "pet"
|
||||
? "pets"
|
||||
: operation.kind
|
||||
}` as `/ase/${string}`,
|
||||
freshness:
|
||||
ageMs > 86_400_000
|
||||
? ("stale" as const)
|
||||
: ("fresh" as const),
|
||||
actions: [],
|
||||
};
|
||||
}),
|
||||
},
|
||||
authorization.correlationId,
|
||||
);
|
||||
} catch {
|
||||
return fail(
|
||||
"DEPENDENCY_UNAVAILABLE",
|
||||
"errors.housekeeping.dependencyUnavailable",
|
||||
authorization.correlationId,
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export const HOTEL_INBOX_SOURCES = createHotelInboxSources({
|
||||
async operations(_context, signal) {
|
||||
if (signal.aborted) throw new Error("aborted Hotel operations");
|
||||
return (await studioOperationRepository.list({ pageSize: 25, offset: 0 }))
|
||||
.items;
|
||||
},
|
||||
});
|
||||
Reference in new issue
Block a user