@nextblock-cms/db 0.11.2 → 0.12.0

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 (59) hide show
  1. package/lib/supabase/server.d.ts +9 -0
  2. package/lib/supabase/types.d.ts +9 -0
  3. package/package.json +1 -1
  4. package/supabase/migrations/00000000000000_baseline_schema.sql +2454 -0
  5. package/supabase/migrations/00000000000001_baseline_constraints_and_indexes.sql +1071 -0
  6. package/supabase/migrations/00000000000002_baseline_security_and_grants.sql +1335 -0
  7. package/supabase/migrations/00000000000003_baseline_seed.sql +468 -0
  8. package/supabase/migrations/00000000000004_default_logo_email_safe.sql +24 -0
  9. package/supabase/migrations/00000000000005_add_custom_canonical.sql +13 -0
  10. package/supabase/migrations/00000000000006_home_live_demo_promo.sql +67 -0
  11. package/supabase/templates/confirmation.html +1 -1
  12. package/supabase/templates/email_change.html +1 -1
  13. package/supabase/templates/invite.html +1 -1
  14. package/supabase/templates/magic_link.html +1 -1
  15. package/supabase/templates/reauthentication.html +1 -1
  16. package/supabase/templates/recovery.html +1 -1
  17. package/supabase/migrations/00000000000000_setup_foundation_and_enums.sql +0 -40
  18. package/supabase/migrations/00000000000001_setup_cms_core.sql +0 -124
  19. package/supabase/migrations/00000000000002_setup_content_tables.sql +0 -138
  20. package/supabase/migrations/00000000000003_setup_catalog_and_licensing.sql +0 -160
  21. package/supabase/migrations/00000000000004_setup_fulfillment_shipping_taxes_and_currencies.sql +0 -797
  22. package/supabase/migrations/00000000000005_setup_functions_and_triggers.sql +0 -767
  23. package/supabase/migrations/00000000000006_setup_rls_and_grants.sql +0 -872
  24. package/supabase/migrations/00000000000007_setup_indexes.sql +0 -158
  25. package/supabase/migrations/00000000000008_seed_platform_defaults.sql +0 -155
  26. package/supabase/migrations/00000000000009_seed_translations.sql +0 -801
  27. package/supabase/migrations/00000000000010_seed_content_scaffold.sql +0 -1088
  28. package/supabase/migrations/00000000000011_setup_cortex_ai_settings.sql +0 -81
  29. package/supabase/migrations/00000000000012_setup_commerce_coupons.sql +0 -275
  30. package/supabase/migrations/00000000000013_setup_cortex_ai_db_mutation_audit.sql +0 -47
  31. package/supabase/migrations/00000000000014_setup_content_drafts.sql +0 -119
  32. package/supabase/migrations/00000000000015_setup_product_drafts.sql +0 -97
  33. package/supabase/migrations/00000000000016_add_feature_image_to_pages.sql +0 -8
  34. package/supabase/migrations/00000000000017_add_product_blocks.sql +0 -118
  35. package/supabase/migrations/00000000000018_setup_bot_protection_settings.sql +0 -77
  36. package/supabase/migrations/00000000000019_add_product_categories.sql +0 -64
  37. package/supabase/migrations/00000000000020_add_category_translations.sql +0 -10
  38. package/supabase/migrations/00000000000021_migrate_hero_blocks_to_sections.sql +0 -29
  39. package/supabase/migrations/00000000000022_seed_cortex_ai_guide_post.sql +0 -184
  40. package/supabase/migrations/00000000000023_setup_custom_block_definitions.sql +0 -183
  41. package/supabase/migrations/00000000000024_setup_ucp_cart_sessions.sql +0 -72
  42. package/supabase/migrations/00000000000025_add_sale_schedule_columns.sql +0 -406
  43. package/supabase/migrations/00000000000026_seed_on_sale_translation.sql +0 -9
  44. package/supabase/migrations/00000000000027_setup_privacy_and_mfa.sql +0 -479
  45. package/supabase/migrations/00000000000028_clear_advisor_warnings.sql +0 -93
  46. package/supabase/migrations/00000000000029_refresh_setup_article.sql +0 -252
  47. package/supabase/migrations/00000000000030_setup_system_configuration.sql +0 -80
  48. package/supabase/migrations/00000000000031_seed_footer_navigation.sql +0 -85
  49. package/supabase/migrations/00000000000032_neutralize_seeded_contact_emails.sql +0 -34
  50. package/supabase/migrations/00000000000033_setup_config_settings.sql +0 -99
  51. package/supabase/migrations/00000000000034_enable_staff_2fa_reminder_default.sql +0 -15
  52. package/supabase/migrations/00000000000035_reassert_advisor_fixes.sql +0 -33
  53. package/supabase/migrations/00000000000036_setup_system_alerts.sql +0 -72
  54. package/supabase/migrations/00000000000037_refresh_setup_article_install_paths.sql +0 -317
  55. package/supabase/migrations/00000000000038_seed_consent_banner_translations.sql +0 -126
  56. package/supabase/migrations/00000000000039_setup_interactions.sql +0 -153
  57. package/supabase/migrations/00000000000040_add_profiles_fk_to_interactions.sql +0 -8
  58. package/supabase/migrations/00000000000041_seed_interactions_translations.sql +0 -145
  59. package/supabase/migrations/00000000000042_seed_deploy_to_vercel_ctas.sql +0 -72
