create-nextblock 0.16.4 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +22 -0
- package/docker-template/.env.docker.example +69 -69
- package/docker-template/docker/db/init/99-jwt.sql +6 -6
- package/docker-template/docker/db/init/99-roles.sql +25 -25
- package/docker-template/docker/kong/kong.yml +112 -112
- package/docker-template/scripts/docker-setup.mjs +310 -310
- package/package.json +1 -1
- package/templates/nextblock-template/.browserslistrc +11 -11
- package/templates/nextblock-template/.swcrc +30 -30
- package/templates/nextblock-template/CLAUDE.md +26 -0
- package/templates/nextblock-template/app/(auth-pages)/sign-up/page.tsx +46 -46
- package/templates/nextblock-template/app/(auth-pages)/two-factor/page.tsx +51 -51
- package/templates/nextblock-template/app/.well-known/ucp/route.ts +16 -16
- package/templates/nextblock-template/app/actions/consent.ts +57 -57
- package/templates/nextblock-template/app/actions/twoFactorEmail.ts +22 -22
- package/templates/nextblock-template/app/api/ai/cortex/build-widget/route.ts +153 -153
- package/templates/nextblock-template/app/api/ai/generate-blocks/route.ts +96 -96
- package/templates/nextblock-template/app/api/brand/email-logo/route.ts +48 -48
- package/templates/nextblock-template/app/api/checkout/freemius/sync/route.ts +29 -29
- package/templates/nextblock-template/app/api/cms/check-updates/route.ts +44 -44
- package/templates/nextblock-template/app/api/cms/full-backup/export/route.ts +33 -33
- package/templates/nextblock-template/app/api/cms/full-backup/restore/route.ts +63 -63
- package/templates/nextblock-template/app/api/cron/reset-sandbox/route.ts +272 -205
- package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +10473 -8467
- package/templates/nextblock-template/app/api/cron/sync-currencies/route.ts +39 -39
- package/templates/nextblock-template/app/api/custom-blocks/db-relations/route.ts +92 -92
- package/templates/nextblock-template/app/api/custom-blocks/editor-definitions/route.ts +43 -43
- package/templates/nextblock-template/app/api/media/library/route.ts +69 -69
- package/templates/nextblock-template/app/api/media/r2-presigned/route.ts +53 -53
- package/templates/nextblock-template/app/api/visual-editing/block-draft/route.ts +47 -47
- package/templates/nextblock-template/app/api/visual-editing/product-draft/route.ts +47 -47
- package/templates/nextblock-template/app/article/[slug]/PostClientContent.tsx +442 -441
- package/templates/nextblock-template/app/article/[slug]/page.utils.ts +4 -1
- package/templates/nextblock-template/app/checkout/UcpCartHydrator.tsx +20 -20
- package/templates/nextblock-template/app/cms/blocks/components/BlockEditorModal.tsx +241 -241
- package/templates/nextblock-template/app/cms/blocks/components/CustomBlockEditorPreview.tsx +160 -160
- package/templates/nextblock-template/app/cms/blocks/editors/DynamicCustomBlockEditor.tsx +167 -167
- package/templates/nextblock-template/app/cms/components/ConnectGitHubButton.tsx +122 -122
- package/templates/nextblock-template/app/cms/components/CortexAiActiveContext.tsx +23 -23
- package/templates/nextblock-template/app/cms/components/SeoScoreBadge.tsx +3 -3
- package/templates/nextblock-template/app/cms/components/SystemAlertsBanner.tsx +112 -112
- package/templates/nextblock-template/app/cms/components/TablePagination.tsx +136 -136
- package/templates/nextblock-template/app/cms/components/TwoFactorReminderBanner.tsx +45 -45
- package/templates/nextblock-template/app/cms/components/github-connect-actions.ts +102 -102
- package/templates/nextblock-template/app/cms/components/system-alerts-actions.ts +31 -31
- package/templates/nextblock-template/app/cms/coupons/[id]/edit/page.tsx +16 -16
- package/templates/nextblock-template/app/cms/coupons/page.tsx +16 -16
- package/templates/nextblock-template/app/cms/custom-blocks/[id]/edit/page.tsx +66 -66
- package/templates/nextblock-template/app/cms/custom-blocks/actions.ts +519 -519
- package/templates/nextblock-template/app/cms/custom-blocks/components/BlocksLibraryTransferControls.tsx +256 -256
- package/templates/nextblock-template/app/cms/custom-blocks/components/DBRelationSelect.tsx +384 -384
- package/templates/nextblock-template/app/cms/custom-blocks/components/ImageR2Picker.tsx +221 -221
- package/templates/nextblock-template/app/cms/custom-blocks/new/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/custom-blocks/page.tsx +438 -438
- package/templates/nextblock-template/app/cms/dashboard/components/DashboardComponents.tsx +200 -200
- package/templates/nextblock-template/app/cms/dashboard/components/DashboardOnboarding.tsx +130 -130
- package/templates/nextblock-template/app/cms/import-export/actions.ts +226 -226
- package/templates/nextblock-template/app/cms/pages/page.tsx +273 -273
- package/templates/nextblock-template/app/cms/posts/page.tsx +251 -251
- package/templates/nextblock-template/app/cms/products/categories/page.tsx +12 -12
- package/templates/nextblock-template/app/cms/products/new/page.tsx +135 -135
- package/templates/nextblock-template/app/cms/promotions/PromotionsWorkspace.tsx +456 -456
- package/templates/nextblock-template/app/cms/promotions/actions.ts +115 -115
- package/templates/nextblock-template/app/cms/promotions/page.tsx +31 -31
- package/templates/nextblock-template/app/cms/settings/backup-restore/BackupRestoreWorkspace.tsx +1004 -1004
- package/templates/nextblock-template/app/cms/settings/backup-restore/page.tsx +29 -29
- package/templates/nextblock-template/app/cms/settings/bot-protection/page.tsx +24 -24
- package/templates/nextblock-template/app/cms/settings/email/page.tsx +28 -28
- package/templates/nextblock-template/app/cms/settings/extra-translations/actions.ts +276 -276
- package/templates/nextblock-template/app/cms/settings/google-analytics/page.tsx +26 -26
- package/templates/nextblock-template/app/cms/settings/logos/components/DeleteLogoButton.tsx +21 -21
- package/templates/nextblock-template/app/cms/settings/logos/components/SetActiveLogoButton.tsx +42 -42
- package/templates/nextblock-template/app/cms/settings/logos/components/SiteSeoSettingsForm.tsx +133 -133
- package/templates/nextblock-template/app/cms/settings/privacy/page.tsx +27 -27
- package/templates/nextblock-template/app/cms/settings/registration/page.tsx +27 -27
- package/templates/nextblock-template/app/cms/settings/security/page.tsx +33 -33
- package/templates/nextblock-template/app/lib/site-settings.ts +105 -105
- package/templates/nextblock-template/app/lib/ucp/protocol.ts +190 -190
- package/templates/nextblock-template/app/lib/ucp/server.test.ts +56 -56
- package/templates/nextblock-template/app/setup/SetupWizard.tsx +678 -678
- package/templates/nextblock-template/app/setup/layout.tsx +13 -13
- package/templates/nextblock-template/app/setup/page.tsx +111 -111
- package/templates/nextblock-template/app/ucp/v1/carts/[id]/cancel/route.ts +38 -38
- package/templates/nextblock-template/app/ucp/v1/carts/[id]/route.ts +68 -68
- package/templates/nextblock-template/app/ucp/v1/carts/route.ts +35 -35
- package/templates/nextblock-template/app/ucp/v1/catalog/lookup/route.ts +35 -35
- package/templates/nextblock-template/app/ucp/v1/catalog/product/route.ts +35 -35
- package/templates/nextblock-template/app/ucp/v1/catalog/search/route.ts +34 -34
- package/templates/nextblock-template/components/CartTranslator.tsx +210 -210
- package/templates/nextblock-template/components/DeferredCartTranslator.tsx +51 -51
- package/templates/nextblock-template/components/DeferredGlobalSearch.tsx +68 -68
- package/templates/nextblock-template/components/DeferredGoogleAnalytics.tsx +70 -70
- package/templates/nextblock-template/components/FeatureImageHero.tsx +47 -47
- package/templates/nextblock-template/components/GlobalSearch.tsx +557 -557
- package/templates/nextblock-template/components/Header.tsx +38 -38
- package/templates/nextblock-template/components/PublicEnvBootstrap.tsx +30 -30
- package/templates/nextblock-template/components/ResponsiveNav.tsx +14 -14
- package/templates/nextblock-template/components/auth/AuthBotProtection.tsx +182 -182
- package/templates/nextblock-template/components/blocks/PostsGridClient.tsx +48 -48
- package/templates/nextblock-template/components/blocks/TestimonialBlock.tsx +9 -9
- package/templates/nextblock-template/components/blocks/publicRendererLoaders.ts +25 -25
- package/templates/nextblock-template/components/blocks/renderers/ButtonBlockRenderer.tsx +92 -92
- package/templates/nextblock-template/components/blocks/renderers/ClientTextBlockRenderer.tsx +11 -0
- package/templates/nextblock-template/components/blocks/renderers/PostsGridBlockRenderer.tsx +24 -24
- package/templates/nextblock-template/components/blocks/renderers/TestimonialBlockRenderer.tsx +57 -57
- package/templates/nextblock-template/components/blocks/renderers/inline/AlertWidgetRenderer.tsx +2 -2
- package/templates/nextblock-template/components/blocks/renderers/inline/CtaWidgetRenderer.tsx +2 -2
- package/templates/nextblock-template/components/media/YouTubeFacade.tsx +105 -105
- package/templates/nextblock-template/components/media/youtube-embed-replace.tsx +32 -32
- package/templates/nextblock-template/components/privacy/ConsentBanner.tsx +170 -170
- package/templates/nextblock-template/components/privacy/ConsentGatedAnalytics.tsx +70 -70
- package/templates/nextblock-template/components/renderers/CachedDynamicLayoutEngine.tsx +28 -28
- package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.test.tsx +166 -166
- package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.tsx +471 -471
- package/templates/nextblock-template/components/visual-editing/DeferredVisualEditing.tsx +21 -21
- package/templates/nextblock-template/context/language-rest-client.ts +32 -32
- package/templates/nextblock-template/docker/db/init/99-jwt.sql +6 -6
- package/templates/nextblock-template/docker/db/init/99-roles.sql +25 -25
- package/templates/nextblock-template/docker/kong/kong.yml +112 -112
- package/templates/nextblock-template/docs/02-ECOMMERCE-CAPABILITIES.md +364 -364
- package/templates/nextblock-template/docs/03-CMS-AND-EDITOR.md +1 -1
- package/templates/nextblock-template/docs/04-DATABASE-AND-AUTH.md +402 -282
- package/templates/nextblock-template/docs/05-DEVELOPER-GUIDE.md +15 -8
- package/templates/nextblock-template/docs/07-BLOCK-SDK-AND-EXTENSIBILITY.md +146 -146
- package/templates/nextblock-template/docs/08-NEXTBLOCK-CORTEX-AI-ARCHITECTURE.md +23 -4
- package/templates/nextblock-template/docs/10-CUSTOM-BLOCKS.md +222 -222
- package/templates/nextblock-template/docs/11-SELF-HOSTED-DOCKER.md +4 -1
- package/templates/nextblock-template/docs/13-STAYING-UP-TO-DATE.md +8 -0
- package/templates/nextblock-template/docs/14-MESSAGES-INBOX.md +2 -2
- package/templates/nextblock-template/docs/TECHNICAL_SPECIFICATION.md +95 -104
- package/templates/nextblock-template/lib/app-secrets.ts +39 -39
- package/templates/nextblock-template/lib/auth/cookies.ts +47 -47
- package/templates/nextblock-template/lib/auth/crypto.ts +45 -45
- package/templates/nextblock-template/lib/auth/trustedDevices.ts +92 -92
- package/templates/nextblock-template/lib/blocks/README.md +13 -13
- package/templates/nextblock-template/lib/botProtection/verify.ts +134 -134
- package/templates/nextblock-template/lib/cms/payments-reminder.test.ts +135 -135
- package/templates/nextblock-template/lib/cms/payments-reminder.ts +105 -105
- package/templates/nextblock-template/lib/cms-transfer/types.ts +145 -145
- package/templates/nextblock-template/lib/custom-block-definitions.ts +87 -87
- package/templates/nextblock-template/lib/custom-block-r2-upload-shared.ts +178 -178
- package/templates/nextblock-template/lib/custom-block-r2-upload.test.ts +140 -140
- package/templates/nextblock-template/lib/custom-block-r2-upload.ts +88 -88
- package/templates/nextblock-template/lib/custom-block-relations.test.ts +227 -227
- package/templates/nextblock-template/lib/custom-block-relations.ts +279 -279
- package/templates/nextblock-template/lib/custom-block-safelist.ts +14 -14
- package/templates/nextblock-template/lib/editor/dynamic-extension-core.test.ts +172 -172
- package/templates/nextblock-template/lib/editor/dynamic-extension-core.ts +213 -213
- package/templates/nextblock-template/lib/editor/dynamic-extension-loader.ts +22 -22
- package/templates/nextblock-template/lib/editor/dynamic-extensions.tsx +193 -193
- package/templates/nextblock-template/lib/email/branding-format.test.ts +133 -133
- package/templates/nextblock-template/lib/email/branding-format.ts +123 -123
- package/templates/nextblock-template/lib/email/branding.ts +76 -76
- package/templates/nextblock-template/lib/full-backup/manifest.test.ts +121 -121
- package/templates/nextblock-template/lib/full-backup/manifest.ts +206 -206
- package/templates/nextblock-template/lib/logos/active-logo.ts +53 -53
- package/templates/nextblock-template/lib/media/resolveMediaUrl.ts +56 -54
- package/templates/nextblock-template/lib/media/youtube.ts +99 -99
- package/templates/nextblock-template/lib/onboarding/actions.ts +31 -31
- package/templates/nextblock-template/lib/privacy/consent-client.ts +57 -57
- package/templates/nextblock-template/lib/privacy/contact-emails.ts +64 -64
- package/templates/nextblock-template/lib/privacy/settings.ts +115 -115
- package/templates/nextblock-template/lib/privacy/types.ts +69 -69
- package/templates/nextblock-template/lib/promotions/server.test.ts +74 -74
- package/templates/nextblock-template/lib/promotions/server.ts +741 -741
- package/templates/nextblock-template/lib/resolve-block-relations.test.ts +142 -142
- package/templates/nextblock-template/lib/resolve-block-relations.ts +255 -255
- package/templates/nextblock-template/lib/seo/page-document.ts +4 -4
- package/templates/nextblock-template/lib/seo/redirect-store.ts +3 -2
- package/templates/nextblock-template/lib/setup/actions.ts +460 -460
- package/templates/nextblock-template/lib/setup/env-status.ts +125 -125
- package/templates/nextblock-template/lib/setup/env-write.ts +111 -111
- package/templates/nextblock-template/lib/setup/migrations-bundle.ts +15 -175
- package/templates/nextblock-template/lib/setup/provisioning.ts +59 -59
- package/templates/nextblock-template/lib/setup/schema-apply.ts +408 -408
- package/templates/nextblock-template/lib/setup/system-config.ts +105 -105
- package/templates/nextblock-template/lib/setup/types.ts +18 -18
- package/templates/nextblock-template/lib/storage/provider.ts +66 -66
- package/templates/nextblock-template/lib/storage/supabase-storage.ts +103 -103
- package/templates/nextblock-template/lib/updates/github-device.ts +206 -206
- package/templates/nextblock-template/lib/updates/repo-identity.ts +56 -56
- package/templates/nextblock-template/lib/visual-editing/draft-content.test.ts +105 -105
- package/templates/nextblock-template/lib/visual-editing/draft-route.test.ts +42 -42
- package/templates/nextblock-template/lib/visual-editing/edit-info.test.ts +143 -143
- package/templates/nextblock-template/lib/visual-editing/edit-info.ts +94 -94
- package/templates/nextblock-template/lib/visual-editing/product-drafts.test.ts +81 -81
- package/templates/nextblock-template/lib/zod-config.ts +5 -5
- package/templates/nextblock-template/next-env.d.ts +1 -0
- package/templates/nextblock-template/package.json +1 -1
- package/templates/nextblock-template/public/images/cortex_post.webp +0 -0
- package/templates/nextblock-template/public/images/update_nextblock.webp +0 -0
- package/templates/nextblock-template/scripts/docker-setup.mjs +310 -310
- package/templates/nextblock-template/scripts/validate-editor-block-schema.ts +112 -112
- package/templates/nextblock-template/scripts/verify-cortex-ai-build-widget.tsx +98 -98
- package/templates/nextblock-template/scripts/verify-cortex-ai-generate-blocks.ts +62 -62
- package/templates/nextblock-template/scripts/verify-cortex-ai-global-tools.ts +537 -537
- package/templates/nextblock-template/scripts/verify-cortex-ai-routing.ts +58 -58
- package/templates/nextblock-template/scripts/verify-custom-block-definitions.ts +188 -188
- package/templates/nextblock-template/scripts/verify-dynamic-custom-block-extensions.ts +123 -123
- package/templates/nextblock-template/scripts/verify-dynamic-layout-engine.tsx +133 -133
- package/templates/nextblock-template/scripts/verify-milestone-2-custom-blocks.ts +65 -65
- package/templates/nextblock-template/tools/deploy-supabase.js +159 -159
- package/templates/nextblock-template/types/jsdom.d.ts +6 -6
|
@@ -27,7 +27,7 @@ The core problem statement — "stop cloning heavy repos; start with our CLI and
|
|
|
27
27
|
|
|
28
28
|
### 1.1.3 Key Stakeholders and Users
|
|
29
29
|
|
|
30
|
-
The system recognizes three database-enforced user roles (defined as the `user_role` enum `ADMIN`, `WRITER`, `USER` in migration `
|
|
30
|
+
The system recognizes three database-enforced user roles (defined as the `user_role` enum `ADMIN`, `WRITER`, `USER` in migration `02001_baseline_schema.sql`) and three additional implicit stakeholder classes observable from the scaffolding and documentation artifacts.
|
|
31
31
|
|
|
32
32
|
| Stakeholder | Role / Audience | Primary Entry Point |
|
|
33
33
|
|:--|:--|:--|
|
|
@@ -267,7 +267,7 @@ The following architectural invariants are enforced at workspace level and must
|
|
|
267
267
|
|
|
268
268
|
**Editor Capabilities** — The `@nextblock-cms/editor` library exports `Editor`, `NotionEditor`, `EditorToolbar`, `EditorBubbleMenu`, `EditorFloatingMenu`, `EnhancedFloatingMenu`, `SlashCommandList`, `DragHandle`, `HtmlContent`, and `editorExtensions`. Feature set includes Tiptap StarterKit rich text, syntax-highlighted code blocks, tables, task lists, slash commands, drag handles, image handling, character counting, typography, mathematics, emoji, mentions, inline alert and call-to-action widgets, and custom HTML-preserving extensions for `div`, `style`, `script`, `svg`, `span`, and catch-all attribute preservation. A media-picker bridge is exposed via `setOpenImagePicker()`.
|
|
269
269
|
|
|
270
|
-
**Translation & Localization** — The set of served locales is the active rows of the `languages` table (managed at `/cms/settings/languages`; the proxy reads them with a 60-second in-memory cache and only falls back to the hardcoded `FALLBACK_LOCALES` `en`/`fr` when the DB is unreadable), backed by `languages` and `translations` tables from migration `
|
|
270
|
+
**Translation & Localization** — The set of served locales is the active rows of the `languages` table (managed at `/cms/settings/languages`; the proxy reads them with a 60-second in-memory cache and only falls back to the hardcoded `FALLBACK_LOCALES` `en`/`fr` when the DB is unreadable), backed by `languages` and `translations` tables from migration `02001_baseline_schema.sql`. First-visit language detection is admin-configurable (see F-007): browser `Accept-Language`, IP-country via host geo headers, combined, or always-default — implemented in `apps/nextblock/lib/i18n/detection.ts` and stored in `site_settings.language_detection_settings`. Content revision history is stored as snapshot + JSON Patch diff (enum `revision_type: snapshot, diff`) per migration `02001_baseline_schema.sql`.
|
|
271
271
|
|
|
272
272
|
**Page Lifecycle** — Pages move through `draft`, `published`, and `archived` statuses (enum `page_status`).
|
|
273
273
|
|
|
@@ -343,7 +343,7 @@ Profile completion is enforced via a redirect: users without a `full_name` value
|
|
|
343
343
|
|
|
344
344
|
#### 1.3.2.3 Geographic and Market Coverage
|
|
345
345
|
|
|
346
|
-
Shipping resolution operates at country and state granularity. Multi-currency pricing is supported with a configurable default currency seeded in migration `
|
|
346
|
+
Shipping resolution operates at country and state granularity. Multi-currency pricing is supported with a configurable default currency seeded in migration `02001_baseline_schema.sql`. The database schema includes a `shipping_zone_locations.postal_code` column for future finer-grained geographic resolution, although the current runtime resolver does not yet consume it.
|
|
347
347
|
|
|
348
348
|
#### 1.3.2.4 Data Domains Included
|
|
349
349
|
|
|
@@ -446,11 +446,14 @@ The system does not natively integrate with: alternative payment processors beyo
|
|
|
446
446
|
|
|
447
447
|
### 1.4.5 Database Migrations
|
|
448
448
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
- `libs/db/src/supabase/migrations/
|
|
452
|
-
- `libs/db/src/supabase/migrations/
|
|
453
|
-
-
|
|
449
|
+
One squash generation at a time (`GGNNN_name.sql`; see §6.2.3.1 and docs/04):
|
|
450
|
+
|
|
451
|
+
- `libs/db/src/supabase/migrations/02000_catchup_gen1.sql` — Version-aware replay of the retired generation-1 forward migrations
|
|
452
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — Enums (`user_role`, `page_status`, `menu_location`, `revision_type`, …), functions, every table and sequence
|
|
453
|
+
- `libs/db/src/supabase/migrations/02002_baseline_constraints_and_indexes.sql` — Constraints and indexes
|
|
454
|
+
- `libs/db/src/supabase/migrations/02003_baseline_security_and_grants.sql` — RLS, policies, triggers, grants
|
|
455
|
+
- `libs/db/src/supabase/migrations/02004_baseline_seed.sql` — Default settings (footer copyright, payment providers, inventory, invoice settings), languages, translations, demo content
|
|
456
|
+
- `02005` onward — forward migrations appended since the generation-2 squash
|
|
454
457
|
|
|
455
458
|
### 1.4.6 Agent Skill Documents
|
|
456
459
|
|
|
@@ -623,7 +626,7 @@ Served locales are the active rows of the `languages` table, managed at `/cms/se
|
|
|
623
626
|
|
|
624
627
|
Locale propagation uses the `NEXT_USER_LOCALE` cookie and the `X-User-Locale` request header, both set by the request proxy. When `rememberVisitorChoice` is `true` (default) the cookie persists for one year (`maxAge: 31_536_000` seconds); when `false` it is a session cookie, so detection re-runs each new browser session (the client `LanguageProvider` mirrors the same expiry for manual switcher choices). A valid cookie always beats detection. The client-side provider chain `LanguageProvider → TranslationsProvider` in `apps/nextblock/app/providers.tsx` bridges server-resolved locale into React context.
|
|
625
628
|
|
|
626
|
-
**Business Value:** Unlocks multi-market deployments without requiring adopters to integrate a separate i18n library. **User Benefits:** Language switching persists across sessions; same content IDs preserve relationships across translations. **Technical Context:** Implemented at schema level (migration `
|
|
629
|
+
**Business Value:** Unlocks multi-market deployments without requiring adopters to integrate a separate i18n library. **User Benefits:** Language switching persists across sessions; same content IDs preserve relationships across translations. **Technical Context:** Implemented at schema level (migration `02001_baseline_schema.sql`), proxy level, and provider level.
|
|
627
630
|
|
|
628
631
|
**Dependencies**
|
|
629
632
|
|
|
@@ -648,7 +651,7 @@ Locale propagation uses the `NEXT_USER_LOCALE` cookie and the `X-User-Locale` re
|
|
|
648
651
|
|
|
649
652
|
**Description**
|
|
650
653
|
|
|
651
|
-
Revisions are implemented in the `page_revisions` and `post_revisions` tables defined in migration `
|
|
654
|
+
Revisions are implemented in the `page_revisions` and `post_revisions` tables defined in migration `02001_baseline_schema.sql`, using a hybrid snapshot/diff model. The `revision_type` enum (`snapshot`, `diff`) distinguishes between full snapshots and JSON Patch diffs (generated via `fast-json-patch`). A UNIQUE constraint on `(page_id, version)` ensures monotonically increasing snapshot versions. The CMS surface resides under `apps/nextblock/app/cms/revisions/`.
|
|
652
655
|
|
|
653
656
|
**Business Value:** Provides audit trail and rollback capability for authored content. **User Benefits:** Authors can restore prior states; accidental deletions are recoverable. **Technical Context:** Diff generation reduces storage overhead for frequently-updated content.
|
|
654
657
|
|
|
@@ -675,7 +678,7 @@ Revisions are implemented in the `page_revisions` and `post_revisions` tables de
|
|
|
675
678
|
|
|
676
679
|
**Description**
|
|
677
680
|
|
|
678
|
-
Three menu locations are supported: `HEADER`, `FOOTER`, and `SIDEBAR`, encoded by the `menu_location` enum. The `navigation_items` table (migration `
|
|
681
|
+
Three menu locations are supported: `HEADER`, `FOOTER`, and `SIDEBAR`, encoded by the `menu_location` enum. The `navigation_items` table (migration `02001_baseline_schema.sql`) supports hierarchical menus through a `parent_id` self-reference, explicit `order` for sibling sorting, translation group affiliation, and optional page references. Administrative CRUD operations reside at `apps/nextblock/app/cms/navigation/`.
|
|
679
682
|
|
|
680
683
|
**Dependencies**
|
|
681
684
|
|
|
@@ -729,7 +732,7 @@ Authentication is layered over Supabase Auth using `@supabase/ssr` and `@supabas
|
|
|
729
732
|
|
|
730
733
|
**Description**
|
|
731
734
|
|
|
732
|
-
RBAC is enforced through a three-valued `user_role` enum (`ADMIN`, `WRITER`, `USER`) defined in migration `
|
|
735
|
+
RBAC is enforced through a three-valued `user_role` enum (`ADMIN`, `WRITER`, `USER`) defined in migration `02001_baseline_schema.sql`. Route-level enforcement is implemented in `apps/nextblock/proxy.ts` (lines 12–17): `/cms` requires `WRITER` or `ADMIN`; `/cms/admin`, `/cms/users`, and `/cms/settings` require `ADMIN` exclusively. A foundational authorization rule encoded in the `on_auth_user_created` trigger elevates the first registered user to `ADMIN` and assigns all subsequent users the `USER` role, guaranteeing each deployment has exactly one guaranteed administrator at bootstrap. Users without a `full_name` value are redirected to `/profile` before being permitted to access other authenticated surfaces. Database-layer enforcement is provided via Row-Level Security with helper functions `get_current_user_role()` and `is_admin()` (both `SECURITY DEFINER`).
|
|
733
736
|
|
|
734
737
|
**Dependencies**
|
|
735
738
|
|
|
@@ -833,7 +836,7 @@ All features in this section are gated by F-022 (Package Activation) via `verify
|
|
|
833
836
|
|
|
834
837
|
**Description**
|
|
835
838
|
|
|
836
|
-
Products are modeled in migration `
|
|
839
|
+
Products are modeled in migration `02001_baseline_schema.sql` with fields including `sku`, `slug`, `title`, `type` (`physical` | `digital`), `payment_provider` (`stripe` | `freemius`), `price` as an integer in the smallest currency unit, a multi-currency `prices` JSONB column, `stock`, `status` (`draft` | `active` | `archived`), descriptions, Freemius-specific fields, UPC, and an `is_taxable` flag. A CHECK constraint named `products_type_provider_consistency_check` enforces that `physical` products use `stripe` and `digital` products use `freemius`. Related tables `product_media`, `product_attributes`, `product_attribute_terms`, `product_variants`, and `variant_attribute_mapping` model assets and variations. CMS surfaces at `apps/nextblock/app/cms/products/` provide list, create, edit, media, attribute, and variation management flows.
|
|
837
840
|
|
|
838
841
|
**Dependencies**
|
|
839
842
|
|
|
@@ -858,7 +861,7 @@ Products are modeled in migration `00000000000003_setup_catalog_and_licensing.sq
|
|
|
858
861
|
|
|
859
862
|
**Description**
|
|
860
863
|
|
|
861
|
-
The `inventory_items` table (migration `
|
|
864
|
+
The `inventory_items` table (migration `02001_baseline_schema.sql`) uses SKU as key and enforces `quantity >= 0` via CHECK constraint. Tracking is globally controlled by the `trackQuantities` setting in `ecommerce_inventory_settings`. Inventory deduction during order finalization follows a resilient pattern: the runtime first calls the `apply_order_inventory_deduction()` Postgres RPC, and if that path is unavailable, falls back to direct SQL via the `POSTGRES_URL` or `DATABASE_URL` connection, as coordinated by `libs/ecommerce/src/lib/shared-inventory.ts` and `libs/ecommerce/src/lib/order-inventory.ts`.
|
|
862
865
|
|
|
863
866
|
**Dependencies**
|
|
864
867
|
|
|
@@ -958,7 +961,7 @@ Freemius integration lives in `libs/ecommerce/src/lib/providers/` and supports d
|
|
|
958
961
|
|
|
959
962
|
**Description**
|
|
960
963
|
|
|
961
|
-
The `currencies` table (migration `
|
|
964
|
+
The `currencies` table (migration `02001_baseline_schema.sql`) stores `exchange_rate` as a `numeric(20,10)`, a single-row `is_default` constraint, `rounding_mode` (`none`, `nearest`, `up`, `down`, `charm`), `rounding_increment`, `rounding_charm_amount`, and flags `auto_update_exchange_rate` and `auto_sync_product_prices`. The default currency must satisfy `exchange_rate = 1`, `auto_update_exchange_rate = false`, and `auto_sync_product_prices = false`. FX rates are pulled from `https://api.frankfurter.dev` (overridable via `FX_API_BASE_URL`). Operations `syncStoreCurrencyRates()` and `rebaseStoreCurrencyExchangeRates()` are exposed by `libs/ecommerce/src/lib/currency-sync.ts`. A daily cron invokes `/api/cron/sync-currencies` at 18:00 UTC (`vercel.json`). Seed migration `02001_baseline_schema.sql` provides USD as the default.
|
|
962
965
|
|
|
963
966
|
**Dependencies**
|
|
964
967
|
|
|
@@ -1008,7 +1011,7 @@ Three shipping tables model geographic and rate configurations: `shipping_zones`
|
|
|
1008
1011
|
|
|
1009
1012
|
**Description**
|
|
1010
1013
|
|
|
1011
|
-
The `tax_rates` table (migration `
|
|
1014
|
+
The `tax_rates` table (migration `02001_baseline_schema.sql`) supports two modes: `manual` (keyed on country and state, supporting stacked rates such as GST + PST) and `automatic` (delegated to Stripe Tax via tax codes on line items). Mode selection is controlled by the `enableTaxes` and `taxCalculationMode` properties in `ecommerce_inventory_settings`. Schema constraints require `tax_rate` between 0 and 100 and enforce a uniqueness constraint on `(country_code, state_code, lower(tax_name))`. Implementation lives in `libs/ecommerce/src/lib/tax-calculation.ts` and `libs/ecommerce/src/lib/order-tax-details.ts`.
|
|
1012
1015
|
|
|
1013
1016
|
**Dependencies**
|
|
1014
1017
|
|
|
@@ -1033,7 +1036,7 @@ The `tax_rates` table (migration `00000000000004_setup_fulfillment_shipping_taxe
|
|
|
1033
1036
|
|
|
1034
1037
|
**Description**
|
|
1035
1038
|
|
|
1036
|
-
Orders transition through five statuses — `pending`, `paid`, `shipped`, `cancelled`, `refunded` — and store totals (`total`, `subtotal`, `shipping_total`, `tax_total`), a JSONB `tax_details` breakdown, `exchange_rate_at_purchase` as `numeric(20,10)`, `invoice_number`, `paid_at`, and `inventory_deducted_at`. Stable invoice numbering is produced by the `order_invoice_number_seq` sequence. The `invoice_settings` site-setting (seeded by `
|
|
1039
|
+
Orders transition through five statuses — `pending`, `paid`, `shipped`, `cancelled`, `refunded` — and store totals (`total`, `subtotal`, `shipping_total`, `tax_total`), a JSONB `tax_details` breakdown, `exchange_rate_at_purchase` as `numeric(20,10)`, `invoice_number`, `paid_at`, and `inventory_deducted_at`. Stable invoice numbering is produced by the `order_invoice_number_seq` sequence. The `invoice_settings` site-setting (seeded by `02001_baseline_schema.sql`) stores business name, email, address, and tax registrations. UI components `InvoiceDocument` and `InvoiceViewerShell` in `libs/ecommerce/src/lib/` render invoices. Customer order history is surfaced via `libs/ecommerce/src/lib/customer-orders.ts`, with admin management at `apps/nextblock/app/cms/orders/` and customer self-service at `apps/nextblock/app/profile/orders/`.
|
|
1037
1040
|
|
|
1038
1041
|
**Dependencies**
|
|
1039
1042
|
|
|
@@ -1058,7 +1061,7 @@ Orders transition through five statuses — `pending`, `paid`, `shipped`, `cance
|
|
|
1058
1061
|
|
|
1059
1062
|
**Description**
|
|
1060
1063
|
|
|
1061
|
-
The `package_activations` table (migration `
|
|
1064
|
+
The `package_activations` table (migration `02001_baseline_schema.sql`) contains `license_key`, `instance_name`, `package_id`, `status` (defaulting to `active`), `meta`, `last_validated_at`, and a UNIQUE constraint on `(license_key, package_id)`. The helper `verifyPackageOnline(packageId, customClient?)` in `libs/db/src/lib/package-validation.ts` returns a boolean based on `status === 'active'` and uses `unstable_cache` with a 60-second revalidation window. This function is invoked from four surfaces: the CMS commerce navigation visibility check, the checkout API gate at `apps/nextblock/app/api/checkout/route.ts` line 36, premium route wrappers injected during scaffold activation, and the CLI's module activation flows.
|
|
1062
1065
|
|
|
1063
1066
|
**Dependencies**
|
|
1064
1067
|
|
|
@@ -1679,7 +1682,7 @@ The following matrix links features to the sections of the technical specificati
|
|
|
1679
1682
|
| Package alignment | `@nextblock-cms/ecom` package name vs. `@nextblock-cms/ecommerce` alias (per §1.3.3.1) requires coordination when republished | F-013–F-022 |
|
|
1680
1683
|
| Locale expansion | Locales are DB-driven: add an active `languages` row (plus translations/content); `FALLBACK_LOCALES` in `proxy.ts` is only a DB-unreachable safety net | F-007 |
|
|
1681
1684
|
| Block registry updates | New block types must satisfy F-024 contract and register in `blockRegistry.ts` | F-004, F-024 |
|
|
1682
|
-
| RLS policy review | Migration `
|
|
1685
|
+
| RLS policy review | Migration `02001_baseline_schema.sql` should be audited on new table introduction | All DB-backed features |
|
|
1683
1686
|
|
|
1684
1687
|
### 2.4.6 Assumptions and Constraints Summary
|
|
1685
1688
|
|
|
@@ -1726,14 +1729,14 @@ The following matrix links features to the sections of the technical specificati
|
|
|
1726
1729
|
- `apps/create-nextblock/bin/create-nextblock.js` — CLI `create` and `activate` commands (F-023)
|
|
1727
1730
|
- `libs/db/src/lib/package-validation.ts` — `verifyPackageOnline()` with 60 s cache (F-022)
|
|
1728
1731
|
- `libs/db/src/lib/media-actions.ts` — Role-gated media recording (F-006)
|
|
1729
|
-
- `libs/db/src/supabase/migrations/
|
|
1730
|
-
- `libs/db/src/supabase/migrations/
|
|
1731
|
-
- `libs/db/src/supabase/migrations/
|
|
1732
|
-
- `libs/db/src/supabase/migrations/
|
|
1733
|
-
- `libs/db/src/supabase/migrations/
|
|
1734
|
-
- `libs/db/src/supabase/migrations/
|
|
1735
|
-
- `libs/db/src/supabase/migrations/
|
|
1736
|
-
- `libs/db/src/supabase/migrations/
|
|
1732
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — `user_role`, `page_status`, `menu_location`, `revision_type` enums
|
|
1733
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — languages, translations, media, profiles (F-002, F-006, F-007)
|
|
1734
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — pages, posts, blocks, navigation, revisions (F-004, F-008, F-009)
|
|
1735
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — products, variants, attributes, inventory, package_activations (F-013, F-014, F-022)
|
|
1736
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — orders, shipping zones, tax rates, currencies (F-018–F-021)
|
|
1737
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — `handle_new_user()`, `on_auth_user_created` (F-002, F-003)
|
|
1738
|
+
- `libs/db/src/supabase/migrations/02003_baseline_security_and_grants.sql` — RLS policies and helper functions
|
|
1739
|
+
- `libs/db/src/supabase/migrations/02004_baseline_seed.sql` — Default settings, USD currency, `en`/`fr` languages
|
|
1737
1740
|
- `libs/db/src/supabase/templates/` — Six Supabase Auth email templates (F-002)
|
|
1738
1741
|
- `libs/editor/README.md` — Public editor surface exports (F-005)
|
|
1739
1742
|
- `libs/editor/ADVANCED_FEATURES.md` — Enhanced floating menu, placeholder behaviors (F-005)
|
|
@@ -2222,14 +2225,14 @@ Supabase PostgreSQL is the authoritative data store for all structured data in t
|
|
|
2222
2225
|
|
|
2223
2226
|
| Migration File | Purpose |
|
|
2224
2227
|
|:--|:--|
|
|
2225
|
-
| `
|
|
2226
|
-
| `
|
|
2227
|
-
| `
|
|
2228
|
-
| `
|
|
2229
|
-
| `
|
|
2228
|
+
| `02001_baseline_schema.sql` | Enums: `user_role`, `page_status`, `menu_location`, `revision_type` |
|
|
2229
|
+
| `02001_baseline_schema.sql` | `languages`, `translations`, `media`, `profiles`, `settings`, `logos` |
|
|
2230
|
+
| `02001_baseline_schema.sql` | `pages`, `posts`, `blocks`, `navigation_items`, `page_revisions`, `post_revisions` |
|
|
2231
|
+
| `02001_baseline_schema.sql` | `products`, `product_variants`, `inventory_items`, `package_activations` |
|
|
2232
|
+
| `02001_baseline_schema.sql` | `currencies`, `tax_rates`, shipping tables |
|
|
2230
2233
|
| `00000000000005` through `00000000000007` | Commerce extensions |
|
|
2231
|
-
| `
|
|
2232
|
-
| `
|
|
2234
|
+
| `02001_baseline_schema.sql` | Row-Level Security policies |
|
|
2235
|
+
| `02001_baseline_schema.sql` | Default settings, USD currency, English language |
|
|
2233
2236
|
| `00000000000009` — `00000000000010` | Additional platform seeds |
|
|
2234
2237
|
| `00000000000011` through `00000000000016` | Cortex AI settings, coupons, audit, drafts, and page feature images |
|
|
2235
2238
|
| `00000000000017` through `00000000000024` | Product blocks, bot-protection settings, product categories + translations, hero→section migration, Cortex AI guide seed, custom block definitions, and cart sessions |
|
|
@@ -4002,14 +4005,14 @@ flowchart LR
|
|
|
4002
4005
|
|
|
4003
4006
|
#### Database Migrations Referenced
|
|
4004
4007
|
|
|
4005
|
-
- `
|
|
4006
|
-
- `
|
|
4007
|
-
- `
|
|
4008
|
-
- `
|
|
4009
|
-
- `
|
|
4010
|
-
- `
|
|
4011
|
-
- `
|
|
4012
|
-
- `
|
|
4008
|
+
- `02001_baseline_schema.sql` - `user_role` enum
|
|
4009
|
+
- `02001_baseline_schema.sql` - Translations schema
|
|
4010
|
+
- `02001_baseline_schema.sql` - Pages, posts, revisions, navigation
|
|
4011
|
+
- `02001_baseline_schema.sql` - Products, inventory, package_activations
|
|
4012
|
+
- `02001_baseline_schema.sql` - Orders, tax, shipping, currencies
|
|
4013
|
+
- `02003_baseline_security_and_grants.sql` - `on_auth_user_created` trigger (first-user ADMIN rule), RLS policies
|
|
4014
|
+
- `02001_baseline_schema.sql` - helper functions
|
|
4015
|
+
- `02001_baseline_schema.sql` - Default currency, languages, invoice settings
|
|
4013
4016
|
|
|
4014
4017
|
#### Folders Explored
|
|
4015
4018
|
|
|
@@ -5643,7 +5646,7 @@ The design leans on the Supabase managed platform for replication, connection po
|
|
|
5643
5646
|
|
|
5644
5647
|
#### 6.2.2.1 Enumerated Types
|
|
5645
5648
|
|
|
5646
|
-
All domain-specific enumerations are declared in migration `
|
|
5649
|
+
All domain-specific enumerations are declared in migration `02001_baseline_schema.sql` using idempotent `DO $$` guards so re-application is safe. The migration also grants `USAGE` on the `public` schema to the Supabase-managed roles `postgres`, `anon`, `authenticated`, and `service_role`.
|
|
5647
5650
|
|
|
5648
5651
|
| Enum | Values | Usage |
|
|
5649
5652
|
|------|--------|-------|
|
|
@@ -5654,7 +5657,7 @@ All domain-specific enumerations are declared in migration `00000000000000_setup
|
|
|
5654
5657
|
|
|
5655
5658
|
#### 6.2.2.2 Identity and Core CMS Entities
|
|
5656
5659
|
|
|
5657
|
-
Declared in migration `
|
|
5660
|
+
Declared in migration `02001_baseline_schema.sql`, these tables bootstrap the authentication mirror, languages, media registry, translations, logos, and the global key/value settings store.
|
|
5658
5661
|
|
|
5659
5662
|
| Table | Primary Key | Key Columns / Constraints |
|
|
5660
5663
|
|-------|-------------|----------------------------|
|
|
@@ -5673,7 +5676,7 @@ Declared in migration `00000000000001_setup_cms_core.sql`, these tables bootstra
|
|
|
5673
5676
|
|
|
5674
5677
|
#### 6.2.2.3 Authoring Content Entities
|
|
5675
5678
|
|
|
5676
|
-
Declared in migration `
|
|
5679
|
+
Declared in migration `02001_baseline_schema.sql`, these tables form the CMS authoring domain. The relationships are summarized in the ER diagram below.
|
|
5677
5680
|
|
|
5678
5681
|
```mermaid
|
|
5679
5682
|
erDiagram
|
|
@@ -5760,7 +5763,7 @@ erDiagram
|
|
|
5760
5763
|
|
|
5761
5764
|
#### 6.2.2.4 Commerce Entities
|
|
5762
5765
|
|
|
5763
|
-
Declared across
|
|
5766
|
+
Declared across migration `02001_baseline_schema.sql`, commerce spans catalog, licensing, orders, shipping, taxation, and currency infrastructure.
|
|
5764
5767
|
|
|
5765
5768
|
```mermaid
|
|
5766
5769
|
erDiagram
|
|
@@ -5896,7 +5899,7 @@ Both `products` and `product_variants` store prices in two redundant shapes —
|
|
|
5896
5899
|
|
|
5897
5900
|
#### 6.2.2.5 Indexing Strategy
|
|
5898
5901
|
|
|
5899
|
-
Migration `
|
|
5902
|
+
Migration `02001_baseline_schema.sql` adds 38 secondary indexes. The strategy groups into four categories:
|
|
5900
5903
|
|
|
5901
5904
|
| Category | Representative Indexes | Optimization Target |
|
|
5902
5905
|
|----------|------------------------|---------------------|
|
|
@@ -5967,29 +5970,17 @@ The `backup.js` script reads `POSTGRES_URL` or `DATABASE_URL` via `dotenv`, pars
|
|
|
5967
5970
|
|
|
5968
5971
|
#### 6.2.3.1 Migration Procedures
|
|
5969
5972
|
|
|
5970
|
-
Migrations live in `libs/db/src/supabase/migrations/` and are managed via the Supabase CLI
|
|
5973
|
+
Migrations live in `libs/db/src/supabase/migrations/` and are managed via the Supabase CLI plus the repo's own appliers (the `/setup` wizard, the build hook, `npm run update`, the Docker migration runner). The folder holds exactly one **squash generation**; files are named `GGNNN_name.sql` (GG = generation, NNN = contiguous sequence) and applied in lexical order. The current generation is 2 (built 2026-09-10); its first five slots are fixed and everything from `02005` upward is an ordinary forward migration.
|
|
5971
5974
|
|
|
5972
5975
|
| File | Purpose | Key Outputs |
|
|
5973
5976
|
|------|---------|-------------|
|
|
5974
|
-
| `
|
|
5975
|
-
| `
|
|
5976
|
-
| `
|
|
5977
|
-
| `
|
|
5978
|
-
| `
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
| `00000000000007_setup_indexes.sql` | Performance indexes | 38 indexes across all domains |
|
|
5982
|
-
| `00000000000008_seed_platform_defaults.sql` | Baseline platform state | English/French languages, USD as default currency, site_settings defaults |
|
|
5983
|
-
| `00000000000009_seed_translations.sql` | Internationalization content | Hundreds of en/es/fr i18n rows |
|
|
5984
|
-
| `00000000000010_seed_content_scaffold.sql` | Starter content | Default logos, home/articles pages, featured posts, navigation |
|
|
5985
|
-
| `00000000000011_setup_cortex_ai_settings.sql` | AI settings | Cortex AI configuration |
|
|
5986
|
-
| `00000000000012_setup_commerce_coupons.sql` | Commerce coupons | Coupon tables and constraints |
|
|
5987
|
-
| `00000000000013_setup_cortex_ai_db_mutation_audit.sql` | AI audit | Cortex AI database mutation audit support |
|
|
5988
|
-
| `00000000000014_setup_content_drafts.sql` | Content drafts | Visual-editing draft support |
|
|
5989
|
-
| `00000000000015_setup_product_drafts.sql` | Product drafts | Product draft workflow support |
|
|
5990
|
-
| `00000000000016_add_feature_image_to_pages.sql` | CMS page media | Optional page feature image relationship |
|
|
5991
|
-
|
|
5992
|
-
Per Section 2.4.5, the **numbered migration files in `libs/db/src/supabase/migrations/` must remain applied in order**; out-of-sequence application will violate referential integrity. For live/shared databases, new changes must be appended as new non-destructive migrations. Do not rewrite, recycle, squash, reorder, or delete migrations that may already be recorded in production.
|
|
5977
|
+
| `02000_catchup_gen1.sql` | Version-aware replay of the retired generation-1 forward migrations | Converges databases that sat behind generation 1; skipped on empty databases and on databases already at generation ≥ 2; sets `site_settings.migration_baseline_generation` |
|
|
5978
|
+
| `02001_baseline_schema.sql` | Schema | All enums, functions, tables, sequences and defaults (`IF NOT EXISTS` / `CREATE OR REPLACE`); the `auth.users` → `handle_new_user` trigger |
|
|
5979
|
+
| `02002_baseline_constraints_and_indexes.sql` | Integrity + performance | Primary/unique/check/foreign-key constraints (catalog-guarded) and every index |
|
|
5980
|
+
| `02003_baseline_security_and_grants.sql` | Security | RLS enablement on every table, all policies, business/timestamp triggers, grants |
|
|
5981
|
+
| `02004_baseline_seed.sql` | Seed | Canonical demo content (languages, currencies, site settings, translations, media, pages/posts/blocks, navigation, shipping defaults, themes) — runs only on an empty database |
|
|
5982
|
+
|
|
5983
|
+
Per Section 2.4.5, the **numbered migration files in `libs/db/src/supabase/migrations/` must remain applied in order**; out-of-sequence application will violate referential integrity. For live/shared databases, new changes must be appended as new non-destructive migrations. Do not rewrite, recycle, reorder, or delete migration files outside the documented squash runbook (`docs/04-DATABASE-AND-AUTH.md` → "Squashing migrations"); a squash always ships a catch-up so existing databases converge.
|
|
5993
5984
|
|
|
5994
5985
|
##### 6.2.3.1.1 Migration Command Surface
|
|
5995
5986
|
|
|
@@ -6000,7 +5991,7 @@ The root `package.json` exposes the following migration-related scripts:
|
|
|
6000
5991
|
| `db:migrate:check` | `node tools/scripts/push-db-migrations.js --check` | Dry-run pending remote migrations |
|
|
6001
5992
|
| `db:migrate` / `db:push` | `node tools/scripts/push-db-migrations.js --confirm` | Apply pending migration files only; no reset, sandbox seed, function deploy, or config push |
|
|
6002
5993
|
| `db:migrate:repair-history:check` | `node tools/scripts/repair-db-migration-history.js --check` | Preview baseline migration-history repair |
|
|
6003
|
-
| `db:migrate:repair-history` | `node tools/scripts/repair-db-migration-history.js --confirm` | Mark existing baseline migrations as applied without running their SQL |
|
|
5994
|
+
| `db:migrate:repair-history` | `node tools/scripts/repair-db-migration-history.js --confirm` | Mark existing baseline migrations as applied without running their SQL; with `-- --reconcile-squash`, record a migration squash (revert retired versions, mark the new generation applied) |
|
|
6004
5995
|
| `db:migrate:fresh` | `node tools/scripts/push-db-migrations.js --confirm --allow-baseline-replay` | Apply the full baseline only to a brand-new empty database |
|
|
6005
5996
|
| `db:reset` | `supabase db reset --workdir libs/db/src` | Full local reset and replay |
|
|
6006
5997
|
| `db:link` | `dotenv + supabase-link` via `tools/scripts/supabase-link.js` | Link local workspace to remote project |
|
|
@@ -6195,7 +6186,7 @@ Detailed in §6.2.2.8. In summary: Supabase-managed PITR and managed backups (pr
|
|
|
6195
6186
|
|
|
6196
6187
|
#### 6.2.4.3 Privacy Controls via Row-Level Security
|
|
6197
6188
|
|
|
6198
|
-
RLS is **enabled on every table** in migration `
|
|
6189
|
+
RLS is **enabled on every table** in migration `02001_baseline_schema.sql`. The policy matrix distributes into four access tiers.
|
|
6199
6190
|
|
|
6200
6191
|
| Access Tier | Representative Scope | Tables |
|
|
6201
6192
|
|-------------|----------------------|--------|
|
|
@@ -6479,17 +6470,17 @@ The local Supabase stack is configured through `libs/db/src/supabase/config.toml
|
|
|
6479
6470
|
|
|
6480
6471
|
#### 6.2.9.1 Files Examined
|
|
6481
6472
|
|
|
6482
|
-
- `libs/db/src/supabase/migrations/
|
|
6483
|
-
- `libs/db/src/supabase/migrations/
|
|
6484
|
-
- `libs/db/src/supabase/migrations/
|
|
6485
|
-
- `libs/db/src/supabase/migrations/
|
|
6486
|
-
- `libs/db/src/supabase/migrations/
|
|
6487
|
-
- `libs/db/src/supabase/migrations/
|
|
6488
|
-
- `libs/db/src/supabase/migrations/
|
|
6489
|
-
- `libs/db/src/supabase/migrations/
|
|
6490
|
-
- `libs/db/src/supabase/migrations/
|
|
6491
|
-
- `libs/db/src/supabase/migrations/
|
|
6492
|
-
- `libs/db/src/supabase/migrations/
|
|
6473
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — Enums and schema grants
|
|
6474
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — Core CMS tables (profiles, media, languages, translations)
|
|
6475
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — Pages, posts, blocks, revisions, navigation
|
|
6476
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — Commerce catalog and license registry
|
|
6477
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — Orders, shipping, tax, currencies
|
|
6478
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — Business logic functions and triggers
|
|
6479
|
+
- `libs/db/src/supabase/migrations/02003_baseline_security_and_grants.sql` — Row-Level Security policy matrix
|
|
6480
|
+
- `libs/db/src/supabase/migrations/02002_baseline_constraints_and_indexes.sql` — 38 secondary indexes
|
|
6481
|
+
- `libs/db/src/supabase/migrations/02004_baseline_seed.sql` — Baseline platform seed
|
|
6482
|
+
- `libs/db/src/supabase/migrations/02004_baseline_seed.sql` — Internationalization seed
|
|
6483
|
+
- `libs/db/src/supabase/migrations/02004_baseline_seed.sql` — Starter content seed
|
|
6493
6484
|
- `libs/db/src/supabase/config.toml` — Supabase local stack configuration
|
|
6494
6485
|
- `libs/db/src/lib/supabase/client.ts` — Browser client factory
|
|
6495
6486
|
- `libs/db/src/lib/supabase/server.ts` — Server / SSR / service-role client factories
|
|
@@ -7507,7 +7498,7 @@ Security controls are layered so that the failure of any single layer does not r
|
|
|
7507
7498
|
|-------|----------------------|------------------|
|
|
7508
7499
|
| Edge | Request proxy — session refresh, path-prefix RBAC, security headers, CSP | `apps/nextblock/proxy.ts` |
|
|
7509
7500
|
| Application | Client-layout role guard, server-action `verifyAdmin()`, role-gated page components | `app/cms/CmsClientLayout.tsx`, `app/cms/users/actions.ts` |
|
|
7510
|
-
| Data | Row-Level Security policies, SECURITY DEFINER helpers, service-role bypass | `libs/db/src/supabase/migrations/
|
|
7501
|
+
| Data | Row-Level Security policies, SECURITY DEFINER helpers, service-role bypass | `libs/db/src/supabase/migrations/02003_baseline_security_and_grants.sql` |
|
|
7511
7502
|
| Integration | Webhook signature verification, Bearer-secret cron, HMAC-verified callbacks | `/api/webhooks/*`, `/api/cron/*`, `/api/revalidate/route.ts` |
|
|
7512
7503
|
|
|
7513
7504
|
#### 6.4.1.3 Security Architecture Topology
|
|
@@ -7617,7 +7608,7 @@ The system manages three distinct token classes, each with differentiated lifecy
|
|
|
7617
7608
|
| OAuth Authorization Code | Short-lived code exchange | URL query parameter, consumed immediately at `/auth/callback` |
|
|
7618
7609
|
| Supabase Service Role Key | Server-only RLS bypass | Vercel environment variable (`SUPABASE_SERVICE_ROLE_KEY`) |
|
|
7619
7610
|
|
|
7620
|
-
A **per-request nonce** is generated via `crypto.randomUUID()` in `proxy.ts` and used for the nonce-based CSP (see §6.4.4.5). JWT claims are read inside Postgres by the `get_my_claim(claim text)` SECURITY DEFINER function via `current_setting('request.jwt.claims', true)::jsonb`, which is defined in `libs/db/src/supabase/migrations/
|
|
7611
|
+
A **per-request nonce** is generated via `crypto.randomUUID()` in `proxy.ts` and used for the nonce-based CSP (see §6.4.4.5). JWT claims are read inside Postgres by the `get_my_claim(claim text)` SECURITY DEFINER function via `current_setting('request.jwt.claims', true)::jsonb`, which is defined in `libs/db/src/supabase/migrations/02001_baseline_schema.sql`.
|
|
7621
7612
|
|
|
7622
7613
|
The **service role key** requires two independent compile-time and runtime guards to prevent leakage into the browser bundle:
|
|
7623
7614
|
|
|
@@ -7715,7 +7706,7 @@ The `resolvePostAuthRedirect` helper enforces four rules as a gate against open-
|
|
|
7715
7706
|
|
|
7716
7707
|
#### 6.4.3.1 Role-Based Access Control Model
|
|
7717
7708
|
|
|
7718
|
-
Authorization is driven by the `user_role` enum declared in migration `
|
|
7709
|
+
Authorization is driven by the `user_role` enum declared in migration `02001_baseline_schema.sql`, which admits three values plus an implicit fourth (Supabase's `service_role`) for server-only privilege elevation:
|
|
7719
7710
|
|
|
7720
7711
|
| Role | Assignment Mechanism | Zone Capabilities |
|
|
7721
7712
|
|------|---------------------|-------------------|
|
|
@@ -7728,7 +7719,7 @@ Role assignment at bootstrap is governed by the `handle_new_user()` SECURITY DEF
|
|
|
7728
7719
|
|
|
7729
7720
|
#### 6.4.3.2 Permission Management and SECURITY DEFINER Helpers
|
|
7730
7721
|
|
|
7731
|
-
Role checks executed inside SQL contexts use SECURITY DEFINER helper functions declared in `libs/db/src/supabase/migrations/
|
|
7722
|
+
Role checks executed inside SQL contexts use SECURITY DEFINER helper functions declared in `libs/db/src/supabase/migrations/02001_baseline_schema.sql`. SECURITY DEFINER elevates the function's execution privileges to the function owner, bypassing caller-side RLS for the function body while `SET search_path = ''` prevents search-path hijacking attacks.
|
|
7732
7723
|
|
|
7733
7724
|
| Function | Purpose | Security Annotation |
|
|
7734
7725
|
|----------|---------|---------------------|
|
|
@@ -7741,7 +7732,7 @@ These helpers form the primitive vocabulary of all RLS policies: a user's effect
|
|
|
7741
7732
|
|
|
7742
7733
|
#### 6.4.3.3 Resource Authorization Matrix
|
|
7743
7734
|
|
|
7744
|
-
RLS is enabled on every table in migration `
|
|
7735
|
+
RLS is enabled on every table in migration `02001_baseline_schema.sql` (872 lines). The matrix distributes into four access tiers:
|
|
7745
7736
|
|
|
7746
7737
|
| Access Tier | Typical Policy Predicate | Representative Tables |
|
|
7747
7738
|
|-------------|--------------------------|------------------------|
|
|
@@ -8127,7 +8118,7 @@ The security control matrix below enumerates the principal threats, their mitiga
|
|
|
8127
8118
|
|
|
8128
8119
|
| Threat | Mitigation | Migration or File |
|
|
8129
8120
|
|--------|-----------|-------------------|
|
|
8130
|
-
| Public data exposure through writes | RLS writes restricted; public read only for intended tables and statuses | `migrations/
|
|
8121
|
+
| Public data exposure through writes | RLS writes restricted; public read only for intended tables and statuses | `migrations/02003_baseline_security_and_grants.sql` |
|
|
8131
8122
|
| Draft content leakage | Anon policy `status='published'` on pages; `status='published' AND published_at<=now()` on posts | `migration 00000000000006` |
|
|
8132
8123
|
| Path traversal in uploads | `sanitizeFolder()` collapses `../`, strips leading slashes, removes illegal chars | `/api/upload/presigned-url/route.ts` |
|
|
8133
8124
|
| Search-path hijacking in SECURITY DEFINER | `SET search_path = ''` on auth helper functions | `migration 00000000000005` |
|
|
@@ -8226,9 +8217,9 @@ For details beyond the scope of this section, consult:
|
|
|
8226
8217
|
|
|
8227
8218
|
#### Database Security Surface
|
|
8228
8219
|
|
|
8229
|
-
- `libs/db/src/supabase/migrations/
|
|
8230
|
-
- `libs/db/src/supabase/migrations/
|
|
8231
|
-
- `libs/db/src/supabase/migrations/
|
|
8220
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — `user_role` enum declaration
|
|
8221
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — `handle_new_user`, `get_current_user_role`, `is_admin`, `get_my_claim` SECURITY DEFINER functions
|
|
8222
|
+
- `libs/db/src/supabase/migrations/02003_baseline_security_and_grants.sql` — 872-line RLS policy matrix
|
|
8232
8223
|
- `libs/db/src/supabase/config.toml` — 341-line Supabase Auth + MFA + rate-limit configuration
|
|
8233
8224
|
- `libs/db/src/supabase/templates/` — Six transactional email templates
|
|
8234
8225
|
|
|
@@ -10787,7 +10778,7 @@ Security is enforced across four layers, with compliance-relevant controls conce
|
|
|
10787
10778
|
|:--|:--|:--|
|
|
10788
10779
|
| Edge | Nonce-based CSP, HSTS, X-Frame-Options SAMEORIGIN, X-Content-Type-Options nosniff, Referrer-Policy origin-when-cross-origin, Permissions-Policy, COOP same-origin | `proxy.ts` |
|
|
10789
10780
|
| Application | RBAC via `cmsRoutePermissions` (`/cms` ADMIN/WRITER; `/cms/admin`, `/cms/users`, `/cms/settings` ADMIN only); `verifyAdmin()` in server actions | `proxy.ts`, server actions |
|
|
10790
|
-
| Data | RLS policies + SECURITY DEFINER helpers (`get_my_claim`, `get_current_user_role`, `is_admin`, `handle_new_user`) | Migration `
|
|
10781
|
+
| Data | RLS policies + SECURITY DEFINER helpers (`get_my_claim`, `get_current_user_role`, `is_admin`, `handle_new_user`) | Migration `02001_baseline_schema.sql` (872 lines) |
|
|
10791
10782
|
| Integration | Stripe `constructEvent` signature verification; Freemius HMAC-SHA-256; `CRON_SECRET` Bearer auth; `REVALIDATE_SECRET_TOKEN` via `x-revalidate-secret` header | Webhook + cron route handlers |
|
|
10792
10783
|
|
|
10793
10784
|
**Service role key hardening**: `SUPABASE_SERVICE_ROLE_KEY` is guarded at runtime via `typeof window !== 'undefined'` checks and build-time via `import 'server-only'` directives to prevent browser exposure.
|
|
@@ -11300,7 +11291,7 @@ Per §4.12, the following are the codified timing constraints. Breaches surface
|
|
|
11300
11291
|
| Vercel function invocations | Vercel dashboard | Auto-scaled; `maxDuration` capping |
|
|
11301
11292
|
| Vercel bandwidth | Vercel dashboard | 1-year image cache TTL, AVIF compression |
|
|
11302
11293
|
| Vercel image optimization count | Vercel dashboard | `minimumCacheTTL: 31_536_000` |
|
|
11303
|
-
| Supabase DB CPU/IO | Supabase dashboard | RLS indexing (`
|
|
11294
|
+
| Supabase DB CPU/IO | Supabase dashboard | RLS indexing (`02001_baseline_schema.sql`) |
|
|
11304
11295
|
| Supabase egress | Supabase dashboard | Server-side aggregation; `count` head queries |
|
|
11305
11296
|
| R2 storage | Cloudflare dashboard | Folder path sanitization prevents duplicates |
|
|
11306
11297
|
| R2 Class A writes | Cloudflare dashboard | Single direct-upload via presigned URL (vs through server) |
|
|
@@ -11702,15 +11693,15 @@ The following enum types are defined in the eleven canonical SQL migrations loca
|
|
|
11702
11693
|
|
|
11703
11694
|
| Enum Type | Allowed Values | Migration |
|
|
11704
11695
|
|:--|:--|:--|
|
|
11705
|
-
| `user_role` | `ADMIN`, `WRITER`, `USER` | `
|
|
11706
|
-
| `page_status` | `draft`, `published`, `archived` | `
|
|
11707
|
-
| `menu_location` | `header`, `footer`, `sidebar` | `
|
|
11708
|
-
| `revision_type` | `snapshot`, `diff` | `
|
|
11709
|
-
| `product_type` | `physical`, `digital` | `
|
|
11710
|
-
| `payment_provider` | `stripe`, `freemius` | `
|
|
11711
|
-
| `order_status` | `pending`, `paid`, `shipped`, `delivered`, `refunded`, `cancelled` | `
|
|
11712
|
-
| `tax_calculation_mode` | `manual`, `automatic` | `
|
|
11713
|
-
| `inventory_deduction_method` | `rpc`, `sql-fallback` | `
|
|
11696
|
+
| `user_role` | `ADMIN`, `WRITER`, `USER` | `02001_baseline_schema.sql` |
|
|
11697
|
+
| `page_status` | `draft`, `published`, `archived` | `02001_baseline_schema.sql` |
|
|
11698
|
+
| `menu_location` | `header`, `footer`, `sidebar` | `02001_baseline_schema.sql` |
|
|
11699
|
+
| `revision_type` | `snapshot`, `diff` | `02001_baseline_schema.sql` |
|
|
11700
|
+
| `product_type` | `physical`, `digital` | `02001_baseline_schema.sql` |
|
|
11701
|
+
| `payment_provider` | `stripe`, `freemius` | `02001_baseline_schema.sql` |
|
|
11702
|
+
| `order_status` | `pending`, `paid`, `shipped`, `delivered`, `refunded`, `cancelled` | `02001_baseline_schema.sql` |
|
|
11703
|
+
| `tax_calculation_mode` | `manual`, `automatic` | `02001_baseline_schema.sql` |
|
|
11704
|
+
| `inventory_deduction_method` | `rpc`, `sql-fallback` | `02001_baseline_schema.sql` |
|
|
11714
11705
|
|
|
11715
11706
|
Cross-reference: Section 6.2 (Database Design), Section 4.11 (Validation Rules and Compliance Checkpoints).
|
|
11716
11707
|
|
|
@@ -12430,10 +12421,10 @@ The appendices above synthesize material from the following sections of this spe
|
|
|
12430
12421
|
|
|
12431
12422
|
- `libs/environment.d.ts` — Typed `NodeJS.ProcessEnv` augmentation used to enumerate every environment variable listed in Section 9.1.1
|
|
12432
12423
|
- `.env.example` — Environment variable inventory and sample values cross-checked against the typed augmentation
|
|
12433
|
-
- `libs/db/src/supabase/migrations/
|
|
12434
|
-
- `libs/db/src/supabase/migrations/
|
|
12435
|
-
- `libs/db/src/supabase/migrations/
|
|
12436
|
-
- `libs/db/src/supabase/migrations/
|
|
12424
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — `user_role` enum definition
|
|
12425
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — `page_status`, `menu_location`, `revision_type` enum definitions
|
|
12426
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — `product_type`, `payment_provider` enum definitions
|
|
12427
|
+
- `libs/db/src/supabase/migrations/02001_baseline_schema.sql` — `order_status`, `tax_calculation_mode`, `inventory_deduction_method` enum definitions
|
|
12437
12428
|
- `libs/db/src/supabase/config.toml` — Supabase local port allocation reproduced in Section 9.1.9
|
|
12438
12429
|
- `apps/nextblock/proxy.ts` — Security header set (Section 9.1.12), prefetch priority mapping (Section 9.1.13), CSP nonce, structured cache observability log schema
|
|
12439
12430
|
- `apps/nextblock/app/layout.tsx` — `PUBLIC_LAYOUT_REVALIDATE_SECONDS` constant reproduced in SLA ledger
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import 'server-only';
|
|
2
|
-
import { createHmac } from 'node:crypto';
|
|
3
|
-
|
|
4
|
-
// Resolve the app's internal shared secrets WITHOUT requiring the operator to paste
|
|
5
|
-
// random strings at deploy time. When an explicit env var is set it always wins;
|
|
6
|
-
// otherwise the secret is derived deterministically from the always-present Supabase
|
|
7
|
-
// service-role key. This is what lets a one-click Vercel deploy work with zero env
|
|
8
|
-
// input while Draft Mode and on-demand revalidation still function out of the box.
|
|
9
|
-
//
|
|
10
|
-
// HMAC-SHA256 is one-way, so a leaked derived secret never exposes the service-role
|
|
11
|
-
// key. The derived value is stable for a given Supabase project; rotating the
|
|
12
|
-
// service-role key rotates these secrets too (set the explicit env var to pin one
|
|
13
|
-
// independently — e.g. to configure a Supabase revalidation webhook with a fixed
|
|
14
|
-
// token). `npm run setup` still writes explicit values locally, so dev is unchanged.
|
|
15
|
-
//
|
|
16
|
-
// CRON_SECRET is intentionally NOT derived here: Vercel Cron injects
|
|
17
|
-
// `Authorization: Bearer ${process.env.CRON_SECRET}` read from the env at invocation,
|
|
18
|
-
// so a derived value could never match it. The cron routes instead treat CRON_SECRET
|
|
19
|
-
// as optional (enforced only when set) — see app/api/cron/*/route.ts.
|
|
20
|
-
|
|
21
|
-
function deriveFromServiceRole(label: string): string {
|
|
22
|
-
// Accept the Marketplace integration's SUPABASE_SECRET_KEY alias too, so derivation
|
|
23
|
-
// still works on a one-click deploy that only injected the new key name.
|
|
24
|
-
const master = (
|
|
25
|
-
process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_SECRET_KEY
|
|
26
|
-
)?.trim();
|
|
27
|
-
if (!master) return '';
|
|
28
|
-
return createHmac('sha256', master).update(`nextblock:${label}`).digest('hex');
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Secret for the programmatic Draft Mode preview entry (`/api/draft?secret=`). */
|
|
32
|
-
export function resolveDraftModeSecret(): string {
|
|
33
|
-
return process.env.DRAFT_MODE_SECRET?.trim() || deriveFromServiceRole('draft-mode');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** Shared secret a Supabase webhook sends to `/api/revalidate` (`x-revalidate-secret`). */
|
|
37
|
-
export function resolveRevalidateSecret(): string {
|
|
38
|
-
return process.env.REVALIDATE_SECRET_TOKEN?.trim() || deriveFromServiceRole('revalidate');
|
|
39
|
-
}
|
|
1
|
+
import 'server-only';
|
|
2
|
+
import { createHmac } from 'node:crypto';
|
|
3
|
+
|
|
4
|
+
// Resolve the app's internal shared secrets WITHOUT requiring the operator to paste
|
|
5
|
+
// random strings at deploy time. When an explicit env var is set it always wins;
|
|
6
|
+
// otherwise the secret is derived deterministically from the always-present Supabase
|
|
7
|
+
// service-role key. This is what lets a one-click Vercel deploy work with zero env
|
|
8
|
+
// input while Draft Mode and on-demand revalidation still function out of the box.
|
|
9
|
+
//
|
|
10
|
+
// HMAC-SHA256 is one-way, so a leaked derived secret never exposes the service-role
|
|
11
|
+
// key. The derived value is stable for a given Supabase project; rotating the
|
|
12
|
+
// service-role key rotates these secrets too (set the explicit env var to pin one
|
|
13
|
+
// independently — e.g. to configure a Supabase revalidation webhook with a fixed
|
|
14
|
+
// token). `npm run setup` still writes explicit values locally, so dev is unchanged.
|
|
15
|
+
//
|
|
16
|
+
// CRON_SECRET is intentionally NOT derived here: Vercel Cron injects
|
|
17
|
+
// `Authorization: Bearer ${process.env.CRON_SECRET}` read from the env at invocation,
|
|
18
|
+
// so a derived value could never match it. The cron routes instead treat CRON_SECRET
|
|
19
|
+
// as optional (enforced only when set) — see app/api/cron/*/route.ts.
|
|
20
|
+
|
|
21
|
+
function deriveFromServiceRole(label: string): string {
|
|
22
|
+
// Accept the Marketplace integration's SUPABASE_SECRET_KEY alias too, so derivation
|
|
23
|
+
// still works on a one-click deploy that only injected the new key name.
|
|
24
|
+
const master = (
|
|
25
|
+
process.env.SUPABASE_SERVICE_ROLE_KEY || process.env.SUPABASE_SECRET_KEY
|
|
26
|
+
)?.trim();
|
|
27
|
+
if (!master) return '';
|
|
28
|
+
return createHmac('sha256', master).update(`nextblock:${label}`).digest('hex');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Secret for the programmatic Draft Mode preview entry (`/api/draft?secret=`). */
|
|
32
|
+
export function resolveDraftModeSecret(): string {
|
|
33
|
+
return process.env.DRAFT_MODE_SECRET?.trim() || deriveFromServiceRole('draft-mode');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Shared secret a Supabase webhook sends to `/api/revalidate` (`x-revalidate-secret`). */
|
|
37
|
+
export function resolveRevalidateSecret(): string {
|
|
38
|
+
return process.env.REVALIDATE_SECRET_TOKEN?.trim() || deriveFromServiceRole('revalidate');
|
|
39
|
+
}
|