create-nextblock 0.13.10 → 0.13.12

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.
Files changed (259) hide show
  1. package/docker-template/.dockerignore +23 -23
  2. package/docker-template/.env.docker.example +69 -69
  3. package/docker-template/docker/db/init/99-jwt.sql +6 -6
  4. package/docker-template/docker/db/init/99-roles.sql +25 -25
  5. package/docker-template/docker/kong/kong.yml +112 -112
  6. package/docker-template/scripts/docker-setup.mjs +310 -310
  7. package/libs/db/tsconfig.lib.json +3 -3
  8. package/libs/editor/tsconfig.lib.json +3 -3
  9. package/libs/ui/tsconfig.lib.json +3 -3
  10. package/libs/utils/tsconfig.json +3 -3
  11. package/package.json +1 -1
  12. package/project.json +19 -19
  13. package/templates/nextblock-template/.browserslistrc +11 -11
  14. package/templates/nextblock-template/.dockerignore +23 -23
  15. package/templates/nextblock-template/README.md +34 -34
  16. package/templates/nextblock-template/app/(auth-pages)/layout.tsx +9 -9
  17. package/templates/nextblock-template/app/(auth-pages)/post-sign-in/page.tsx +27 -27
  18. package/templates/nextblock-template/app/(auth-pages)/sign-up/page.tsx +46 -46
  19. package/templates/nextblock-template/app/(auth-pages)/two-factor/page.tsx +51 -51
  20. package/templates/nextblock-template/app/ToasterProvider.tsx +17 -17
  21. package/templates/nextblock-template/app/[slug]/pageClientActions.ts +7 -7
  22. package/templates/nextblock-template/app/actions/consent.ts +57 -57
  23. package/templates/nextblock-template/app/actions/postActions.ts +146 -146
  24. package/templates/nextblock-template/app/actions/productGridActions.ts +40 -40
  25. package/templates/nextblock-template/app/actions/twoFactorEmail.ts +22 -22
  26. package/templates/nextblock-template/app/api/ai/cortex/build-widget/route.ts +153 -153
  27. package/templates/nextblock-template/app/api/ai/generate-blocks/route.ts +96 -96
  28. package/templates/nextblock-template/app/api/brand/email-logo/route.ts +48 -48
  29. package/templates/nextblock-template/app/api/cms/check-updates/route.ts +44 -44
  30. package/templates/nextblock-template/app/api/cms/ecommerce/product-picker/route.ts +151 -151
  31. package/templates/nextblock-template/app/api/cms/full-backup/export/route.ts +33 -33
  32. package/templates/nextblock-template/app/api/cms/full-backup/restore/route.ts +63 -63
  33. package/templates/nextblock-template/app/api/cron/reset-sandbox/route.ts +3490 -3490
  34. package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +5749 -5609
  35. package/templates/nextblock-template/app/api/cron/sync-currencies/route.ts +39 -39
  36. package/templates/nextblock-template/app/api/custom-blocks/db-relations/route.ts +92 -92
  37. package/templates/nextblock-template/app/api/custom-blocks/editor-definitions/route.ts +43 -43
  38. package/templates/nextblock-template/app/api/media/library/route.ts +69 -69
  39. package/templates/nextblock-template/app/api/media/r2-presigned/route.ts +53 -53
  40. package/templates/nextblock-template/app/api/media/record/route.ts +160 -160
  41. package/templates/nextblock-template/app/api/search/route.ts +43 -43
  42. package/templates/nextblock-template/app/article/[slug]/PostClientContent.tsx +441 -441
  43. package/templates/nextblock-template/app/auth/callback/route.ts +31 -31
  44. package/templates/nextblock-template/app/cart/page.tsx +7 -7
  45. package/templates/nextblock-template/app/cms/blocks/components/CustomBlockEditorPreview.tsx +160 -160
  46. package/templates/nextblock-template/app/cms/blocks/components/MediaLibraryModal.tsx +149 -149
  47. package/templates/nextblock-template/app/cms/blocks/components/MultiEntityPicker.tsx +263 -263
  48. package/templates/nextblock-template/app/cms/blocks/editors/DynamicCustomBlockEditor.tsx +167 -167
  49. package/templates/nextblock-template/app/cms/blocks/editors/TextBlockEditor.tsx +90 -90
  50. package/templates/nextblock-template/app/cms/components/ConnectGitHubButton.tsx +122 -122
  51. package/templates/nextblock-template/app/cms/components/CortexAiPageContext.tsx +58 -58
  52. package/templates/nextblock-template/app/cms/components/EcommerceActiveContext.tsx +27 -27
  53. package/templates/nextblock-template/app/cms/components/FeedbackModal.tsx +36 -36
  54. package/templates/nextblock-template/app/cms/components/SystemAlertsBanner.tsx +112 -112
  55. package/templates/nextblock-template/app/cms/components/TwoFactorReminderBanner.tsx +45 -45
  56. package/templates/nextblock-template/app/cms/components/github-connect-actions.ts +102 -102
  57. package/templates/nextblock-template/app/cms/components/system-alerts-actions.ts +31 -31
  58. package/templates/nextblock-template/app/cms/custom-blocks/[id]/edit/page.tsx +66 -66
  59. package/templates/nextblock-template/app/cms/custom-blocks/actions.ts +519 -519
  60. package/templates/nextblock-template/app/cms/custom-blocks/components/BlocksLibraryTransferControls.tsx +256 -256
  61. package/templates/nextblock-template/app/cms/custom-blocks/components/DBRelationSelect.tsx +384 -384
  62. package/templates/nextblock-template/app/cms/custom-blocks/components/ImageR2Picker.tsx +221 -221
  63. package/templates/nextblock-template/app/cms/custom-blocks/new/page.tsx +12 -12
  64. package/templates/nextblock-template/app/cms/custom-blocks/page.tsx +438 -438
  65. package/templates/nextblock-template/app/cms/dashboard/actions.ts +228 -228
  66. package/templates/nextblock-template/app/cms/dashboard/components/DashboardOnboarding.tsx +130 -130
  67. package/templates/nextblock-template/app/cms/import-export/actions.ts +226 -226
  68. package/templates/nextblock-template/app/cms/interactions/EmailRecipientsInput.tsx +189 -189
  69. package/templates/nextblock-template/app/cms/layout.tsx +73 -73
  70. package/templates/nextblock-template/app/cms/media/components/FolderNavigator.tsx +273 -273
  71. package/templates/nextblock-template/app/cms/media/components/FolderTree.tsx +122 -122
  72. package/templates/nextblock-template/app/cms/media/components/MediaEditForm.tsx +26 -26
  73. package/templates/nextblock-template/app/cms/media/components/MediaGridClient.tsx +69 -69
  74. package/templates/nextblock-template/app/cms/navigation/components/NavigationMenuDnd.tsx +3 -3
  75. package/templates/nextblock-template/app/cms/products/attributes/page.tsx +12 -12
  76. package/templates/nextblock-template/app/cms/products/categories/page.tsx +12 -12
  77. package/templates/nextblock-template/app/cms/products/inventory/page.tsx +13 -13
  78. package/templates/nextblock-template/app/cms/products/new/page.tsx +135 -135
  79. package/templates/nextblock-template/app/cms/products/productFormData.ts +133 -133
  80. package/templates/nextblock-template/app/cms/products/settings/page.tsx +5 -5
  81. package/templates/nextblock-template/app/cms/promotions/PromotionsWorkspace.tsx +456 -456
  82. package/templates/nextblock-template/app/cms/promotions/actions.ts +115 -115
  83. package/templates/nextblock-template/app/cms/promotions/page.tsx +31 -31
  84. package/templates/nextblock-template/app/cms/revisions/service.ts +19 -19
  85. package/templates/nextblock-template/app/cms/revisions/utils.ts +132 -132
  86. package/templates/nextblock-template/app/cms/settings/backup-restore/BackupRestoreWorkspace.tsx +1004 -1004
  87. package/templates/nextblock-template/app/cms/settings/backup-restore/page.tsx +29 -29
  88. package/templates/nextblock-template/app/cms/settings/bot-protection/page.tsx +24 -24
  89. package/templates/nextblock-template/app/cms/settings/cortex-ai/SandboxCortexAiSettingsClient.tsx +497 -497
  90. package/templates/nextblock-template/app/cms/settings/currencies/actions.ts +331 -331
  91. package/templates/nextblock-template/app/cms/settings/currencies/page.tsx +494 -494
  92. package/templates/nextblock-template/app/cms/settings/email/page.tsx +28 -28
  93. package/templates/nextblock-template/app/cms/settings/extra-translations/ExtraTranslationsWorkspace.tsx +767 -767
  94. package/templates/nextblock-template/app/cms/settings/extra-translations/page.tsx +93 -93
  95. package/templates/nextblock-template/app/cms/settings/google-analytics/page.tsx +26 -26
  96. package/templates/nextblock-template/app/cms/settings/languages/components/LanguageDetectionPanel.tsx +188 -188
  97. package/templates/nextblock-template/app/cms/settings/logos/[id]/edit/page.tsx +7 -7
  98. package/templates/nextblock-template/app/cms/settings/logos/components/BrandingSettingsForm.tsx +339 -339
  99. package/templates/nextblock-template/app/cms/settings/logos/components/DeleteLogoButton.tsx +21 -21
  100. package/templates/nextblock-template/app/cms/settings/logos/components/LogoForm.tsx +20 -20
  101. package/templates/nextblock-template/app/cms/settings/logos/components/SetActiveLogoButton.tsx +42 -42
  102. package/templates/nextblock-template/app/cms/settings/logos/components/SiteSeoSettingsForm.tsx +133 -133
  103. package/templates/nextblock-template/app/cms/settings/logos/new/page.tsx +8 -8
  104. package/templates/nextblock-template/app/cms/settings/packages/package-card.tsx +122 -122
  105. package/templates/nextblock-template/app/cms/settings/privacy/page.tsx +27 -27
  106. package/templates/nextblock-template/app/cms/settings/registration/page.tsx +27 -27
  107. package/templates/nextblock-template/app/cms/settings/security/page.tsx +33 -33
  108. package/templates/nextblock-template/app/cms/settings/taxes/page.tsx +21 -21
  109. package/templates/nextblock-template/app/cms/shipping/page.tsx +20 -20
  110. package/templates/nextblock-template/app/cms/users/components/CreateUserForm.tsx +217 -217
  111. package/templates/nextblock-template/app/cms/users/components/DeleteUserButton.tsx +12 -12
  112. package/templates/nextblock-template/app/cms/users/new/page.tsx +44 -44
  113. package/templates/nextblock-template/app/lib/site-settings.ts +105 -105
  114. package/templates/nextblock-template/app/profile/ProfilePageHeader.tsx +16 -16
  115. package/templates/nextblock-template/app/profile/ProfilePageMissingState.tsx +9 -9
  116. package/templates/nextblock-template/app/profile/account-links.ts +22 -22
  117. package/templates/nextblock-template/app/profile/orders/CustomerOrdersPageClient.tsx +124 -124
  118. package/templates/nextblock-template/app/profile/orders/page.tsx +19 -19
  119. package/templates/nextblock-template/app/profile/password/PasswordSettingsPageClient.tsx +128 -128
  120. package/templates/nextblock-template/app/profile/password/actions.ts +59 -59
  121. package/templates/nextblock-template/app/profile/password/page.tsx +27 -27
  122. package/templates/nextblock-template/app/setup/SetupWizard.tsx +678 -678
  123. package/templates/nextblock-template/app/setup/layout.tsx +13 -13
  124. package/templates/nextblock-template/app/setup/page.tsx +111 -111
  125. package/templates/nextblock-template/app/sitemap.ts +130 -130
  126. package/templates/nextblock-template/components/BlockRenderer.tsx +4 -0
  127. package/templates/nextblock-template/components/CartDrawerLoader.tsx +7 -7
  128. package/templates/nextblock-template/components/DeferredCartDrawer.tsx +23 -23
  129. package/templates/nextblock-template/components/DeferredGoogleAnalytics.tsx +70 -70
  130. package/templates/nextblock-template/components/DeferredGoogleTagManager.tsx +70 -70
  131. package/templates/nextblock-template/components/DeferredSpeedInsights.tsx +69 -69
  132. package/templates/nextblock-template/components/FeatureImageHero.tsx +47 -47
  133. package/templates/nextblock-template/components/FooterNavigation.tsx +32 -32
  134. package/templates/nextblock-template/components/HtmlScriptExecutor.tsx +47 -47
  135. package/templates/nextblock-template/components/LanguageSwitcher.tsx +2 -2
  136. package/templates/nextblock-template/components/PublicEnvBootstrap.tsx +30 -30
  137. package/templates/nextblock-template/components/ResponsiveNav.tsx +14 -14
  138. package/templates/nextblock-template/components/auth/AuthBotProtection.tsx +182 -182
  139. package/templates/nextblock-template/components/blocks/PostCardSkeleton.tsx +12 -12
  140. package/templates/nextblock-template/components/blocks/PostsGridBlock.tsx +12 -12
  141. package/templates/nextblock-template/components/blocks/ProductGridClient.tsx +114 -114
  142. package/templates/nextblock-template/components/blocks/ecommerceRendererLoaders.ts +23 -23
  143. package/templates/nextblock-template/components/blocks/renderers/ClientTextBlockRenderer.tsx +8 -1
  144. package/templates/nextblock-template/components/blocks/renderers/FormBlockRenderer.tsx +249 -249
  145. package/templates/nextblock-template/components/blocks/renderers/SectionBlockRenderer.tsx +3 -0
  146. package/templates/nextblock-template/components/blocks/renderers/VideoEmbedBlockRenderer.tsx +33 -23
  147. package/templates/nextblock-template/components/blocks/types.ts +7 -7
  148. package/templates/nextblock-template/components/env-var-warning.tsx +3 -3
  149. package/templates/nextblock-template/components/form-message.tsx +32 -32
  150. package/templates/nextblock-template/components/media/YouTubeFacade.tsx +105 -0
  151. package/templates/nextblock-template/components/media/youtube-embed-replace.tsx +32 -0
  152. package/templates/nextblock-template/components/privacy/ConsentBanner.tsx +170 -170
  153. package/templates/nextblock-template/components/privacy/ConsentGatedAnalytics.tsx +70 -70
  154. package/templates/nextblock-template/components/renderers/CachedDynamicLayoutEngine.tsx +28 -28
  155. package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.test.tsx +166 -166
  156. package/templates/nextblock-template/components/renderers/DynamicLayoutEngine.tsx +471 -464
  157. package/templates/nextblock-template/components/submit-button.tsx +23 -23
  158. package/templates/nextblock-template/components/theme-switcher.tsx +8 -8
  159. package/templates/nextblock-template/components/visual-editing/DeferredVisualEditing.tsx +21 -21
  160. package/templates/nextblock-template/context/AuthContext.tsx +23 -23
  161. package/templates/nextblock-template/context/language-rest-client.ts +32 -32
  162. package/templates/nextblock-template/docker/db/init/99-jwt.sql +6 -6
  163. package/templates/nextblock-template/docker/db/init/99-roles.sql +25 -25
  164. package/templates/nextblock-template/docker/kong/kong.yml +112 -112
  165. package/templates/nextblock-template/docs/01-PROJECT-OVERVIEW.md +94 -94
  166. package/templates/nextblock-template/docs/02-ECOMMERCE-CAPABILITIES.md +364 -364
  167. package/templates/nextblock-template/docs/03-CMS-AND-EDITOR.md +202 -202
  168. package/templates/nextblock-template/docs/04-DATABASE-AND-AUTH.md +246 -246
  169. package/templates/nextblock-template/docs/06-CLI-AND-SCAFFOLDING.md +176 -176
  170. package/templates/nextblock-template/docs/07-BLOCK-SDK-AND-EXTENSIBILITY.md +146 -146
  171. package/templates/nextblock-template/docs/10-CUSTOM-BLOCKS.md +222 -222
  172. package/templates/nextblock-template/docs/11-SELF-HOSTED-DOCKER.md +173 -173
  173. package/templates/nextblock-template/docs/12-VERCEL-DEPLOYMENT.md +170 -170
  174. package/templates/nextblock-template/docs/13-STAYING-UP-TO-DATE.md +151 -151
  175. package/templates/nextblock-template/docs/README.md +39 -39
  176. package/templates/nextblock-template/hooks/use-hotkeys.ts +21 -21
  177. package/templates/nextblock-template/hooks/useGlobalSearch.ts +101 -101
  178. package/templates/nextblock-template/index.d.ts +7 -7
  179. package/templates/nextblock-template/lib/app-secrets.ts +39 -39
  180. package/templates/nextblock-template/lib/auth/cookies.ts +47 -47
  181. package/templates/nextblock-template/lib/auth/crypto.ts +45 -45
  182. package/templates/nextblock-template/lib/auth/trustedDevices.ts +92 -92
  183. package/templates/nextblock-template/lib/auth-redirects.ts +46 -46
  184. package/templates/nextblock-template/lib/blocks/ProductGridBlock.tsx +78 -78
  185. package/templates/nextblock-template/lib/blocks/README.md +13 -13
  186. package/templates/nextblock-template/lib/blocks/product-grid-data.ts +210 -210
  187. package/templates/nextblock-template/lib/botProtection/verify.ts +134 -134
  188. package/templates/nextblock-template/lib/cms-transfer/server.ts +2243 -2243
  189. package/templates/nextblock-template/lib/cms-transfer/types.ts +145 -145
  190. package/templates/nextblock-template/lib/custom-block-definitions.ts +87 -87
  191. package/templates/nextblock-template/lib/custom-block-r2-upload-shared.ts +178 -178
  192. package/templates/nextblock-template/lib/custom-block-r2-upload.test.ts +140 -140
  193. package/templates/nextblock-template/lib/custom-block-r2-upload.ts +88 -88
  194. package/templates/nextblock-template/lib/custom-block-relations.test.ts +227 -227
  195. package/templates/nextblock-template/lib/custom-block-relations.ts +279 -279
  196. package/templates/nextblock-template/lib/custom-block-safelist.ts +14 -14
  197. package/templates/nextblock-template/lib/editor/dynamic-extension-core.test.ts +172 -172
  198. package/templates/nextblock-template/lib/editor/dynamic-extension-core.ts +213 -213
  199. package/templates/nextblock-template/lib/editor/dynamic-extension-loader.ts +22 -22
  200. package/templates/nextblock-template/lib/editor/dynamic-extensions.tsx +193 -193
  201. package/templates/nextblock-template/lib/email/branding-format.test.ts +133 -133
  202. package/templates/nextblock-template/lib/email/branding-format.ts +123 -123
  203. package/templates/nextblock-template/lib/email/branding.ts +76 -76
  204. package/templates/nextblock-template/lib/full-backup/manifest.test.ts +121 -121
  205. package/templates/nextblock-template/lib/full-backup/manifest.ts +206 -206
  206. package/templates/nextblock-template/lib/full-backup/server.ts +743 -743
  207. package/templates/nextblock-template/lib/i18n/country-languages.ts +247 -247
  208. package/templates/nextblock-template/lib/i18n/detection.test.ts +197 -197
  209. package/templates/nextblock-template/lib/i18n/detection.ts +192 -192
  210. package/templates/nextblock-template/lib/logos/active-logo.ts +53 -53
  211. package/templates/nextblock-template/lib/media/resolveMediaUrl.ts +54 -54
  212. package/templates/nextblock-template/lib/media/youtube.ts +99 -0
  213. package/templates/nextblock-template/lib/onboarding/actions.ts +31 -31
  214. package/templates/nextblock-template/lib/onboarding/status.ts +222 -222
  215. package/templates/nextblock-template/lib/posts/readTime.ts +60 -60
  216. package/templates/nextblock-template/lib/privacy/consent-client.ts +57 -57
  217. package/templates/nextblock-template/lib/privacy/contact-emails.ts +64 -64
  218. package/templates/nextblock-template/lib/privacy/settings.ts +115 -115
  219. package/templates/nextblock-template/lib/privacy/types.ts +69 -69
  220. package/templates/nextblock-template/lib/promotions/server.test.ts +74 -74
  221. package/templates/nextblock-template/lib/promotions/server.ts +741 -741
  222. package/templates/nextblock-template/lib/resolve-block-relations.test.ts +142 -142
  223. package/templates/nextblock-template/lib/resolve-block-relations.ts +255 -255
  224. package/templates/nextblock-template/lib/search/types.ts +27 -27
  225. package/templates/nextblock-template/lib/setup/actions.ts +460 -460
  226. package/templates/nextblock-template/lib/setup/env-status.ts +125 -125
  227. package/templates/nextblock-template/lib/setup/env-write.ts +111 -111
  228. package/templates/nextblock-template/lib/setup/migrations-bundle.ts +87 -82
  229. package/templates/nextblock-template/lib/setup/provisioning.ts +59 -59
  230. package/templates/nextblock-template/lib/setup/schema-apply.ts +408 -408
  231. package/templates/nextblock-template/lib/setup/system-config.ts +105 -105
  232. package/templates/nextblock-template/lib/setup/types.ts +18 -18
  233. package/templates/nextblock-template/lib/site-url.ts +48 -48
  234. package/templates/nextblock-template/lib/storage/provider.ts +66 -66
  235. package/templates/nextblock-template/lib/storage/supabase-storage.ts +103 -103
  236. package/templates/nextblock-template/lib/updates/check-upstream.ts +441 -441
  237. package/templates/nextblock-template/lib/updates/github-device.ts +206 -206
  238. package/templates/nextblock-template/lib/updates/repo-identity.ts +56 -56
  239. package/templates/nextblock-template/next-env.d.ts +1 -1
  240. package/templates/nextblock-template/next.config.js +6 -0
  241. package/templates/nextblock-template/package.json +1 -1
  242. package/templates/nextblock-template/postcss.config.js +6 -6
  243. package/templates/nextblock-template/scripts/backup.js +115 -115
  244. package/templates/nextblock-template/scripts/docker-setup.mjs +310 -310
  245. package/templates/nextblock-template/scripts/restore.js +385 -385
  246. package/templates/nextblock-template/scripts/verify-cortex-ai-build-widget.tsx +98 -98
  247. package/templates/nextblock-template/scripts/verify-cortex-ai-generate-blocks.ts +62 -62
  248. package/templates/nextblock-template/scripts/verify-cortex-ai-global-tools.ts +537 -537
  249. package/templates/nextblock-template/scripts/verify-cortex-ai-routing.ts +58 -58
  250. package/templates/nextblock-template/scripts/verify-custom-block-definitions.ts +188 -188
  251. package/templates/nextblock-template/scripts/verify-dynamic-custom-block-extensions.ts +123 -123
  252. package/templates/nextblock-template/scripts/verify-dynamic-layout-engine.tsx +133 -133
  253. package/templates/nextblock-template/scripts/verify-milestone-2-custom-blocks.ts +65 -65
  254. package/templates/nextblock-template/tailwind.config.js +25 -25
  255. package/templates/nextblock-template/tools/build-migrate.mjs +209 -209
  256. package/templates/nextblock-template/tools/configure-supabase-auth.js +282 -282
  257. package/templates/nextblock-template/tsconfig.tsbuildinfo +1 -1
  258. package/templates/nextblock-template/types/jsdom.d.ts +6 -6
  259. package/tsconfig.base.json +3 -3