@@ -1,252 +0,0 @@
1
- -- Refresh the seeded "How to Setup NextBlock" tutorial (EN + FR) so it matches the
2
- -- current installer flow:
3
- -- * `npm run setup` now requires Supabase + Cloudflare R2 + SMTP up front,
4
- -- * `npx nx serve nextblock` serves on http://localhost:4200, and
5
- -- * the first account to sign up automatically becomes the admin (email confirmation on).
6
- --
7
- -- Forward-only and idempotent: it replaces the single text block of the two setup-article
8
- -- posts seeded in 00000000000010_seed_content_scaffold.sql. Safe to re-run; a no-op if the
9
- -- posts do not exist.
10
-
11
- WITH target_posts AS (
12
- SELECT id, language_id, slug
13
- FROM public.posts
14
- WHERE slug IN ('how-to-setup-nextblock', 'comment-configurer-nextblock')
15
- ),
16
- purged AS (
17
- DELETE FROM public.blocks
18
- WHERE post_id IN (SELECT id FROM target_posts)
19
- )
20
- INSERT INTO public.blocks (post_id, language_id, block_type, content, "order")
21
-
22
- -- EN: How to Setup NextBlock
23
- SELECT tp.id, tp.language_id, 'text', jsonb_build_object('html_content',
24
- $$<p class='text-lg leading-8 text-slate-700 dark:text-slate-300'>There are two strong ways to start with NextBlock: clone the full monorepo if you want the whole platform, or scaffold a standalone app if you want to ship quickly. Both paths land you on the same editorial model, design system, and CMS foundation.</p>
25
-
26
- <div class='rounded-[2rem] border border-blue-200 bg-blue-50/80 p-6 my-10 dark:border-blue-500/20 dark:bg-blue-500/10'>
27
- <p class='text-xs font-semibold uppercase tracking-[0.22em] text-blue-700 dark:text-blue-200'>Choose your path</p>
28
- <div class='grid gap-6 md:grid-cols-2 mt-5'>
29
- <div class='rounded-2xl border border-blue-100 bg-white p-5 dark:border-blue-500/10 dark:bg-slate-900/40'>
30
- <h3 class='mt-0 text-xl text-slate-900 dark:text-white'>Monorepo</h3>
31
- <p class='text-sm text-slate-600 dark:text-slate-300'>Best for contributors, plugin authors, and teams that want direct access to every app and shared package.</p>
32
- </div>
33
- <div class='rounded-2xl border border-blue-100 bg-white p-5 dark:border-blue-500/10 dark:bg-slate-900/40'>
34
- <h3 class='mt-0 text-xl text-slate-900 dark:text-white'>CLI starter</h3>
35
- <p class='text-sm text-slate-600 dark:text-slate-300'>Best for launching a production-ready Next.js project with NextBlock&trade; already wired in and easy to deploy.</p>
36
- </div>
37
- </div>
38
- </div>
39
-
40
- <figure class='my-12 overflow-hidden rounded-[2rem] border border-slate-200/80 bg-slate-950 shadow-2xl dark:border-white/10'>
41
- <img src='/images/included.webp' alt='NextBlock&trade; platform artwork showing the CMS, blocks, and integrations that ship together' class='w-full h-auto object-cover' />
42
- <figcaption class='border-t border-white/10 px-6 py-4 text-sm text-slate-300'>Whichever path you choose, you still inherit the same block editor, CMS shell, and shared product language.</figcaption>
43
- </figure>
44
-
45
- <div class='rounded-[2rem] border border-amber-200 bg-amber-50/80 p-6 my-10 dark:border-amber-500/20 dark:bg-amber-500/10'>
46
- <p class='text-xs font-semibold uppercase tracking-[0.22em] text-amber-700 dark:text-amber-200'>Before you start</p>
47
- <p class='mt-3 mb-0 text-sm text-slate-700 dark:text-slate-200'>The setup wizard asks for credentials from three services, so create them first:</p>
48
- <ul class='mt-4 list-disc pl-6 space-y-2 text-sm text-slate-700 dark:text-slate-200'>
49
- <li><strong>Supabase project</strong> &ndash; Reference ID (Project Settings &gt; General), connection string (Connect &gt; Direct connection &gt; URI), the anon and service_role keys, and a Personal Access Token (Account &gt; Access Tokens).</li>
50
- <li><strong>Cloudflare R2 bucket</strong> &ndash; create a bucket, enable its Public Development URL, and create an Account API token with Object Read &amp; Write. Copy the Access Key ID and Secret Access Key (shown only once).</li>
51
- <li><strong>SMTP credentials</strong> &ndash; SMTP2GO works very well; required so Supabase can email the confirmation link your first admin needs to sign in.</li>
52
- </ul>
53
- </div>
54
-
55
- <h2>Path 1: Clone the Monorepo</h2>
56
- <p>This route is ideal when you want the full Nx workspace and every internal package available locally.</p>
57
-
58
- <div class='grid gap-6 md:grid-cols-2 my-8'>
59
- <div class='rounded-3xl border border-slate-200/80 bg-slate-50 p-6 dark:border-white/10 dark:bg-white/5'>
60
- <p class='text-xs font-semibold uppercase tracking-[0.22em] text-slate-500 dark:text-slate-400'>You get</p>
61
- <ul class='mt-4 list-disc pl-5 space-y-2 text-sm'>
62
- <li>The public site, CMS app, CLI source, and shared libraries</li>
63
- <li>Direct access to <code>libs/</code> for custom block and package work</li>
64
- <li>Workspace tools like <code>nx graph</code> for dependency visibility</li>
65
- </ul>
66
- </div>
67
- <div class='rounded-3xl border border-slate-200/80 bg-slate-50 p-6 dark:border-white/10 dark:bg-white/5'>
68
- <p class='text-xs font-semibold uppercase tracking-[0.22em] text-slate-500 dark:text-slate-400'>Good fit for</p>
69
- <ul class='mt-4 list-disc pl-5 space-y-2 text-sm'>
70
- <li>Core contributors and maintainers</li>
71
- <li>Teams building custom modules or premium extensions</li>
72
- <li>Agencies that want end-to-end control over the platform</li>
73
- </ul>
74
- </div>
75
- </div>
76
-
77
- <pre><code>git clone https://github.com/nextblock-cms/nextblock.git
78
- cd nextblock
79
- npm install
80
- npm run setup</code></pre>
81
-
82
- <p>The <code>npm run setup</code> wizard creates <code>.env.local</code>, collects your Supabase, Cloudflare R2, and SMTP details, generates local secrets (<code>CRON_SECRET</code>, <code>DRAFT_MODE_SECRET</code>, <code>REVALIDATE_SECRET_TOKEN</code>), links the Supabase CLI, and applies the full schema to your database with <code>npm run db:migrate:fresh</code>.</p>
83
-
84
- <p>Then start the app:</p>
85
- <pre><code>npx nx serve nextblock</code></pre>
86
-
87
- <div class='rounded-3xl border border-emerald-200 bg-emerald-50/80 p-6 my-8 dark:border-emerald-500/20 dark:bg-emerald-500/10'>
88
- <p class='text-xs font-semibold uppercase tracking-[0.22em] text-emerald-700 dark:text-emerald-200'>First login</p>
89
- <p class='mt-3 mb-0 text-sm text-slate-700 dark:text-slate-200'>The dev server runs at <code>http://localhost:4200</code>. Open <code>/sign-up</code> and create your account &ndash; the first account to register automatically becomes the admin. Confirm your email (or confirm the user in Supabase &gt; Authentication &gt; Users), then sign in to reach the CMS at <code>/cms/dashboard</code>.</p>
90
- </div>
91
-
92
- <p>Useful monorepo commands:</p>
93
- <pre><code># Build every workspace package
94
- npm run all-builds
95
-
96
- # Lint the main application
97
- npm run nx:lint:nextblock
98
-
99
- # Regenerate database types
100
- npm run db:types
101
-
102
- # Inspect workspace relationships
103
- npx nx graph</code></pre>
104
-
105
- <h2>Path 2: Use the CLI Starter</h2>
106
- <p>If your goal is to launch quickly, the CLI gives you a standalone Next.js app with NextBlock&trade; already embedded.</p>
107
-
108
- <pre><code>npm create nextblock@latest my-site
109
- cd my-site</code></pre>
110
-
111
- <p>The CLI copies a production-ready template, rewrites workspace imports to published packages, and can run the same setup flow for you. Your result is a normal Next.js app with no Nx requirement, so <code>npm run dev</code> serves it on <code>http://localhost:3000</code>.</p>
112
-
113
- <p>Configure your environment in <code>.env.local</code>:</p>
114
- <pre><code>NEXT_PUBLIC_SUPABASE_URL=your-project-url
115
- NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
116
- SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
117
- NEXT_PUBLIC_URL=http://localhost:3000</code></pre>
118
-
119
- <p>Push the schema and start developing:</p>
120
- <pre><code>npm run db:push
121
- npm run dev</code></pre>
122
-
123
- <div class='rounded-3xl border border-amber-200 bg-amber-50/80 p-6 my-8 dark:border-amber-500/20 dark:bg-amber-500/10'>
124
- <p class='text-xs font-semibold uppercase tracking-[0.22em] text-amber-700 dark:text-amber-200'>Tip</p>
125
- <p class='mb-0 text-sm text-slate-700 dark:text-slate-200'>The CLI path is the fastest way to evaluate NextBlock&trade; with your own content model before you decide whether you need the full workspace.</p>
126
- </div>
127
-
128
- <h2>Activating Premium Modules</h2>
129
- <p>For CLI-generated projects, the commerce package can be activated with a single command:</p>
130
- <pre><code>npx create-nextblock activate ecommerce</code></pre>
131
- <p>This injects wrappers for <code>/cms/orders</code>, <code>/cms/products</code>, <code>/checkout</code>, and the checkout API, all gated through <code>verifyPackageOnline()</code> so premium routes stay aligned with your license.</p>
132
-
133
- <h2>Deployment</h2>
134
- <p>NextBlock&trade; deploys like a standard Next.js app. Push to Vercel, Netlify, or any Node.js host, then make sure your server-side environment variables such as the Supabase service role, R2 credentials, SMTP, and <code>CRON_SECRET</code> are configured in that environment, and set <code>NEXT_PUBLIC_URL</code> to your production domain.</p>$$
135
- ), 0 FROM target_posts tp WHERE tp.slug = 'how-to-setup-nextblock'
136
-
137
- UNION ALL
138
-
139
- -- FR: Comment configurer NextBlock
140
- SELECT tp.id, tp.language_id, 'text', jsonb_build_object('html_content',
141
- $$<p class='text-lg leading-8 text-slate-700 dark:text-slate-300'>Il existe deux bonnes facons de lancer NextBlock&trade; : cloner le monorepo complet si vous voulez toute la plateforme, ou partir du CLI si vous voulez aller vite. Dans les deux cas, vous retrouvez le meme modele editorial, le meme shell CMS et la meme base produit.</p>
142
-
143
- <div class='rounded-[2rem] border border-blue-200 bg-blue-50/80 p-6 my-10 dark:border-blue-500/20 dark:bg-blue-500/10'>
144
- <p class='text-xs font-semibold uppercase tracking-[0.22em] text-blue-700 dark:text-blue-200'>Choisissez votre chemin</p>
145
- <div class='grid gap-6 md:grid-cols-2 mt-5'>
146
- <div class='rounded-2xl border border-blue-100 bg-white p-5 dark:border-blue-500/10 dark:bg-slate-900/40'>
147
- <h3 class='mt-0 text-xl text-slate-900 dark:text-white'>Monorepo</h3>
148
- <p class='text-sm text-slate-600 dark:text-slate-300'>Ideal pour les contributeurs, auteurs de plugins et equipes qui veulent travailler directement dans tous les packages partages.</p>
149
- </div>
150
- <div class='rounded-2xl border border-blue-100 bg-white p-5 dark:border-blue-500/10 dark:bg-slate-900/40'>
151
- <h3 class='mt-0 text-xl text-slate-900 dark:text-white'>Starter CLI</h3>
152
- <p class='text-sm text-slate-600 dark:text-slate-300'>Ideal pour demarrer une app Next.js prete a deployer avec NextBlock&trade; deja integre.</p>
153
- </div>
154
- </div>
155
- </div>
156
-
157
- <figure class='my-12 overflow-hidden rounded-[2rem] border border-slate-200/80 bg-slate-950 shadow-2xl dark:border-white/10'>
158
- <img src='/images/included.webp' alt='Visuel NextBlock&trade; montrant le CMS, les blocs et les integrations qui arrivent ensemble' class='w-full h-auto object-cover' />
159
- <figcaption class='border-t border-white/10 px-6 py-4 text-sm text-slate-300'>Quel que soit le chemin choisi, vous heritez du meme editeur de blocs, du meme shell CMS et du meme langage produit.</figcaption>
160
- </figure>
161
-
162
- <div class='rounded-[2rem] border border-amber-200 bg-amber-50/80 p-6 my-10 dark:border-amber-500/20 dark:bg-amber-500/10'>
163
- <p class='text-xs font-semibold uppercase tracking-[0.22em] text-amber-700 dark:text-amber-200'>Avant de commencer</p>
164
- <p class='mt-3 mb-0 text-sm text-slate-700 dark:text-slate-200'>L'assistant de configuration demande des identifiants de trois services, alors creez-les d'abord :</p>
165
- <ul class='mt-4 list-disc pl-6 space-y-2 text-sm text-slate-700 dark:text-slate-200'>
166
- <li><strong>Projet Supabase</strong> &ndash; Reference ID (Project Settings &gt; General), chaine de connexion (Connect &gt; Direct connection &gt; URI), les cles anon et service_role, et un Personal Access Token (Account &gt; Access Tokens).</li>
167
- <li><strong>Bucket Cloudflare R2</strong> &ndash; creez un bucket, activez son Public Development URL, et creez un Account API token avec Object Read &amp; Write. Copiez l'Access Key ID et la Secret Access Key (affichee une seule fois).</li>
168
- <li><strong>Identifiants SMTP</strong> &ndash; SMTP2GO fonctionne tres bien ; requis pour que Supabase envoie le lien de confirmation dont votre premier admin a besoin pour se connecter.</li>
169
- </ul>
170
- </div>
171
-
172
- <h2>Chemin 1 : cloner le monorepo</h2>
173
- <p>Cette option est la meilleure si vous voulez tout le workspace Nx et chaque package interne disponible en local.</p>
174
-
175
- <div class='grid gap-6 md:grid-cols-2 my-8'>
176
- <div class='rounded-3xl border border-slate-200/80 bg-slate-50 p-6 dark:border-white/10 dark:bg-white/5'>
177
- <p class='text-xs font-semibold uppercase tracking-[0.22em] text-slate-500 dark:text-slate-400'>Vous obtenez</p>
178
- <ul class='mt-4 list-disc pl-5 space-y-2 text-sm'>
179
- <li>Le site public, l'app CMS, le code du CLI et les librairies partagees</li>
180
- <li>Un acces direct a <code>libs/</code> pour les blocs et modules personnalises</li>
181
- <li>Les outils de workspace comme <code>nx graph</code> pour visualiser les dependances</li>
182
- </ul>
183
- </div>
184
- <div class='rounded-3xl border border-slate-200/80 bg-slate-50 p-6 dark:border-white/10 dark:bg-white/5'>
185
- <p class='text-xs font-semibold uppercase tracking-[0.22em] text-slate-500 dark:text-slate-400'>Bon choix pour</p>
186
- <ul class='mt-4 list-disc pl-5 space-y-2 text-sm'>
187
- <li>Les mainteneurs et contributeurs coeur</li>
188
- <li>Les equipes qui construisent des extensions sur mesure</li>
189
- <li>Les agences qui veulent un controle complet de la plateforme</li>
190
- </ul>
191
- </div>
192
- </div>
193
-
194
- <pre><code>git clone https://github.com/nextblock-cms/nextblock.git
195
- cd nextblock
196
- npm install
197
- npm run setup</code></pre>
198
-
199
- <p>L'assistant <code>npm run setup</code> cree <code>.env.local</code>, collecte vos identifiants Supabase, Cloudflare R2 et SMTP, genere les secrets locaux (<code>CRON_SECRET</code>, <code>DRAFT_MODE_SECRET</code>, <code>REVALIDATE_SECRET_TOKEN</code>), lie le CLI Supabase et applique le schema complet a votre base avec <code>npm run db:migrate:fresh</code>.</p>
200
-
201
- <p>Puis lancez l'application :</p>
202
- <pre><code>npx nx serve nextblock</code></pre>
203
-
204
- <div class='rounded-3xl border border-emerald-200 bg-emerald-50/80 p-6 my-8 dark:border-emerald-500/20 dark:bg-emerald-500/10'>
205
- <p class='text-xs font-semibold uppercase tracking-[0.22em] text-emerald-700 dark:text-emerald-200'>Premiere connexion</p>
206
- <p class='mt-3 mb-0 text-sm text-slate-700 dark:text-slate-200'>Le serveur de dev tourne sur <code>http://localhost:4200</code>. Ouvrez <code>/sign-up</code> et creez votre compte &ndash; le premier compte inscrit devient automatiquement l'administrateur. Confirmez votre email (ou confirmez l'utilisateur dans Supabase &gt; Authentication &gt; Users), puis connectez-vous pour acceder au CMS sur <code>/cms/dashboard</code>.</p>
207
- </div>
208
-
209
- <p>Commandes utiles dans le monorepo :</p>
210
- <pre><code># Build de tous les packages
211
- npm run all-builds
212
-
213
- # Lint de l'application principale
214
- npm run nx:lint:nextblock
215
-
216
- # Regenerer les types base de donnees
217
- npm run db:types
218
-
219
- # Inspecter les relations du workspace
220
- npx nx graph</code></pre>
221
-
222
- <h2>Chemin 2 : utiliser le starter CLI</h2>
223
- <p>Si votre but est d'aller vite, le CLI vous donne une app Next.js autonome avec NextBlock&trade; deja integre.</p>
224
-
225
- <pre><code>npm create nextblock@latest mon-site
226
- cd mon-site</code></pre>
227
-
228
- <p>Le CLI copie un template pret pour la production, remplace les imports workspace par les packages publies, et peut lancer la meme configuration initiale. Le resultat reste une app Next.js classique, sans dependance a Nx, donc <code>npm run dev</code> la sert sur <code>http://localhost:3000</code>.</p>
229
-
230
- <p>Configurez votre environnement dans <code>.env.local</code> :</p>
231
- <pre><code>NEXT_PUBLIC_SUPABASE_URL=your-project-url
232
- NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
233
- SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
234
- NEXT_PUBLIC_URL=http://localhost:3000</code></pre>
235
-
236
- <p>Poussez le schema puis demarrez :</p>
237
- <pre><code>npm run db:push
238
- npm run dev</code></pre>
239
-
240
- <div class='rounded-3xl border border-amber-200 bg-amber-50/80 p-6 my-8 dark:border-amber-500/20 dark:bg-amber-500/10'>
241
- <p class='text-xs font-semibold uppercase tracking-[0.22em] text-amber-700 dark:text-amber-200'>Conseil</p>
242
- <p class='mb-0 text-sm text-slate-700 dark:text-slate-200'>Le chemin CLI est le moyen le plus rapide d'evaluer NextBlock&trade; avec votre propre modele de contenu avant de passer, si besoin, au workspace complet.</p>
243
- </div>
244
-
245
- <h2>Activer les modules premium</h2>
246
- <p>Pour un projet genere via le CLI, le package commerce peut etre active avec une seule commande :</p>
247
- <pre><code>npx create-nextblock activate ecommerce</code></pre>
248
- <p>Cette commande injecte les wrappers pour <code>/cms/orders</code>, <code>/cms/products</code>, <code>/checkout</code> et l'API checkout, le tout protege par <code>verifyPackageOnline()</code> afin de garder les routes premium alignees avec la licence.</p>
249
-
250
- <h2>Deploiement</h2>
251
- <p>NextBlock&trade; se deploie comme une app Next.js standard. Publiez sur Vercel, Netlify ou tout hebergeur Node.js, puis configurez les variables serveur comme la cle service role Supabase, les identifiants R2, le SMTP et <code>CRON_SECRET</code>, et definissez <code>NEXT_PUBLIC_URL</code> sur votre domaine de production.</p>$$
252
- ), 0 FROM target_posts tp WHERE tp.slug = 'comment-configurer-nextblock';
@@ -1,80 +0,0 @@
1
- -- 00000000000030_setup_system_configuration.sql
2
- -- First-Boot Setup Wizard: global system configuration.
3
- --
4
- -- Adds a dedicated, RLS-locked `system_configuration` table that holds settings the
5
- -- browser /setup wizard manages and that don't belong in the public key-value
6
- -- `site_settings` store. It is a singleton (exactly one row, id = 1).
7
- --
8
- -- Shape:
9
- -- auto_accept_signups boolean -- when true, new public sign-ups skip outbound email
10
- -- verification (the signup route uses a service-role
11
- -- admin.createUser({ email_confirm: true }) path).
12
- -- settings jsonb -- forward-compatible catch-all for future feature
13
- -- toggles ({} by default). Do NOT store true secrets
14
- -- here (Turnstile/AI secrets keep living in their
15
- -- existing site_settings sensitive keys).
16
- --
17
- -- Access is locked to the ADMIN role for normal clients (NextBlock has no separate
18
- -- "super-admin" tier — ADMIN is the top level). The service_role retains full access
19
- -- so the wizard can seed/read it before any admin exists.
20
-
21
- CREATE TABLE IF NOT EXISTS public.system_configuration (
22
- id integer PRIMARY KEY DEFAULT 1,
23
- auto_accept_signups boolean NOT NULL DEFAULT false,
24
- settings jsonb NOT NULL DEFAULT '{}'::jsonb,
25
- updated_at timestamptz NOT NULL DEFAULT now(),
26
- CONSTRAINT system_configuration_singleton CHECK (id = 1)
27
- );
28
-
29
- COMMENT ON TABLE public.system_configuration IS
30
- 'Singleton (id = 1) of global setup-wizard configuration. ADMIN-only via RLS; never store secrets in settings.';
31
-
32
- -- Seed the single row so reads always find it.
33
- INSERT INTO public.system_configuration (id, auto_accept_signups, settings)
34
- VALUES (1, false, '{}'::jsonb)
35
- ON CONFLICT (id) DO NOTHING;
36
-
37
- ALTER TABLE public.system_configuration ENABLE ROW LEVEL SECURITY;
38
-
39
- GRANT SELECT, INSERT, UPDATE, DELETE ON public.system_configuration TO authenticated;
40
- GRANT ALL ON public.system_configuration TO service_role;
41
-
42
- -- ADMIN-only for every operation by authenticated clients.
43
- DROP POLICY IF EXISTS system_configuration_admin_select ON public.system_configuration;
44
- CREATE POLICY system_configuration_admin_select
45
- ON public.system_configuration
46
- FOR SELECT
47
- TO authenticated
48
- USING ((SELECT public.get_current_user_role()) = 'ADMIN');
49
-
50
- DROP POLICY IF EXISTS system_configuration_admin_insert ON public.system_configuration;
51
- CREATE POLICY system_configuration_admin_insert
52
- ON public.system_configuration
53
- FOR INSERT
54
- TO authenticated
55
- WITH CHECK ((SELECT public.get_current_user_role()) = 'ADMIN');
56
-
57
- DROP POLICY IF EXISTS system_configuration_admin_update ON public.system_configuration;
58
- CREATE POLICY system_configuration_admin_update
59
- ON public.system_configuration
60
- FOR UPDATE
61
- TO authenticated
62
- USING ((SELECT public.get_current_user_role()) = 'ADMIN')
63
- WITH CHECK ((SELECT public.get_current_user_role()) = 'ADMIN');
64
-
65
- DROP POLICY IF EXISTS system_configuration_admin_delete ON public.system_configuration;
66
- CREATE POLICY system_configuration_admin_delete
67
- ON public.system_configuration
68
- FOR DELETE
69
- TO authenticated
70
- USING ((SELECT public.get_current_user_role()) = 'ADMIN');
71
-
72
- -- Service role bypasses the ADMIN checks (used by the wizard before an admin exists,
73
- -- and by the signup route to read auto_accept_signups as an anonymous visitor).
74
- DROP POLICY IF EXISTS system_configuration_service_role_all ON public.system_configuration;
75
- CREATE POLICY system_configuration_service_role_all
76
- ON public.system_configuration
77
- FOR ALL
78
- TO service_role
79
- USING (true)
80
- WITH CHECK (true);
@@ -1,85 +0,0 @@
1
- -- 00000000000031_seed_footer_navigation.sql
2
- -- Seed editable FOOTER navigation items (EN + FR).
3
- --
4
- -- The public footer used to hard-code its "Privacy Policy" and "Terms of Service"
5
- -- links in apps/nextblock/components/AppShell.tsx. This migration turns them into
6
- -- real navigation_items rows under the FOOTER menu so they are editable from the
7
- -- CMS (/cms/navigation) like any other menu, and so the French footer points at
8
- -- the localized page slugs.
9
- --
10
- -- Targets the legal pages seeded by 00000000000027_setup_privacy_and_mfa.sql:
11
- -- EN /privacy-policy FR /politique-de-confidentialite
12
- -- EN /terms-of-service FR /conditions-utilisation
13
- --
14
- -- Idempotent: re-running replaces the FOOTER rows it owns (matched by URL) and
15
- -- reuses their translation_group_id so EN/FR stay linked across re-runs.
16
- DO $seed_footer$
17
- DECLARE
18
- v_en bigint;
19
- v_fr bigint;
20
- v_privacy_group uuid;
21
- v_terms_group uuid;
22
- v_en_privacy_page bigint;
23
- v_fr_privacy_page bigint;
24
- v_en_terms_page bigint;
25
- v_fr_terms_page bigint;
26
- BEGIN
27
- SELECT id INTO v_en FROM public.languages WHERE code = 'en' LIMIT 1;
28
- SELECT id INTO v_fr FROM public.languages WHERE code = 'fr' LIMIT 1;
29
-
30
- IF v_en IS NULL THEN
31
- RAISE NOTICE 'Default language "en" not found; skipping footer navigation seed.';
32
- RETURN;
33
- END IF;
34
-
35
- -- Reuse existing translation groups if these footer items were seeded before,
36
- -- so EN <-> FR stay paired and re-runs do not orphan translations.
37
- SELECT translation_group_id INTO v_privacy_group
38
- FROM public.navigation_items
39
- WHERE menu_key = 'FOOTER' AND url = '/privacy-policy' LIMIT 1;
40
- IF v_privacy_group IS NULL THEN v_privacy_group := gen_random_uuid(); END IF;
41
-
42
- SELECT translation_group_id INTO v_terms_group
43
- FROM public.navigation_items
44
- WHERE menu_key = 'FOOTER' AND url = '/terms-of-service' LIMIT 1;
45
- IF v_terms_group IS NULL THEN v_terms_group := gen_random_uuid(); END IF;
46
-
47
- -- Best-effort link each item to its underlying page (ON DELETE SET NULL keeps
48
- -- the link working even if a page is later removed; url remains the source of truth).
49
- SELECT id INTO v_en_privacy_page
50
- FROM public.pages WHERE slug = 'privacy-policy' AND language_id = v_en LIMIT 1;
51
- SELECT id INTO v_en_terms_page
52
- FROM public.pages WHERE slug = 'terms-of-service' AND language_id = v_en LIMIT 1;
53
-
54
- -- Remove any prior copies of the footer links we own, then re-insert cleanly.
55
- DELETE FROM public.navigation_items
56
- WHERE menu_key = 'FOOTER'
57
- AND url IN (
58
- '/privacy-policy', '/terms-of-service',
59
- '/politique-de-confidentialite', '/conditions-utilisation'
60
- );
61
-
62
- -- ----- English footer -----
63
- INSERT INTO public.navigation_items
64
- (language_id, menu_key, label, url, "order", page_id, translation_group_id)
65
- VALUES
66
- (v_en, 'FOOTER', 'Privacy Policy', '/privacy-policy', 0, v_en_privacy_page, v_privacy_group),
67
- (v_en, 'FOOTER', 'Terms of Service', '/terms-of-service', 1, v_en_terms_page, v_terms_group);
68
-
69
- -- ----- French footer (only if the French language exists) -----
70
- IF v_fr IS NOT NULL THEN
71
- SELECT id INTO v_fr_privacy_page
72
- FROM public.pages WHERE slug = 'politique-de-confidentialite' AND language_id = v_fr LIMIT 1;
73
- SELECT id INTO v_fr_terms_page
74
- FROM public.pages WHERE slug = 'conditions-utilisation' AND language_id = v_fr LIMIT 1;
75
-
76
- INSERT INTO public.navigation_items
77
- (language_id, menu_key, label, url, "order", page_id, translation_group_id)
78
- VALUES
79
- (v_fr, 'FOOTER', 'Politique de confidentialité', '/politique-de-confidentialite', 0, v_fr_privacy_page, v_privacy_group),
80
- (v_fr, 'FOOTER', 'Conditions d''utilisation', '/conditions-utilisation', 1, v_fr_terms_page, v_terms_group);
81
- END IF;
82
-
83
- RAISE NOTICE 'Seeded FOOTER navigation items (Privacy Policy, Terms of Service).';
84
- END;
85
- $seed_footer$;
@@ -1,34 +0,0 @@
1
- -- 00000000000032_neutralize_seeded_contact_emails.sql
2
- -- Remove the original authors' contact addresses from seeded content so a
3
- -- downloaded / self-hosted copy of NextBlock never routes mail to us.
4
- --
5
- -- Earlier migrations baked real addresses into block content:
6
- -- * 00000000000027 seeded `privacy@nextblock.dev` across the Privacy Policy and
7
- -- Terms pages (EN + FR), as visible text and `mailto:` links.
8
- -- * 00000000000010 seeded a `mailto:info@nextblock.dev` CTA on the French home
9
- -- page (the English page correctly links to /contact), and `foo@bar.com` as
10
- -- the contact form recipient.
11
- --
12
- -- Migrations are append-only, so this is a forward-only data fix rather than an
13
- -- edit of those files. Each statement is idempotent (a no-op once applied).
14
- --
15
- -- The `{{privacy_email}}` token is resolved at render time by the app
16
- -- (apps/nextblock/lib/privacy/contact-emails.ts): admin "Support email" setting
17
- -- -> SANDBOX_PRIVACY_EMAIL env (sandbox only) -> privacy@example.com fallback.
18
-
19
- -- 1. Privacy / Terms legal pages: swap the hard-coded address for a merge tag.
20
- UPDATE public.blocks
21
- SET content = replace(content::text, 'privacy@nextblock.dev', '{{privacy_email}}')::jsonb
22
- WHERE content::text LIKE '%privacy@nextblock.dev%';
23
-
24
- -- 2. French home "Nous contacter" CTA: point at the contact form like the English
25
- -- page instead of a mailto to our inbox.
26
- UPDATE public.blocks
27
- SET content = replace(content::text, 'mailto:info@nextblock.dev', '/contact')::jsonb
28
- WHERE content::text LIKE '%mailto:info@nextblock.dev%';
29
-
30
- -- 3. Contact form default recipient: use a neutral placeholder. In sandbox the
31
- -- app overrides this with SANDBOX_CONTACT_EMAIL at submit time.
32
- UPDATE public.blocks
33
- SET content = replace(content::text, 'foo@bar.com', 'contact@example.com')::jsonb
34
- WHERE content::text LIKE '%foo@bar.com%';
@@ -1,99 +0,0 @@
1
- -- 00000000000033_setup_config_settings.sql
2
- -- DB-backed CMS configuration: move SMTP and payment-provider credentials out of
3
- -- environment variables and into `site_settings`. Secret rows (email_secret,
4
- -- payment_secret) hold AES-256-GCM envelopes and are restricted to ADMIN / service_role,
5
- -- extending the sensitive-key masking established in migration 018. Public rows hold
6
- -- non-secret config (SMTP host/from, publishable keys, provider flags) and the dashboard
7
- -- onboarding state.
8
- --
9
- -- This re-issues ALL FOUR site_settings policies because the masked-key list is embedded
10
- -- in each policy body and Postgres has no incremental "add a key" operation.
11
-
12
- COMMENT ON TABLE public.site_settings IS 'Key-value store for global site settings. Sensitive keys (Cortex AI BYOK, Bot Protection Secret, Email secret, Payment secret) hold encrypted envelopes and are restricted to ADMIN via row-level policies.';
13
-
14
- DROP POLICY IF EXISTS site_settings_read_policy ON public.site_settings;
15
- DROP POLICY IF EXISTS site_settings_insert_policy ON public.site_settings;
16
- DROP POLICY IF EXISTS site_settings_update_policy ON public.site_settings;
17
- DROP POLICY IF EXISTS site_settings_delete_policy ON public.site_settings;
18
-
19
- CREATE POLICY site_settings_read_policy
20
- ON public.site_settings
21
- FOR SELECT
22
- TO public
23
- USING (
24
- key NOT IN ('cortex_ai_openrouter_api_key', 'bot_protection_secret', 'email_secret', 'payment_secret')
25
- OR (
26
- key IN ('cortex_ai_openrouter_api_key', 'bot_protection_secret', 'email_secret', 'payment_secret')
27
- AND (SELECT auth.role()) = 'authenticated'
28
- AND (SELECT public.get_current_user_role()) = 'ADMIN'
29
- )
30
- );
31
-
32
- CREATE POLICY site_settings_insert_policy
33
- ON public.site_settings
34
- FOR INSERT
35
- TO authenticated
36
- WITH CHECK (
37
- (
38
- key NOT IN ('cortex_ai_openrouter_api_key', 'bot_protection_secret', 'email_secret', 'payment_secret')
39
- AND (SELECT public.get_current_user_role()) IN ('ADMIN', 'WRITER')
40
- )
41
- OR (
42
- key IN ('cortex_ai_openrouter_api_key', 'bot_protection_secret', 'email_secret', 'payment_secret')
43
- AND (SELECT public.get_current_user_role()) = 'ADMIN'
44
- )
45
- );
46
-
47
- CREATE POLICY site_settings_update_policy
48
- ON public.site_settings
49
- FOR UPDATE
50
- TO authenticated
51
- USING (
52
- (
53
- key NOT IN ('cortex_ai_openrouter_api_key', 'bot_protection_secret', 'email_secret', 'payment_secret')
54
- AND (SELECT public.get_current_user_role()) IN ('ADMIN', 'WRITER')
55
- )
56
- OR (
57
- key IN ('cortex_ai_openrouter_api_key', 'bot_protection_secret', 'email_secret', 'payment_secret')
58
- AND (SELECT public.get_current_user_role()) = 'ADMIN'
59
- )
60
- )
61
- WITH CHECK (
62
- (
63
- key NOT IN ('cortex_ai_openrouter_api_key', 'bot_protection_secret', 'email_secret', 'payment_secret')
64
- AND (SELECT public.get_current_user_role()) IN ('ADMIN', 'WRITER')
65
- )
66
- OR (
67
- key IN ('cortex_ai_openrouter_api_key', 'bot_protection_secret', 'email_secret', 'payment_secret')
68
- AND (SELECT public.get_current_user_role()) = 'ADMIN'
69
- )
70
- );
71
-
72
- CREATE POLICY site_settings_delete_policy
73
- ON public.site_settings
74
- FOR DELETE
75
- TO authenticated
76
- USING (
77
- (
78
- key NOT IN ('cortex_ai_openrouter_api_key', 'bot_protection_secret', 'email_secret', 'payment_secret')
79
- AND (SELECT public.get_current_user_role()) IN ('ADMIN', 'WRITER')
80
- )
81
- OR (
82
- key IN ('cortex_ai_openrouter_api_key', 'bot_protection_secret', 'email_secret', 'payment_secret')
83
- AND (SELECT public.get_current_user_role()) = 'ADMIN'
84
- )
85
- );
86
-
87
- -- Seed the new configuration rows (idempotent). Secret rows are created on first save
88
- -- from the CMS, so they are intentionally not seeded here.
89
- INSERT INTO public.site_settings (key, value)
90
- VALUES ('email_public', '{"host": "", "port": "", "fromEmail": "", "fromName": "", "secure": true}'::jsonb)
91
- ON CONFLICT (key) DO NOTHING;
92
-
93
- INSERT INTO public.site_settings (key, value)
94
- VALUES ('payment_public', '{"stripe": {"publishableKey": ""}, "freemius": {"developerId": "", "publicKey": "", "productId": "", "sandboxEnabled": false}}'::jsonb)
95
- ON CONFLICT (key) DO NOTHING;
96
-
97
- INSERT INTO public.site_settings (key, value)
98
- VALUES ('onboarding_state', '{"dismissed": false, "skipped": []}'::jsonb)
99
- ON CONFLICT (key) DO NOTHING;
@@ -1,15 +0,0 @@
1
- -- 00000000000034_enable_staff_2fa_reminder_default.sql
2
- -- Turn the "Encourage staff to enable 2FA" policy ON by default. The reminder banner is
3
- -- now implemented (shown to ADMIN/WRITER accounts without a second factor); migration 027
4
- -- seeded this flag to false, so flip the existing security_settings row to true. Admins can
5
- -- still turn it off afterward. The sandbox never enforces it (handled at runtime), so the
6
- -- stored value here is harmless after a sandbox reset.
7
-
8
- UPDATE public.site_settings
9
- SET value = jsonb_set(coalesce(value, '{}'::jsonb), '{enforce_staff_2fa}', 'true'::jsonb)
10
- WHERE key = 'security_settings';
11
-
12
- -- Cover the unlikely case where the row is missing (it is seeded in migration 027).
13
- INSERT INTO public.site_settings (key, value)
14
- VALUES ('security_settings', '{"trusted_device_days": 30, "enforce_staff_2fa": true}'::jsonb)
15
- ON CONFLICT (key) DO NOTHING;
@@ -1,33 +0,0 @@
1
- -- 00000000000035_reassert_advisor_fixes.sql
2
- -- Re-assert two Supabase Advisor (database linter) fixes that were first applied in
3
- -- migration 00000000000028 but can be lost when a database is restored/reset to a
4
- -- pre-028 state while its migration history still records 028 as applied (so the forward
5
- -- tooling never re-runs it). These two advisors reappeared, so we re-apply the fixes in a
6
- -- forward-only, idempotent way. No application behaviour changes.
7
- --
8
- -- 1. 0011 function_search_path_mutable
9
- -- public.handle_ucp_cart_sessions_update() needs a pinned search_path.
10
- -- 2. 0029 authenticated_security_definer_function_executable
11
- -- public.duplicate_block_definition(uuid) must run as SECURITY INVOKER (it already
12
- -- keeps its own ADMIN/WRITER role check and is gated by custom_block_definitions RLS).
13
-
14
- -- 1. Pin the search_path. Re-create the function with SET search_path baked into its
15
- -- definition (not just an ALTER) so a future CREATE OR REPLACE can't silently drop it.
16
- -- The body only calls now() (pg_catalog, always implicitly searched), so an empty
17
- -- search_path is safe. CREATE OR REPLACE keeps the function OID, so the existing
18
- -- trg_handle_ucp_cart_sessions_update trigger and the service_role grant are preserved.
19
- CREATE OR REPLACE FUNCTION public.handle_ucp_cart_sessions_update()
20
- RETURNS trigger
21
- LANGUAGE plpgsql
22
- SET search_path = ''
23
- AS $$
24
- BEGIN
25
- NEW.updated_at = now();
26
- RETURN NEW;
27
- END;
28
- $$;
29
-
30
- -- 2. Ensure the duplicate helper runs with the caller's privileges. The function body
31
- -- (unchanged) still raises 42501 for non-ADMIN/WRITER callers, and its SELECT/INSERT
32
- -- are gated by custom_block_definitions RLS, so it does not need definer privileges.
33
- ALTER FUNCTION public.duplicate_block_definition(uuid) SECURITY INVOKER;