fix(housekeeping): harden audited command dispatch
This commit is contained in:
1 parent
6aed71a8b2
commit
796d009c07
7 files changed
+775
-124
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -1,27 +1,58 @@
|
|||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
|
import { AuditOutcomePersistenceError } from "@/features/housekeeping/foundation/commands/audit-envelope";
|
||||||
|
import { dispatchHousekeepingCommand } from "@/features/housekeeping/foundation/commands/dispatcher";
|
||||||
|
import { sealHousekeepingCommandRegistry } from "@/features/housekeeping/foundation/commands/registry";
|
||||||
import {
|
import {
|
||||||
dispatchHousekeepingCommand,
|
fail,
|
||||||
type HousekeepingCommandRequest,
|
type HousekeepingResult,
|
||||||
} from "@/features/housekeeping/foundation/commands/dispatcher";
|
mapUnknownError,
|
||||||
import type { HousekeepingResult } from "@/features/housekeeping/foundation/contracts";
|
} from "@/features/housekeeping/foundation/contracts";
|
||||||
import { getHousekeepingCapabilityContext } from "@/features/housekeeping/foundation/server-capability-context";
|
import { getHousekeepingCapabilityContext } from "@/features/housekeeping/foundation/server-capability-context";
|
||||||
import { clientIp, rateLimit } from "@/lib/rate-limit";
|
import { clientIp, rateLimit } from "@/lib/rate-limit";
|
||||||
import { housekeepingAuditWriter } from "@/lib/services/audit";
|
import { housekeepingAuditWriter } from "@/lib/services/audit";
|
||||||
|
|
||||||
export async function executeHousekeepingCommand(
|
export async function executeHousekeepingCommand(
|
||||||
request: HousekeepingCommandRequest,
|
request: unknown,
|
||||||
): Promise<HousekeepingResult<unknown>> {
|
): Promise<HousekeepingResult<unknown>> {
|
||||||
const [context, ipAddress] = await Promise.all([
|
sealHousekeepingCommandRegistry();
|
||||||
getHousekeepingCapabilityContext(),
|
try {
|
||||||
clientIp(),
|
const [context, ipAddress] = await Promise.all([
|
||||||
]);
|
getHousekeepingCapabilityContext(),
|
||||||
|
clientIp(),
|
||||||
|
]);
|
||||||
|
|
||||||
return dispatchHousekeepingCommand(request, {
|
return await dispatchHousekeepingCommand(request, {
|
||||||
context,
|
context,
|
||||||
ipAddress,
|
ipAddress,
|
||||||
audit: housekeepingAuditWriter,
|
audit: housekeepingAuditWriter,
|
||||||
rateLimit: async (key, attempts, windowMs) =>
|
rateLimit: async (key, attempts, windowMs) =>
|
||||||
(await rateLimit(key, attempts, windowMs)).ok,
|
(await rateLimit(key, attempts, windowMs)).ok,
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
if (
|
||||||
|
error instanceof AuditOutcomePersistenceError &&
|
||||||
|
isHousekeepingResult(error.operationResult)
|
||||||
|
) {
|
||||||
|
return fail(
|
||||||
|
"INTERNAL",
|
||||||
|
"errors.housekeeping.partial",
|
||||||
|
error.operationResult.correlationId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return mapUnknownError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isHousekeepingResult(
|
||||||
|
value: unknown,
|
||||||
|
): value is HousekeepingResult<unknown> {
|
||||||
|
return (
|
||||||
|
typeof value === "object" &&
|
||||||
|
value !== null &&
|
||||||
|
"ok" in value &&
|
||||||
|
typeof (value as { ok?: unknown }).ok === "boolean" &&
|
||||||
|
"correlationId" in value &&
|
||||||
|
typeof (value as { correlationId?: unknown }).correlationId === "string"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -8,8 +8,8 @@ export interface HousekeepingCommandConfirmation {
|
|||||||
reason?: string;
|
reason?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function confirmHousekeepingCommand(
|
export function confirmHousekeepingCommand<I, O>(
|
||||||
command: HousekeepingCommand<unknown, unknown>,
|
command: HousekeepingCommand<I, O>,
|
||||||
reason: string | undefined,
|
reason: string | undefined,
|
||||||
correlationId: string,
|
correlationId: string,
|
||||||
): HousekeepingResult<HousekeepingCommandConfirmation> {
|
): HousekeepingResult<HousekeepingCommandConfirmation> {
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user