@@ -1,105 +1,105 @@
1
- import 'server-only';
2
- // Server-only read/write for the `system_configuration` singleton table.
3
- // Mirrors lib/privacy/settings.ts, but the data lives in a dedicated ADMIN-only
4
- // table (migration 00000000000030) rather than the public site_settings store.
5
- import { createClient, getServiceRoleSupabaseClient } from '@nextblock-cms/db/server';
6
- import type { Json } from '@nextblock-cms/db';
7
- import { DEFAULT_SYSTEM_CONFIGURATION, type SystemConfiguration } from './types';
8
-
9
- function asBool(value: unknown, fallback: boolean): boolean {
10
- if (typeof value === 'boolean') return value;
11
- if (typeof value === 'string') return value === 'true' || value === 'on';
12
- return fallback;
13
- }
14
-
15
- function asSettings(value: unknown): Record<string, unknown> {
16
- return value && typeof value === 'object' && !Array.isArray(value)
17
- ? (value as Record<string, unknown>)
18
- : {};
19
- }
20
-
21
- type ConfigPatch = Partial<Pick<SystemConfiguration, 'auto_accept_signups'>> & {
22
- settings?: Record<string, unknown>;
23
- };
24
-
25
- /** Build a row payload, casting the loosely-typed settings object to the DB Json type. */
26
- function toRow(patch: ConfigPatch): {
27
- auto_accept_signups?: boolean;
28
- settings?: Json;
29
- updated_at: string;
30
- } {
31
- const row: { auto_accept_signups?: boolean; settings?: Json; updated_at: string } = {
32
- updated_at: new Date().toISOString(),
33
- };
34
- if (patch.auto_accept_signups !== undefined) {
35
- row.auto_accept_signups = patch.auto_accept_signups;
36
- }
37
- if (patch.settings !== undefined) {
38
- row.settings = patch.settings as unknown as Json;
39
- }
40
- return row;
41
- }
42
-
43
- /**
44
- * Read the singleton config. Uses the service-role client so it works from any
45
- * context — including the anonymous public sign-up path, which must read
46
- * `auto_accept_signups` even though the table is otherwise ADMIN-only. Falls back to
47
- * safe defaults when the service-role key is absent (unconfigured instance).
48
- */
49
- export async function getSystemConfiguration(): Promise<SystemConfiguration> {
50
- let supabase: ReturnType<typeof getServiceRoleSupabaseClient>;
51
- try {
52
- supabase = getServiceRoleSupabaseClient();
53
- } catch {
54
- return DEFAULT_SYSTEM_CONFIGURATION;
55
- }
56
-
57
- const { data, error } = await supabase
58
- .from('system_configuration')
59
- .select('auto_accept_signups, settings')
60
- .eq('id', 1)
61
- .maybeSingle();
62
-
63
- if (error || !data) {
64
- return DEFAULT_SYSTEM_CONFIGURATION;
65
- }
66
-
67
- return {
68
- auto_accept_signups: asBool(data.auto_accept_signups, false),
69
- settings: asSettings(data.settings),
70
- };
71
- }
72
-
73
- /**
74
- * Persist a partial update via the request-scoped client. RLS enforces ADMIN — used
75
- * by the CMS security settings page. The wizard (which runs before any admin exists)
76
- * uses setSystemConfigurationServiceRole instead.
77
- */
78
- export async function updateSystemConfiguration(patch: ConfigPatch): Promise<void> {
79
- const supabase = createClient();
80
- const { error } = await supabase
81
- .from('system_configuration')
82
- .update(toRow(patch))
83
- .eq('id', 1);
84
-
85
- if (error) {
86
- console.error('Error saving system configuration:', error.message);
87
- throw new Error('Failed to save system configuration.');
88
- }
89
- }
90
-
91
- /**
92
- * Seed/update the singleton with the service-role client (bypasses RLS). Used by the
93
- * /setup wizard before the first admin exists.
94
- */
95
- export async function setSystemConfigurationServiceRole(patch: ConfigPatch): Promise<void> {
96
- const supabase = getServiceRoleSupabaseClient();
97
- const { error } = await supabase
98
- .from('system_configuration')
99
- .upsert({ id: 1, ...toRow(patch) });
100
-
101
- if (error) {
102
- console.error('Error seeding system configuration:', error.message);
103
- throw new Error('Failed to seed system configuration.');
104
- }
105
- }
1
+ import 'server-only';
2
+ // Server-only read/write for the `system_configuration` singleton table.
3
+ // Mirrors lib/privacy/settings.ts, but the data lives in a dedicated ADMIN-only
4
+ // table (migration 00000000000030) rather than the public site_settings store.
5
+ import { createClient, getServiceRoleSupabaseClient } from '@nextblock-cms/db/server';
6
+ import type { Json } from '@nextblock-cms/db';
7
+ import { DEFAULT_SYSTEM_CONFIGURATION, type SystemConfiguration } from './types';
8
+
9
+ function asBool(value: unknown, fallback: boolean): boolean {
10
+ if (typeof value === 'boolean') return value;
11
+ if (typeof value === 'string') return value === 'true' || value === 'on';
12
+ return fallback;
13
+ }
14
+
15
+ function asSettings(value: unknown): Record<string, unknown> {
16
+ return value && typeof value === 'object' && !Array.isArray(value)
17
+ ? (value as Record<string, unknown>)
18
+ : {};
19
+ }
20
+
21
+ type ConfigPatch = Partial<Pick<SystemConfiguration, 'auto_accept_signups'>> & {
22
+ settings?: Record<string, unknown>;
23
+ };
24
+
25
+ /** Build a row payload, casting the loosely-typed settings object to the DB Json type. */
26
+ function toRow(patch: ConfigPatch): {
27
+ auto_accept_signups?: boolean;
28
+ settings?: Json;
29
+ updated_at: string;
30
+ } {
31
+ const row: { auto_accept_signups?: boolean; settings?: Json; updated_at: string } = {
32
+ updated_at: new Date().toISOString(),
33
+ };
34
+ if (patch.auto_accept_signups !== undefined) {
35
+ row.auto_accept_signups = patch.auto_accept_signups;
36
+ }
37
+ if (patch.settings !== undefined) {
38
+ row.settings = patch.settings as unknown as Json;
39
+ }
40
+ return row;
41
+ }
42
+
43
+ /**
44
+ * Read the singleton config. Uses the service-role client so it works from any
45
+ * context — including the anonymous public sign-up path, which must read
46
+ * `auto_accept_signups` even though the table is otherwise ADMIN-only. Falls back to
47
+ * safe defaults when the service-role key is absent (unconfigured instance).
48
+ */
49
+ export async function getSystemConfiguration(): Promise<SystemConfiguration> {
50
+ let supabase: ReturnType<typeof getServiceRoleSupabaseClient>;
51
+ try {
52
+ supabase = getServiceRoleSupabaseClient();
53
+ } catch {
54
+ return DEFAULT_SYSTEM_CONFIGURATION;
55
+ }
56
+
57
+ const { data, error } = await supabase
58
+ .from('system_configuration')
59
+ .select('auto_accept_signups, settings')
60
+ .eq('id', 1)
61
+ .maybeSingle();
62
+
63
+ if (error || !data) {
64
+ return DEFAULT_SYSTEM_CONFIGURATION;
65
+ }
66
+
67
+ return {
68
+ auto_accept_signups: asBool(data.auto_accept_signups, false),
69
+ settings: asSettings(data.settings),
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Persist a partial update via the request-scoped client. RLS enforces ADMIN — used
75
+ * by the CMS security settings page. The wizard (which runs before any admin exists)
76
+ * uses setSystemConfigurationServiceRole instead.
77
+ */
78
+ export async function updateSystemConfiguration(patch: ConfigPatch): Promise<void> {
79
+ const supabase = createClient();
80
+ const { error } = await supabase
81
+ .from('system_configuration')
82
+ .update(toRow(patch))
83
+ .eq('id', 1);
84
+
85
+ if (error) {
86
+ console.error('Error saving system configuration:', error.message);
87
+ throw new Error('Failed to save system configuration.');
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Seed/update the singleton with the service-role client (bypasses RLS). Used by the
93
+ * /setup wizard before the first admin exists.
94
+ */
95
+ export async function setSystemConfigurationServiceRole(patch: ConfigPatch): Promise<void> {
96
+ const supabase = getServiceRoleSupabaseClient();
97
+ const { error } = await supabase
98
+ .from('system_configuration')
99
+ .upsert({ id: 1, ...toRow(patch) });
100
+
101
+ if (error) {
102
+ console.error('Error seeding system configuration:', error.message);
103
+ throw new Error('Failed to seed system configuration.');
104
+ }
105
+ }
@@ -1,18 +1,18 @@
1
- // Pure, client-safe types for the system_configuration singleton.
2
- // No server-only dependencies — importable from client and server modules.
3
-
4
- export interface SystemConfiguration {
5
- /**
6
- * When true, new public sign-ups skip outbound email verification: the signup
7
- * route creates an already-confirmed account via the service role instead of
8
- * relying on an SMTP confirmation link. Default false (defense-in-depth).
9
- */
10
- auto_accept_signups: boolean;
11
- /** Forward-compatible feature toggles. Never holds secrets. */
12
- settings: Record<string, unknown>;
13
- }
14
-
15
- export const DEFAULT_SYSTEM_CONFIGURATION: SystemConfiguration = {
16
- auto_accept_signups: false,
17
- settings: {},
18
- };
1
+ // Pure, client-safe types for the system_configuration singleton.
2
+ // No server-only dependencies — importable from client and server modules.
3
+
4
+ export interface SystemConfiguration {
5
+ /**
6
+ * When true, new public sign-ups skip outbound email verification: the signup
7
+ * route creates an already-confirmed account via the service role instead of
8
+ * relying on an SMTP confirmation link. Default false (defense-in-depth).
9
+ */
10
+ auto_accept_signups: boolean;
11
+ /** Forward-compatible feature toggles. Never holds secrets. */
12
+ settings: Record<string, unknown>;
13
+ }
14
+
15
+ export const DEFAULT_SYSTEM_CONFIGURATION: SystemConfiguration = {
16
+ auto_accept_signups: false,
17
+ settings: {},
18
+ };
@@ -1,48 +1,48 @@
1
- // Resolve the canonical public site URL across deploy channels WITHOUT requiring
2
- // NEXT_PUBLIC_URL to be set. On Vercel it falls back to the auto-provisioned
3
- // production URL, so a one-click deploy needs no URL input at all.
4
- //
5
- // Dependency-free and safe to import anywhere — server components, route handlers,
6
- // or client components — like lib/setup/env-status.ts. It only reads `process.env`:
7
- // NEXT_PUBLIC_* names are inlined into the browser bundle at build time, the others
8
- // resolve server-side only (and are simply absent — harmless — in the browser).
9
-
10
- const stripTrailingSlash = (value: string): string => value.replace(/\/+$/, '');
11
-
12
- /**
13
- * Vercel always sets a production domain (even on preview deployments):
14
- * `VERCEL_PROJECT_PRODUCTION_URL` server-side, and Vercel exposes the
15
- * framework-prefixed `NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL` to the browser
16
- * bundle at build time. Neither includes the protocol. We prefer the production URL
17
- * over the per-deployment `VERCEL_URL` so absolute links (sitemap, canonical, OG)
18
- * stay stable across deploys.
19
- */
20
- function vercelProductionUrl(): string {
21
- const host =
22
- process.env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL?.trim() ||
23
- process.env.VERCEL_PROJECT_PRODUCTION_URL?.trim();
24
- return host ? `https://${stripTrailingSlash(host)}` : '';
25
- }
26
-
27
- /**
28
- * Canonical absolute site origin (e.g. `https://example.com`), no trailing slash.
29
- * Precedence: explicit `NEXT_PUBLIC_URL` → Vercel production URL → `fallback`.
30
- */
31
- export function resolveSiteUrl(fallback = 'http://localhost:3000'): string {
32
- const explicit = process.env.NEXT_PUBLIC_URL?.trim();
33
- if (explicit) return stripTrailingSlash(explicit);
34
-
35
- const vercel = vercelProductionUrl();
36
- if (vercel) return vercel;
37
-
38
- return stripTrailingSlash(fallback);
39
- }
40
-
41
- /**
42
- * True when a real, production-intended site URL is available (an explicit
43
- * `NEXT_PUBLIC_URL` or the Vercel production URL) — i.e. {@link resolveSiteUrl}
44
- * is NOT returning the local-dev fallback. Use this to gate "URL not set" warnings.
45
- */
46
- export function hasResolvedSiteUrl(): boolean {
47
- return Boolean(process.env.NEXT_PUBLIC_URL?.trim() || vercelProductionUrl());
48
- }
1
+ // Resolve the canonical public site URL across deploy channels WITHOUT requiring
2
+ // NEXT_PUBLIC_URL to be set. On Vercel it falls back to the auto-provisioned
3
+ // production URL, so a one-click deploy needs no URL input at all.
4
+ //
5
+ // Dependency-free and safe to import anywhere — server components, route handlers,
6
+ // or client components — like lib/setup/env-status.ts. It only reads `process.env`:
7
+ // NEXT_PUBLIC_* names are inlined into the browser bundle at build time, the others
8
+ // resolve server-side only (and are simply absent — harmless — in the browser).
9
+
10
+ const stripTrailingSlash = (value: string): string => value.replace(/\/+$/, '');
11
+
12
+ /**
13
+ * Vercel always sets a production domain (even on preview deployments):
14
+ * `VERCEL_PROJECT_PRODUCTION_URL` server-side, and Vercel exposes the
15
+ * framework-prefixed `NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL` to the browser
16
+ * bundle at build time. Neither includes the protocol. We prefer the production URL
17
+ * over the per-deployment `VERCEL_URL` so absolute links (sitemap, canonical, OG)
18
+ * stay stable across deploys.
19
+ */
20
+ function vercelProductionUrl(): string {
21
+ const host =
22
+ process.env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL?.trim() ||
23
+ process.env.VERCEL_PROJECT_PRODUCTION_URL?.trim();
24
+ return host ? `https://${stripTrailingSlash(host)}` : '';
25
+ }
26
+
27
+ /**
28
+ * Canonical absolute site origin (e.g. `https://example.com`), no trailing slash.
29
+ * Precedence: explicit `NEXT_PUBLIC_URL` → Vercel production URL → `fallback`.
30
+ */
31
+ export function resolveSiteUrl(fallback = 'http://localhost:3000'): string {
32
+ const explicit = process.env.NEXT_PUBLIC_URL?.trim();
33
+ if (explicit) return stripTrailingSlash(explicit);
34
+
35
+ const vercel = vercelProductionUrl();
36
+ if (vercel) return vercel;
37
+
38
+ return stripTrailingSlash(fallback);
39
+ }
40
+
41
+ /**
42
+ * True when a real, production-intended site URL is available (an explicit
43
+ * `NEXT_PUBLIC_URL` or the Vercel production URL) — i.e. {@link resolveSiteUrl}
44
+ * is NOT returning the local-dev fallback. Use this to gate "URL not set" warnings.
45
+ */
46
+ export function hasResolvedSiteUrl(): boolean {
47
+ return Boolean(process.env.NEXT_PUBLIC_URL?.trim() || vercelProductionUrl());
48
+ }
@@ -1,66 +1,66 @@
1
- import 'server-only';
2
-
3
- import { resolveSupabaseServiceKey, resolveSupabaseUrl } from '../setup/env-status';
4
-
5
- // NextBlock has two storage backends:
6
- //
7
- // 's3' — the existing S3-compatible path (Cloudflare R2, self-hosted MinIO, or
8
- // even Supabase Storage's S3 endpoint when you create S3 access keys).
9
- // Driven by the R2_* env vars + @aws-sdk/client-s3. Unchanged.
10
- // 'supabase' — native Supabase Storage via the supabase-js client and the injected
11
- // service-role/secret key. NO S3 access keys required. This is what makes
12
- // the one-click Vercel deploy work with zero storage configuration: the
13
- // Marketplace integration injects the Supabase URL + secret key but NOT S3
14
- // keys, so the S3 path can't authenticate — the native API can.
15
- //
16
- // The S3 path always wins when its keys are present, so local dev, Docker/MinIO, and BYO
17
- // Cloudflare R2 are completely unaffected. The native path is only chosen as the zero-key
18
- // fallback for a connected Supabase project.
19
-
20
- export type StorageBackend = 's3' | 'supabase';
21
-
22
- /** Which storage backend to use. Explicit STORAGE_PROVIDER wins; otherwise inferred. */
23
- export function getStorageBackend(): StorageBackend {
24
- const explicit = process.env.STORAGE_PROVIDER?.trim().toLowerCase();
25
- if (explicit === 'supabase') return 'supabase';
26
- if (explicit === 's3' || explicit === 'r2' || explicit === 'minio') return 's3';
27
-
28
- // S3/R2/MinIO wins whenever its credentials exist (local, Docker, BYO R2, or
29
- // Supabase-S3-with-keys). Only fall back to native Supabase Storage when there are no
30
- // S3 keys but a Supabase project IS connected (service key + URL) — the Vercel case.
31
- const hasS3Keys = Boolean(
32
- process.env.R2_ACCESS_KEY_ID && process.env.R2_SECRET_ACCESS_KEY,
33
- );
34
- if (hasS3Keys) return 's3';
35
- if (resolveSupabaseServiceKey() && resolveSupabaseUrl()) return 'supabase';
36
- return 's3';
37
- }
38
-
39
- /** The bucket name to read/write. Defaults to "media" on the native Supabase backend. */
40
- export function getStorageBucket(): string {
41
- const explicit = process.env.STORAGE_BUCKET || process.env.R2_BUCKET_NAME;
42
- if (explicit) return explicit;
43
- return getStorageBackend() === 'supabase' ? 'media' : '';
44
- }
45
-
46
- /**
47
- * The public base URL objects are served from — what resolveMediaUrl() joins object keys
48
- * onto. An explicit R2 public/base URL always wins; otherwise, on the native Supabase
49
- * backend, derive Supabase Storage's public object endpoint
50
- * (`<project>/storage/v1/object/public/<bucket>`) so every display call site works with
51
- * zero extra config. Kept in sync with the CJS computation in next.config.js.
52
- */
53
- export function resolveMediaBaseUrl(): string {
54
- const explicit =
55
- process.env.NEXT_PUBLIC_R2_BASE_URL || process.env.NEXT_PUBLIC_R2_PUBLIC_URL;
56
- if (explicit) return explicit.replace(/\/+$/, '');
57
-
58
- if (getStorageBackend() === 'supabase') {
59
- const url = resolveSupabaseUrl();
60
- const bucket = getStorageBucket();
61
- if (url && bucket) {
62
- return `${url.replace(/\/+$/, '')}/storage/v1/object/public/${bucket}`;
63
- }
64
- }
65
- return '';
66
- }
1
+ import 'server-only';
2
+
3
+ import { resolveSupabaseServiceKey, resolveSupabaseUrl } from '../setup/env-status';
4
+
5
+ // NextBlock has two storage backends:
6
+ //
7
+ // 's3' — the existing S3-compatible path (Cloudflare R2, self-hosted MinIO, or
8
+ // even Supabase Storage's S3 endpoint when you create S3 access keys).
9
+ // Driven by the R2_* env vars + @aws-sdk/client-s3. Unchanged.
10
+ // 'supabase' — native Supabase Storage via the supabase-js client and the injected
11
+ // service-role/secret key. NO S3 access keys required. This is what makes
12
+ // the one-click Vercel deploy work with zero storage configuration: the
13
+ // Marketplace integration injects the Supabase URL + secret key but NOT S3
14
+ // keys, so the S3 path can't authenticate — the native API can.
15
+ //
16
+ // The S3 path always wins when its keys are present, so local dev, Docker/MinIO, and BYO
17
+ // Cloudflare R2 are completely unaffected. The native path is only chosen as the zero-key
18
+ // fallback for a connected Supabase project.
19
+
20
+ export type StorageBackend = 's3' | 'supabase';
21
+
22
+ /** Which storage backend to use. Explicit STORAGE_PROVIDER wins; otherwise inferred. */
23
+ export function getStorageBackend(): StorageBackend {
24
+ const explicit = process.env.STORAGE_PROVIDER?.trim().toLowerCase();
25
+ if (explicit === 'supabase') return 'supabase';
26
+ if (explicit === 's3' || explicit === 'r2' || explicit === 'minio') return 's3';
27
+
28
+ // S3/R2/MinIO wins whenever its credentials exist (local, Docker, BYO R2, or
29
+ // Supabase-S3-with-keys). Only fall back to native Supabase Storage when there are no
30
+ // S3 keys but a Supabase project IS connected (service key + URL) — the Vercel case.
31
+ const hasS3Keys = Boolean(
32
+ process.env.R2_ACCESS_KEY_ID && process.env.R2_SECRET_ACCESS_KEY,
33
+ );
34
+ if (hasS3Keys) return 's3';
35
+ if (resolveSupabaseServiceKey() && resolveSupabaseUrl()) return 'supabase';
36
+ return 's3';
37
+ }
38
+
39
+ /** The bucket name to read/write. Defaults to "media" on the native Supabase backend. */
40
+ export function getStorageBucket(): string {
41
+ const explicit = process.env.STORAGE_BUCKET || process.env.R2_BUCKET_NAME;
42
+ if (explicit) return explicit;
43
+ return getStorageBackend() === 'supabase' ? 'media' : '';
44
+ }
45
+
46
+ /**
47
+ * The public base URL objects are served from — what resolveMediaUrl() joins object keys
48
+ * onto. An explicit R2 public/base URL always wins; otherwise, on the native Supabase
49
+ * backend, derive Supabase Storage's public object endpoint
50
+ * (`<project>/storage/v1/object/public/<bucket>`) so every display call site works with
51
+ * zero extra config. Kept in sync with the CJS computation in next.config.js.
52
+ */
53
+ export function resolveMediaBaseUrl(): string {
54
+ const explicit =
55
+ process.env.NEXT_PUBLIC_R2_BASE_URL || process.env.NEXT_PUBLIC_R2_PUBLIC_URL;
56
+ if (explicit) return explicit.replace(/\/+$/, '');
57
+
58
+ if (getStorageBackend() === 'supabase') {
59
+ const url = resolveSupabaseUrl();
60
+ const bucket = getStorageBucket();
61
+ if (url && bucket) {
62
+ return `${url.replace(/\/+$/, '')}/storage/v1/object/public/${bucket}`;
63
+ }
64
+ }
65
+ return '';
66
+ }