@nextblock-cms/db 0.17.5 → 0.17.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextblock-cms/db",
3
- "version": "0.17.5",
3
+ "version": "0.17.7",
4
4
  "main": "index.cjs.js",
5
5
  "module": "index.es.js",
6
6
  "types": "index.d.ts",
@@ -0,0 +1,72 @@
1
+ -- 02010: shop hero redesign -- shorter copy, a product image, and a compact benefits
2
+ -- strip below the product grid (EN 'shop' and FR 'boutique').
3
+ --
4
+ -- The previous hero was a full-width primary-coloured block holding an h1, an intro
5
+ -- line, a second heading, two long paragraphs and a four-item bullet list: ~1,000 px
6
+ -- of text and no image, written to satisfy the SEO grader rather than a visitor. It
7
+ -- now mirrors the home hero: dark gradient, two columns, one h1, one sentence, two
8
+ -- CTAs, and the NextBlock Commerce storefront image (bundled `/images/commerce-wide.webp`,
9
+ -- which the rich-text renderer serves through next/image; as the first hero image it
10
+ -- is preloaded as the page's LCP element). The four purchase benefits move into a
11
+ -- compact card strip after the product grid so the page keeps its supporting copy
12
+ -- without it living in the hero.
13
+ --
14
+ -- Both languages ship together (bilingual content rule). Scoped by parent page +
15
+ -- position + a signature of the OLD copy, never by blocks.id: each UPDATE runs once
16
+ -- and a re-run matches nothing. The benefits strip is inserted once per page, guarded
17
+ -- by its `nb-store-benefits` sentinel. Identity sequences are re-synced first because
18
+ -- the seed inserts explicit ids.
19
+
20
+ DO $$
21
+ DECLARE
22
+ v_en bigint;
23
+ v_fr bigint;
24
+ v_shop bigint;
25
+ v_boutique 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
+ IF v_en IS NULL THEN
30
+ RAISE NOTICE '02010: no English language row; nothing to do.';
31
+ RETURN;
32
+ END IF;
33
+
34
+ SELECT id INTO v_shop FROM public.pages WHERE slug = 'shop' AND language_id = v_en LIMIT 1;
35
+ IF v_fr IS NOT NULL THEN
36
+ SELECT id INTO v_boutique FROM public.pages WHERE slug = 'boutique' AND language_id = v_fr LIMIT 1;
37
+ END IF;
38
+
39
+ PERFORM setval('public.blocks_id_seq', COALESCE((SELECT MAX(id) FROM public.blocks), 0) + 1, false);
40
+
41
+ -- ---------------------------------------------------------------- EN: /shop
42
+ IF v_shop IS NOT NULL THEN
43
+ UPDATE public.blocks
44
+ SET content = $nb${"is_hero":true,"padding":{"top":"lg","bottom":"lg"},"background":{"type":"gradient","gradient":{"type":"linear","direction":"135deg","stops":[{"color":"#020817","position":0},{"color":"#0f172a","position":50},{"color":"#1e293b","position":100}]}},"column_gap":"xl","container_type":"container","responsive_columns":{"mobile":1,"tablet":1,"desktop":2},"vertical_alignment":"center","column_blocks":[[{"block_type":"text","content":{"html_content":"<p class='text-xs uppercase tracking-[0.3em] text-cyan-300 font-semibold mb-3'>Official store</p><h1 class='text-4xl md:text-5xl font-extrabold text-white leading-tight mb-4'>NextBlock™ Store</h1><p class='text-lg text-slate-300 leading-relaxed mb-2'>Official licenses for NextBlock™ Commerce and Cortex AI. Instant access, full source code and secure checkout with Stripe and Freemius.</p>"}},{"block_type":"button","content":{"text":"Get NextBlock™ Commerce","url":"/product/nextblock-commerce-pro-commerce-license","variant":"default","size":"lg","position":"left"}},{"block_type":"button","content":{"text":"Start the free Cortex AI trial","url":"/product/nextblock-cortex-ai-cortex-ai-license","variant":"outline","size":"lg","position":"left"}}],[{"block_type":"text","content":{"html_content":"<div class='rounded-3xl overflow-hidden border border-white/10 bg-white/5 p-3 shadow-2xl'><img src='/images/commerce-wide.webp' alt='NextBlock™ Commerce storefront with catalog, cart and checkout' class='w-full h-auto rounded-2xl' /></div>"}}]]}$nb$::jsonb,
45
+ updated_at = now()
46
+ WHERE page_id = v_shop
47
+ AND "order" = 0
48
+ AND block_type = 'section'
49
+ AND content::text LIKE '%Discover our selection of official commercial add-ons%';
50
+
51
+ IF NOT EXISTS (SELECT 1 FROM public.blocks WHERE page_id = v_shop AND content::text LIKE '%nb-store-benefits%') THEN
52
+ INSERT INTO public.blocks (page_id, language_id, block_type, content, "order")
53
+ VALUES (v_shop, v_en, 'section', $nb${"padding":{"top":"lg","bottom":"xl"},"background":{"type":"none"},"column_gap":"md","container_type":"container","responsive_columns":{"mobile":1,"tablet":1,"desktop":1},"vertical_alignment":"start","column_blocks":[[{"block_type":"text","content":{"html_content":"<div class='nb-store-benefits'><h2 class='text-2xl md:text-3xl font-bold text-foreground text-center mb-8'>Why buy from the official store</h2><div class='grid gap-6 md:grid-cols-2 lg:grid-cols-4'><div class='rounded-2xl border border-slate-200 bg-white p-6 dark:border-slate-800 dark:bg-slate-900'><p class='text-2xl mb-3'>🧩</p><h3 class='text-base font-bold text-foreground mb-2'>Full source code</h3><p class='text-sm text-slate-600 dark:text-slate-400'>Inspect, customize and adapt every module to your exact requirements.</p></div><div class='rounded-2xl border border-slate-200 bg-white p-6 dark:border-slate-800 dark:bg-slate-900'><p class='text-2xl mb-3'>♾️</p><h3 class='text-base font-bold text-foreground mb-2'>Perpetual use</h3><p class='text-sm text-slate-600 dark:text-slate-400'>Use the software on your project with full peace of mind.</p></div><div class='rounded-2xl border border-slate-200 bg-white p-6 dark:border-slate-800 dark:bg-slate-900'><p class='text-2xl mb-3'>🔄</p><h3 class='text-base font-bold text-foreground mb-2'>Automated updates</h3><p class='text-sm text-slate-600 dark:text-slate-400'>Smooth updates that follow each release of NextBlock and Next.js.</p></div><div class='rounded-2xl border border-slate-200 bg-white p-6 dark:border-slate-800 dark:bg-slate-900'><p class='text-2xl mb-3'>🔒</p><h3 class='text-base font-bold text-foreground mb-2'>Secure checkout</h3><p class='text-sm text-slate-600 dark:text-slate-400'>Payments processed safely by Stripe and Freemius, invoice included.</p></div></div></div>"}}]]}$nb$::jsonb, 2);
54
+ END IF;
55
+ END IF;
56
+
57
+ -- ---------------------------------------------------------------- FR: /boutique
58
+ IF v_boutique IS NOT NULL THEN
59
+ UPDATE public.blocks
60
+ SET content = $nb${"is_hero":true,"padding":{"top":"lg","bottom":"lg"},"background":{"type":"gradient","gradient":{"type":"linear","direction":"135deg","stops":[{"color":"#020817","position":0},{"color":"#0f172a","position":50},{"color":"#1e293b","position":100}]}},"column_gap":"xl","container_type":"container","responsive_columns":{"mobile":1,"tablet":1,"desktop":2},"vertical_alignment":"center","column_blocks":[[{"block_type":"text","content":{"html_content":"<p class='text-xs uppercase tracking-[0.3em] text-cyan-300 font-semibold mb-3'>Boutique officielle</p><h1 class='text-4xl md:text-5xl font-extrabold text-white leading-tight mb-4'>Boutique NextBlock™</h1><p class='text-lg text-slate-300 leading-relaxed mb-2'>Licences officielles pour NextBlock™ Commerce et Cortex AI. Accès immédiat, code source complet et paiement sécurisé avec Stripe et Freemius.</p>"}},{"block_type":"button","content":{"text":"Obtenir NextBlock™ Commerce","url":"/product/nextblock-commerce-pro-commerce-license-fr","variant":"default","size":"lg","position":"left"}},{"block_type":"button","content":{"text":"Commencer l'essai gratuit de Cortex AI","url":"/product/nextblock-cortex-ai-cortex-ai-license-fr","variant":"outline","size":"lg","position":"left"}}],[{"block_type":"text","content":{"html_content":"<div class='rounded-3xl overflow-hidden border border-white/10 bg-white/5 p-3 shadow-2xl'><img src='/images/commerce-wide.webp' alt='Vitrine NextBlock™ Commerce avec catalogue, panier et paiement' class='w-full h-auto rounded-2xl' /></div>"}}]]}$nb$::jsonb,
61
+ updated_at = now()
62
+ WHERE page_id = v_boutique
63
+ AND "order" = 0
64
+ AND block_type = 'section'
65
+ AND content::text LIKE '%nos extensions officielles et nos outils%';
66
+
67
+ IF NOT EXISTS (SELECT 1 FROM public.blocks WHERE page_id = v_boutique AND content::text LIKE '%nb-store-benefits%') THEN
68
+ INSERT INTO public.blocks (page_id, language_id, block_type, content, "order")
69
+ VALUES (v_boutique, v_fr, 'section', $nb${"padding":{"top":"lg","bottom":"xl"},"background":{"type":"none"},"column_gap":"md","container_type":"container","responsive_columns":{"mobile":1,"tablet":1,"desktop":1},"vertical_alignment":"start","column_blocks":[[{"block_type":"text","content":{"html_content":"<div class='nb-store-benefits'><h2 class='text-2xl md:text-3xl font-bold text-foreground text-center mb-8'>Pourquoi acheter sur la boutique officielle</h2><div class='grid gap-6 md:grid-cols-2 lg:grid-cols-4'><div class='rounded-2xl border border-slate-200 bg-white p-6 dark:border-slate-800 dark:bg-slate-900'><p class='text-2xl mb-3'>🧩</p><h3 class='text-base font-bold text-foreground mb-2'>Code source complet</h3><p class='text-sm text-slate-600 dark:text-slate-400'>Lisez, adaptez et faites évoluer chaque module selon vos besoins.</p></div><div class='rounded-2xl border border-slate-200 bg-white p-6 dark:border-slate-800 dark:bg-slate-900'><p class='text-2xl mb-3'>♾️</p><h3 class='text-base font-bold text-foreground mb-2'>Licence perpétuelle</h3><p class='text-sm text-slate-600 dark:text-slate-400'>Utilisez le logiciel sur votre projet en toute sérénité.</p></div><div class='rounded-2xl border border-slate-200 bg-white p-6 dark:border-slate-800 dark:bg-slate-900'><p class='text-2xl mb-3'>🔄</p><h3 class='text-base font-bold text-foreground mb-2'>Mises à jour suivies</h3><p class='text-sm text-slate-600 dark:text-slate-400'>Des mises à jour fluides qui suivent chaque version de NextBlock et de Next.js.</p></div><div class='rounded-2xl border border-slate-200 bg-white p-6 dark:border-slate-800 dark:bg-slate-900'><p class='text-2xl mb-3'>🔒</p><h3 class='text-base font-bold text-foreground mb-2'>Paiement sécurisé</h3><p class='text-sm text-slate-600 dark:text-slate-400'>Paiements traités en toute sécurité par Stripe et Freemius, facture incluse.</p></div></div></div>"}}]]}$nb$::jsonb, 2);
70
+ END IF;
71
+ END IF;
72
+ END $$;
@@ -0,0 +1,26 @@
1
+ -- 02011: remove `backdrop-blur-*` from page blocks (home EN/FR cards, articles heroes).
2
+ --
3
+ -- Every card sitting on the dark gradient heroes used `backdrop-filter: blur()` via
4
+ -- Tailwind's backdrop-blur utilities (`backdrop-blur-xl` on the home hero card and the
5
+ -- live-demo promo, `backdrop-blur` on the three MCP-step cards, `backdrop-blur-sm` on
6
+ -- the commerce card and the articles heroes). A backdrop blur forces the compositor to
7
+ -- read back and blur everything behind the element on every frame; in Lighthouse's
8
+ -- trace of the home page the GPU process spent ~390 ms drawing the very first frame,
9
+ -- which is the gap between the hero being laid out and the first contentful paint.
10
+ -- Behind these cards there is only a smooth gradient or a solid colour, so the blur has
11
+ -- no visible effect: `bg-white/5` alone gives the same translucent card.
12
+ --
13
+ -- Scoped to page blocks that carry the utility (posts and products never did), both
14
+ -- languages at once; `regexp_replace` strips the class token wherever it appears in a
15
+ -- class list. Idempotent: once stripped, the row no longer matches. page_revisions get
16
+ -- the same rewrite so restoring a revision cannot bring the blur back.
17
+
18
+ UPDATE public.blocks
19
+ SET content = regexp_replace(content::text, '\sbackdrop-blur(-[a-z0-9]+)?', '', 'g')::jsonb,
20
+ updated_at = now()
21
+ WHERE page_id IS NOT NULL
22
+ AND content::text ~ 'backdrop-blur';
23
+
24
+ UPDATE public.page_revisions
25
+ SET content = regexp_replace(content::text, '\sbackdrop-blur(-[a-z0-9]+)?', '', 'g')::jsonb
26
+ WHERE content::text ~ 'backdrop-blur';
@@ -0,0 +1,17 @@
1
+ -- 02012: follow-up to 02011 -- also strip variant-prefixed backdrop blurs.
2
+ --
3
+ -- 02011 removed bare `backdrop-blur*` tokens, but the live-demo promo card on the EN
4
+ -- and FR home pages carries `dark:backdrop-blur-xl`, whose token starts with a variant
5
+ -- prefix and so did not match `\sbackdrop-blur`. Same reasoning as 02011: the card sits
6
+ -- on a solid slate background in dark mode, the blur is invisible and costs a GPU
7
+ -- read-back per frame. Idempotent and scoped exactly like 02011.
8
+
9
+ UPDATE public.blocks
10
+ SET content = regexp_replace(content::text, '\s([a-z]+:)?backdrop-blur(-[a-z0-9]+)?', '', 'g')::jsonb,
11
+ updated_at = now()
12
+ WHERE page_id IS NOT NULL
13
+ AND content::text ~ 'backdrop-blur';
14
+
15
+ UPDATE public.page_revisions
16
+ SET content = regexp_replace(content::text, '\s([a-z]+:)?backdrop-blur(-[a-z0-9]+)?', '', 'g')::jsonb
17
+ WHERE content::text ~ 'backdrop-blur';
@@ -0,0 +1,102 @@
1
+ -- 02013: clear the Supabase Advisor warnings on RLS policies and trigger-function grants.
2
+ --
3
+ -- Performance advisor
4
+ -- * auth_rls_initplan: cms_interactions_insert_policy called auth.uid() per candidate row.
5
+ -- Wrapped as (select auth.uid()) it becomes an InitPlan evaluated once per statement.
6
+ -- * multiple_permissive_policies: cms_redirects and site_scripts each had two SELECT
7
+ -- policies for `authenticated` ("Admins read all …" and "Public read active …"), and
8
+ -- form_endpoints had an admin FOR ALL policy overlapping the editor SELECT policy. Every
9
+ -- permissive policy that matches a role + command is evaluated for every row, so the
10
+ -- policies are re-cut so exactly one applies per role + command. Who can read or write
11
+ -- what is unchanged:
12
+ -- - anon reads active redirects / scripts;
13
+ -- - authenticated reads active rows, ADMIN reads all;
14
+ -- - form_endpoints: ADMIN + WRITER read, ADMIN writes (now three command-scoped
15
+ -- policies instead of FOR ALL), service_role unchanged.
16
+ --
17
+ -- Security advisor
18
+ -- * anon_/authenticated_security_definer_function_executable: handle_new_user() and
19
+ -- update_product_ratings() are SECURITY DEFINER trigger functions. Postgres checks
20
+ -- EXECUTE on a trigger function only at CREATE TRIGGER time, never when the trigger
21
+ -- fires (and PostgREST cannot call a `RETURNS trigger` function via /rpc anyway), so
22
+ -- anon and authenticated never needed the privilege. 02003 already revokes it on
23
+ -- handle_new_user() for fresh installs, but production recorded the baseline as applied
24
+ -- without running it (docs/04 → "How a squash crosses live databases"), so the
25
+ -- generation-1 grants were still live there. Idempotent: safe on a fresh install too.
26
+ --
27
+ -- The two Auth-dashboard warnings (leaked password protection, MFA options) are project
28
+ -- settings, not schema, and are not addressed here.
29
+
30
+ -- ---------------------------------------------------------------------------
31
+ -- cms_interactions: InitPlan-friendly auth.uid()
32
+ -- ---------------------------------------------------------------------------
33
+ DROP POLICY IF EXISTS cms_interactions_insert_policy ON public.cms_interactions;
34
+ CREATE POLICY cms_interactions_insert_policy ON public.cms_interactions
35
+ FOR INSERT TO authenticated
36
+ WITH CHECK (
37
+ ((SELECT auth.uid()) = user_id)
38
+ AND (
39
+ status = 'pending'::public.approval_status
40
+ OR (SELECT public.get_current_user_role()) = ANY (ARRAY['ADMIN'::public.user_role, 'WRITER'::public.user_role])
41
+ )
42
+ );
43
+
44
+ -- ---------------------------------------------------------------------------
45
+ -- cms_redirects: one SELECT policy per role
46
+ -- ---------------------------------------------------------------------------
47
+ DROP POLICY IF EXISTS "Admins read all redirects" ON public.cms_redirects;
48
+ DROP POLICY IF EXISTS "Public read active redirects" ON public.cms_redirects;
49
+ DROP POLICY IF EXISTS "Authenticated read redirects" ON public.cms_redirects;
50
+
51
+ CREATE POLICY "Public read active redirects" ON public.cms_redirects
52
+ FOR SELECT TO anon
53
+ USING (is_active);
54
+
55
+ CREATE POLICY "Authenticated read redirects" ON public.cms_redirects
56
+ FOR SELECT TO authenticated
57
+ USING (is_active OR (SELECT public.get_current_user_role()) = 'ADMIN'::public.user_role);
58
+
59
+ -- ---------------------------------------------------------------------------
60
+ -- site_scripts: one SELECT policy per role
61
+ -- ---------------------------------------------------------------------------
62
+ DROP POLICY IF EXISTS "Admins read all site scripts" ON public.site_scripts;
63
+ DROP POLICY IF EXISTS "Public read active site scripts" ON public.site_scripts;
64
+ DROP POLICY IF EXISTS "Authenticated read site scripts" ON public.site_scripts;
65
+
66
+ CREATE POLICY "Public read active site scripts" ON public.site_scripts
67
+ FOR SELECT TO anon
68
+ USING (is_active);
69
+
70
+ CREATE POLICY "Authenticated read site scripts" ON public.site_scripts
71
+ FOR SELECT TO authenticated
72
+ USING (is_active OR (SELECT public.get_current_user_role()) = 'ADMIN'::public.user_role);
73
+
74
+ -- ---------------------------------------------------------------------------
75
+ -- form_endpoints: admin FOR ALL -> INSERT / UPDATE / DELETE, editor SELECT stays the only read
76
+ -- ---------------------------------------------------------------------------
77
+ DROP POLICY IF EXISTS form_endpoints_admin_write_policy ON public.form_endpoints;
78
+ DROP POLICY IF EXISTS form_endpoints_admin_insert_policy ON public.form_endpoints;
79
+ DROP POLICY IF EXISTS form_endpoints_admin_update_policy ON public.form_endpoints;
80
+ DROP POLICY IF EXISTS form_endpoints_admin_delete_policy ON public.form_endpoints;
81
+
82
+ CREATE POLICY form_endpoints_admin_insert_policy ON public.form_endpoints
83
+ FOR INSERT TO authenticated
84
+ WITH CHECK ((SELECT public.get_current_user_role()) = 'ADMIN'::public.user_role);
85
+
86
+ CREATE POLICY form_endpoints_admin_update_policy ON public.form_endpoints
87
+ FOR UPDATE TO authenticated
88
+ USING ((SELECT public.get_current_user_role()) = 'ADMIN'::public.user_role)
89
+ WITH CHECK ((SELECT public.get_current_user_role()) = 'ADMIN'::public.user_role);
90
+
91
+ CREATE POLICY form_endpoints_admin_delete_policy ON public.form_endpoints
92
+ FOR DELETE TO authenticated
93
+ USING ((SELECT public.get_current_user_role()) = 'ADMIN'::public.user_role);
94
+
95
+ -- ---------------------------------------------------------------------------
96
+ -- SECURITY DEFINER trigger functions: not callable by API roles
97
+ -- ---------------------------------------------------------------------------
98
+ REVOKE EXECUTE ON FUNCTION public.handle_new_user() FROM PUBLIC, anon, authenticated;
99
+ GRANT EXECUTE ON FUNCTION public.handle_new_user() TO service_role;
100
+
101
+ REVOKE EXECUTE ON FUNCTION public.update_product_ratings() FROM PUBLIC, anon, authenticated;
102
+ GRANT EXECUTE ON FUNCTION public.update_product_ratings() TO service_role;