Files
Epicnabbo-Catalogus-Updated…/src/actions/save-favicon.ts
T

97 lines
2.7 KiB
TypeScript

"use server";
import { revalidatePath } from "next/cache";
import type { ContentMutationSnapshot } from "@/features/housekeeping/domains/content/services/mutations";
import { createCorrelationId } from "@/features/housekeeping/foundation/contracts";
import { requirePermission, type StaffUser } from "@/lib/admin/guard";
import { PERMS } from "@/lib/permissions";
const MAX_SIZE = 2 * 1024 * 1024;
const ALLOWED = [
"image/png",
"image/jpeg",
"image/gif",
"image/webp",
"image/x-icon",
"image/svg+xml",
];
async function executeAuditedFaviconMutation(
staff: StaffUser,
operation: "favicon.save" | "favicon.delete",
input: unknown,
): Promise<ContentMutationSnapshot> {
const [
{ contentProductionMutationAdapter },
{ getHousekeepingCapabilityContext },
] = await Promise.all([
import(
"@/features/housekeeping/domains/content/services/mutations-production"
),
import("@/features/housekeeping/foundation/server-capability-context"),
]);
const capability = await getHousekeepingCapabilityContext();
if (capability.actor.id !== staff.id)
throw new Error("Authenticated staff changed during favicon mutation");
return contentProductionMutationAdapter.execute(operation, input, {
capability,
correlationId: createCorrelationId(),
legacy: true,
});
}
export async function saveFavicon(
formData: FormData,
): Promise<{ success: boolean; url?: string; error?: string }> {
const staff = await requirePermission(PERMS.SETTINGS_VIEW);
try {
const file = formData.get("file") as File | null;
if (!file || file.size === 0)
return { success: false, error: "No file provided" };
if (file.size > MAX_SIZE)
return { success: false, error: "File too large (max 2MB)" };
if (!ALLOWED.includes(file.type))
return {
success: false,
error: "Invalid file type. Allowed: PNG, JPEG, GIF, WebP, ICO, SVG",
};
const result = await executeAuditedFaviconMutation(staff, "favicon.save", {
file,
});
siteRevalidate();
return {
success: true,
...(typeof result.output?.url === "string"
? { url: result.output.url }
: {}),
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
};
}
}
export async function deleteFavicon(): Promise<{
success: boolean;
error?: string;
}> {
const staff = await requirePermission(PERMS.SETTINGS_VIEW);
try {
await executeAuditedFaviconMutation(staff, "favicon.delete", {});
siteRevalidate();
return { success: true };
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error",
};
}
}
function siteRevalidate(): void {
revalidatePath("/", "layout");
revalidatePath("/admin/favicon");
}