@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,153 +0,0 @@
1
- -- 00000000000039_setup_interactions.sql
2
- -- Migration establishing Product Reviews and Post Comments schema.
3
-
4
- -- 1. Create Enums
5
- CREATE TYPE public.interaction_type AS ENUM ('review', 'comment');
6
- CREATE TYPE public.approval_status AS ENUM ('pending', 'approved', 'denied');
7
-
8
- -- 2. Alter Products Table to support aggregations
9
- ALTER TABLE public.products
10
- ADD COLUMN average_rating numeric(3,2) NOT NULL DEFAULT 0.00,
11
- ADD COLUMN total_reviews integer NOT NULL DEFAULT 0;
12
-
13
- COMMENT ON COLUMN public.products.average_rating IS 'Aggregated average 1-5 rating of approved reviews.';
14
- COMMENT ON COLUMN public.products.total_reviews IS 'Total count of approved reviews for this product.';
15
-
16
- -- 3. Create Interactions Table
17
- CREATE TABLE public.cms_interactions (
18
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
19
- type public.interaction_type NOT NULL,
20
- status public.approval_status NOT NULL DEFAULT 'pending',
21
- content text NOT NULL,
22
- rating integer,
23
- user_id uuid NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
24
- product_id uuid REFERENCES public.products(id) ON DELETE CASCADE,
25
- post_id bigint REFERENCES public.posts(id) ON DELETE CASCADE,
26
- reactions jsonb NOT NULL DEFAULT '{}'::jsonb,
27
- created_at timestamptz NOT NULL DEFAULT now(),
28
- updated_at timestamptz NOT NULL DEFAULT now(),
29
-
30
- -- Ensure polymorphism is respected (either product or post)
31
- CONSTRAINT check_product_or_post CHECK (
32
- (product_id IS NOT NULL AND post_id IS NULL)
33
- OR (post_id IS NOT NULL AND product_id IS NULL)
34
- ),
35
-
36
- -- Ensure rating matches business rules
37
- CONSTRAINT check_rating_only_for_review CHECK (
38
- (type = 'review' AND rating IS NOT NULL AND rating >= 1 AND rating <= 5)
39
- OR (type = 'comment' AND rating IS NULL)
40
- )
41
- );
42
-
43
- COMMENT ON TABLE public.cms_interactions IS 'Stores user-submitted product reviews and blog post comments.';
44
- COMMENT ON COLUMN public.cms_interactions.rating IS 'Star rating 1-5, only populated for product reviews.';
45
- COMMENT ON COLUMN public.cms_interactions.reactions IS 'JSONB structure tracking counts of reactions (likes, etc.).';
46
-
47
- -- 4. Create rating aggregation trigger function
48
- CREATE OR REPLACE FUNCTION public.update_product_ratings()
49
- RETURNS TRIGGER AS $$
50
- DECLARE
51
- v_product_id uuid;
52
- v_avg numeric(3,2);
53
- v_count integer;
54
- BEGIN
55
- -- Determine which product_id we need to update
56
- IF TG_OP = 'DELETE' THEN
57
- v_product_id := OLD.product_id;
58
- ELSE
59
- v_product_id := NEW.product_id;
60
- END IF;
61
-
62
- IF v_product_id IS NOT NULL THEN
63
- -- Calculate average and count of approved reviews for this product
64
- SELECT COALESCE(avg(rating), 0.00), count(*)
65
- INTO v_avg, v_count
66
- FROM public.cms_interactions
67
- WHERE product_id = v_product_id
68
- AND type = 'review'
69
- AND status = 'approved';
70
-
71
- -- Update products table
72
- UPDATE public.products
73
- SET average_rating = ROUND(COALESCE(v_avg, 0.00), 2),
74
- total_reviews = v_count
75
- WHERE id = v_product_id;
76
- END IF;
77
-
78
- -- Handle old product_id if it changed on UPDATE (e.g. transfer of reviews)
79
- IF TG_OP = 'UPDATE' AND OLD.product_id IS NOT NULL AND OLD.product_id <> NEW.product_id THEN
80
- SELECT COALESCE(avg(rating), 0.00), count(*)
81
- INTO v_avg, v_count
82
- FROM public.cms_interactions
83
- WHERE product_id = OLD.product_id
84
- AND type = 'review'
85
- AND status = 'approved';
86
-
87
- UPDATE public.products
88
- SET average_rating = ROUND(COALESCE(v_avg, 0.00), 2),
89
- total_reviews = v_count
90
- WHERE id = OLD.product_id;
91
- END IF;
92
-
93
- RETURN NULL;
94
- END;
95
- $$ LANGUAGE plpgsql SECURITY DEFINER SET search_path = public;
96
-
97
- -- 5. Attach trigger to interactions table
98
- CREATE TRIGGER trigger_update_product_ratings
99
- AFTER INSERT OR UPDATE OR DELETE
100
- ON public.cms_interactions
101
- FOR EACH ROW
102
- EXECUTE FUNCTION public.update_product_ratings();
103
-
104
- -- 6. Attach update_at timestamp trigger
105
- CREATE TRIGGER set_updated_at
106
- BEFORE UPDATE ON public.cms_interactions
107
- FOR EACH ROW
108
- EXECUTE FUNCTION public.set_current_timestamp_updated_at();
109
-
110
- -- 7. Enable RLS and define policies
111
- ALTER TABLE public.cms_interactions ENABLE ROW LEVEL SECURITY;
112
-
113
- CREATE POLICY cms_interactions_read_policy
114
- ON public.cms_interactions
115
- FOR SELECT
116
- TO public
117
- USING (
118
- status = 'approved'
119
- OR ((SELECT public.get_current_user_role()) IN ('ADMIN', 'WRITER'))
120
- );
121
-
122
- CREATE POLICY cms_interactions_insert_policy
123
- ON public.cms_interactions
124
- FOR INSERT
125
- TO authenticated
126
- WITH CHECK (
127
- auth.uid() = user_id
128
- AND (status = 'pending' OR (SELECT public.get_current_user_role()) IN ('ADMIN', 'WRITER'))
129
- );
130
-
131
- CREATE POLICY cms_interactions_update_policy
132
- ON public.cms_interactions
133
- FOR UPDATE
134
- TO authenticated
135
- USING (
136
- ((SELECT public.get_current_user_role()) IN ('ADMIN', 'WRITER'))
137
- )
138
- WITH CHECK (
139
- ((SELECT public.get_current_user_role()) IN ('ADMIN', 'WRITER'))
140
- );
141
-
142
- CREATE POLICY cms_interactions_delete_policy
143
- ON public.cms_interactions
144
- FOR DELETE
145
- TO authenticated
146
- USING (
147
- ((SELECT public.get_current_user_role()) IN ('ADMIN', 'WRITER'))
148
- );
149
-
150
- -- 8. Grant Table Permissions
151
- GRANT SELECT ON public.cms_interactions TO anon;
152
- GRANT ALL ON public.cms_interactions TO authenticated;
153
- GRANT ALL ON public.cms_interactions TO service_role;
@@ -1,8 +0,0 @@
1
- -- 00000000000040_add_profiles_fk_to_interactions.sql
2
- -- Alter cms_interactions.user_id to reference public.profiles directly for PostgREST joins.
3
-
4
- ALTER TABLE public.cms_interactions
5
- DROP CONSTRAINT IF EXISTS cms_interactions_user_id_fkey,
6
- ADD CONSTRAINT cms_interactions_user_id_profiles_fkey FOREIGN KEY (user_id) REFERENCES public.profiles(id) ON DELETE CASCADE;
7
-
8
- COMMENT ON COLUMN public.cms_interactions.user_id IS 'References public.profiles.id';
@@ -1,145 +0,0 @@
1
- -- 00000000000041_seed_interactions_translations.sql
2
- -- Adds translation strings for Product Reviews and Post Comments storefront modules.
3
-
4
- BEGIN;
5
-
6
- INSERT INTO public.translations (key, translations)
7
- VALUES
8
- (
9
- 'reviews.customer_reviews',
10
- '{"en": "Customer Reviews", "fr": "Avis clients"}'::jsonb
11
- ),
12
- (
13
- 'reviews.share_thoughts',
14
- '{"en": "Share your thoughts and experience with this product.", "fr": "Partagez vos impressions et votre expérience avec ce produit."}'::jsonb
15
- ),
16
- (
17
- 'reviews.write_review',
18
- '{"en": "Write a Review", "fr": "Rédiger un avis"}'::jsonb
19
- ),
20
- (
21
- 'reviews.cancel_review',
22
- '{"en": "Cancel Review", "fr": "Annuler l''avis"}'::jsonb
23
- ),
24
- (
25
- 'reviews.login_to_write',
26
- '{"en": "Please log in to write a review.", "fr": "Veuillez vous connecter pour rédiger un avis."}'::jsonb
27
- ),
28
- (
29
- 'reviews.write_your_review',
30
- '{"en": "Write your review", "fr": "Rédigez votre avis"}'::jsonb
31
- ),
32
- (
33
- 'reviews.rating',
34
- '{"en": "Rating", "fr": "Note"}'::jsonb
35
- ),
36
- (
37
- 'reviews.description',
38
- '{"en": "Review Description", "fr": "Description de l''avis"}'::jsonb
39
- ),
40
- (
41
- 'reviews.description_placeholder',
42
- '{"en": "What did you like or dislike? How does it perform?", "fr": "Qu''avez-vous aimé ou déploré ? Comment se comporte-t-il ?"}'::jsonb
43
- ),
44
- (
45
- 'reviews.submit_review',
46
- '{"en": "Submit Review", "fr": "Soumettre l''avis"}'::jsonb
47
- ),
48
- (
49
- 'reviews.submitting',
50
- '{"en": "Submitting...", "fr": "Envoi en cours..."}'::jsonb
51
- ),
52
- (
53
- 'reviews.success_pending',
54
- '{"en": "Your review has been submitted successfully and is pending moderation.", "fr": "Votre avis a été soumis avec succès et est en attente de modération."}'::jsonb
55
- ),
56
- (
57
- 'reviews.cancel',
58
- '{"en": "Cancel", "fr": "Annuler"}'::jsonb
59
- ),
60
- (
61
- 'reviews.helpful',
62
- '{"en": "Helpful", "fr": "Utile"}'::jsonb
63
- ),
64
- (
65
- 'reviews.no_reviews',
66
- '{"en": "No reviews yet", "fr": "Aucun avis pour le moment"}'::jsonb
67
- ),
68
- (
69
- 'reviews.be_the_first',
70
- '{"en": "There are no reviews for this product yet. Be the first to write one!", "fr": "Il n''y a pas encore d''avis sur ce produit. Soyez le premier à en rédiger un !"}'::jsonb
71
- ),
72
- (
73
- 'reviews.load_more',
74
- '{"en": "Load More Reviews", "fr": "Charger plus d''avis"}'::jsonb
75
- ),
76
- (
77
- 'reviews.review_count_one',
78
- '{"en": "{count} review", "fr": "{count} avis"}'::jsonb
79
- ),
80
- (
81
- 'reviews.review_count_other',
82
- '{"en": "{count} reviews", "fr": "{count} avis"}'::jsonb
83
- ),
84
- (
85
- 'comments.discussion',
86
- '{"en": "Discussion & Comments", "fr": "Discussion & Commentaires"}'::jsonb
87
- ),
88
- (
89
- 'comments.join_conversation',
90
- '{"en": "Join the conversation and express your thoughts.", "fr": "Rejoignez la conversation et exprimez vos pensées."}'::jsonb
91
- ),
92
- (
93
- 'comments.write_comment',
94
- '{"en": "Write a Comment", "fr": "Écrire un commentaire"}'::jsonb
95
- ),
96
- (
97
- 'comments.cancel_comment',
98
- '{"en": "Cancel Comment", "fr": "Annuler le commentaire"}'::jsonb
99
- ),
100
- (
101
- 'comments.login_to_write',
102
- '{"en": "Please log in to write a comment.", "fr": "Veuillez vous connecter pour écrire un commentaire."}'::jsonb
103
- ),
104
- (
105
- 'comments.join_discussion',
106
- '{"en": "Join the Discussion", "fr": "Rejoindre la discussion"}'::jsonb
107
- ),
108
- (
109
- 'comments.your_message',
110
- '{"en": "Your Message", "fr": "Votre message"}'::jsonb
111
- ),
112
- (
113
- 'comments.message_placeholder',
114
- '{"en": "What are your thoughts on this article?", "fr": "Quelles sont vos pensées sur cet article ?"}'::jsonb
115
- ),
116
- (
117
- 'comments.post_comment',
118
- '{"en": "Post Comment", "fr": "Publier le commentaire"}'::jsonb
119
- ),
120
- (
121
- 'comments.success_pending',
122
- '{"en": "Your comment has been submitted successfully and is pending moderation.", "fr": "Votre commentaire a été soumis avec succès et est en attente de modération."}'::jsonb
123
- ),
124
- (
125
- 'comments.like',
126
- '{"en": "Like", "fr": "J''aime"}'::jsonb
127
- ),
128
- (
129
- 'comments.no_comments',
130
- '{"en": "No comments yet", "fr": "Aucun commentaire pour le moment"}'::jsonb
131
- ),
132
- (
133
- 'comments.be_the_first',
134
- '{"en": "Be the first to share your thoughts!", "fr": "Soyez le premier à partager vos pensées !"}'::jsonb
135
- ),
136
- (
137
- 'comments.load_more',
138
- '{"en": "Load More Comments", "fr": "Charger plus de commentaires"}'::jsonb
139
- )
140
- ON CONFLICT (key) DO UPDATE
141
- SET
142
- translations = EXCLUDED.translations,
143
- updated_at = now();
144
-
145
- COMMIT;
@@ -1,72 +0,0 @@
1
- -- Promote the one-click "Deploy to Vercel" button as a CTA in the seeded homepage.
2
- --
3
- -- Two placements per language, both rendered through the normal 'button' block
4
- -- (ButtonBlockRenderer), so external URLs open in a new tab with rel=noopener:
5
- -- 1. Home HERO — replace the secondary "View on GitHub" / "Voir sur GitHub"
6
- -- outline button with a "Deploy on Vercel" / "Déployer sur Vercel" button
7
- -- (same variant/size/position; the primary "Get Started" button is untouched,
8
- -- and GitHub is still linked from the hero social row + the community section).
9
- -- 2. Home CLOSING CTA band ("Have Questions?" / "Des questions ?") — append a
10
- -- secondary (outline) "Deploy on Vercel" button after the contact button so
11
- -- the page also ends on the deploy action, styled to match the hero's Deploy
12
- -- button and keep the filled "Get in Touch" button as the band's primary.
13
- --
14
- -- The canonical deploy URL is the exact href from the README's Deploy button
15
- -- (native Supabase Marketplace `stores` flow). It is stored as a raw button `url`
16
- -- value (literal '&', not '&amp;') because the renderer uses it directly as an href.
17
- --
18
- -- Forward-only and idempotent:
19
- -- * The hero swap matches the old button label, so a second run is a no-op
20
- -- (the label no longer exists) and it silently skips any install where the
21
- -- button was already customized or removed.
22
- -- * The closing-band append is guarded by NOT LIKE '%Deploy on Vercel%' /
23
- -- '%Déployer sur Vercel%', so it never appends twice.
24
- -- Scoping is by unique block text, so exactly the home hero / closing band match.
25
-
26
- -- ---------------------------------------------------------------------------
27
- -- 1a. EN home hero: "View on GitHub" -> "Deploy on Vercel"
28
- -- ---------------------------------------------------------------------------
29
- UPDATE public.blocks
30
- SET content = replace(
31
- replace(content::text, '"View on GitHub"', '"Deploy on Vercel"'),
32
- '"https://github.com/nextblock-cms/nextblock"',
33
- '"https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fnextblock-cms%2Fnextblock&project-name=nextblock&repository-name=nextblock&stores=%5B%7B%22type%22%3A%22integration%22%2C%22integrationSlug%22%3A%22supabase%22%2C%22productSlug%22%3A%22supabase%22%7D%5D"'
34
- )::jsonb
35
- WHERE content::text LIKE '%"View on GitHub"%';
36
-
37
- -- ---------------------------------------------------------------------------
38
- -- 1b. FR home hero: "Voir sur GitHub" -> "Déployer sur Vercel"
39
- -- ---------------------------------------------------------------------------
40
- UPDATE public.blocks
41
- SET content = replace(
42
- replace(content::text, '"Voir sur GitHub"', '"Déployer sur Vercel"'),
43
- '"https://github.com/nextblock-cms/nextblock"',
44
- '"https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fnextblock-cms%2Fnextblock&project-name=nextblock&repository-name=nextblock&stores=%5B%7B%22type%22%3A%22integration%22%2C%22integrationSlug%22%3A%22supabase%22%2C%22productSlug%22%3A%22supabase%22%7D%5D"'
45
- )::jsonb
46
- WHERE content::text LIKE '%"Voir sur GitHub"%';
47
-
48
- -- ---------------------------------------------------------------------------
49
- -- 2a. EN home closing band ("Have Questions?"): append a Deploy on Vercel button
50
- -- ---------------------------------------------------------------------------
51
- UPDATE public.blocks
52
- SET content = jsonb_set(
53
- content,
54
- '{column_blocks,0}',
55
- (content #> '{column_blocks,0}') ||
56
- '[{"block_type":"button","content":{"text":"Deploy on Vercel","url":"https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fnextblock-cms%2Fnextblock&project-name=nextblock&repository-name=nextblock&stores=%5B%7B%22type%22%3A%22integration%22%2C%22integrationSlug%22%3A%22supabase%22%2C%22productSlug%22%3A%22supabase%22%7D%5D","variant":"outline","size":"lg","position":"center"}}]'::jsonb
57
- )
58
- WHERE content::text LIKE '%Have Questions?%'
59
- AND content::text NOT LIKE '%Deploy on Vercel%';
60
-
61
- -- ---------------------------------------------------------------------------
62
- -- 2b. FR home closing band ("Des questions ?"): append a Déployer sur Vercel button
63
- -- ---------------------------------------------------------------------------
64
- UPDATE public.blocks
65
- SET content = jsonb_set(
66
- content,
67
- '{column_blocks,0}',
68
- (content #> '{column_blocks,0}') ||
69
- '[{"block_type":"button","content":{"text":"Déployer sur Vercel","url":"https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fnextblock-cms%2Fnextblock&project-name=nextblock&repository-name=nextblock&stores=%5B%7B%22type%22%3A%22integration%22%2C%22integrationSlug%22%3A%22supabase%22%2C%22productSlug%22%3A%22supabase%22%7D%5D","variant":"outline","size":"lg","position":"center"}}]'::jsonb
70
- )
71
- WHERE content::text LIKE '%Des questions ?%'
72
- AND content::text NOT LIKE '%Déployer sur Vercel%';