import "server-only"; import { z } from "zod"; import { PERMS } from "@/lib/permission-slugs"; import type { HousekeepingCommand } from "../../../foundation/commands/registry"; import { anyCapability } from "../../../foundation/contracts"; import { type ContentMutationOperation, type ContentMutationService, contentMutationService, } from "../services/mutations"; const CONTENT_COMMAND_DEFINITIONS = [ ["content.editorial.article.change", "article.change", PERMS.NEWS_EDIT], ["content.media.ad.change", "ad.change", PERMS.PAGES_EDIT], ["content.media.banner.change", "banner.change", PERMS.BANNERS_EDIT], [ "content.engagement.event-type.change", "event-type.change", PERMS.EVENTS_EDIT, ], ["content.engagement.event.change", "event.change", PERMS.EVENTS_EDIT], [ "content.engagement.event-prize.change", "event-prize.change", PERMS.EVENTS_EDIT, ], [ "content.engagement.event-winner.add", "event-winner.add", PERMS.EVENTS_EDIT, ], ["content.engagement.poll.change", "poll.change", PERMS.POLLS_EDIT], [ "content.engagement.poll-question.change", "poll-question.change", PERMS.POLLS_EDIT, ], ["content.media.photo.delete", "photo.delete", PERMS.PAGES_EDIT], ["content.media.asset.upload", "media.upload", PERMS.PAGES_EDIT], ["content.media.asset.delete", "media.delete", PERMS.PAGES_EDIT], ["content.editorial.tag.change", "tag.change", PERMS.PAGES_EDIT], ["content.engagement.prefix.change", "prefix.change", PERMS.PREFIXES_EDIT], [ "content.engagement.prefix-blacklist.change", "prefix-blacklist.change", PERMS.PREFIXES_EDIT, ], [ "content.engagement.prefix-settings.update", "prefix-settings.update", PERMS.PREFIXES_EDIT, ], ["content.help.question.change", "help-question.change", PERMS.PAGES_EDIT], [ "content.editorial.writeable-box.change", "writeable-box.change", PERMS.PAGES_EDIT, ], [ "content.help.email-template.change", "email-template.change", PERMS.PAGES_EDIT, ], ["content.brand.theme.update", "theme.update", PERMS.SETTINGS_EDIT], [ "content.brand.theme.apply-preset", "theme.apply-preset", PERMS.SETTINGS_EDIT, ], [ "content.brand.theme.custom-change", "theme.custom-change", PERMS.SETTINGS_EDIT, ], [ "content.brand.theme.apply-custom", "theme.apply-custom", PERMS.SETTINGS_EDIT, ], ["content.brand.favicon.save", "favicon.save", PERMS.SETTINGS_EDIT], ["content.brand.favicon.delete", "favicon.delete", PERMS.SETTINGS_EDIT], ["content.brand.logo.save", "logo.save", PERMS.SETTINGS_EDIT], [ "content.localization.cms.save", "translation.cms.save", PERMS.SETTINGS_EDIT, ], [ "content.localization.client.save", "translation.client.save", PERMS.SETTINGS_EDIT, ], [ "content.localization.emulator.save", "translation.emulator.save", PERMS.SETTINGS_EDIT, ], ] as const; export const CONTENT_COMMAND_IDS = CONTENT_COMMAND_DEFINITIONS.map( ([id]) => id, ) as ReadonlyArray<(typeof CONTENT_COMMAND_DEFINITIONS)[number][0]>; type ContentCommand = HousekeepingCommand, unknown> & { readonly operation: ContentMutationOperation; }; const commandInput = z.object({}).catchall(z.unknown()); export function createContentCommands( service: Pick, ): readonly ContentCommand[] { return CONTENT_COMMAND_DEFINITIONS.map( ([id, operation, permission]): ContentCommand => ({ id, owner: "content", operation, risk: "sensitive", capability: anyCapability(permission), input: commandInput, requiresReason: id.startsWith("content.brand.") || id.startsWith("content.localization."), rateLimit: { attempts: 10, windowMs: 60_000 }, execute: (context, input) => service.execute( { correlationId: context.correlationId, expectedActorId: context.capability.actor.id, }, operation, input, ), }), ); } export const CONTENT_COMMANDS = createContentCommands(contentMutationService);