diff --git a/src/features/admin/api-keys.ts b/src/features/admin/api-keys.ts index d497a6b780..de4b2c8c88 100644 --- a/src/features/admin/api-keys.ts +++ b/src/features/admin/api-keys.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin API key management routes */ @@ -172,16 +172,16 @@ const handleApiDocsGet: TypedRouteHandler<"GET /admin/api-keys/docs"> = ( ), ); -export const adminHandlers = handlersFor("apiKeys")({ - getApiKeys: handleApiKeysGet, - getApiKeysByApiKeyId: (request, { apiKeyId }) => +export const adminHandlers = defineRoutes({ + "GET /admin/api-keys": handleApiKeysGet, + "GET /admin/api-keys/:apiKeyId": (request, { apiKeyId }) => apiKeyPage.renderTab(request, apiKeyId, ""), - getApiKeysByApiKeyIdByTab: (request, { apiKeyId, tab }) => + "GET /admin/api-keys/:apiKeyId/:tab": (request, { apiKeyId, tab }) => apiKeyPage.renderTab(request, apiKeyId, tab), - getApiKeysByApiKeyIdDelete: (request, { apiKeyId }) => + "GET /admin/api-keys/:apiKeyId/delete": (request, { apiKeyId }) => apiKeyDelete.get(request, apiKeyId), - getApiKeysDocs: handleApiDocsGet, - postApiKeys: handleApiKeysPost, - postApiKeysByApiKeyIdDelete: (request, { apiKeyId }) => + "GET /admin/api-keys/docs": handleApiDocsGet, + "POST /admin/api-keys": handleApiKeysPost, + "POST /admin/api-keys/:apiKeyId/delete": (request, { apiKeyId }) => apiKeyDelete.post(request, apiKeyId), }); diff --git a/src/features/admin/area-loaders.ts b/src/features/admin/area-loaders.ts index 3a61fa4ee5..09676997a2 100644 --- a/src/features/admin/area-loaders.ts +++ b/src/features/admin/area-loaders.ts @@ -4,7 +4,7 @@ import type { AdminAreaId } from "#shared/admin-surface/definitions.ts"; type HandlerMap = Record unknown>; -/** One admin area's lazy route-ID handlers and message ownership. */ +/** One admin area's lazy routes and message ownership. */ export type AdminAreaLoader = { load: () => Promise; messageGroupsFor: (segment: string) => readonly MessageGroup[]; @@ -28,7 +28,7 @@ const messageGroupsBySegment = return groups[segment]!; }; -/** Declare an area without importing its handlers until that area is requested. */ +/** Declare an area without importing its routes until that area is requested. */ const area = ( load: () => Promise, messageGroups: readonly MessageGroup[] = [], diff --git a/src/features/admin/attendee-notes.ts b/src/features/admin/attendee-notes.ts index 939dd10a2c..3e9b1fbcde 100644 --- a/src/features/admin/attendee-notes.ts +++ b/src/features/admin/attendee-notes.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin routes for per-attendee operator notes. * @@ -170,9 +170,9 @@ const handleDeleteNotePost: TypedRouteHandler<"POST /admin/attendee/:attendeeId/ ); }); -export const adminHandlers = handlersFor("attendeeNotes")({ - getAttendeeByAttendeeIdNote: handleAddNoteGet, - getAttendeeByAttendeeIdNoteByNoteIdDelete: handleDeleteNoteGet, - postAttendeeByAttendeeIdNote: handleAddNotePost, - postAttendeeByAttendeeIdNoteByNoteIdDelete: handleDeleteNotePost, +export const adminHandlers = defineRoutes({ + "GET /admin/attendee/:attendeeId/note": handleAddNoteGet, + "GET /admin/attendee/:attendeeId/note/:noteId/delete": handleDeleteNoteGet, + "POST /admin/attendee/:attendeeId/note": handleAddNotePost, + "POST /admin/attendee/:attendeeId/note/:noteId/delete": handleDeleteNotePost, }); diff --git a/src/features/admin/attendee-refunds.ts b/src/features/admin/attendee-refunds.ts index ab4dfa6bde..d90926c835 100644 --- a/src/features/admin/attendee-refunds.ts +++ b/src/features/admin/attendee-refunds.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin attendee refund routes (single + bulk) */ @@ -298,9 +298,9 @@ const handleAdminRefundAllPost = createAuthedHandler({ }); /** Attendee refund routes */ -export const adminHandlers = handlersFor("attendeeRefunds")({ - getAttendeesByAttendeeIdRefund: handleAdminAttendeeRefundGet, - getListingByIdRefundAll: handleAdminRefundAllGet, - postAttendeesByAttendeeIdRefund: handleAttendeeRefund, - postListingByIdRefundAll: handleAdminRefundAllPost, +export const adminHandlers = defineRoutes({ + "GET /admin/attendees/:attendeeId/refund": handleAdminAttendeeRefundGet, + "GET /admin/listing/:id/refund-all": handleAdminRefundAllGet, + "POST /admin/attendees/:attendeeId/refund": handleAttendeeRefund, + "POST /admin/listing/:id/refund-all": handleAdminRefundAllPost, }); diff --git a/src/features/admin/attendees.ts b/src/features/admin/attendees.ts index fdcd409de7..aafe940e69 100644 --- a/src/features/admin/attendees.ts +++ b/src/features/admin/attendees.ts @@ -1,5 +1,5 @@ /* jscpd:ignore-start */ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin attendee management routes */ @@ -359,26 +359,29 @@ const handleResendNotification = verifiedAttendeeAction( * Merge: attendees-merge.ts * Refunds: attendee-refunds.ts */ -export const adminHandlers = handlersFor("attendees")({ - deleteAttendeesByAttendeeIdDelete: handleAttendeeDelete, - getAttendees: handleAttendeesListGet, - getAttendeesByAttendeeId: (request, { attendeeId }) => +export const adminHandlers = defineRoutes({ + "DELETE /admin/attendees/:attendeeId/delete": handleAttendeeDelete, + "GET /admin/attendees": handleAttendeesListGet, + "GET /admin/attendees/:attendeeId": (request, { attendeeId }) => attendeePage.renderTab(request, attendeeId, ""), - getAttendeesByAttendeeIdByTab: (request, { attendeeId, tab }) => + "GET /admin/attendees/:attendeeId/:tab": (request, { attendeeId, tab }) => attendeePage.renderTab(request, attendeeId, tab), - getAttendeesByAttendeeIdDelete: handleAdminAttendeeDeleteGet, - getAttendeesByAttendeeIdResendNotification: handleAdminResendNotificationGet, - getAttendeesCsv: handleAttendeesCsvExport, - getAttendeesNew: handleAttendeeNewGet, - postAttendeesByAttendeeId: handleAttendeeEditPost, - postAttendeesByAttendeeIdDelete: handleAttendeeDelete, - postAttendeesByAttendeeIdLogistics: handleAttendeeLogisticsPost, - postAttendeesByAttendeeIdMerge: handleMergePost, - postAttendeesByAttendeeIdRefreshPayment: handleRefreshPayment, - postAttendeesByAttendeeIdResendNotification: handleResendNotification, - postAttendeesNew: handleAttendeeNewPost, - postListingByListingIdAttendee: handleAddAttendee, - postListingByListingIdAttendeeByAttendeeIdCheckin: handleAttendeeCheckin, - postListingByListingIdAttendeeByAttendeeIdDeleteIncomplete: + "GET /admin/attendees/:attendeeId/delete": handleAdminAttendeeDeleteGet, + "GET /admin/attendees/:attendeeId/resend-notification": + handleAdminResendNotificationGet, + "GET /admin/attendees/csv": handleAttendeesCsvExport, + "GET /admin/attendees/new": handleAttendeeNewGet, + "POST /admin/attendees/:attendeeId": handleAttendeeEditPost, + "POST /admin/attendees/:attendeeId/delete": handleAttendeeDelete, + "POST /admin/attendees/:attendeeId/logistics": handleAttendeeLogisticsPost, + "POST /admin/attendees/:attendeeId/merge": handleMergePost, + "POST /admin/attendees/:attendeeId/refresh-payment": handleRefreshPayment, + "POST /admin/attendees/:attendeeId/resend-notification": + handleResendNotification, + "POST /admin/attendees/new": handleAttendeeNewPost, + "POST /admin/listing/:listingId/attendee": handleAddAttendee, + "POST /admin/listing/:listingId/attendee/:attendeeId/checkin": + handleAttendeeCheckin, + "POST /admin/listing/:listingId/attendee/:attendeeId/delete-incomplete": handleDeleteIncomplete, }); diff --git a/src/features/admin/attributes.ts b/src/features/admin/attributes.ts index e7a199f704..6d40889c4a 100644 --- a/src/features/admin/attributes.ts +++ b/src/features/admin/attributes.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin routes for listing attributes. */ @@ -357,23 +357,23 @@ const handleListingAttributesPost = createListingChoicePost({ tab: "attributes", }); -export const adminHandlers = handlersFor("attributes")({ - getAttributes: handleAttributesGet, - getAttributesById: handleAttributeGet, - getAttributesByIdDelete: (request, { id }) => +export const adminHandlers = defineRoutes({ + "GET /admin/attributes": handleAttributesGet, + "GET /admin/attributes/:id": handleAttributeGet, + "GET /admin/attributes/:id/delete": (request, { id }) => attributeDelete.get(request, id), - getAttributesByIdOptionsByOptionIdDelete: handleDeleteOptionGet, - getAttributesByIdOptionsByOptionIdEdit: handleEditOptionGet, - postAttributes: handleAttributesPost, - postAttributesByIdDelete: (request, { id }) => + "GET /admin/attributes/:id/options/:optionId/delete": handleDeleteOptionGet, + "GET /admin/attributes/:id/options/:optionId/edit": handleEditOptionGet, + "POST /admin/attributes": handleAttributesPost, + "POST /admin/attributes/:id/delete": (request, { id }) => attributeDelete.post(request, id), - postAttributesByIdEdit: handleAttributeEdit, - postAttributesByIdMoveDown: attributeOrder.down, - postAttributesByIdMoveUp: attributeOrder.up, - postAttributesByIdOptions: handleAddOption, - postAttributesByIdOptionsByOptionIdDelete: handleDeleteOptionPost, - postAttributesByIdOptionsByOptionIdEdit: handleEditOptionPost, - postAttributesByIdOptionsByOptionIdMoveDown: optionOrder.down, - postAttributesByIdOptionsByOptionIdMoveUp: optionOrder.up, - postListingByIdAttributes: handleListingAttributesPost, + "POST /admin/attributes/:id/edit": handleAttributeEdit, + "POST /admin/attributes/:id/move-down": attributeOrder.down, + "POST /admin/attributes/:id/move-up": attributeOrder.up, + "POST /admin/attributes/:id/options": handleAddOption, + "POST /admin/attributes/:id/options/:optionId/delete": handleDeleteOptionPost, + "POST /admin/attributes/:id/options/:optionId/edit": handleEditOptionPost, + "POST /admin/attributes/:id/options/:optionId/move-down": optionOrder.down, + "POST /admin/attributes/:id/options/:optionId/move-up": optionOrder.up, + "POST /admin/listing/:id/attributes": handleListingAttributesPost, }); diff --git a/src/features/admin/auth.ts b/src/features/admin/auth.ts index 9794e80ae1..b01d62eb84 100644 --- a/src/features/admin/auth.ts +++ b/src/features/admin/auth.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin authentication routes - login and logout */ @@ -192,9 +192,9 @@ const handleLoginGet = (request: Request): Promise => const handleLogoutGet = anyUserPage((session) => adminLogoutPage(session)); /** Authentication routes */ -export const adminHandlers = handlersFor("auth")({ - getLogin: handleLoginGet, - getLogout: handleLogoutGet, - postLogin: handleAdminLogin, - postLogout: handleAdminLogout, +export const adminHandlers = defineRoutes({ + "GET /admin/login": handleLoginGet, + "GET /admin/logout": handleLogoutGet, + "POST /admin/login": handleAdminLogin, + "POST /admin/logout": handleAdminLogout, }); diff --git a/src/features/admin/backup.ts b/src/features/admin/backup.ts index 1412376a8b..4c7b8ef806 100644 --- a/src/features/admin/backup.ts +++ b/src/features/admin/backup.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; import { namedError } from "#shared/named-error.ts"; /** * Admin backup/restore routes — owner only @@ -315,10 +315,10 @@ const handleBackupRestoreConfirm: TypedRouteHandler<"POST /admin/backup/restore/ }); /** Backup routes */ -export const adminHandlers = handlersFor("backup")({ - getBackup: handleBackupGet, - getBackupDownloadByFilename: handleBackupDownload, - postBackupCreate: handleBackupCreate, - postBackupRestore: handleBackupRestore, - postBackupRestoreConfirm: handleBackupRestoreConfirm, +export const adminHandlers = defineRoutes({ + "GET /admin/backup": handleBackupGet, + "GET /admin/backup/download/:filename": handleBackupDownload, + "POST /admin/backup/create": handleBackupCreate, + "POST /admin/backup/restore": handleBackupRestore, + "POST /admin/backup/restore/confirm": handleBackupRestoreConfirm, }); diff --git a/src/features/admin/builder.ts b/src/features/admin/builder.ts index 050723e2f0..87796335c8 100644 --- a/src/features/admin/builder.ts +++ b/src/features/admin/builder.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin builder routes — create new Tickets instances via Bunny API * Owner-only access, gated behind CAN_BUILD_SITES=true env var @@ -209,8 +209,8 @@ const builderPost = createAuthedFormRoute({ }, }); -export const adminHandlers = handlersFor("builder")({ - getBuilder: handleBuilderGet, - postBuilder: (r: Request) => +export const adminHandlers = defineRoutes({ + "GET /admin/builder": handleBuilderGet, + "POST /admin/builder": (r: Request) => isBuilderEnabled() ? builderPost(r, {}) : notFoundResponse(), }); diff --git a/src/features/admin/built-sites.ts b/src/features/admin/built-sites.ts index 768f732bcb..140a51b2b0 100644 --- a/src/features/admin/built-sites.ts +++ b/src/features/admin/built-sites.ts @@ -1,6 +1,6 @@ import { mapValues } from "@std/collections"; import { t } from "#i18n"; -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin built site management routes - owner only */ @@ -351,24 +351,25 @@ const gateOnBuilder = ( /** Built site routes (all gated on CAN_BUILD_SITES via gateOnBuilder). */ export const adminHandlers = gateOnBuilder( - handlersFor("builtSites")({ - getBuiltSites: handleBuiltSitesListGet, - getBuiltSitesById: (request, { id }) => + defineRoutes({ + "GET /admin/built-sites": handleBuiltSitesListGet, + "GET /admin/built-sites/:id": (request, { id }) => builtSitePage.renderTab(request, id, ""), - getBuiltSitesByIdByTab: (request, { id, tab }) => + "GET /admin/built-sites/:id/:tab": (request, { id, tab }) => builtSitePage.renderTab(request, id, tab), - getBuiltSitesByIdDelete: crud.deleteGet, - getBuiltSitesNew: crud.newGet, - postBuiltSites: crud.createPost, - postBuiltSitesByIdAddSecrets: handleAddSecrets, - postBuiltSitesByIdBumpDeadline: handleBumpDeadline, - postBuiltSitesByIdDelete: crud.deletePost, - postBuiltSitesByIdEdit: crud.editPost, - postBuiltSitesByIdOverrideDeadline: handleOverrideDeadline, - postBuiltSitesByIdProvisionRenewal: handleProvisionRenewal, - postBuiltSitesByIdProvisionScheduler: handleProvisionSiteScheduler, - postBuiltSitesByIdReSyncDeadline: handleReSyncDeadline, - postBuiltSitesByIdRotateRenewalToken: handleRotateToken, - postBuiltSitesByIdUpdate: handleUpdateSite, + "GET /admin/built-sites/:id/delete": crud.deleteGet, + "GET /admin/built-sites/new": crud.newGet, + "POST /admin/built-sites": crud.createPost, + "POST /admin/built-sites/:id/add-secrets": handleAddSecrets, + "POST /admin/built-sites/:id/bump-deadline": handleBumpDeadline, + "POST /admin/built-sites/:id/delete": crud.deletePost, + "POST /admin/built-sites/:id/edit": crud.editPost, + "POST /admin/built-sites/:id/override-deadline": handleOverrideDeadline, + "POST /admin/built-sites/:id/provision-renewal": handleProvisionRenewal, + "POST /admin/built-sites/:id/provision-scheduler": + handleProvisionSiteScheduler, + "POST /admin/built-sites/:id/re-sync-deadline": handleReSyncDeadline, + "POST /admin/built-sites/:id/rotate-renewal-token": handleRotateToken, + "POST /admin/built-sites/:id/update": handleUpdateSite, }), ); diff --git a/src/features/admin/bulk-actions.ts b/src/features/admin/bulk-actions.ts index ba4760f2b2..5b5693a683 100644 --- a/src/features/admin/bulk-actions.ts +++ b/src/features/admin/bulk-actions.ts @@ -1,6 +1,6 @@ /* jscpd:ignore-start */ import { identity, mapById, requiredMapValue } from "#fp"; -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /* jscpd:ignore-end */ /** * Bulk actions for groups. @@ -350,12 +350,12 @@ const handleDuplicateGroupPost = groupFormPost(async (group, form) => { }); /** Bulk actions routes */ -export const adminHandlers = handlersFor("bulkActions")({ - getGroupsByIdBulkActions: handleBulkActionsGet, - getGroupsByIdBulkActionsDeactivate: handleDeactivateGroupGet, - getGroupsByIdBulkActionsDuplicate: handleDuplicateGroupGet, - getGroupsByIdBulkActionsReactivate: handleReactivateGroupGet, - postGroupsByIdBulkActionsDeactivate: handleDeactivateGroupPost, - postGroupsByIdBulkActionsDuplicate: handleDuplicateGroupPost, - postGroupsByIdBulkActionsReactivate: handleReactivateGroupPost, +export const adminHandlers = defineRoutes({ + "GET /admin/groups/:id/bulk-actions": handleBulkActionsGet, + "GET /admin/groups/:id/bulk-actions/deactivate": handleDeactivateGroupGet, + "GET /admin/groups/:id/bulk-actions/duplicate": handleDuplicateGroupGet, + "GET /admin/groups/:id/bulk-actions/reactivate": handleReactivateGroupGet, + "POST /admin/groups/:id/bulk-actions/deactivate": handleDeactivateGroupPost, + "POST /admin/groups/:id/bulk-actions/duplicate": handleDuplicateGroupPost, + "POST /admin/groups/:id/bulk-actions/reactivate": handleReactivateGroupPost, }); diff --git a/src/features/admin/bulk-email.ts b/src/features/admin/bulk-email.ts index ed79f4a967..daa5428774 100644 --- a/src/features/admin/bulk-email.ts +++ b/src/features/admin/bulk-email.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Bulk email routes — compose, preview, send, and template management. * Owner-only. @@ -453,14 +453,14 @@ const templateDelete = createConfirmedHandlers<{ id: number; subject: string }>( }, ); -export const adminHandlers = handlersFor("bulkEmail")({ - getEmails: handleComposeGet, - getEmailsPreview: handlePreviewGet, - getEmailsTemplatesByIdDelete: (request, { id }) => +export const adminHandlers = defineRoutes({ + "GET /admin/emails": handleComposeGet, + "GET /admin/emails/preview": handlePreviewGet, + "GET /admin/emails/templates/:id/delete": (request, { id }) => templateDelete.get(request, id), - postEmailsPreview: handlePreviewPost, - postEmailsSend: handleSendPost, - postEmailsTemplates: handleTemplateSavePost, - postEmailsTemplatesByIdDelete: (request, { id }) => + "POST /admin/emails/preview": handlePreviewPost, + "POST /admin/emails/send": handleSendPost, + "POST /admin/emails/templates": handleTemplateSavePost, + "POST /admin/emails/templates/:id/delete": (request, { id }) => templateDelete.post(request, id), }); diff --git a/src/features/admin/calendar.ts b/src/features/admin/calendar.ts index 540fca4c24..6f36dfd97d 100644 --- a/src/features/admin/calendar.ts +++ b/src/features/admin/calendar.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin calendar view routes */ @@ -441,7 +441,7 @@ const handleAdminCalendarExport = (request: Request) => }); /** Calendar routes */ -export const adminHandlers = handlersFor("calendar")({ - getCalendar: handleAdminCalendarGet, - getCalendarExport: handleAdminCalendarExport, +export const adminHandlers = defineRoutes({ + "GET /admin/calendar": handleAdminCalendarGet, + "GET /admin/calendar/export": handleAdminCalendarExport, }); diff --git a/src/features/admin/catalog-transfer/routes.ts b/src/features/admin/catalog-transfer/routes.ts index fc0b73c3a1..b3a35153e9 100644 --- a/src/features/admin/catalog-transfer/routes.ts +++ b/src/features/admin/catalog-transfer/routes.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin routes for catalog import/export. * @@ -149,9 +149,9 @@ const handleImportPost: TypedRouteHandler<"POST /admin/catalog/import"> = }); /** Catalog import/export routes. */ -export const adminHandlers = handlersFor("catalogTransfer")({ - getCatalogImport: handleImportGet, - getGroupsByIdExportJson: handleGroupExport, - getListingByIdExportJson: handleListingExport, - postCatalogImport: handleImportPost, +export const adminHandlers = defineRoutes({ + "GET /admin/catalog/import": handleImportGet, + "GET /admin/groups/:id/export.json": handleGroupExport, + "GET /admin/listing/:id/export.json": handleListingExport, + "POST /admin/catalog/import": handleImportPost, }); diff --git a/src/features/admin/contact-history.ts b/src/features/admin/contact-history.ts index 9219b1063e..9dc49cb6ca 100644 --- a/src/features/admin/contact-history.ts +++ b/src/features/admin/contact-history.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Editor for a single contact_preferences record, keyed by its HMAC blind * index (contact_hash). The attendee page's contact-history panel links here so @@ -100,7 +100,7 @@ export const handleContactHistoryPost: TypedRouteHandler< return redirect(`/admin/history/${hmac}`, t("contact_history.saved"), true); }); -export const adminHandlers = handlersFor("contactHistory")({ - getHistoryByHmac: handleContactHistoryGet, - postHistoryByHmac: handleContactHistoryPost, +export const adminHandlers = defineRoutes({ + "GET /admin/history/:hmac": handleContactHistoryGet, + "POST /admin/history/:hmac": handleContactHistoryPost, }); diff --git a/src/features/admin/dashboard.ts b/src/features/admin/dashboard.ts index cef871f648..97dc7b5b83 100644 --- a/src/features/admin/dashboard.ts +++ b/src/features/admin/dashboard.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin dashboard route */ @@ -238,9 +238,9 @@ const handleAdminLog: TypedRouteHandler<"GET /admin/log"> = sessionPage( ); /** Dashboard routes */ -export const adminHandlers = handlersFor("dashboard")({ - getAdmin: handleAdminGet, - getListings: handleAdminListingsGet, - getListingsCsv: handleListingsCsvExport, - getLog: handleAdminLog, +export const adminHandlers = defineRoutes({ + "GET /admin": handleAdminGet, + "GET /admin/listings": handleAdminListingsGet, + "GET /admin/listings/csv": handleListingsCsvExport, + "GET /admin/log": handleAdminLog, }); diff --git a/src/features/admin/debug.ts b/src/features/admin/debug.ts index 28f18f3067..3895c90131 100644 --- a/src/features/admin/debug.ts +++ b/src/features/admin/debug.ts @@ -1,5 +1,5 @@ /* jscpd:ignore-start */ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /* jscpd:ignore-end */ /** * Admin debug route - shows configuration status for troubleshooting @@ -311,7 +311,7 @@ const handleSentryTestPost: TypedRouteHandler<"POST /admin/debug/sentry"> = }); /** Debug routes */ -export const adminHandlers = handlersFor("debug")({ - getDebug: handleAdminDebugGet, - postSentryTest: handleSentryTestPost, +export const adminHandlers = defineRoutes({ + "GET /admin/debug": handleAdminDebugGet, + "POST /admin/debug/sentry": handleSentryTestPost, }); diff --git a/src/features/admin/deliveries.ts b/src/features/admin/deliveries.ts index a61fcb5946..6fcbe23539 100644 --- a/src/features/admin/deliveries.ts +++ b/src/features/admin/deliveries.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Delivery run sheet routes. * @@ -276,7 +276,7 @@ const handleDeliveriesMark = (request: Request): Promise => }); /** Delivery agent routes. */ -export const adminHandlers = handlersFor("deliveries")({ - getDeliveries: handleDeliveriesGet, - postDeliveriesMark: handleDeliveriesMark, +export const adminHandlers = defineRoutes({ + "GET /admin/deliveries": handleDeliveriesGet, + "POST /admin/deliveries/mark": handleDeliveriesMark, }); diff --git a/src/features/admin/groups.ts b/src/features/admin/groups.ts index ed155237c4..1e589a3b13 100644 --- a/src/features/admin/groups.ts +++ b/src/features/admin/groups.ts @@ -1,5 +1,5 @@ /* jscpd:ignore-start */ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin group management routes - accessible to owners and managers @@ -410,8 +410,8 @@ const groupImageHandlers = createItemImageHandlers({ }); /** Group routes */ -export const adminHandlers = handlersFor("groups")({ - getGroups: content.listGet, +export const adminHandlers = defineRoutes({ + "GET /admin/groups": content.listGet, // The detail + edit pages are one tabbed entity page now: `/admin/groups/:id` // is its Overview, `/admin/groups/:id/:tab` its other tabs (attendees, edit, @@ -420,16 +420,17 @@ export const adminHandlers = handlersFor("groups")({ // their own files) are matched ahead of the `:tab` wildcard. The edit POST is // still the generic CRUD route — groupsResource handles package prices + the // invariant via validate/afterWrite. - getGroupsById: (request, { id }) => groupPage.renderTab(request, id, ""), - getGroupsByIdByTab: (request, { id, tab }) => + "GET /admin/groups/:id": (request, { id }) => + groupPage.renderTab(request, id, ""), + "GET /admin/groups/:id/:tab": (request, { id, tab }) => groupPage.renderTab(request, id, tab), - getGroupsByIdDelete: staffCrud.deleteGet, + "GET /admin/groups/:id/delete": staffCrud.deleteGet, // Create uses the auto-generated-slug resource. - getGroupsNew: contentCreate.newGet, - postGroups: contentCreate.createPost, - postGroupsByIdAddListings: handleAddListingsToGroup, - postGroupsByIdDelete: staffCrud.deletePost, - postGroupsByIdEdit: content.editPost, - postGroupsByIdImages: groupImageHandlers.set, - postGroupsByIdImagesUpload: groupImageHandlers.upload, + "GET /admin/groups/new": contentCreate.newGet, + "POST /admin/groups": contentCreate.createPost, + "POST /admin/groups/:id/add-listings": handleAddListingsToGroup, + "POST /admin/groups/:id/delete": staffCrud.deletePost, + "POST /admin/groups/:id/edit": content.editPost, + "POST /admin/groups/:id/images": groupImageHandlers.set, + "POST /admin/groups/:id/images/upload": groupImageHandlers.upload, }); diff --git a/src/features/admin/guide.ts b/src/features/admin/guide.ts index 17b941ceb7..70c02a6d89 100644 --- a/src/features/admin/guide.ts +++ b/src/features/admin/guide.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin guide route */ @@ -47,7 +47,7 @@ const handleAdminFormattingGet = contentPage((session) => ); /** Guide routes */ -export const adminHandlers = handlersFor("guide")({ - getFormatting: handleAdminFormattingGet, - getGuide: handleAdminGuideGet, +export const adminHandlers = defineRoutes({ + "GET /admin/formatting": handleAdminFormattingGet, + "GET /admin/guide": handleAdminGuideGet, }); diff --git a/src/features/admin/handlers.ts b/src/features/admin/handlers.ts deleted file mode 100644 index 88491b1cc3..0000000000 --- a/src/features/admin/handlers.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { RouteHandlerFn, TypedRouteHandler } from "#routes/router.ts"; -import type { AdminAreaId } from "#shared/admin-surface/definitions.ts"; -import { ADMIN_SURFACE } from "#shared/admin-surface.ts"; - -type RouteForArea = Extract< - (typeof ADMIN_SURFACE.routes)[number], - { readonly area: Area } ->; - -export type AdminHandlers = { - [Route in RouteForArea as Route["id"]]: TypedRouteHandler<`${Route["method"]} ${Route["pattern"]}`>; -}; - -/** Type-check one lazy area's handlers against every route the surface owns. */ -export const handlersFor = - (_area: Area) => - (handlers: AdminHandlers): AdminHandlers => - handlers; - -/** Restore the router's method/path map only at its generic dispatch boundary. */ -export const routeMapForArea = ( - area: AdminAreaId, - handlers: Record unknown>, -): Record => { - const runtimeHandlers = handlers as Record; - return Object.fromEntries( - ADMIN_SURFACE.routes - .filter((route) => route.area === area) - .map((route) => [ - `${route.method} ${route.pattern}`, - // Every lazy module passes through handlersFor before this runtime boundary. - runtimeHandlers[route.id]!, - ]), - ); -}; diff --git a/src/features/admin/holidays.ts b/src/features/admin/holidays.ts index b506fc95b4..473fc2f286 100644 --- a/src/features/admin/holidays.ts +++ b/src/features/admin/holidays.ts @@ -4,8 +4,8 @@ /* jscpd:ignore-start */ import { t } from "#i18n"; -import { handlersFor } from "#routes/admin/handlers.ts"; import { createOwnerCrudHandlers } from "#routes/admin/owner-crud.ts"; +import { defineRoutes } from "#routes/router.ts"; import { type HolidayInput, holidays } from "#shared/db/holidays.ts"; import { HOLIDAY_DEMO_FIELDS, @@ -64,14 +64,15 @@ export const holidaysCrud = createOwnerCrudHandlers({ singular: "Holiday", }); -export const adminHandlers = handlersFor("holidays")({ - getHolidays: holidaysCrud.listGet, - getHolidaysById: (request, { id }) => holidayPage.renderTab(request, id, ""), - getHolidaysByIdByTab: (request, { id, tab }) => +export const adminHandlers = defineRoutes({ + "GET /admin/holidays": holidaysCrud.listGet, + "GET /admin/holidays/:id": (request, { id }) => + holidayPage.renderTab(request, id, ""), + "GET /admin/holidays/:id/:tab": (request, { id, tab }) => holidayPage.renderTab(request, id, tab), - getHolidaysByIdDelete: holidaysCrud.deleteGet, - getHolidaysNew: holidaysCrud.newGet, - postHolidays: holidaysCrud.createPost, - postHolidaysByIdDelete: holidaysCrud.deletePost, - postHolidaysByIdEdit: holidaysCrud.editPost, + "GET /admin/holidays/:id/delete": holidaysCrud.deleteGet, + "GET /admin/holidays/new": holidaysCrud.newGet, + "POST /admin/holidays": holidaysCrud.createPost, + "POST /admin/holidays/:id/delete": holidaysCrud.deletePost, + "POST /admin/holidays/:id/edit": holidaysCrud.editPost, }); diff --git a/src/features/admin/images.ts b/src/features/admin/images.ts index 961e5a0708..f5abb8ab16 100644 --- a/src/features/admin/images.ts +++ b/src/features/admin/images.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * First-class image library admin routes. */ @@ -275,12 +275,12 @@ const handleImageDeletePost: TypedRouteHandler<"POST /admin/images/:id/delete"> return redirect("/admin/images", t("images.deleted"), true); }); -export const adminHandlers = handlersFor("images")({ - getImages: handleImagesListGet, - getImagesByIdDelete: handleImageDeleteGet, - getImagesByIdEdit: handleImageEditGet, - getImagesNew: handleImageNewGet, - postImages: handleImageCreatePost, - postImagesByIdDelete: handleImageDeletePost, - postImagesByIdEdit: handleImageEditPost, +export const adminHandlers = defineRoutes({ + "GET /admin/images": handleImagesListGet, + "GET /admin/images/:id/delete": handleImageDeleteGet, + "GET /admin/images/:id/edit": handleImageEditGet, + "GET /admin/images/new": handleImageNewGet, + "POST /admin/images": handleImageCreatePost, + "POST /admin/images/:id/delete": handleImageDeletePost, + "POST /admin/images/:id/edit": handleImageEditPost, }); diff --git a/src/features/admin/index.ts b/src/features/admin/index.ts index e14c05ba95..bc20c043ed 100644 --- a/src/features/admin/index.ts +++ b/src/features/admin/index.ts @@ -14,7 +14,6 @@ import { ADMIN_AREA_LOADERS, type AdminAreaLoader, } from "#routes/admin/area-loaders.ts"; -import { routeMapForArea } from "#routes/admin/handlers.ts"; import { isJsonApiPath } from "#routes/middleware.ts"; import { createRouter } from "#routes/router.ts"; import type { PathMethodRoute } from "#routes/types.ts"; @@ -34,16 +33,13 @@ type AdminSegment = { /** Build one lazy router per segment without importing any area handlers. */ const buildSegmentRouters = (): Record => { - const areasBySegment: Record< - string, - Array<{ id: AdminAreaId; loader: AdminAreaLoader }> - > = {}; + const areasBySegment: Record = {}; for (const [id, segments] of Object.entries(ADMIN_SURFACE.areas)) { const areaId = id as AdminAreaId; const loader = ADMIN_AREA_LOADERS[areaId]; for (const segment of segments) { const areas = areasBySegment[segment] ?? []; - areas.push({ id: areaId, loader }); + areas.push(loader); areasBySegment[segment] = areas; } } @@ -52,17 +48,13 @@ const buildSegmentRouters = (): Record => { for (const [segment, areas] of Object.entries(areasBySegment)) { routers[segment] = { load: once(async () => { - const maps = await Promise.all( - areas.map(async ({ id, loader }) => - routeMapForArea(id, await loader.load()), - ), - ); + const maps = await Promise.all(areas.map((loader) => loader.load())); return createRouter(Object.assign({}, ...maps)); }), messageGroups: [ ...new Set([ ...ADMIN_SHELL_MESSAGE_GROUPS, - ...areas.flatMap(({ loader }) => loader.messageGroupsFor(segment)), + ...areas.flatMap((loader) => loader.messageGroupsFor(segment)), ]), ], }; diff --git a/src/features/admin/ledger.ts b/src/features/admin/ledger.ts index 6f70f98001..d010466a25 100644 --- a/src/features/admin/ledger.ts +++ b/src/features/admin/ledger.ts @@ -1,4 +1,3 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; import { handleLedgerEntryAddGet, handleLedgerEntryAddPost, @@ -8,13 +7,14 @@ import { } from "#routes/admin/ledger/entries.ts"; import { handleLedgerGet } from "#routes/admin/ledger/page.ts"; import { handleAccountStatementGet } from "#routes/admin/ledger/statements.ts"; +import { defineRoutes } from "#routes/router.ts"; -export const adminHandlers = handlersFor("ledger")({ - getLedger: handleLedgerGet, - getLedgerByTypeByRef: handleAccountStatementGet, - getLedgerByTypeByRefAdd: handleLedgerEntryAddGet, - getLedgerEntriesByIdEdit: handleLedgerEntryEditGet, - postLedgerByTypeByRefAdd: handleLedgerEntryAddPost, - postLedgerEntriesByIdDelete: handleLedgerEntryDeletePost, - postLedgerEntriesByIdEdit: handleLedgerEntryEditPost, +export const adminHandlers = defineRoutes({ + "GET /admin/ledger": handleLedgerGet, + "GET /admin/ledger/:type/:ref": handleAccountStatementGet, + "GET /admin/ledger/:type/:ref/add": handleLedgerEntryAddGet, + "GET /admin/ledger/entries/:id/edit": handleLedgerEntryEditGet, + "POST /admin/ledger/:type/:ref/add": handleLedgerEntryAddPost, + "POST /admin/ledger/entries/:id/delete": handleLedgerEntryDeletePost, + "POST /admin/ledger/entries/:id/edit": handleLedgerEntryEditPost, }); diff --git a/src/features/admin/listing-qr.ts b/src/features/admin/listing-qr.ts index 49af74925b..739209dded 100644 --- a/src/features/admin/listing-qr.ts +++ b/src/features/admin/listing-qr.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin route for generating pre-filled booking QR codes for an listing. * @@ -319,7 +319,7 @@ const handleJsonGet: TypedRouteHandler<"GET /admin/listing/:id/qr.json"> = ( /** Exported admin routes for the QR generator. The GET form is the listing * entity page's QR tab now; the POST (result render) + qr.json refresh stay * here. */ -export const adminHandlers = handlersFor("listingQr")({ - getListingByIdQrJson: handleJsonGet, - postListingByIdQr: handlePost, +export const adminHandlers = defineRoutes({ + "GET /admin/listing/:id/qr.json": handleJsonGet, + "POST /admin/listing/:id/qr": handlePost, }); diff --git a/src/features/admin/listings.ts b/src/features/admin/listings.ts index 9de20acf71..9a3bf69722 100644 --- a/src/features/admin/listings.ts +++ b/src/features/admin/listings.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin listing management routes — assembled from per-feature modules: * - listings-form.ts form parsing + create/update resources @@ -50,32 +50,34 @@ const listingImageHandlers = createItemImageHandlers({ * literal segments over the `:tab` param, so the remaining literal sub-routes * below (duplicate, export, new, …) and those in the scanner / qr.json / refund * bundles keep resolving to their own handlers. */ -export const adminHandlers = handlersFor("listings")({ - deleteListingByIdDelete: handleAdminListingDelete, - getListingById: (request, { id }) => listingPage.renderTab(request, id, ""), - getListingByIdAttendeesCsv: handleAdminListingExport, - getListingByIdByTab: (request, { id, tab }) => +export const adminHandlers = defineRoutes({ + "DELETE /admin/listing/:id/delete": handleAdminListingDelete, + "GET /admin/listing/:id": (request, { id }) => + listingPage.renderTab(request, id, ""), + "GET /admin/listing/:id/:tab": (request, { id, tab }) => listingPage.renderTab(request, id, tab), - getListingByIdDeactivate: (request, { id }) => + "GET /admin/listing/:id/attendees.csv": handleAdminListingExport, + "GET /admin/listing/:id/deactivate": (request, { id }) => listingDeactivate.get(request, id), - getListingByIdDelete: (request, { id }) => listingDelete.get(request, id), - getListingByIdDuplicate: handleAdminListingDuplicateGet, - getListingByIdExport: handleAdminListingExport, - getListingByIdReactivate: (request, { id }) => + "GET /admin/listing/:id/delete": (request, { id }) => + listingDelete.get(request, id), + "GET /admin/listing/:id/duplicate": handleAdminListingDuplicateGet, + "GET /admin/listing/:id/export": handleAdminListingExport, + "GET /admin/listing/:id/reactivate": (request, { id }) => listingReactivate.get(request, id), - getListingNew: handleNewListingGet, - getListingsRecalculateByListingId: handleListingRecalculateGet, - postListing: handleCreateListing, - postListingByIdAttachmentDelete: handleAttachmentDelete, - postListingByIdChildren: handleAdminListingChildren, - postListingByIdDeactivate: (request, { id }) => + "GET /admin/listing/new": handleNewListingGet, + "GET /admin/listings/recalculate/:listingId": handleListingRecalculateGet, + "POST /admin/listing": handleCreateListing, + "POST /admin/listing/:id/attachment/delete": handleAttachmentDelete, + "POST /admin/listing/:id/children": handleAdminListingChildren, + "POST /admin/listing/:id/deactivate": (request, { id }) => listingDeactivate.post(request, id), - postListingByIdDelete: handleAdminListingDelete, - postListingByIdEdit: handleAdminListingEditPost, - postListingByIdImages: listingImageHandlers.set, - postListingByIdImagesUpload: listingImageHandlers.upload, - postListingByIdIncome: handleAdminListingIncomePost, - postListingByIdReactivate: (request, { id }) => + "POST /admin/listing/:id/delete": handleAdminListingDelete, + "POST /admin/listing/:id/edit": handleAdminListingEditPost, + "POST /admin/listing/:id/images": listingImageHandlers.set, + "POST /admin/listing/:id/images/upload": listingImageHandlers.upload, + "POST /admin/listing/:id/income": handleAdminListingIncomePost, + "POST /admin/listing/:id/reactivate": (request, { id }) => listingReactivate.post(request, id), - postListingsRecalculateByListingId: handleListingRecalculatePost, + "POST /admin/listings/recalculate/:listingId": handleListingRecalculatePost, }); diff --git a/src/features/admin/markdown-preview.ts b/src/features/admin/markdown-preview.ts index 569b848295..4c115637c4 100644 --- a/src/features/admin/markdown-preview.ts +++ b/src/features/admin/markdown-preview.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Markdown preview endpoint. * @@ -28,6 +28,6 @@ const handleMarkdownPreviewPost = (request: Request): Promise => }); /** Markdown preview routes */ -export const adminHandlers = handlersFor("markdownPreview")({ - postMarkdownPreview: handleMarkdownPreviewPost, +export const adminHandlers = defineRoutes({ + "POST /admin/markdown-preview": handleMarkdownPreviewPost, }); diff --git a/src/features/admin/modifiers.ts b/src/features/admin/modifiers.ts index 8d7410a831..59c9e21d1b 100644 --- a/src/features/admin/modifiers.ts +++ b/src/features/admin/modifiers.ts @@ -10,12 +10,12 @@ import { defineEditEntityPage, type EditEntityPage, } from "#routes/admin/entity-write-tab.ts"; -import { handlersFor } from "#routes/admin/handlers.ts"; import { loadAccountLedger } from "#routes/admin/ledger/statements.ts"; import { createCrudHandlers } from "#routes/admin/owner-crud.ts"; import { AUTH_FORM, requireSessionOr, withAuth } from "#routes/auth.ts"; import { errorRedirect, notFoundResponse, redirect } from "#routes/response.ts"; import type { TypedRouteHandler } from "#routes/router.ts"; +import { defineRoutes } from "#routes/router.ts"; import { modifierAccount } from "#shared/accounting/accounts.ts"; import { createAuthedHandler } from "#shared/app-forms.ts"; import { hmacHash } from "#shared/crypto/hashing.ts"; @@ -503,20 +503,21 @@ const handleAnswerLinks: TypedRouteHandler< ); /** Modifier routes */ -export const adminHandlers = handlersFor("modifiers")({ - getModifiers: crud.listGet, - getModifiersById: (request, { id }) => +export const adminHandlers = defineRoutes({ + "GET /admin/modifiers": crud.listGet, + "GET /admin/modifiers/:id": (request, { id }) => modifierPage.renderTab(request, id, ""), - getModifiersByIdByTab: (request, { id, tab }) => + "GET /admin/modifiers/:id/:tab": (request, { id, tab }) => modifierPage.renderTab(request, id, tab), - getModifiersByIdDelete: crud.deleteGet, - getModifiersNew: crud.newGet, - getModifiersRecalculateByModifierId: handleModifierRecalculateGet, - postModifiers: crud.createPost, - postModifiersByIdAnswers: handleAnswerLinks, - postModifiersByIdDelete: crud.deletePost, - postModifiersByIdEdit: handleEditPost, - postModifiersByIdLinks: handleScopeLinks, - postModifiersByIdRevenue: handleRevenueAdjust, - postModifiersRecalculateByModifierId: handleModifierRecalculatePost, + "GET /admin/modifiers/:id/delete": crud.deleteGet, + "GET /admin/modifiers/new": crud.newGet, + "GET /admin/modifiers/recalculate/:modifierId": handleModifierRecalculateGet, + "POST /admin/modifiers": crud.createPost, + "POST /admin/modifiers/:id/answers": handleAnswerLinks, + "POST /admin/modifiers/:id/delete": crud.deletePost, + "POST /admin/modifiers/:id/edit": handleEditPost, + "POST /admin/modifiers/:id/links": handleScopeLinks, + "POST /admin/modifiers/:id/revenue": handleRevenueAdjust, + "POST /admin/modifiers/recalculate/:modifierId": + handleModifierRecalculatePost, }); diff --git a/src/features/admin/news.ts b/src/features/admin/news.ts index a121343593..61743b931a 100644 --- a/src/features/admin/news.ts +++ b/src/features/admin/news.ts @@ -8,7 +8,7 @@ /* jscpd:ignore-start */ import { t } from "#i18n"; -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; import { logActivity } from "#shared/db/activityLog.ts"; import { createNewsPost, @@ -96,15 +96,15 @@ const content = defineSiteContent("/admin/site/news", (paths) => ({ // ─── Routes ───────────────────────────────────────────────────── -export const adminHandlers = handlersFor("news")({ - getSiteNews: content.list, - getSiteNewsById: content.entity, - getSiteNewsByIdByTab: content.entityTab, - getSiteNewsByIdDelete: content.deletePage, - getSiteNewsNew: content.newPage, - postSiteNews: content.create, - postSiteNewsByIdDelete: content.delete, - postSiteNewsByIdEdit: content.update, - postSiteNewsByIdImages: content.images.set, - postSiteNewsByIdImagesUpload: content.images.upload, +export const adminHandlers = defineRoutes({ + "GET /admin/site/news": content.list, + "GET /admin/site/news/:id": content.entity, + "GET /admin/site/news/:id/:tab": content.entityTab, + "GET /admin/site/news/:id/delete": content.deletePage, + "GET /admin/site/news/new": content.newPage, + "POST /admin/site/news": content.create, + "POST /admin/site/news/:id/delete": content.delete, + "POST /admin/site/news/:id/edit": content.update, + "POST /admin/site/news/:id/images": content.images.set, + "POST /admin/site/news/:id/images/upload": content.images.upload, }); diff --git a/src/features/admin/privacy.ts b/src/features/admin/privacy.ts index 5ecc356210..953b386f7a 100644 --- a/src/features/admin/privacy.ts +++ b/src/features/admin/privacy.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Privacy page routes (owner-only). * @@ -96,8 +96,8 @@ const handleErasePost = ownerFormHandler(async ({ form }) => { }); /** Privacy routes */ -export const adminHandlers = handlersFor("privacy")({ - getPrivacy: handlePrivacyGet, - postPrivacyErase: handleErasePost, - postPrivacyOrphans: handleOrphansPost, +export const adminHandlers = defineRoutes({ + "GET /admin/privacy": handlePrivacyGet, + "POST /admin/privacy/erase": handleErasePost, + "POST /admin/privacy/orphans": handleOrphansPost, }); diff --git a/src/features/admin/questions.ts b/src/features/admin/questions.ts index c40fedfb20..fc1a94348a 100644 --- a/src/features/admin/questions.ts +++ b/src/features/admin/questions.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin routes for custom questions management (owner-only) */ @@ -548,25 +548,28 @@ const handleListingQuestionsPost = createListingChoicePost({ }); /** Questions routes */ -export const adminHandlers = handlersFor("questions")({ - getQuestions: handleQuestionsGet, - getQuestionsById: handleQuestionGet, - getQuestionsByIdAnswersByAnswerIdDelete: handleDeleteAnswerGet, - getQuestionsByIdAnswersByAnswerIdEdit: handleEditAnswerGet, - getQuestionsByIdAnswersByAnswerIdRecalculate: handleAnswerRecalculateGet, - getQuestionsByIdDelete: (request, { id }) => questionDelete.get(request, id), - postListingByIdQuestions: handleListingQuestionsPost, - postQuestions: handleQuestionsPost, - postQuestionsByIdAnswers: handleAddAnswer, - postQuestionsByIdAnswersByAnswerIdDelete: handleDeleteAnswerPost, - postQuestionsByIdAnswersByAnswerIdEdit: handleEditAnswerPost, - postQuestionsByIdAnswersByAnswerIdMoveDown: answerOrder.down, - postQuestionsByIdAnswersByAnswerIdMoveUp: answerOrder.up, - postQuestionsByIdAnswersByAnswerIdRecalculate: handleAnswerRecalculatePost, - postQuestionsByIdDelete: (request, { id }) => +export const adminHandlers = defineRoutes({ + "GET /admin/questions": handleQuestionsGet, + "GET /admin/questions/:id": handleQuestionGet, + "GET /admin/questions/:id/answers/:answerId/delete": handleDeleteAnswerGet, + "GET /admin/questions/:id/answers/:answerId/edit": handleEditAnswerGet, + "GET /admin/questions/:id/answers/:answerId/recalculate": + handleAnswerRecalculateGet, + "GET /admin/questions/:id/delete": (request, { id }) => + questionDelete.get(request, id), + "POST /admin/listing/:id/questions": handleListingQuestionsPost, + "POST /admin/questions": handleQuestionsPost, + "POST /admin/questions/:id/answers": handleAddAnswer, + "POST /admin/questions/:id/answers/:answerId/delete": handleDeleteAnswerPost, + "POST /admin/questions/:id/answers/:answerId/edit": handleEditAnswerPost, + "POST /admin/questions/:id/answers/:answerId/move-down": answerOrder.down, + "POST /admin/questions/:id/answers/:answerId/move-up": answerOrder.up, + "POST /admin/questions/:id/answers/:answerId/recalculate": + handleAnswerRecalculatePost, + "POST /admin/questions/:id/delete": (request, { id }) => questionDelete.post(request, id), - postQuestionsByIdEdit: handleQuestionEdit, - postQuestionsByIdListings: handleQuestionListings, - postQuestionsByIdMoveDown: questionOrder.down, - postQuestionsByIdMoveUp: questionOrder.up, + "POST /admin/questions/:id/edit": handleQuestionEdit, + "POST /admin/questions/:id/listings": handleQuestionListings, + "POST /admin/questions/:id/move-down": questionOrder.down, + "POST /admin/questions/:id/move-up": questionOrder.up, }); diff --git a/src/features/admin/scanner.ts b/src/features/admin/scanner.ts index bcb8b80100..3daf2b2f57 100644 --- a/src/features/admin/scanner.ts +++ b/src/features/admin/scanner.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * QR scanner routes for admin check-in * GET /admin/listing/:id/scanner - Scanner page with camera UI @@ -188,7 +188,7 @@ const handleScanPost: IdRouteHandler = (request, { id }) => }); /** Scanner routes */ -export const adminHandlers = handlersFor("scanner")({ - getListingByIdScanner: handleScannerGet, - postListingByIdScan: handleScanPost, +export const adminHandlers = defineRoutes({ + "GET /admin/listing/:id/scanner": handleScannerGet, + "POST /admin/listing/:id/scan": handleScanPost, }); diff --git a/src/features/admin/seeds.ts b/src/features/admin/seeds.ts index 0538675c22..a8ac3609d1 100644 --- a/src/features/admin/seeds.ts +++ b/src/features/admin/seeds.ts @@ -1,5 +1,5 @@ /* jscpd:ignore-start */ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin seed data routes - populate database with sample listings and attendees */ @@ -79,7 +79,7 @@ const handleSeedsPost: TypedRouteHandler<"POST /admin/seeds"> = (request) => }); /** Seeds routes */ -export const adminHandlers = handlersFor("seeds")({ - getSeeds: handleSeedsGet, - postSeeds: handleSeedsPost, +export const adminHandlers = defineRoutes({ + "GET /admin/seeds": handleSeedsGet, + "POST /admin/seeds": handleSeedsPost, }); diff --git a/src/features/admin/servicing.tsx b/src/features/admin/servicing.tsx index 9b5a27b13f..03daa38712 100644 --- a/src/features/admin/servicing.tsx +++ b/src/features/admin/servicing.tsx @@ -2,7 +2,6 @@ import { identity, map, mapById, unique } from "#fp"; import { t } from "#i18n"; -import { handlersFor } from "#routes/admin/handlers.ts"; import { normalizeServicingForSave, parseServicingForm, @@ -25,6 +24,7 @@ import { applyFlash } from "#routes/csrf.ts"; import { createEntityHandler, createIdEntityHandler } from "#routes/entity.ts"; import { htmlResponse, notFoundResponse, redirect } from "#routes/response.ts"; import type { TypedRouteHandler } from "#routes/router.ts"; +import { defineRoutes } from "#routes/router.ts"; import { costBelongsToServicing, createServicingEvent, @@ -277,13 +277,13 @@ const handleServicingCostPost: TypedRouteHandler<"POST /admin/servicing/:id/cost }, ); -export const adminHandlers = handlersFor("servicing")({ - getServicing: handleServicingListGet, - getServicingById: handleServicingGet, - getServicingNew: handleServicingNewGet, - postServicingById: handleServicingPost, - postServicingByIdCostByCostId: handleServicingCostPost, - postServicingByIdDelete: handleServicingDeletePost, - postServicingByIdDuplicate: handleServicingDuplicatePost, - postServicingNew: handleServicingNewPost, +export const adminHandlers = defineRoutes({ + "GET /admin/servicing": handleServicingListGet, + "GET /admin/servicing/:id": handleServicingGet, + "GET /admin/servicing/new": handleServicingNewGet, + "POST /admin/servicing/:id": handleServicingPost, + "POST /admin/servicing/:id/cost/:costId": handleServicingCostPost, + "POST /admin/servicing/:id/delete": handleServicingDeletePost, + "POST /admin/servicing/:id/duplicate": handleServicingDuplicatePost, + "POST /admin/servicing/new": handleServicingNewPost, }); diff --git a/src/features/admin/sessions.ts b/src/features/admin/sessions.ts index dd4ef9542d..2c93876c16 100644 --- a/src/features/admin/sessions.ts +++ b/src/features/admin/sessions.ts @@ -1,5 +1,5 @@ /* jscpd:ignore-start */ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin session management routes */ @@ -32,7 +32,7 @@ const handleAdminSessionsPost = gatedPost(OWNER_FORM)(async (session) => { }); /** Session management routes */ -export const adminHandlers = handlersFor("sessions")({ - getSessions: handleAdminSessionsGet, - postSessions: handleAdminSessionsPost, +export const adminHandlers = defineRoutes({ + "GET /admin/sessions": handleAdminSessionsGet, + "POST /admin/sessions": handleAdminSessionsPost, }); diff --git a/src/features/admin/settings-logistics.ts b/src/features/admin/settings-logistics.ts index c7874ded9a..5ea1c9cb6c 100644 --- a/src/features/admin/settings-logistics.ts +++ b/src/features/admin/settings-logistics.ts @@ -1,6 +1,7 @@ /* jscpd:ignore-start */ -import { handlersFor } from "#routes/admin/handlers.ts"; + import { ownerFormById } from "#routes/entity.ts"; +import { defineRoutes } from "#routes/router.ts"; /* jscpd:ignore-end */ /** * Admin logistics settings + logistics-agent management — owner only. @@ -107,15 +108,15 @@ const handleAgentEditPost: IdRouteHandler = ownerFormById( ); /** Logistics settings + agent routes. */ -export const adminHandlers = handlersFor("settingsLogistics")({ - getLogistics: crud.listGet, - getLogisticsById: (request, { id }) => +export const adminHandlers = defineRoutes({ + "GET /admin/logistics": crud.listGet, + "GET /admin/logistics/:id": (request, { id }) => logisticsAgentPage.renderTab(request, id, ""), - getLogisticsByIdByTab: (request, { id, tab }) => + "GET /admin/logistics/:id/:tab": (request, { id, tab }) => logisticsAgentPage.renderTab(request, id, tab), - getLogisticsByIdDelete: crud.deleteGet, - getLogisticsNew: crud.newGet, - postLogistics: crud.createPost, - postLogisticsByIdDelete: crud.deletePost, - postLogisticsByIdEdit: handleAgentEditPost, + "GET /admin/logistics/:id/delete": crud.deleteGet, + "GET /admin/logistics/new": crud.newGet, + "POST /admin/logistics": crud.createPost, + "POST /admin/logistics/:id/delete": crud.deletePost, + "POST /admin/logistics/:id/edit": handleAgentEditPost, }); diff --git a/src/features/admin/settings-statuses.ts b/src/features/admin/settings-statuses.ts index 3473155de3..2b2f0eaea6 100644 --- a/src/features/admin/settings-statuses.ts +++ b/src/features/admin/settings-statuses.ts @@ -1,5 +1,5 @@ /* jscpd:ignore-start */ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /* jscpd:ignore-end */ /** * Admin routes for managing attendee statuses (owner-only). @@ -125,17 +125,17 @@ const statusOrder = createOrderedCollectionHandlers({ target: ({ context }) => context.id, }); -export const adminHandlers = handlersFor("settingsStatuses")({ - getSettingsStatuses: crud.listGet, - getSettingsStatusesById: (request, { id }) => +export const adminHandlers = defineRoutes({ + "GET /admin/settings/statuses": crud.listGet, + "GET /admin/settings/statuses/:id": (request, { id }) => attendeeStatusPage.renderTab(request, id, ""), - getSettingsStatusesByIdByTab: (request, { id, tab }) => + "GET /admin/settings/statuses/:id/:tab": (request, { id, tab }) => attendeeStatusPage.renderTab(request, id, tab), - getSettingsStatusesByIdDelete: crud.deleteGet, - getSettingsStatusesNew: crud.newGet, - postSettingsStatuses: crud.createPost, - postSettingsStatusesByIdDelete: crud.deletePost, - postSettingsStatusesByIdEdit: crud.editPost, - postSettingsStatusesByIdMoveDown: statusOrder.down, - postSettingsStatusesByIdMoveUp: statusOrder.up, + "GET /admin/settings/statuses/:id/delete": crud.deleteGet, + "GET /admin/settings/statuses/new": crud.newGet, + "POST /admin/settings/statuses": crud.createPost, + "POST /admin/settings/statuses/:id/delete": crud.deletePost, + "POST /admin/settings/statuses/:id/edit": crud.editPost, + "POST /admin/settings/statuses/:id/move-down": statusOrder.down, + "POST /admin/settings/statuses/:id/move-up": statusOrder.up, }); diff --git a/src/features/admin/settings.ts b/src/features/admin/settings.ts index 38c8f2d782..48e9cf6784 100644 --- a/src/features/admin/settings.ts +++ b/src/features/admin/settings.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin settings route map - aggregates the owner-only settings handlers * from the per-feature settings-*.ts modules into a single route table. @@ -64,48 +64,50 @@ import { } from "#routes/admin/settings-wallets.ts"; /** Settings routes */ -export const adminHandlers = handlersFor("settings")({ - getFeaturesBySlug: handleFeatureGet, - getListingDefaults: handleListingDefaultsGet, - getSettings: handleAdminSettingsGet, - getSettingsAdvanced: handleAdminSettingsAdvancedGet, - postFeaturesBySlug: handleFeaturePost, - postListingDefaults: handleListingDefaultsPost, - postSettings: handleAdminSettingsPost, - postSettingsAddressLookup: handleAddressLookupPost, - postSettingsAppleWallet: handleAppleWalletPost, - postSettingsAttendeeColumnOrder: handleAttendeeColumnOrderPost, - postSettingsBookingFee: handleBookingFeePost, - postSettingsBusinessEmail: handleBusinessEmailPost, - postSettingsCalendarFeeds: handleCalendarFeedsPost, - postSettingsCustomCss: handleCustomCssPost, - postSettingsCustomDomain: handleCustomDomainPost, - postSettingsCustomDomainValidate: handleCustomDomainValidatePost, - postSettingsEmail: handleEmailPost, - postSettingsEmailTemplatesAdmin: handleEmailTemplatePost("admin"), - postSettingsEmailTemplatesConfirmation: +export const adminHandlers = defineRoutes({ + "GET /admin/features/:slug": handleFeatureGet, + "GET /admin/listing-defaults": handleListingDefaultsGet, + "GET /admin/settings": handleAdminSettingsGet, + "GET /admin/settings-advanced": handleAdminSettingsAdvancedGet, + "POST /admin/features/:slug": handleFeaturePost, + "POST /admin/listing-defaults": handleListingDefaultsPost, + "POST /admin/settings": handleAdminSettingsPost, + "POST /admin/settings/address-lookup": handleAddressLookupPost, + "POST /admin/settings/apple-wallet": handleAppleWalletPost, + "POST /admin/settings/attendee-column-order": handleAttendeeColumnOrderPost, + "POST /admin/settings/booking-fee": handleBookingFeePost, + "POST /admin/settings/business-email": handleBusinessEmailPost, + "POST /admin/settings/calendar-feeds": handleCalendarFeedsPost, + "POST /admin/settings/custom-css": handleCustomCssPost, + "POST /admin/settings/custom-domain": handleCustomDomainPost, + "POST /admin/settings/custom-domain/validate": handleCustomDomainValidatePost, + "POST /admin/settings/email": handleEmailPost, + "POST /admin/settings/email-templates/admin": + handleEmailTemplatePost("admin"), + "POST /admin/settings/email-templates/confirmation": handleEmailTemplatePost("confirmation"), - postSettingsEmailTemplatesPreview: handleEmailTemplatePreviewPost, - postSettingsEmailTest: handleEmailTestPost, - postSettingsEmbedHosts: handleEmbedHostsPost, - postSettingsExternalOrder: handleExternalOrderPost, - postSettingsGoogleWallet: handleGoogleWalletPost, - postSettingsHeaderImage: handleHeaderImagePost, - postSettingsHeaderImageDelete: handleHeaderImageDeletePost, - postSettingsHostSubdomain: handleHostSubdomainPost, - postSettingsListingColumnOrder: handleListingColumnOrderPost, - postSettingsPaymentProvider: handlePaymentProviderPost, - postSettingsResetDatabase: handleResetDatabasePost, - postSettingsShowPublicApi: handleShowPublicApiPost, - postSettingsSmsGateway: handleSmsGatewayPost, - postSettingsSquare: squareRoutes.save, - postSettingsSquareTest: squareRoutes.test, - postSettingsSquareWebhook: handleAdminSquareWebhookPost, - postSettingsStripe: stripeRoutes.save, - postSettingsStripeTest: stripeRoutes.test, - postSettingsSumup: sumupRoutes.save, - postSettingsSumupTest: sumupRoutes.test, - postSettingsSuperuser: handleSuperuserPost, - postSettingsTerms: handleTermsPost, - postSettingsTheme: handleThemePost, + "POST /admin/settings/email-templates/preview": + handleEmailTemplatePreviewPost, + "POST /admin/settings/email/test": handleEmailTestPost, + "POST /admin/settings/embed-hosts": handleEmbedHostsPost, + "POST /admin/settings/external-order": handleExternalOrderPost, + "POST /admin/settings/google-wallet": handleGoogleWalletPost, + "POST /admin/settings/header-image": handleHeaderImagePost, + "POST /admin/settings/header-image/delete": handleHeaderImageDeletePost, + "POST /admin/settings/host-subdomain": handleHostSubdomainPost, + "POST /admin/settings/listing-column-order": handleListingColumnOrderPost, + "POST /admin/settings/payment-provider": handlePaymentProviderPost, + "POST /admin/settings/reset-database": handleResetDatabasePost, + "POST /admin/settings/show-public-api": handleShowPublicApiPost, + "POST /admin/settings/sms-gateway": handleSmsGatewayPost, + "POST /admin/settings/square": squareRoutes.save, + "POST /admin/settings/square-webhook": handleAdminSquareWebhookPost, + "POST /admin/settings/square/test": squareRoutes.test, + "POST /admin/settings/stripe": stripeRoutes.save, + "POST /admin/settings/stripe/test": stripeRoutes.test, + "POST /admin/settings/sumup": sumupRoutes.save, + "POST /admin/settings/sumup/test": sumupRoutes.test, + "POST /admin/settings/superuser": handleSuperuserPost, + "POST /admin/settings/terms": handleTermsPost, + "POST /admin/settings/theme": handleThemePost, }); diff --git a/src/features/admin/site-pages.ts b/src/features/admin/site-pages.ts index 8066ff9b69..a66898cff8 100644 --- a/src/features/admin/site-pages.ts +++ b/src/features/admin/site-pages.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin CRUD for user-created content Pages, under Site → Pages. Owner + editor * (SITE_FORM / requireSiteOr), hand-wired because create must assign a root @@ -289,21 +289,23 @@ const pageItemOrder = createOrderedCollectionHandlers({ // ─── Routes ───────────────────────────────────────────────────── -export const adminHandlers = handlersFor("sitePages")({ - getSitePages: content.list, - getSitePagesById: content.entity, - getSitePagesByIdByTab: content.entityTab, - getSitePagesByIdDelete: content.deletePage, - getSitePagesNew: content.newPage, - postSitePages: content.create, - postSitePagesByIdDelete: content.delete, - postSitePagesByIdEdit: content.update, - postSitePagesByIdImages: content.images.set, - postSitePagesByIdImagesUpload: content.images.upload, - postSitePagesByIdItems: handleAddItem, - postSitePagesByIdItemsByItemTypeByItemIdMoveDown: pageItemOrder.down, - postSitePagesByIdItemsByItemTypeByItemIdMoveUp: pageItemOrder.up, - postSitePagesByIdItemsByItemTypeByItemIdRemove: handleRemoveItem, - postSitePagesByIdMoveDown: rootPageOrder.down, - postSitePagesByIdMoveUp: rootPageOrder.up, +export const adminHandlers = defineRoutes({ + "GET /admin/site/pages": content.list, + "GET /admin/site/pages/:id": content.entity, + "GET /admin/site/pages/:id/:tab": content.entityTab, + "GET /admin/site/pages/:id/delete": content.deletePage, + "GET /admin/site/pages/new": content.newPage, + "POST /admin/site/pages": content.create, + "POST /admin/site/pages/:id/delete": content.delete, + "POST /admin/site/pages/:id/edit": content.update, + "POST /admin/site/pages/:id/images": content.images.set, + "POST /admin/site/pages/:id/images/upload": content.images.upload, + "POST /admin/site/pages/:id/items": handleAddItem, + "POST /admin/site/pages/:id/items/:itemType/:itemId/move-down": + pageItemOrder.down, + "POST /admin/site/pages/:id/items/:itemType/:itemId/move-up": + pageItemOrder.up, + "POST /admin/site/pages/:id/items/:itemType/:itemId/remove": handleRemoveItem, + "POST /admin/site/pages/:id/move-down": rootPageOrder.down, + "POST /admin/site/pages/:id/move-up": rootPageOrder.up, }); diff --git a/src/features/admin/site.ts b/src/features/admin/site.ts index c832fc26bd..c1f750a26f 100644 --- a/src/features/admin/site.ts +++ b/src/features/admin/site.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin site page editor routes - manage public site content. * Access: owner + editor (managers stay excluded — see SITE_ADMIN_LEVELS). @@ -217,13 +217,13 @@ const handleSiteOrderPost = settingsHandler({ }); /** Site editor routes */ -export const adminHandlers = handlersFor("site")({ - getSite: siteGetRoute(renderHomePage), - getSiteContact: siteGetRoute(renderContactPage), - getSiteOrder: handleSiteOrderGet, - postSite: handleSiteHomePost, - postSiteContact: handleSiteContactPost, - postSiteContactForm: handleSiteContactFormTogglePost, - postSiteOrder: handleSiteOrderPost, - postSiteOrderToggle: handleSiteOrderTogglePost, +export const adminHandlers = defineRoutes({ + "GET /admin/site": siteGetRoute(renderHomePage), + "GET /admin/site/contact": siteGetRoute(renderContactPage), + "GET /admin/site/order": handleSiteOrderGet, + "POST /admin/site": handleSiteHomePost, + "POST /admin/site/contact": handleSiteContactPost, + "POST /admin/site/contact/form": handleSiteContactFormTogglePost, + "POST /admin/site/order": handleSiteOrderPost, + "POST /admin/site/order/toggle": handleSiteOrderTogglePost, }); diff --git a/src/features/admin/sms.ts b/src/features/admin/sms.ts index e3bc9cf3f9..15c89ec29f 100644 --- a/src/features/admin/sms.ts +++ b/src/features/admin/sms.ts @@ -1,5 +1,5 @@ /* jscpd:ignore-start */ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin SMS page — text an attendee via the gateway queue. * @@ -168,7 +168,7 @@ const sendSms = ( const handleSmsPost = (request: Request): Promise => withAuth(request, AUTH_FORM, sendSms); -export const adminHandlers = handlersFor("sms")({ - getSms: handleSmsGet, - postSms: handleSmsPost, +export const adminHandlers = defineRoutes({ + "GET /admin/sms": handleSmsGet, + "POST /admin/sms": handleSmsPost, }); diff --git a/src/features/admin/support.ts b/src/features/admin/support.ts index 044de78477..a4e6c85394 100644 --- a/src/features/admin/support.ts +++ b/src/features/admin/support.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin Support page routes — let the site operator message the platform host. * @@ -65,7 +65,7 @@ const submitSupportMessage = async (form: FormParams): Promise => { const handleSupportPost = formPost(OWNER_FORM)(submitSupportMessage); /** Support routes */ -export const adminHandlers = handlersFor("support")({ - getSupport: handleSupportGet, - postSupport: handleSupportPost, +export const adminHandlers = defineRoutes({ + "GET /admin/support": handleSupportGet, + "POST /admin/support": handleSupportPost, }); diff --git a/src/features/admin/update.ts b/src/features/admin/update.ts index 7ff68229a2..a0f06d622e 100644 --- a/src/features/admin/update.ts +++ b/src/features/admin/update.ts @@ -1,4 +1,4 @@ -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin update routes — check for and apply software updates * Owner-only access @@ -95,8 +95,9 @@ const deployUpdate = async (): Promise => { }); }; -export const adminHandlers = handlersFor("update")({ - getUpdate: handleUpdateGet, - postUpdate: (r: Request) => withAuth(r, OWNER_FORM, deployUpdate), - postUpdateCheck: (r: Request) => withAuth(r, OWNER_FORM, checkForUpdate), +export const adminHandlers = defineRoutes({ + "GET /admin/update": handleUpdateGet, + "POST /admin/update": (r: Request) => withAuth(r, OWNER_FORM, deployUpdate), + "POST /admin/update/check": (r: Request) => + withAuth(r, OWNER_FORM, checkForUpdate), }); diff --git a/src/features/admin/users.ts b/src/features/admin/users.ts index 344cab13c4..14f3b27f65 100644 --- a/src/features/admin/users.ts +++ b/src/features/admin/users.ts @@ -1,7 +1,7 @@ /* jscpd:ignore-start */ import { fieldById } from "#fp"; -import { handlersFor } from "#routes/admin/handlers.ts"; +import { defineRoutes } from "#routes/router.ts"; /** * Admin user management routes - owner only */ @@ -393,15 +393,17 @@ const userDelete = createConfirmedHandlers({ }); /** User management routes */ -export const adminHandlers = handlersFor("users")({ - getUserNew: handleUserNewGet, - getUsers: handleUsersGet, - getUsersById: handleUserManageGet, - getUsersByIdAgents: handleUserAgentsGet, - getUsersByIdByTab: (request, { id, tab }) => +export const adminHandlers = defineRoutes({ + "GET /admin/user/new": handleUserNewGet, + "GET /admin/users": handleUsersGet, + "GET /admin/users/:id": handleUserManageGet, + "GET /admin/users/:id/:tab": (request, { id, tab }) => userPage.renderTab(request, id, tab), - getUsersByIdDelete: (request, { id }) => userDelete.get(request, id), - postUsers: handleUsersPost, - postUsersByIdAgents: handleUserAgentsPost, - postUsersByIdDelete: (request, { id }) => userDelete.post(request, id), + "GET /admin/users/:id/agents": handleUserAgentsGet, + "GET /admin/users/:id/delete": (request, { id }) => + userDelete.get(request, id), + "POST /admin/users": handleUsersPost, + "POST /admin/users/:id/agents": handleUserAgentsPost, + "POST /admin/users/:id/delete": (request, { id }) => + userDelete.post(request, id), }); diff --git a/src/features/app/read-only.ts b/src/features/app/read-only.ts index b470be74ea..86815309b4 100644 --- a/src/features/app/read-only.ts +++ b/src/features/app/read-only.ts @@ -1,17 +1,21 @@ import { readOnlyGetRoutePatterns } from "#shared/admin-pages.ts"; -import { ADMIN_SURFACE } from "#shared/admin-surface.ts"; import { routePathPatternToRegex } from "#shared/route-pattern.ts"; const READ_ONLY_GET_PATTERNS = readOnlyGetRoutePatterns().map( routePathPatternToRegex, ); -const READ_ONLY_ADMIN_OPERATIONS = ADMIN_SURFACE.routes - .filter((route) => route.method !== "GET" && route.readOnly === "allow") - .map((route) => ({ - method: route.method, - pattern: routePathPatternToRegex(route.pattern), - })); +const READ_ONLY_ADMIN_OPERATION_PATTERNS = [ + "/admin/attendees/:attendeeId/refresh-payment", + "/admin/backup/create", + "/admin/debug/sentry", + "/admin/deliveries/mark", + "/admin/listing/:id/scan", + "/admin/listing/:listingId/attendee/:attendeeId/checkin", + "/admin/login", + "/admin/logout", + "/admin/support", +].map(routePathPatternToRegex); const READ_ONLY_SAFE_PATHS = [ /^\/renew$/, @@ -37,9 +41,8 @@ const isAdminMutation = (path: string, method: string): boolean => isMutatingMethod(method) && path.startsWith("/admin/"); const isAllowedAdminOperation = (path: string, method: string): boolean => - READ_ONLY_ADMIN_OPERATIONS.some( - (route) => route.method === method && route.pattern.test(path), - ); + method === "POST" && + READ_ONLY_ADMIN_OPERATION_PATTERNS.some((pattern) => pattern.test(path)); type ReadOnlyRequest = { method: string; diff --git a/src/shared/admin-surface.ts b/src/shared/admin-surface.ts index 748bdc9cb0..c14e472b63 100644 --- a/src/shared/admin-surface.ts +++ b/src/shared/admin-surface.ts @@ -3,7 +3,6 @@ import { ADMIN_SURFACE_AREAS, } from "#shared/admin-surface/definitions.ts"; import { ADMIN_NAV_ROUTES } from "#shared/admin-surface/nav-routes.ts"; -import { ADMIN_ROUTES } from "#shared/admin-surface/routes/index.ts"; import { ADMIN_WRITE_ROUTES_A_M } from "#shared/admin-surface/write-routes-a-m.ts"; import { ADMIN_WRITE_ROUTES_N_Z } from "#shared/admin-surface/write-routes-n-z.ts"; import type { RouteParamNames } from "#shared/route-pattern.ts"; @@ -15,17 +14,13 @@ const ADMIN_DESTINATIONS = [ ...ADMIN_WRITE_ROUTES_N_Z, ] as const; -const ADMIN_PATHS = [...ADMIN_DESTINATIONS, ...ADMIN_ROUTES] as const; - export type AdminDestinationId = (typeof ADMIN_DESTINATIONS)[number]["id"]; -export type AdminRouteId = (typeof ADMIN_ROUTES)[number]["id"]; -export type AdminPathId = (typeof ADMIN_PATHS)[number]["id"]; -type PathFor = Extract< - (typeof ADMIN_PATHS)[number], +type PathFor = Extract< + (typeof ADMIN_DESTINATIONS)[number], { readonly id: Id } >; -export type AdminPathParams = Record< +export type AdminPathParams = Record< RouteParamNames["pattern"]>, string | number >; @@ -34,15 +29,11 @@ export type AdminPathParams = Record< export const adminDestination = (id: AdminDestinationId) => ADMIN_DESTINATIONS.find((candidate) => candidate.id === id)!; -// AdminPathId is derived from destinations and routes, so the lookup cannot miss. -const adminPathDefinition = (id: AdminPathId) => - ADMIN_PATHS.find((candidate) => candidate.id === id)!; - -export const adminPath = ( +export const adminPath = ( id: Id, params: AdminPathParams, ): string => - adminPathDefinition(id).pattern.replace( + adminDestination(id).pattern.replace( /:(\w+)/g, (_, name: RouteParamNames["pattern"]>) => String(params[name]), ); @@ -62,6 +53,5 @@ export const adminDestinationAllowed = ( export const ADMIN_SURFACE = { areas: ADMIN_SURFACE_AREAS, destinations: ADMIN_DESTINATIONS, - routes: ADMIN_ROUTES, sections: ADMIN_SECTIONS, } as const; diff --git a/src/shared/admin-surface/definitions.ts b/src/shared/admin-surface/definitions.ts index e211e68161..279bf2d5e5 100644 --- a/src/shared/admin-surface/definitions.ts +++ b/src/shared/admin-surface/definitions.ts @@ -60,79 +60,6 @@ export type AdminAreaId = keyof typeof ADMIN_SURFACE_AREAS; export type AdminAudience = readonly AdminLevel[]; export type AdminRouteIntent = "view" | "write-form"; export type AdminNavKind = "landing" | "link" | "create" | "import"; -export type AdminMethod = "DELETE" | "GET" | "PATCH" | "POST" | "PUT"; - -export type AdminRouteDef = { - readonly area: AdminAreaId; - readonly id: string; - readonly method: AdminMethod; - readonly pattern: string; - readonly readOnly: "allow" | "block"; -}; - -type LiteralAdminRoute< - Id extends string, - Area extends AdminAreaId, - Method extends AdminMethod, - Pattern extends string, -> = AdminRouteDef & { - readonly area: Area; - readonly id: Id; - readonly method: Method; - readonly pattern: Pattern; -}; - -const defineAdminRoute = - (allowedInReadOnly: (method: AdminMethod) => boolean) => - < - Id extends string, - Area extends AdminAreaId, - Method extends AdminMethod, - Pattern extends string, - >( - id: Id, - area: Area, - method: Method, - pattern: Pattern, - ): LiteralAdminRoute => ({ - area, - id, - method, - pattern, - readOnly: allowedInReadOnly(method) ? "allow" : "block", - }); - -export const route = defineAdminRoute((method) => method === "GET"); -export const moveRoutes = < - IdPrefix extends string, - Area extends AdminAreaId, - Pattern extends string, ->( - idPrefix: IdPrefix, - area: Area, - pattern: Pattern, -): readonly [ - LiteralAdminRoute< - `${IdPrefix}MoveDown`, - Area, - "POST", - `${Pattern}/move-down` - >, - LiteralAdminRoute<`${IdPrefix}MoveUp`, Area, "POST", `${Pattern}/move-up`>, -] => [ - route(`${idPrefix}MoveDown`, area, "POST", `${pattern}/move-down`), - route(`${idPrefix}MoveUp`, area, "POST", `${pattern}/move-up`), -]; -const readOnlyRoute = defineAdminRoute(() => true); -export const operation = < - Id extends string, - Area extends AdminAreaId, - Pattern extends string, ->( - id: Id, - area: Area, - pattern: Pattern, -) => readOnlyRoute(id, area, "POST", pattern); export const OWNER_AUDIENCE = ["owner"] as const; diff --git a/src/shared/admin-surface/routes/api-keys.ts b/src/shared/admin-surface/routes/api-keys.ts deleted file mode 100644 index 4c00b022aa..0000000000 --- a/src/shared/admin-surface/routes/api-keys.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "getApiKeysByApiKeyIdDelete", - "apiKeys", - "GET", - "/admin/api-keys/:apiKeyId/delete", - ), - route( - "postApiKeysByApiKeyIdDelete", - "apiKeys", - "POST", - "/admin/api-keys/:apiKeyId/delete", - ), - route("getApiKeys", "apiKeys", "GET", "/admin/api-keys"), - route("getApiKeysDocs", "apiKeys", "GET", "/admin/api-keys/docs"), - route("postApiKeys", "apiKeys", "POST", "/admin/api-keys"), - route("getApiKeysByApiKeyId", "apiKeys", "GET", "/admin/api-keys/:apiKeyId"), - route( - "getApiKeysByApiKeyIdByTab", - "apiKeys", - "GET", - "/admin/api-keys/:apiKeyId/:tab", - ), -] as const; diff --git a/src/shared/admin-surface/routes/attendee-notes.ts b/src/shared/admin-surface/routes/attendee-notes.ts deleted file mode 100644 index c3c1482e26..0000000000 --- a/src/shared/admin-surface/routes/attendee-notes.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "getAttendeeByAttendeeIdNote", - "attendeeNotes", - "GET", - "/admin/attendee/:attendeeId/note", - ), - route( - "getAttendeeByAttendeeIdNoteByNoteIdDelete", - "attendeeNotes", - "GET", - "/admin/attendee/:attendeeId/note/:noteId/delete", - ), - route( - "postAttendeeByAttendeeIdNote", - "attendeeNotes", - "POST", - "/admin/attendee/:attendeeId/note", - ), - route( - "postAttendeeByAttendeeIdNoteByNoteIdDelete", - "attendeeNotes", - "POST", - "/admin/attendee/:attendeeId/note/:noteId/delete", - ), -] as const; diff --git a/src/shared/admin-surface/routes/attendee-refunds.ts b/src/shared/admin-surface/routes/attendee-refunds.ts deleted file mode 100644 index 8a48e4cc73..0000000000 --- a/src/shared/admin-surface/routes/attendee-refunds.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "getAttendeesByAttendeeIdRefund", - "attendeeRefunds", - "GET", - "/admin/attendees/:attendeeId/refund", - ), - route( - "getListingByIdRefundAll", - "attendeeRefunds", - "GET", - "/admin/listing/:id/refund-all", - ), - route( - "postAttendeesByAttendeeIdRefund", - "attendeeRefunds", - "POST", - "/admin/attendees/:attendeeId/refund", - ), - route( - "postListingByIdRefundAll", - "attendeeRefunds", - "POST", - "/admin/listing/:id/refund-all", - ), -] as const; diff --git a/src/shared/admin-surface/routes/attendees.ts b/src/shared/admin-surface/routes/attendees.ts deleted file mode 100644 index e9eadd706c..0000000000 --- a/src/shared/admin-surface/routes/attendees.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { operation, route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "deleteAttendeesByAttendeeIdDelete", - "attendees", - "DELETE", - "/admin/attendees/:attendeeId/delete", - ), - route("getAttendees", "attendees", "GET", "/admin/attendees"), - route( - "getAttendeesByAttendeeId", - "attendees", - "GET", - "/admin/attendees/:attendeeId", - ), - route( - "getAttendeesByAttendeeIdByTab", - "attendees", - "GET", - "/admin/attendees/:attendeeId/:tab", - ), - route( - "getAttendeesByAttendeeIdDelete", - "attendees", - "GET", - "/admin/attendees/:attendeeId/delete", - ), - route( - "getAttendeesByAttendeeIdResendNotification", - "attendees", - "GET", - "/admin/attendees/:attendeeId/resend-notification", - ), - route("getAttendeesCsv", "attendees", "GET", "/admin/attendees/csv"), - route("getAttendeesNew", "attendees", "GET", "/admin/attendees/new"), - route( - "postAttendeesByAttendeeId", - "attendees", - "POST", - "/admin/attendees/:attendeeId", - ), - route( - "postAttendeesByAttendeeIdDelete", - "attendees", - "POST", - "/admin/attendees/:attendeeId/delete", - ), - route( - "postAttendeesByAttendeeIdLogistics", - "attendees", - "POST", - "/admin/attendees/:attendeeId/logistics", - ), - route( - "postAttendeesByAttendeeIdMerge", - "attendees", - "POST", - "/admin/attendees/:attendeeId/merge", - ), - operation( - "postAttendeesByAttendeeIdRefreshPayment", - "attendees", - "/admin/attendees/:attendeeId/refresh-payment", - ), - route( - "postAttendeesByAttendeeIdResendNotification", - "attendees", - "POST", - "/admin/attendees/:attendeeId/resend-notification", - ), - route("postAttendeesNew", "attendees", "POST", "/admin/attendees/new"), - route( - "postListingByListingIdAttendee", - "attendees", - "POST", - "/admin/listing/:listingId/attendee", - ), - operation( - "postListingByListingIdAttendeeByAttendeeIdCheckin", - "attendees", - "/admin/listing/:listingId/attendee/:attendeeId/checkin", - ), - route( - "postListingByListingIdAttendeeByAttendeeIdDeleteIncomplete", - "attendees", - "POST", - "/admin/listing/:listingId/attendee/:attendeeId/delete-incomplete", - ), -] as const; diff --git a/src/shared/admin-surface/routes/attributes.ts b/src/shared/admin-surface/routes/attributes.ts deleted file mode 100644 index eeea002680..0000000000 --- a/src/shared/admin-surface/routes/attributes.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { moveRoutes, route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "getAttributesByIdDelete", - "attributes", - "GET", - "/admin/attributes/:id/delete", - ), - route( - "postAttributesByIdDelete", - "attributes", - "POST", - "/admin/attributes/:id/delete", - ), - route("getAttributes", "attributes", "GET", "/admin/attributes"), - route("getAttributesById", "attributes", "GET", "/admin/attributes/:id"), - route( - "getAttributesByIdOptionsByOptionIdDelete", - "attributes", - "GET", - "/admin/attributes/:id/options/:optionId/delete", - ), - route( - "getAttributesByIdOptionsByOptionIdEdit", - "attributes", - "GET", - "/admin/attributes/:id/options/:optionId/edit", - ), - route("postAttributes", "attributes", "POST", "/admin/attributes"), - route( - "postAttributesByIdEdit", - "attributes", - "POST", - "/admin/attributes/:id/edit", - ), - ...moveRoutes("postAttributesById", "attributes", "/admin/attributes/:id"), - route( - "postAttributesByIdOptions", - "attributes", - "POST", - "/admin/attributes/:id/options", - ), - route( - "postAttributesByIdOptionsByOptionIdDelete", - "attributes", - "POST", - "/admin/attributes/:id/options/:optionId/delete", - ), - route( - "postAttributesByIdOptionsByOptionIdEdit", - "attributes", - "POST", - "/admin/attributes/:id/options/:optionId/edit", - ), - ...moveRoutes( - "postAttributesByIdOptionsByOptionId", - "attributes", - "/admin/attributes/:id/options/:optionId", - ), - route( - "postListingByIdAttributes", - "attributes", - "POST", - "/admin/listing/:id/attributes", - ), -] as const; diff --git a/src/shared/admin-surface/routes/auth.ts b/src/shared/admin-surface/routes/auth.ts deleted file mode 100644 index 767de89087..0000000000 --- a/src/shared/admin-surface/routes/auth.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { operation, route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getLogin", "auth", "GET", "/admin/login"), - route("getLogout", "auth", "GET", "/admin/logout"), - operation("postLogin", "auth", "/admin/login"), - operation("postLogout", "auth", "/admin/logout"), -] as const; diff --git a/src/shared/admin-surface/routes/backup.ts b/src/shared/admin-surface/routes/backup.ts deleted file mode 100644 index b8ed4be589..0000000000 --- a/src/shared/admin-surface/routes/backup.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { operation, route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getBackup", "backup", "GET", "/admin/backup"), - route( - "getBackupDownloadByFilename", - "backup", - "GET", - "/admin/backup/download/:filename", - ), - operation("postBackupCreate", "backup", "/admin/backup/create"), - route("postBackupRestore", "backup", "POST", "/admin/backup/restore"), - route( - "postBackupRestoreConfirm", - "backup", - "POST", - "/admin/backup/restore/confirm", - ), -] as const; diff --git a/src/shared/admin-surface/routes/builder.ts b/src/shared/admin-surface/routes/builder.ts deleted file mode 100644 index 6cedc9fdfc..0000000000 --- a/src/shared/admin-surface/routes/builder.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getBuilder", "builder", "GET", "/admin/builder"), - route("postBuilder", "builder", "POST", "/admin/builder"), -] as const; diff --git a/src/shared/admin-surface/routes/built-sites.ts b/src/shared/admin-surface/routes/built-sites.ts deleted file mode 100644 index 49f62c5304..0000000000 --- a/src/shared/admin-surface/routes/built-sites.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "getBuiltSitesByIdDelete", - "builtSites", - "GET", - "/admin/built-sites/:id/delete", - ), - route( - "postBuiltSitesByIdDelete", - "builtSites", - "POST", - "/admin/built-sites/:id/delete", - ), - route("getBuiltSites", "builtSites", "GET", "/admin/built-sites"), - route("getBuiltSitesNew", "builtSites", "GET", "/admin/built-sites/new"), - route("postBuiltSites", "builtSites", "POST", "/admin/built-sites"), - route("getBuiltSitesById", "builtSites", "GET", "/admin/built-sites/:id"), - route( - "getBuiltSitesByIdByTab", - "builtSites", - "GET", - "/admin/built-sites/:id/:tab", - ), - route( - "postBuiltSitesByIdEdit", - "builtSites", - "POST", - "/admin/built-sites/:id/edit", - ), - route( - "postBuiltSitesByIdAddSecrets", - "builtSites", - "POST", - "/admin/built-sites/:id/add-secrets", - ), - route( - "postBuiltSitesByIdBumpDeadline", - "builtSites", - "POST", - "/admin/built-sites/:id/bump-deadline", - ), - route( - "postBuiltSitesByIdOverrideDeadline", - "builtSites", - "POST", - "/admin/built-sites/:id/override-deadline", - ), - route( - "postBuiltSitesByIdProvisionRenewal", - "builtSites", - "POST", - "/admin/built-sites/:id/provision-renewal", - ), - route( - "postBuiltSitesByIdProvisionScheduler", - "builtSites", - "POST", - "/admin/built-sites/:id/provision-scheduler", - ), - route( - "postBuiltSitesByIdReSyncDeadline", - "builtSites", - "POST", - "/admin/built-sites/:id/re-sync-deadline", - ), - route( - "postBuiltSitesByIdRotateRenewalToken", - "builtSites", - "POST", - "/admin/built-sites/:id/rotate-renewal-token", - ), - route( - "postBuiltSitesByIdUpdate", - "builtSites", - "POST", - "/admin/built-sites/:id/update", - ), -] as const; diff --git a/src/shared/admin-surface/routes/bulk-actions.ts b/src/shared/admin-surface/routes/bulk-actions.ts deleted file mode 100644 index 19a42c4daa..0000000000 --- a/src/shared/admin-surface/routes/bulk-actions.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "getGroupsByIdBulkActions", - "bulkActions", - "GET", - "/admin/groups/:id/bulk-actions", - ), - route( - "getGroupsByIdBulkActionsDeactivate", - "bulkActions", - "GET", - "/admin/groups/:id/bulk-actions/deactivate", - ), - route( - "getGroupsByIdBulkActionsDuplicate", - "bulkActions", - "GET", - "/admin/groups/:id/bulk-actions/duplicate", - ), - route( - "getGroupsByIdBulkActionsReactivate", - "bulkActions", - "GET", - "/admin/groups/:id/bulk-actions/reactivate", - ), - route( - "postGroupsByIdBulkActionsDeactivate", - "bulkActions", - "POST", - "/admin/groups/:id/bulk-actions/deactivate", - ), - route( - "postGroupsByIdBulkActionsDuplicate", - "bulkActions", - "POST", - "/admin/groups/:id/bulk-actions/duplicate", - ), - route( - "postGroupsByIdBulkActionsReactivate", - "bulkActions", - "POST", - "/admin/groups/:id/bulk-actions/reactivate", - ), -] as const; diff --git a/src/shared/admin-surface/routes/bulk-email.ts b/src/shared/admin-surface/routes/bulk-email.ts deleted file mode 100644 index ae456480cc..0000000000 --- a/src/shared/admin-surface/routes/bulk-email.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "getEmailsTemplatesByIdDelete", - "bulkEmail", - "GET", - "/admin/emails/templates/:id/delete", - ), - route( - "postEmailsTemplatesByIdDelete", - "bulkEmail", - "POST", - "/admin/emails/templates/:id/delete", - ), - route("getEmails", "bulkEmail", "GET", "/admin/emails"), - route("getEmailsPreview", "bulkEmail", "GET", "/admin/emails/preview"), - route("postEmailsPreview", "bulkEmail", "POST", "/admin/emails/preview"), - route("postEmailsSend", "bulkEmail", "POST", "/admin/emails/send"), - route("postEmailsTemplates", "bulkEmail", "POST", "/admin/emails/templates"), -] as const; diff --git a/src/shared/admin-surface/routes/calendar.ts b/src/shared/admin-surface/routes/calendar.ts deleted file mode 100644 index 6c54f5d3a3..0000000000 --- a/src/shared/admin-surface/routes/calendar.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getCalendar", "calendar", "GET", "/admin/calendar"), - route("getCalendarExport", "calendar", "GET", "/admin/calendar/export"), -] as const; diff --git a/src/shared/admin-surface/routes/catalog-transfer.ts b/src/shared/admin-surface/routes/catalog-transfer.ts deleted file mode 100644 index 1e7ced49c6..0000000000 --- a/src/shared/admin-surface/routes/catalog-transfer.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getCatalogImport", "catalogTransfer", "GET", "/admin/catalog/import"), - route( - "getGroupsByIdExportJson", - "catalogTransfer", - "GET", - "/admin/groups/:id/export.json", - ), - route( - "getListingByIdExportJson", - "catalogTransfer", - "GET", - "/admin/listing/:id/export.json", - ), - route( - "postCatalogImport", - "catalogTransfer", - "POST", - "/admin/catalog/import", - ), -] as const; diff --git a/src/shared/admin-surface/routes/contact-history.ts b/src/shared/admin-surface/routes/contact-history.ts deleted file mode 100644 index 0d77593564..0000000000 --- a/src/shared/admin-surface/routes/contact-history.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getHistoryByHmac", "contactHistory", "GET", "/admin/history/:hmac"), - route("postHistoryByHmac", "contactHistory", "POST", "/admin/history/:hmac"), -] as const; diff --git a/src/shared/admin-surface/routes/dashboard.ts b/src/shared/admin-surface/routes/dashboard.ts deleted file mode 100644 index 344d5e79b8..0000000000 --- a/src/shared/admin-surface/routes/dashboard.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getAdmin", "dashboard", "GET", "/admin"), - route("getListings", "dashboard", "GET", "/admin/listings"), - route("getListingsCsv", "dashboard", "GET", "/admin/listings/csv"), - route("getLog", "dashboard", "GET", "/admin/log"), -] as const; diff --git a/src/shared/admin-surface/routes/debug.ts b/src/shared/admin-surface/routes/debug.ts deleted file mode 100644 index b2764da86e..0000000000 --- a/src/shared/admin-surface/routes/debug.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { operation, route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getDebug", "debug", "GET", "/admin/debug"), - operation("postSentryTest", "debug", "/admin/debug/sentry"), -] as const; diff --git a/src/shared/admin-surface/routes/deliveries.ts b/src/shared/admin-surface/routes/deliveries.ts deleted file mode 100644 index 335a8633fc..0000000000 --- a/src/shared/admin-surface/routes/deliveries.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { operation, route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getDeliveries", "deliveries", "GET", "/admin/deliveries"), - operation("postDeliveriesMark", "deliveries", "/admin/deliveries/mark"), -] as const; diff --git a/src/shared/admin-surface/routes/groups.ts b/src/shared/admin-surface/routes/groups.ts deleted file mode 100644 index 37a0b81e76..0000000000 --- a/src/shared/admin-surface/routes/groups.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getGroupsByIdDelete", "groups", "GET", "/admin/groups/:id/delete"), - route("postGroupsByIdDelete", "groups", "POST", "/admin/groups/:id/delete"), - route("getGroups", "groups", "GET", "/admin/groups"), - route("getGroupsNew", "groups", "GET", "/admin/groups/new"), - route("postGroups", "groups", "POST", "/admin/groups"), - route("postGroupsByIdEdit", "groups", "POST", "/admin/groups/:id/edit"), - route("getGroupsById", "groups", "GET", "/admin/groups/:id"), - route("getGroupsByIdByTab", "groups", "GET", "/admin/groups/:id/:tab"), - route( - "postGroupsByIdAddListings", - "groups", - "POST", - "/admin/groups/:id/add-listings", - ), - route("postGroupsByIdImages", "groups", "POST", "/admin/groups/:id/images"), - route( - "postGroupsByIdImagesUpload", - "groups", - "POST", - "/admin/groups/:id/images/upload", - ), -] as const; diff --git a/src/shared/admin-surface/routes/guide.ts b/src/shared/admin-surface/routes/guide.ts deleted file mode 100644 index b2dce5d3ce..0000000000 --- a/src/shared/admin-surface/routes/guide.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getFormatting", "guide", "GET", "/admin/formatting"), - route("getGuide", "guide", "GET", "/admin/guide"), -] as const; diff --git a/src/shared/admin-surface/routes/holidays.ts b/src/shared/admin-surface/routes/holidays.ts deleted file mode 100644 index 7c8fd43205..0000000000 --- a/src/shared/admin-surface/routes/holidays.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "getHolidaysByIdDelete", - "holidays", - "GET", - "/admin/holidays/:id/delete", - ), - route( - "postHolidaysByIdDelete", - "holidays", - "POST", - "/admin/holidays/:id/delete", - ), - route("getHolidays", "holidays", "GET", "/admin/holidays"), - route("getHolidaysNew", "holidays", "GET", "/admin/holidays/new"), - route("postHolidays", "holidays", "POST", "/admin/holidays"), - route("postHolidaysByIdEdit", "holidays", "POST", "/admin/holidays/:id/edit"), - route("getHolidaysById", "holidays", "GET", "/admin/holidays/:id"), - route("getHolidaysByIdByTab", "holidays", "GET", "/admin/holidays/:id/:tab"), -] as const; diff --git a/src/shared/admin-surface/routes/images.ts b/src/shared/admin-surface/routes/images.ts deleted file mode 100644 index ad09d8afe4..0000000000 --- a/src/shared/admin-surface/routes/images.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getImages", "images", "GET", "/admin/images"), - route("getImagesByIdDelete", "images", "GET", "/admin/images/:id/delete"), - route("getImagesByIdEdit", "images", "GET", "/admin/images/:id/edit"), - route("getImagesNew", "images", "GET", "/admin/images/new"), - route("postImages", "images", "POST", "/admin/images"), - route("postImagesByIdDelete", "images", "POST", "/admin/images/:id/delete"), - route("postImagesByIdEdit", "images", "POST", "/admin/images/:id/edit"), -] as const; diff --git a/src/shared/admin-surface/routes/index.ts b/src/shared/admin-surface/routes/index.ts deleted file mode 100644 index 18bdf1ca7b..0000000000 --- a/src/shared/admin-surface/routes/index.ts +++ /dev/null @@ -1,87 +0,0 @@ -import * as apiKeys from "#shared/admin-surface/routes/api-keys.ts"; -import * as attendeeNotes from "#shared/admin-surface/routes/attendee-notes.ts"; -import * as attendeeRefunds from "#shared/admin-surface/routes/attendee-refunds.ts"; -import * as attendees from "#shared/admin-surface/routes/attendees.ts"; -import * as attributes from "#shared/admin-surface/routes/attributes.ts"; -import * as auth from "#shared/admin-surface/routes/auth.ts"; -import * as backup from "#shared/admin-surface/routes/backup.ts"; -import * as builder from "#shared/admin-surface/routes/builder.ts"; -import * as builtSites from "#shared/admin-surface/routes/built-sites.ts"; -import * as bulkActions from "#shared/admin-surface/routes/bulk-actions.ts"; -import * as bulkEmail from "#shared/admin-surface/routes/bulk-email.ts"; -import * as calendar from "#shared/admin-surface/routes/calendar.ts"; -import * as catalogTransfer from "#shared/admin-surface/routes/catalog-transfer.ts"; -import * as contactHistory from "#shared/admin-surface/routes/contact-history.ts"; -import * as dashboard from "#shared/admin-surface/routes/dashboard.ts"; -import * as debug from "#shared/admin-surface/routes/debug.ts"; -import * as deliveries from "#shared/admin-surface/routes/deliveries.ts"; -import * as groups from "#shared/admin-surface/routes/groups.ts"; -import * as guide from "#shared/admin-surface/routes/guide.ts"; -import * as holidays from "#shared/admin-surface/routes/holidays.ts"; -import * as images from "#shared/admin-surface/routes/images.ts"; -import * as ledger from "#shared/admin-surface/routes/ledger.ts"; -import * as listingQr from "#shared/admin-surface/routes/listing-qr.ts"; -import * as listings from "#shared/admin-surface/routes/listings.ts"; -import * as markdownPreview from "#shared/admin-surface/routes/markdown-preview.ts"; -import * as modifiers from "#shared/admin-surface/routes/modifiers.ts"; -import * as news from "#shared/admin-surface/routes/news.ts"; -import * as privacy from "#shared/admin-surface/routes/privacy.ts"; -import * as questions from "#shared/admin-surface/routes/questions.ts"; -import * as scanner from "#shared/admin-surface/routes/scanner.ts"; -import * as seeds from "#shared/admin-surface/routes/seeds.ts"; -import * as servicing from "#shared/admin-surface/routes/servicing.ts"; -import * as sessions from "#shared/admin-surface/routes/sessions.ts"; -import * as settings from "#shared/admin-surface/routes/settings.ts"; -import * as settingsLogistics from "#shared/admin-surface/routes/settings-logistics.ts"; -import * as settingsStatuses from "#shared/admin-surface/routes/settings-statuses.ts"; -import * as site from "#shared/admin-surface/routes/site.ts"; -import * as sitePages from "#shared/admin-surface/routes/site-pages.ts"; -import * as sms from "#shared/admin-surface/routes/sms.ts"; -import * as support from "#shared/admin-surface/routes/support.ts"; -import * as update from "#shared/admin-surface/routes/update.ts"; -import * as users from "#shared/admin-surface/routes/users.ts"; - -export const ADMIN_ROUTES = [ - ...apiKeys.routes, - ...attendeeNotes.routes, - ...attendeeRefunds.routes, - ...attendees.routes, - ...attributes.routes, - ...auth.routes, - ...backup.routes, - ...builder.routes, - ...builtSites.routes, - ...bulkActions.routes, - ...bulkEmail.routes, - ...calendar.routes, - ...catalogTransfer.routes, - ...contactHistory.routes, - ...dashboard.routes, - ...debug.routes, - ...deliveries.routes, - ...groups.routes, - ...guide.routes, - ...holidays.routes, - ...images.routes, - ...ledger.routes, - ...listingQr.routes, - ...listings.routes, - ...markdownPreview.routes, - ...modifiers.routes, - ...news.routes, - ...privacy.routes, - ...questions.routes, - ...scanner.routes, - ...seeds.routes, - ...servicing.routes, - ...sessions.routes, - ...settings.routes, - ...settingsLogistics.routes, - ...settingsStatuses.routes, - ...site.routes, - ...sitePages.routes, - ...sms.routes, - ...support.routes, - ...update.routes, - ...users.routes, -] as const; diff --git a/src/shared/admin-surface/routes/ledger.ts b/src/shared/admin-surface/routes/ledger.ts deleted file mode 100644 index c77c7f2bdc..0000000000 --- a/src/shared/admin-surface/routes/ledger.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getLedger", "ledger", "GET", "/admin/ledger"), - route("getLedgerByTypeByRef", "ledger", "GET", "/admin/ledger/:type/:ref"), - route( - "getLedgerByTypeByRefAdd", - "ledger", - "GET", - "/admin/ledger/:type/:ref/add", - ), - route( - "getLedgerEntriesByIdEdit", - "ledger", - "GET", - "/admin/ledger/entries/:id/edit", - ), - route( - "postLedgerByTypeByRefAdd", - "ledger", - "POST", - "/admin/ledger/:type/:ref/add", - ), - route( - "postLedgerEntriesByIdDelete", - "ledger", - "POST", - "/admin/ledger/entries/:id/delete", - ), - route( - "postLedgerEntriesByIdEdit", - "ledger", - "POST", - "/admin/ledger/entries/:id/edit", - ), -] as const; diff --git a/src/shared/admin-surface/routes/listing-qr.ts b/src/shared/admin-surface/routes/listing-qr.ts deleted file mode 100644 index 2798cf9d95..0000000000 --- a/src/shared/admin-surface/routes/listing-qr.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "getListingByIdQrJson", - "listingQr", - "GET", - "/admin/listing/:id/qr.json", - ), - route("postListingByIdQr", "listingQr", "POST", "/admin/listing/:id/qr"), -] as const; diff --git a/src/shared/admin-surface/routes/listings.ts b/src/shared/admin-surface/routes/listings.ts deleted file mode 100644 index fc6bcef98b..0000000000 --- a/src/shared/admin-surface/routes/listings.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "getListingByIdDeactivate", - "listings", - "GET", - "/admin/listing/:id/deactivate", - ), - route( - "postListingByIdDeactivate", - "listings", - "POST", - "/admin/listing/:id/deactivate", - ), - route( - "getListingByIdReactivate", - "listings", - "GET", - "/admin/listing/:id/reactivate", - ), - route( - "postListingByIdReactivate", - "listings", - "POST", - "/admin/listing/:id/reactivate", - ), - route("getListingByIdDelete", "listings", "GET", "/admin/listing/:id/delete"), - route( - "postListingByIdDelete", - "listings", - "POST", - "/admin/listing/:id/delete", - ), - route( - "deleteListingByIdDelete", - "listings", - "DELETE", - "/admin/listing/:id/delete", - ), - route("getListingById", "listings", "GET", "/admin/listing/:id"), - route("getListingByIdByTab", "listings", "GET", "/admin/listing/:id/:tab"), - route( - "getListingByIdAttendeesCsv", - "listings", - "GET", - "/admin/listing/:id/attendees.csv", - ), - route( - "getListingByIdDuplicate", - "listings", - "GET", - "/admin/listing/:id/duplicate", - ), - route("getListingByIdExport", "listings", "GET", "/admin/listing/:id/export"), - route("getListingNew", "listings", "GET", "/admin/listing/new"), - route( - "getListingsRecalculateByListingId", - "listings", - "GET", - "/admin/listings/recalculate/:listingId", - ), - route("postListing", "listings", "POST", "/admin/listing"), - route( - "postListingByIdAttachmentDelete", - "listings", - "POST", - "/admin/listing/:id/attachment/delete", - ), - route( - "postListingByIdChildren", - "listings", - "POST", - "/admin/listing/:id/children", - ), - route("postListingByIdEdit", "listings", "POST", "/admin/listing/:id/edit"), - route( - "postListingByIdImages", - "listings", - "POST", - "/admin/listing/:id/images", - ), - route( - "postListingByIdImagesUpload", - "listings", - "POST", - "/admin/listing/:id/images/upload", - ), - route( - "postListingByIdIncome", - "listings", - "POST", - "/admin/listing/:id/income", - ), - route( - "postListingsRecalculateByListingId", - "listings", - "POST", - "/admin/listings/recalculate/:listingId", - ), -] as const; diff --git a/src/shared/admin-surface/routes/markdown-preview.ts b/src/shared/admin-surface/routes/markdown-preview.ts deleted file mode 100644 index 70137c8b40..0000000000 --- a/src/shared/admin-surface/routes/markdown-preview.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "postMarkdownPreview", - "markdownPreview", - "POST", - "/admin/markdown-preview", - ), -] as const; diff --git a/src/shared/admin-surface/routes/modifiers.ts b/src/shared/admin-surface/routes/modifiers.ts deleted file mode 100644 index 23f6090af0..0000000000 --- a/src/shared/admin-surface/routes/modifiers.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "getModifiersByIdDelete", - "modifiers", - "GET", - "/admin/modifiers/:id/delete", - ), - route( - "postModifiersByIdDelete", - "modifiers", - "POST", - "/admin/modifiers/:id/delete", - ), - route("getModifiers", "modifiers", "GET", "/admin/modifiers"), - route("getModifiersNew", "modifiers", "GET", "/admin/modifiers/new"), - route("postModifiers", "modifiers", "POST", "/admin/modifiers"), - route( - "postModifiersByIdEdit", - "modifiers", - "POST", - "/admin/modifiers/:id/edit", - ), - route("getModifiersById", "modifiers", "GET", "/admin/modifiers/:id"), - route( - "getModifiersByIdByTab", - "modifiers", - "GET", - "/admin/modifiers/:id/:tab", - ), - route( - "getModifiersRecalculateByModifierId", - "modifiers", - "GET", - "/admin/modifiers/recalculate/:modifierId", - ), - route( - "postModifiersByIdAnswers", - "modifiers", - "POST", - "/admin/modifiers/:id/answers", - ), - route( - "postModifiersByIdLinks", - "modifiers", - "POST", - "/admin/modifiers/:id/links", - ), - route( - "postModifiersByIdRevenue", - "modifiers", - "POST", - "/admin/modifiers/:id/revenue", - ), - route( - "postModifiersRecalculateByModifierId", - "modifiers", - "POST", - "/admin/modifiers/recalculate/:modifierId", - ), -] as const; diff --git a/src/shared/admin-surface/routes/news.ts b/src/shared/admin-surface/routes/news.ts deleted file mode 100644 index afa24f10a5..0000000000 --- a/src/shared/admin-surface/routes/news.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getSiteNewsByIdDelete", "news", "GET", "/admin/site/news/:id/delete"), - route( - "postSiteNewsByIdDelete", - "news", - "POST", - "/admin/site/news/:id/delete", - ), - route("getSiteNews", "news", "GET", "/admin/site/news"), - route("getSiteNewsById", "news", "GET", "/admin/site/news/:id"), - route("getSiteNewsByIdByTab", "news", "GET", "/admin/site/news/:id/:tab"), - route("getSiteNewsNew", "news", "GET", "/admin/site/news/new"), - route("postSiteNews", "news", "POST", "/admin/site/news"), - route("postSiteNewsByIdEdit", "news", "POST", "/admin/site/news/:id/edit"), - route( - "postSiteNewsByIdImages", - "news", - "POST", - "/admin/site/news/:id/images", - ), - route( - "postSiteNewsByIdImagesUpload", - "news", - "POST", - "/admin/site/news/:id/images/upload", - ), -] as const; diff --git a/src/shared/admin-surface/routes/privacy.ts b/src/shared/admin-surface/routes/privacy.ts deleted file mode 100644 index 9746f0ca90..0000000000 --- a/src/shared/admin-surface/routes/privacy.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getPrivacy", "privacy", "GET", "/admin/privacy"), - route("postPrivacyErase", "privacy", "POST", "/admin/privacy/erase"), - route("postPrivacyOrphans", "privacy", "POST", "/admin/privacy/orphans"), -] as const; diff --git a/src/shared/admin-surface/routes/questions.ts b/src/shared/admin-surface/routes/questions.ts deleted file mode 100644 index fcec8af57f..0000000000 --- a/src/shared/admin-surface/routes/questions.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { moveRoutes, route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "getQuestionsByIdDelete", - "questions", - "GET", - "/admin/questions/:id/delete", - ), - route( - "postQuestionsByIdDelete", - "questions", - "POST", - "/admin/questions/:id/delete", - ), - route("getQuestions", "questions", "GET", "/admin/questions"), - route("getQuestionsById", "questions", "GET", "/admin/questions/:id"), - route( - "getQuestionsByIdAnswersByAnswerIdDelete", - "questions", - "GET", - "/admin/questions/:id/answers/:answerId/delete", - ), - route( - "getQuestionsByIdAnswersByAnswerIdEdit", - "questions", - "GET", - "/admin/questions/:id/answers/:answerId/edit", - ), - route( - "getQuestionsByIdAnswersByAnswerIdRecalculate", - "questions", - "GET", - "/admin/questions/:id/answers/:answerId/recalculate", - ), - route( - "postListingByIdQuestions", - "questions", - "POST", - "/admin/listing/:id/questions", - ), - route("postQuestions", "questions", "POST", "/admin/questions"), - route( - "postQuestionsByIdAnswers", - "questions", - "POST", - "/admin/questions/:id/answers", - ), - route( - "postQuestionsByIdAnswersByAnswerIdDelete", - "questions", - "POST", - "/admin/questions/:id/answers/:answerId/delete", - ), - route( - "postQuestionsByIdAnswersByAnswerIdEdit", - "questions", - "POST", - "/admin/questions/:id/answers/:answerId/edit", - ), - ...moveRoutes( - "postQuestionsByIdAnswersByAnswerId", - "questions", - "/admin/questions/:id/answers/:answerId", - ), - route( - "postQuestionsByIdAnswersByAnswerIdRecalculate", - "questions", - "POST", - "/admin/questions/:id/answers/:answerId/recalculate", - ), - route( - "postQuestionsByIdEdit", - "questions", - "POST", - "/admin/questions/:id/edit", - ), - route( - "postQuestionsByIdListings", - "questions", - "POST", - "/admin/questions/:id/listings", - ), - ...moveRoutes("postQuestionsById", "questions", "/admin/questions/:id"), -] as const; diff --git a/src/shared/admin-surface/routes/scanner.ts b/src/shared/admin-surface/routes/scanner.ts deleted file mode 100644 index bc805190bb..0000000000 --- a/src/shared/admin-surface/routes/scanner.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { operation, route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "getListingByIdScanner", - "scanner", - "GET", - "/admin/listing/:id/scanner", - ), - operation("postListingByIdScan", "scanner", "/admin/listing/:id/scan"), -] as const; diff --git a/src/shared/admin-surface/routes/seeds.ts b/src/shared/admin-surface/routes/seeds.ts deleted file mode 100644 index 4bcb63070e..0000000000 --- a/src/shared/admin-surface/routes/seeds.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getSeeds", "seeds", "GET", "/admin/seeds"), - route("postSeeds", "seeds", "POST", "/admin/seeds"), -] as const; diff --git a/src/shared/admin-surface/routes/servicing.ts b/src/shared/admin-surface/routes/servicing.ts deleted file mode 100644 index ed2a386aab..0000000000 --- a/src/shared/admin-surface/routes/servicing.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getServicing", "servicing", "GET", "/admin/servicing"), - route("getServicingById", "servicing", "GET", "/admin/servicing/:id"), - route("getServicingNew", "servicing", "GET", "/admin/servicing/new"), - route("postServicingById", "servicing", "POST", "/admin/servicing/:id"), - route( - "postServicingByIdCostByCostId", - "servicing", - "POST", - "/admin/servicing/:id/cost/:costId", - ), - route( - "postServicingByIdDelete", - "servicing", - "POST", - "/admin/servicing/:id/delete", - ), - route( - "postServicingByIdDuplicate", - "servicing", - "POST", - "/admin/servicing/:id/duplicate", - ), - route("postServicingNew", "servicing", "POST", "/admin/servicing/new"), -] as const; diff --git a/src/shared/admin-surface/routes/sessions.ts b/src/shared/admin-surface/routes/sessions.ts deleted file mode 100644 index ca9648ada5..0000000000 --- a/src/shared/admin-surface/routes/sessions.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getSessions", "sessions", "GET", "/admin/sessions"), - route("postSessions", "sessions", "POST", "/admin/sessions"), -] as const; diff --git a/src/shared/admin-surface/routes/settings-logistics.ts b/src/shared/admin-surface/routes/settings-logistics.ts deleted file mode 100644 index 0538180a2d..0000000000 --- a/src/shared/admin-surface/routes/settings-logistics.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "getLogisticsByIdDelete", - "settingsLogistics", - "GET", - "/admin/logistics/:id/delete", - ), - route( - "postLogisticsByIdDelete", - "settingsLogistics", - "POST", - "/admin/logistics/:id/delete", - ), - route("getLogistics", "settingsLogistics", "GET", "/admin/logistics"), - route("getLogisticsNew", "settingsLogistics", "GET", "/admin/logistics/new"), - route("postLogistics", "settingsLogistics", "POST", "/admin/logistics"), - route("getLogisticsById", "settingsLogistics", "GET", "/admin/logistics/:id"), - route( - "getLogisticsByIdByTab", - "settingsLogistics", - "GET", - "/admin/logistics/:id/:tab", - ), - route( - "postLogisticsByIdEdit", - "settingsLogistics", - "POST", - "/admin/logistics/:id/edit", - ), -] as const; diff --git a/src/shared/admin-surface/routes/settings-statuses.ts b/src/shared/admin-surface/routes/settings-statuses.ts deleted file mode 100644 index 8d6d523c80..0000000000 --- a/src/shared/admin-surface/routes/settings-statuses.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { moveRoutes, route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "getSettingsStatuses", - "settingsStatuses", - "GET", - "/admin/settings/statuses", - ), - route( - "getSettingsStatusesByIdDelete", - "settingsStatuses", - "GET", - "/admin/settings/statuses/:id/delete", - ), - route( - "getSettingsStatusesNew", - "settingsStatuses", - "GET", - "/admin/settings/statuses/new", - ), - route( - "postSettingsStatuses", - "settingsStatuses", - "POST", - "/admin/settings/statuses", - ), - route( - "postSettingsStatusesByIdDelete", - "settingsStatuses", - "POST", - "/admin/settings/statuses/:id/delete", - ), - route( - "postSettingsStatusesByIdEdit", - "settingsStatuses", - "POST", - "/admin/settings/statuses/:id/edit", - ), - ...moveRoutes( - "postSettingsStatusesById", - "settingsStatuses", - "/admin/settings/statuses/:id", - ), - route( - "getSettingsStatusesById", - "settingsStatuses", - "GET", - "/admin/settings/statuses/:id", - ), - route( - "getSettingsStatusesByIdByTab", - "settingsStatuses", - "GET", - "/admin/settings/statuses/:id/:tab", - ), -] as const; diff --git a/src/shared/admin-surface/routes/settings.ts b/src/shared/admin-surface/routes/settings.ts deleted file mode 100644 index 757ed048dc..0000000000 --- a/src/shared/admin-surface/routes/settings.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getFeaturesBySlug", "settings", "GET", "/admin/features/:slug"), - route("getListingDefaults", "settings", "GET", "/admin/listing-defaults"), - route("getSettings", "settings", "GET", "/admin/settings"), - route("getSettingsAdvanced", "settings", "GET", "/admin/settings-advanced"), - route("postListingDefaults", "settings", "POST", "/admin/listing-defaults"), - route("postFeaturesBySlug", "settings", "POST", "/admin/features/:slug"), - route("postSettings", "settings", "POST", "/admin/settings"), - route( - "postSettingsAddressLookup", - "settings", - "POST", - "/admin/settings/address-lookup", - ), - route( - "postSettingsAppleWallet", - "settings", - "POST", - "/admin/settings/apple-wallet", - ), - route( - "postSettingsAttendeeColumnOrder", - "settings", - "POST", - "/admin/settings/attendee-column-order", - ), - route( - "postSettingsBookingFee", - "settings", - "POST", - "/admin/settings/booking-fee", - ), - route( - "postSettingsBusinessEmail", - "settings", - "POST", - "/admin/settings/business-email", - ), - route( - "postSettingsCalendarFeeds", - "settings", - "POST", - "/admin/settings/calendar-feeds", - ), - route( - "postSettingsCustomCss", - "settings", - "POST", - "/admin/settings/custom-css", - ), - route( - "postSettingsCustomDomain", - "settings", - "POST", - "/admin/settings/custom-domain", - ), - route( - "postSettingsCustomDomainValidate", - "settings", - "POST", - "/admin/settings/custom-domain/validate", - ), - route("postSettingsEmail", "settings", "POST", "/admin/settings/email"), - route( - "postSettingsEmailTemplatesAdmin", - "settings", - "POST", - "/admin/settings/email-templates/admin", - ), - route( - "postSettingsEmailTemplatesConfirmation", - "settings", - "POST", - "/admin/settings/email-templates/confirmation", - ), - route( - "postSettingsEmailTemplatesPreview", - "settings", - "POST", - "/admin/settings/email-templates/preview", - ), - route( - "postSettingsEmailTest", - "settings", - "POST", - "/admin/settings/email/test", - ), - route( - "postSettingsEmbedHosts", - "settings", - "POST", - "/admin/settings/embed-hosts", - ), - route( - "postSettingsExternalOrder", - "settings", - "POST", - "/admin/settings/external-order", - ), - route( - "postSettingsGoogleWallet", - "settings", - "POST", - "/admin/settings/google-wallet", - ), - route( - "postSettingsHeaderImage", - "settings", - "POST", - "/admin/settings/header-image", - ), - route( - "postSettingsHeaderImageDelete", - "settings", - "POST", - "/admin/settings/header-image/delete", - ), - route( - "postSettingsHostSubdomain", - "settings", - "POST", - "/admin/settings/host-subdomain", - ), - route( - "postSettingsListingColumnOrder", - "settings", - "POST", - "/admin/settings/listing-column-order", - ), - route( - "postSettingsPaymentProvider", - "settings", - "POST", - "/admin/settings/payment-provider", - ), - route( - "postSettingsResetDatabase", - "settings", - "POST", - "/admin/settings/reset-database", - ), - route( - "postSettingsShowPublicApi", - "settings", - "POST", - "/admin/settings/show-public-api", - ), - route( - "postSettingsSmsGateway", - "settings", - "POST", - "/admin/settings/sms-gateway", - ), - route("postSettingsSquare", "settings", "POST", "/admin/settings/square"), - route( - "postSettingsSquareWebhook", - "settings", - "POST", - "/admin/settings/square-webhook", - ), - route( - "postSettingsSquareTest", - "settings", - "POST", - "/admin/settings/square/test", - ), - route("postSettingsStripe", "settings", "POST", "/admin/settings/stripe"), - route( - "postSettingsStripeTest", - "settings", - "POST", - "/admin/settings/stripe/test", - ), - route("postSettingsSumup", "settings", "POST", "/admin/settings/sumup"), - route( - "postSettingsSumupTest", - "settings", - "POST", - "/admin/settings/sumup/test", - ), - route( - "postSettingsSuperuser", - "settings", - "POST", - "/admin/settings/superuser", - ), - route("postSettingsTerms", "settings", "POST", "/admin/settings/terms"), - route("postSettingsTheme", "settings", "POST", "/admin/settings/theme"), -] as const; diff --git a/src/shared/admin-surface/routes/site-pages.ts b/src/shared/admin-surface/routes/site-pages.ts deleted file mode 100644 index 223f315056..0000000000 --- a/src/shared/admin-surface/routes/site-pages.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { moveRoutes, route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route( - "getSitePagesByIdDelete", - "sitePages", - "GET", - "/admin/site/pages/:id/delete", - ), - route( - "postSitePagesByIdDelete", - "sitePages", - "POST", - "/admin/site/pages/:id/delete", - ), - route("getSitePages", "sitePages", "GET", "/admin/site/pages"), - route("getSitePagesById", "sitePages", "GET", "/admin/site/pages/:id"), - route( - "getSitePagesByIdByTab", - "sitePages", - "GET", - "/admin/site/pages/:id/:tab", - ), - route("getSitePagesNew", "sitePages", "GET", "/admin/site/pages/new"), - route("postSitePages", "sitePages", "POST", "/admin/site/pages"), - route( - "postSitePagesByIdEdit", - "sitePages", - "POST", - "/admin/site/pages/:id/edit", - ), - route( - "postSitePagesByIdImages", - "sitePages", - "POST", - "/admin/site/pages/:id/images", - ), - route( - "postSitePagesByIdImagesUpload", - "sitePages", - "POST", - "/admin/site/pages/:id/images/upload", - ), - route( - "postSitePagesByIdItems", - "sitePages", - "POST", - "/admin/site/pages/:id/items", - ), - ...moveRoutes( - "postSitePagesByIdItemsByItemTypeByItemId", - "sitePages", - "/admin/site/pages/:id/items/:itemType/:itemId", - ), - route( - "postSitePagesByIdItemsByItemTypeByItemIdRemove", - "sitePages", - "POST", - "/admin/site/pages/:id/items/:itemType/:itemId/remove", - ), - ...moveRoutes("postSitePagesById", "sitePages", "/admin/site/pages/:id"), -] as const; diff --git a/src/shared/admin-surface/routes/site.ts b/src/shared/admin-surface/routes/site.ts deleted file mode 100644 index dffbe88c3a..0000000000 --- a/src/shared/admin-surface/routes/site.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getSite", "site", "GET", "/admin/site"), - route("getSiteContact", "site", "GET", "/admin/site/contact"), - route("getSiteOrder", "site", "GET", "/admin/site/order"), - route("postSite", "site", "POST", "/admin/site"), - route("postSiteContact", "site", "POST", "/admin/site/contact"), - route("postSiteContactForm", "site", "POST", "/admin/site/contact/form"), - route("postSiteOrder", "site", "POST", "/admin/site/order"), - route("postSiteOrderToggle", "site", "POST", "/admin/site/order/toggle"), -] as const; diff --git a/src/shared/admin-surface/routes/sms.ts b/src/shared/admin-surface/routes/sms.ts deleted file mode 100644 index 7aed928716..0000000000 --- a/src/shared/admin-surface/routes/sms.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getSms", "sms", "GET", "/admin/sms"), - route("postSms", "sms", "POST", "/admin/sms"), -] as const; diff --git a/src/shared/admin-surface/routes/support.ts b/src/shared/admin-surface/routes/support.ts deleted file mode 100644 index 7bca807dd1..0000000000 --- a/src/shared/admin-surface/routes/support.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { operation, route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getSupport", "support", "GET", "/admin/support"), - operation("postSupport", "support", "/admin/support"), -] as const; diff --git a/src/shared/admin-surface/routes/update.ts b/src/shared/admin-surface/routes/update.ts deleted file mode 100644 index ec389d0d0c..0000000000 --- a/src/shared/admin-surface/routes/update.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getUpdate", "update", "GET", "/admin/update"), - route("postUpdate", "update", "POST", "/admin/update"), - route("postUpdateCheck", "update", "POST", "/admin/update/check"), -] as const; diff --git a/src/shared/admin-surface/routes/users.ts b/src/shared/admin-surface/routes/users.ts deleted file mode 100644 index 0410991890..0000000000 --- a/src/shared/admin-surface/routes/users.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { route } from "#shared/admin-surface/definitions.ts"; - -export const routes = [ - route("getUsersByIdDelete", "users", "GET", "/admin/users/:id/delete"), - route("postUsersByIdDelete", "users", "POST", "/admin/users/:id/delete"), - route("getUserNew", "users", "GET", "/admin/user/new"), - route("getUsers", "users", "GET", "/admin/users"), - route("getUsersById", "users", "GET", "/admin/users/:id"), - route("getUsersByIdAgents", "users", "GET", "/admin/users/:id/agents"), - route("getUsersByIdByTab", "users", "GET", "/admin/users/:id/:tab"), - route("postUsers", "users", "POST", "/admin/users"), - route("postUsersByIdAgents", "users", "POST", "/admin/users/:id/agents"), -] as const; diff --git a/src/ui/templates/admin/attributes.tsx b/src/ui/templates/admin/attributes.tsx index 8ed047fa9e..15969172b3 100644 --- a/src/ui/templates/admin/attributes.tsx +++ b/src/ui/templates/admin/attributes.tsx @@ -7,7 +7,7 @@ import { attributeNameForm, attributeOptionForm, } from "#routes/admin/attributes.ts"; -import { type AdminRouteId, adminPath } from "#shared/admin-surface.ts"; +import { adminPath } from "#shared/admin-surface.ts"; import type { AttributeOption, AttributeWithOptions, @@ -22,7 +22,6 @@ import { IdCheckboxLabel, } from "#templates/components/aggregate-sections.tsx"; import { DataTable } from "#templates/components/data-table.tsx"; -import type { ReorderDirection } from "#templates/components/reorder.tsx"; import { itemsOrEmptyNote, reorderableListPage, @@ -39,11 +38,6 @@ import { WritableDangerLink, WritableOnly } from "./writable-only.tsx"; export const attributeNameFlat = (name: string): string => name.replace(/\r?\n/g, " / "); -const ATTRIBUTE_OPTION_MOVE_ROUTES = { - down: "postAttributesByIdOptionsByOptionIdMoveDown", - up: "postAttributesByIdOptionsByOptionIdMoveUp", -} satisfies Record; - export const adminAttributesPage = ( attributes: AttributeWithOptions[], session: AdminSession, @@ -167,10 +161,7 @@ export const adminAttributePage = ( label: (option) => option.text, labelHeader: t("attributes.option_column"), moveAction: (option) => (direction) => - adminPath(ATTRIBUTE_OPTION_MOVE_ROUTES[direction], { - id: attribute.id, - optionId: option.id, - }), + `/admin/attributes/${attribute.id}/options/${option.id}/move-${direction}`, orderLabel: t("attributes.order_column"), })} diff --git a/src/ui/templates/admin/questions.tsx b/src/ui/templates/admin/questions.tsx index 00cf7448de..b28fff12e9 100644 --- a/src/ui/templates/admin/questions.tsx +++ b/src/ui/templates/admin/questions.tsx @@ -6,7 +6,7 @@ import { map } from "#fp"; import { t } from "#i18n"; import { Raw } from "#jsx/jsx-runtime.ts"; import { answerTextForm, questionTextForm } from "#routes/admin/questions.ts"; -import { type AdminRouteId, adminPath } from "#shared/admin-surface.ts"; +import { adminPath } from "#shared/admin-surface.ts"; import type { Answer, QuestionWithAnswers } from "#shared/db/question-types.ts"; import type { AnswerAggregateField, @@ -38,7 +38,6 @@ import { LinkedItemsCheckboxes, toLinkedItemOptions, } from "#templates/components/linked-items.tsx"; -import type { ReorderDirection } from "#templates/components/reorder.tsx"; import { reorderableListPage, reorderCountTable, @@ -60,11 +59,6 @@ import { WritableDangerLink, WritableOnly } from "./writable-only.tsx"; export const questionTextFlat = (text: string): string => text.replace(/\r?\n/g, " / "); -const QUESTION_ANSWER_MOVE_ROUTES = { - down: "postQuestionsByIdAnswersByAnswerIdMoveDown", - up: "postQuestionsByIdAnswersByAnswerIdMoveUp", -} satisfies Record; - /** Listings cell for a question row: a count whose title attribute spells out * the assigned listing names (comma + space separated), or "All" when the * question is assigned to every listing. */ @@ -276,10 +270,7 @@ export const adminQuestionPage = ( label: (a) => a.text, labelHeader: t("questions.answer_column"), moveAction: (a) => (d) => - adminPath(QUESTION_ANSWER_MOVE_ROUTES[d], { - answerId: a.id, - id: question.id, - }), + `/admin/questions/${question.id}/answers/${a.id}/move-${d}`, orderLabel: t("questions.order_column"), })} diff --git a/src/ui/templates/admin/settings/boolean-settings-section.tsx b/src/ui/templates/admin/settings/boolean-settings-section.tsx deleted file mode 100644 index ee60aec16c..0000000000 --- a/src/ui/templates/admin/settings/boolean-settings-section.tsx +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Config-driven form for a one-yes/no-toggle boolean settings section. - * - * Several settings sections (show public API and the external-order library) - * differ only by action, title, description, field name, and the - * state selector that flips the boolean — every other line is the same - * `SettingsSection` + `YesNoRadios` scaffold. Declaring those four facts lets - * the shared scaffold own the markup so a new boolean toggle is one config - * plus an export, not another hand-authored component. - * - * Generic over the page state (`SettingsPageState` and - * `AdvancedSettingsPageState` are different types) so neither caller has to - * widen to `any`. - */ - -import { t } from "#i18n"; -import type { Child } from "#shared/jsx/jsx-runtime.ts"; -import { settingsSectionWith } from "#templates/components/settings-section.tsx"; -import { YesNoRadios } from "#templates/components/yes-no-radios.tsx"; - -export type BooleanSettingsSectionConfig = { - action: string; - title: string; - description: Child; - fieldName: string; - value: (state: TState) => boolean; - submitLabel?: string; -}; - -/** Build a config-driven boolean settings section renderer. */ -export const booleanSettingsSection = - (config: BooleanSettingsSectionConfig) => - (state: TState): JSX.Element => - settingsSectionWith( - { ...config, submitLabel: config.submitLabel ?? t("common.save") }, - , - ); diff --git a/test/features/app/read-only.test.ts b/test/features/app/read-only.test.ts index ceddc0497a..080c844029 100644 --- a/test/features/app/read-only.test.ts +++ b/test/features/app/read-only.test.ts @@ -27,11 +27,23 @@ describe("read-only request guard", () => { expect(readOnlyBlock("/admin/listings", "GET")).toBeNull(); }); - test("blocks admin writes unless the route schema allows them", () => { + test("allows only the admin operations that must work in read-only mode", () => { expect(readOnlyBlock("/admin", "POST")).toBe("page"); expect(readOnlyBlock("/admin/settings/email", "POST")).toBe("page"); - expect(readOnlyBlock("/admin/logout", "POST")).toBeNull(); - expect(readOnlyBlock("/admin/backup/create", "POST")).toBeNull(); + for (const path of [ + "/admin/attendees/99/refresh-payment", + "/admin/backup/create", + "/admin/debug/sentry", + "/admin/deliveries/mark", + "/admin/listing/42/scan", + "/admin/listing/42/attendee/99/checkin", + "/admin/login", + "/admin/logout", + "/admin/support", + ]) { + expect(readOnlyBlock(path, "POST"), path).toBeNull(); + } + expect(readOnlyBlock("/admin/logout", "PUT")).toBe("page"); }); test("allows public operations that must continue in read-only mode", () => { diff --git a/test/integration/routes/read-only.test.ts b/test/integration/routes/read-only.test.ts index 9115aa9b46..4c841e2edb 100644 --- a/test/integration/routes/read-only.test.ts +++ b/test/integration/routes/read-only.test.ts @@ -259,6 +259,7 @@ describeWithEnv( "/join/some-code", "/unsubscribe", "/contact", + "/admin/debug/sentry", "/admin/support", "/instance/site-credentials", "/admin/backup/create", diff --git a/test/lib/admin-route-manifest.test.ts b/test/lib/admin-route-manifest.test.ts index d539481140..d9c5a38410 100644 --- a/test/lib/admin-route-manifest.test.ts +++ b/test/lib/admin-route-manifest.test.ts @@ -1,14 +1,14 @@ /** * Proves the admin surface stays honest as areas and routes change: * - * - every schema route lives under `/admin` and a segment its area declares; + * - every lazy route lives under `/admin` and a segment its area declares; * - every declared segment serves at least one route (no stale entries); - * - every lazy module implements exactly its area's route IDs; - * - no two route IDs or method/path pairs collide. + * - no two method/path pairs collide; + * - every UI destination has a matching GET route. */ import { expect } from "@std/expect"; -import { describe, it as test } from "@std/testing/bdd"; +import { beforeAll, describe, it as test } from "@std/testing/bdd"; import { ADMIN_AREA_LOADERS } from "#routes/admin/area-loaders.ts"; import { adminPathSegment } from "#routes/admin/index.ts"; import { ADMIN_SURFACE } from "#shared/admin-surface.ts"; @@ -16,34 +16,50 @@ import { routePathPatternToRegex } from "#shared/route-pattern.ts"; import { describeWithEnv } from "#test-utils/db.ts"; import { awaitTestRequest } from "#test-utils/mocks.ts"; -const loadAreaHandlers = async (): Promise> => { - const handlersByArea = new Map(); +const loadAreaRoutes = async (): Promise> => { + const routesByArea = new Map(); for (const [name, area] of Object.entries(ADMIN_AREA_LOADERS)) { - handlersByArea.set(name, Object.keys(await area.load())); + routesByArea.set(name, Object.keys(await area.load())); } - return handlersByArea; + return routesByArea; +}; + +const routeParts = (route: string): readonly [method: string, path: string] => { + const boundary = route.indexOf(" "); + if (boundary === -1) throw new Error(`Invalid route: ${route}`); + return [route.slice(0, boundary), route.slice(boundary + 1)]; }; describe("admin route manifest", () => { + let routesByArea: Map; + + beforeAll(async () => { + routesByArea = await loadAreaRoutes(); + }); + test("every route falls under /admin and a segment its area declares", () => { - for (const route of ADMIN_SURFACE.routes) { - expect( - route.pattern === "/admin" || route.pattern.startsWith("/admin/"), - route.id, - ).toBe(true); - const segment = adminPathSegment(route.pattern); - expect( - ADMIN_SURFACE.areas[route.area], - `${route.id}: ${route.method} ${route.pattern}`, - ).toContain(segment); + for (const [area, routes] of routesByArea) { + for (const route of routes) { + const [method, path] = routeParts(route); + expect(["DELETE", "GET", "PATCH", "POST", "PUT"], route).toContain( + method, + ); + expect(path === "/admin" || path.startsWith("/admin/"), route).toBe( + true, + ); + expect( + ADMIN_SURFACE.areas[area as keyof typeof ADMIN_SURFACE.areas], + route, + ).toContain(adminPathSegment(path)); + } } }); test("every declared segment serves at least one of its area's routes", () => { for (const [name, segments] of Object.entries(ADMIN_SURFACE.areas)) { - const served = ADMIN_SURFACE.routes - .filter((route) => route.area === name) - .map((route) => adminPathSegment(route.pattern)); + const served = routesByArea + .get(name)! + .map((route) => adminPathSegment(routeParts(route)[1])); for (const segment of segments) { expect(served, `${name}: stale segment "${segment}"`).toContain( segment, @@ -52,34 +68,13 @@ describe("admin route manifest", () => { } }); - test("every lazy area implements exactly its declared route IDs", async () => { - for (const [name, handlerIds] of await loadAreaHandlers()) { - const routeIds = ADMIN_SURFACE.routes - .filter((route) => route.area === name) - .map((route) => route.id) - .sort(); - expect(handlerIds.sort(), name).toEqual(routeIds); - } - }); - - test("route IDs and method/path pairs are unique", () => { - const ids = new Set(); - const patterns = new Set(); - for (const route of ADMIN_SURFACE.routes) { - const methodPath = `${route.method} ${route.pattern}`; - expect(ids.has(route.id), route.id).toBe(false); - expect(patterns.has(methodPath), methodPath).toBe(false); - ids.add(route.id); - patterns.add(methodPath); - } - }); - - test("destination and server route IDs do not collide", () => { - const destinationIds = new Set( - ADMIN_SURFACE.destinations.map((destination) => destination.id), - ); - for (const route of ADMIN_SURFACE.routes) { - expect(destinationIds.has(route.id), route.id).toBe(false); + test("method/path pairs are unique", () => { + const routes = new Set(); + for (const areaRoutes of routesByArea.values()) { + for (const route of areaRoutes) { + expect(routes.has(route), route).toBe(false); + routes.add(route); + } } }); @@ -91,12 +86,12 @@ describe("admin route manifest", () => { ) .replace(/\/$/, ""); expect( - ADMIN_SURFACE.routes.some( - (route) => - route.area === destination.area && - route.method === "GET" && - routePathPatternToRegex(route.pattern).test(concretePath), - ), + routesByArea.get(destination.area)!.some((route) => { + const [method, path] = routeParts(route); + return ( + method === "GET" && routePathPatternToRegex(path).test(concretePath) + ); + }), `${destination.id}: GET ${destination.pattern}`, ).toBe(true); } diff --git a/test/shared/admin-surface.test.ts b/test/shared/admin-surface.test.ts index e8152b8655..39e3207cd5 100644 --- a/test/shared/admin-surface.test.ts +++ b/test/shared/admin-surface.test.ts @@ -26,15 +26,6 @@ describe("admin surface paths", () => { ); }); - test("fills parameters for server action routes", () => { - expect( - adminPath("postAttributesByIdOptionsByOptionIdMoveDown", { - id: 1, - optionId: 10, - }), - ).toBe("/admin/attributes/1/options/10/move-down"); - }); - test("allows write forms only for their audience while writable", () => { expect(adminDestinationAllowed("modifierEdit", "manager", false)).toBe( true, @@ -49,30 +40,6 @@ describe("admin surface paths", () => { expect(adminDestinationAllowed("modifiers", "manager", true)).toBe(true); }); - test("marks ordinary GET routes readable", () => { - expect( - ADMIN_SURFACE.routes.find((route) => route.id === "getSettings"), - ).toEqual({ - area: "settings", - id: "getSettings", - method: "GET", - pattern: "/admin/settings", - readOnly: "allow", - }); - }); - - test("marks ordinary mutations blocked", () => { - expect( - ADMIN_SURFACE.routes.find((route) => route.id === "postSettingsEmail"), - ).toEqual({ - area: "settings", - id: "postSettingsEmail", - method: "POST", - pattern: "/admin/settings/email", - readOnly: "block", - }); - }); - test("keeps the complete top-level section order", () => { expect(ADMIN_SURFACE.sections.map((section) => section.id)).toEqual([ "home", diff --git a/test/shared/admin-surface/definitions.test.ts b/test/shared/admin-surface/definitions.test.ts deleted file mode 100644 index 0e6e33adfd..0000000000 --- a/test/shared/admin-surface/definitions.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { expect } from "@std/expect"; -import { it as test } from "@std/testing/bdd"; -import { moveRoutes } from "#shared/admin-surface/definitions.ts"; - -test("builds literal move-down and move-up POST routes", () => { - const routes = moveRoutes( - "postQuestionsByIdAnswersByAnswerId", - "questions", - "/admin/questions/:id/answers/:answerId", - ); - const downId: "postQuestionsByIdAnswersByAnswerIdMoveDown" = routes[0].id; - const upPath: "/admin/questions/:id/answers/:answerId/move-up" = - routes[1].pattern; - - expect(downId).toBe("postQuestionsByIdAnswersByAnswerIdMoveDown"); - expect(upPath).toBe("/admin/questions/:id/answers/:answerId/move-up"); - expect(routes).toEqual([ - { - area: "questions", - id: "postQuestionsByIdAnswersByAnswerIdMoveDown", - method: "POST", - pattern: "/admin/questions/:id/answers/:answerId/move-down", - readOnly: "block", - }, - { - area: "questions", - id: "postQuestionsByIdAnswersByAnswerIdMoveUp", - method: "POST", - pattern: "/admin/questions/:id/answers/:answerId/move-up", - readOnly: "block", - }, - ]); -}); diff --git a/test/shared/admin-surface/routes/built-sites.test.ts b/test/shared/admin-surface/routes/built-sites.test.ts index 150d66fda4..26142ae18c 100644 --- a/test/shared/admin-surface/routes/built-sites.test.ts +++ b/test/shared/admin-surface/routes/built-sites.test.ts @@ -1,28 +1,30 @@ import { expect } from "@std/expect"; import { it as test } from "@std/testing/bdd"; -import { ADMIN_SURFACE } from "#shared/admin-surface.ts"; -import { jsonHash } from "#test-utils/hash.ts"; +import { ADMIN_AREA_LOADERS } from "#routes/admin/area-loaders.ts"; test("keeps the complete built-site route catalog exact", async () => { - const routes = ADMIN_SURFACE.routes.filter( - ({ area }) => area === "builtSites", - ); - - expect(await jsonHash(routes)).toBe( - "ad669c386d0694be463bbe43624a6d7bc0cd6364043d9b27e540f9a6db43a6a6", - ); + expect(Object.keys(await ADMIN_AREA_LOADERS.builtSites.load())).toEqual([ + "GET /admin/built-sites", + "GET /admin/built-sites/:id", + "GET /admin/built-sites/:id/:tab", + "GET /admin/built-sites/:id/delete", + "GET /admin/built-sites/new", + "POST /admin/built-sites", + "POST /admin/built-sites/:id/add-secrets", + "POST /admin/built-sites/:id/bump-deadline", + "POST /admin/built-sites/:id/delete", + "POST /admin/built-sites/:id/edit", + "POST /admin/built-sites/:id/override-deadline", + "POST /admin/built-sites/:id/provision-renewal", + "POST /admin/built-sites/:id/provision-scheduler", + "POST /admin/built-sites/:id/re-sync-deadline", + "POST /admin/built-sites/:id/rotate-renewal-token", + "POST /admin/built-sites/:id/update", + ]); }); -test("declares the scheduler provisioning action as a blocked POST route", () => { - expect( - ADMIN_SURFACE.routes.filter(({ id }) => id.includes("Scheduler")), - ).toEqual([ - { - area: "builtSites", - id: "postBuiltSitesByIdProvisionScheduler", - method: "POST", - pattern: "/admin/built-sites/:id/provision-scheduler", - readOnly: "block", - }, - ]); +test("routes the scheduler provisioning action as a POST", async () => { + expect(Object.keys(await ADMIN_AREA_LOADERS.builtSites.load())).toContain( + "POST /admin/built-sites/:id/provision-scheduler", + ); }); diff --git a/test/ui/templates/admin/settings/boolean-settings-section.test.tsx b/test/ui/templates/admin/settings/boolean-settings-section.test.tsx deleted file mode 100644 index e562e90795..0000000000 --- a/test/ui/templates/admin/settings/boolean-settings-section.test.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { expect } from "@std/expect"; -import { describe, it as test } from "@std/testing/bdd"; -import { t } from "#i18n"; -import { booleanSettingsSection } from "#templates/admin/settings/boolean-settings-section.tsx"; - -type TestState = { enabled: boolean }; - -const section = booleanSettingsSection({ - action: "/admin/settings/test-toggle", - description:

A test toggle.

, - fieldName: "test_enabled", - submitLabel: t("common.save"), - title: "Test toggle", - value: (s) => s.enabled, -}); - -describe("booleanSettingsSection", () => { - test("renders the SettingsSection shell with action, title, and description", () => { - const html = String(section({ enabled: false })); - expect(html).toContain('action="/admin/settings/test-toggle"'); - expect(html).toContain("

Test toggle

"); - expect(html).toContain("

A test toggle.

"); - expect(html).toContain('id="settings-test-toggle"'); - }); - - test("renders the save button under the radios", () => { - const html = String(section({ enabled: false })); - expect(html).toContain('name="test_enabled"'); - expect(html).toContain(t("common.save")); - }); - - test("checks the yes radio when the state is on and the no radio when off", () => { - const onHtml = String(section({ enabled: true })); - const offHtml = String(section({ enabled: false })); - const yesRadio = - ''; - const noRadio = - ''; - // Both render both options; exactly one carries checked, on the right side. - expect(onHtml).toContain(yesRadio); - expect(onHtml).not.toContain(noRadio); - expect(offHtml).toContain(noRadio); - expect(offHtml).not.toContain(yesRadio); - }); - - test("falls back to common.save when submitLabel is omitted", () => { - const noLabel = booleanSettingsSection({ - action: "/admin/settings/no-label", - description:

No label.

, - fieldName: "x", - title: "No label", - value: (s) => s.enabled, - }); - expect(String(noLabel({ enabled: false }))).toContain(t("common.save")); - }); -});