create-nextblock 0.12.13 → 0.12.14

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 (24) hide show
  1. package/package.json +1 -1
  2. package/templates/nextblock-template/app/(auth-pages)/sign-in/page.tsx +0 -13
  3. package/templates/nextblock-template/app/(auth-pages)/sign-up/SignUpForm.tsx +0 -14
  4. package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +184 -101
  5. package/templates/nextblock-template/app/cms/CmsClientLayout.tsx +2 -3
  6. package/templates/nextblock-template/app/cms/interactions/InteractionsModerationClient.tsx +1 -1
  7. package/templates/nextblock-template/app/cms/interactions/page.tsx +1 -1
  8. package/templates/nextblock-template/app/cms/revisions/RevisionHistoryButton.tsx +2 -2
  9. package/templates/nextblock-template/app/cms/revisions/actions.ts +5 -5
  10. package/templates/nextblock-template/app/cms/users/actions.ts +0 -3
  11. package/templates/nextblock-template/app/cms/users/components/UserForm.tsx +0 -2
  12. package/templates/nextblock-template/app/cms/users/page.tsx +0 -2
  13. package/templates/nextblock-template/app/profile/ProfileAccountSidebar.tsx +1 -1
  14. package/templates/nextblock-template/app/profile/account-data.ts +1 -1
  15. package/templates/nextblock-template/app/profile/account-types.ts +0 -1
  16. package/templates/nextblock-template/app/profile/page.tsx +0 -1
  17. package/templates/nextblock-template/components/PostCommentsSection.tsx +2 -2
  18. package/templates/nextblock-template/components/ProductReviewsSection.tsx +2 -2
  19. package/templates/nextblock-template/components/header-auth.tsx +1 -1
  20. package/templates/nextblock-template/docs/TECHNICAL_SPECIFICATION.md +30 -32
  21. package/templates/nextblock-template/lib/custom-block-relation-registry.ts +3 -3
  22. package/templates/nextblock-template/lib/setup/migrations-bundle.ts +72 -37
  23. package/templates/nextblock-template/package.json +1 -1
  24. package/templates/nextblock-template/components/GitHubLoginButton.tsx +0 -36
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nextblock",
3
- "version": "0.12.13",
3
+ "version": "0.12.14",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -26,8 +26,6 @@ function getMessage(searchParams: URLSearchParams): Message | undefined {
26
26
  return undefined;
27
27
  }
28
28
 
29
- import { GitHubLoginButton } from "../../../components/GitHubLoginButton";
30
-
31
29
  export default function Login() {
32
30
  const { t } = useTranslations();
33
31
  const searchParams = useSearchParams();
@@ -46,17 +44,6 @@ export default function Login() {
46
44
  </p>
47
45
 
48
46
  <div className="flex flex-col gap-2 mt-8">
49
- <GitHubLoginButton t={t} redirectTo={redirectParam || undefined} />
50
-
51
- <div className="relative py-2">
52
- <div className="absolute inset-0 flex items-center">
53
- <span className="w-full border-t" />
54
- </div>
55
- <div className="relative flex justify-center text-xs uppercase">
56
- <span className="bg-background px-2 text-muted-foreground">{t('or_continue_with') || "Or continue with"}</span>
57
- </div>
58
- </div>
59
-
60
47
  <form className="flex flex-col gap-2 [&>input]:mb-3">
61
48
  {redirectParam && <input type="hidden" name="redirect" value={redirectParam} />}
62
49
  <Label htmlFor="email">{t('email')}</Label>
@@ -7,7 +7,6 @@ import { Button, Input, Label } from "@nextblock-cms/ui";
7
7
  import Link from "next/link";
8
8
  import { useTranslations } from "@nextblock-cms/utils";
9
9
  import { useSearchParams } from "next/navigation";
10
- import { GitHubLoginButton } from "../../../components/GitHubLoginButton";
11
10
  import { ArrowRight, CheckCircle2, Mail } from "lucide-react";
12
11
 
13
12
  import { SandboxCredentialsAlert } from "../../../components/SandboxCredentialsAlert";
@@ -100,19 +99,6 @@ export default function SignUpForm({ botProtection, scriptNonce }: SignUpFormPro
100
99
  </p>
101
100
 
102
101
  <div className="mt-8 flex flex-col gap-2">
103
- <GitHubLoginButton t={t} redirectTo="/profile" />
104
-
105
- <div className="relative py-2">
106
- <div className="absolute inset-0 flex items-center">
107
- <span className="w-full border-t" />
108
- </div>
109
- <div className="relative flex justify-center text-xs uppercase">
110
- <span className="bg-background px-2 text-muted-foreground">
111
- {t('or_continue_with') || "Or continue with"}
112
- </span>
113
- </div>
114
- </div>
115
-
116
102
  <div className="flex flex-col gap-2 [&>input]:mb-3">
117
103
  <Label htmlFor="email">{t('email')}</Label>
118
104
  <Input name="email" placeholder={t('you_at_example_com')} required />
@@ -1,37 +1,37 @@
1
- // AUTO-GENERATED FILE. Do not edit this file directly.
2
- // Regenerate it from repo root with: npm run generate:sandbox
3
- // Source of truth: tools/scripts/generate-sandbox-reset.ts and libs/db/src/supabase/migrations/*.sql
4
-
5
- export const SANDBOX_RESET_SQL = `
6
- -- AUTO-GENERATED: Sandbox Reset Script. Do Not Edit Manually.
7
- -- Generated by tools/scripts/generate-sandbox-reset.ts
8
-
9
- -- Step A: Erase all non-demo users
10
- DELETE FROM auth.users
11
- WHERE id NOT IN (
12
- '63a8aa07-ef6e-4cc7-bafa-ece023c74366'::uuid,
13
- 'e3a2c121-70a4-4239-a9df-f70ce717f5a1'::uuid
14
- );
15
-
16
- -- Step B: Wipe public schema and truncate migrations
17
- DROP SCHEMA public CASCADE;
18
- CREATE SCHEMA public;
19
-
20
- -- Restore default public schema privileges so the standard migrations succeed
21
- GRANT USAGE ON SCHEMA public TO postgres, anon, authenticated, service_role;
22
- GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO postgres, anon, authenticated, service_role;
23
- GRANT ALL PRIVILEGES ON ALL ROUTINES IN SCHEMA public TO postgres, anon, authenticated, service_role;
24
- GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO postgres, anon, authenticated, service_role;
25
- ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres, anon, authenticated, service_role;
26
- ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON ROUTINES TO postgres, anon, authenticated, service_role;
27
- ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres, anon, authenticated, service_role;
28
-
29
- TRUNCATE TABLE supabase_migrations.schema_migrations;
30
-
31
- -- Step C: Execute full schema & seed from production migrations
32
-
33
-
34
- -- >>> FROM: 00000000000000_baseline_schema.sql <<<
1
+ // AUTO-GENERATED FILE. Do not edit this file directly.
2
+ // Regenerate it from repo root with: npm run generate:sandbox
3
+ // Source of truth: tools/scripts/generate-sandbox-reset.ts and libs/db/src/supabase/migrations/*.sql
4
+
5
+ export const SANDBOX_RESET_SQL = `
6
+ -- AUTO-GENERATED: Sandbox Reset Script. Do Not Edit Manually.
7
+ -- Generated by tools/scripts/generate-sandbox-reset.ts
8
+
9
+ -- Step A: Erase all non-demo users
10
+ DELETE FROM auth.users
11
+ WHERE id NOT IN (
12
+ '63a8aa07-ef6e-4cc7-bafa-ece023c74366'::uuid,
13
+ 'e3a2c121-70a4-4239-a9df-f70ce717f5a1'::uuid
14
+ );
15
+
16
+ -- Step B: Wipe public schema and truncate migrations
17
+ DROP SCHEMA public CASCADE;
18
+ CREATE SCHEMA public;
19
+
20
+ -- Restore default public schema privileges so the standard migrations succeed
21
+ GRANT USAGE ON SCHEMA public TO postgres, anon, authenticated, service_role;
22
+ GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO postgres, anon, authenticated, service_role;
23
+ GRANT ALL PRIVILEGES ON ALL ROUTINES IN SCHEMA public TO postgres, anon, authenticated, service_role;
24
+ GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO postgres, anon, authenticated, service_role;
25
+ ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres, anon, authenticated, service_role;
26
+ ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON ROUTINES TO postgres, anon, authenticated, service_role;
27
+ ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres, anon, authenticated, service_role;
28
+
29
+ TRUNCATE TABLE supabase_migrations.schema_migrations;
30
+
31
+ -- Step C: Execute full schema & seed from production migrations
32
+
33
+
34
+ -- >>> FROM: 00000000000000_baseline_schema.sql <<<
35
35
  -- AUTO-GENERATED baseline (re-baseline of migrations 000..044). Idempotent; safe to replay.
36
36
  -- 00 · schema: enums, functions, tables, sequences, defaults
37
37
  -- Regenerate via tools/scripts/rebaseline-transform.mjs. Do not hand-edit.
@@ -2486,9 +2486,9 @@ CREATE TABLE IF NOT EXISTS public.variant_attribute_mapping (
2486
2486
  variant_id uuid NOT NULL,
2487
2487
  attribute_term_id uuid NOT NULL
2488
2488
  );
2489
-
2490
-
2491
- -- >>> FROM: 00000000000001_baseline_constraints_and_indexes.sql <<<
2489
+
2490
+
2491
+ -- >>> FROM: 00000000000001_baseline_constraints_and_indexes.sql <<<
2492
2492
  -- AUTO-GENERATED baseline (re-baseline of migrations 000..044). Idempotent; safe to replay.
2493
2493
  -- 01 · constraints (PK / unique / check / FK) + indexes
2494
2494
  -- Regenerate via tools/scripts/rebaseline-transform.mjs. Do not hand-edit.
@@ -3560,9 +3560,9 @@ DO $rb$ BEGIN
3560
3560
  ADD CONSTRAINT variant_attribute_mapping_variant_id_fkey FOREIGN KEY (variant_id) REFERENCES public.product_variants(id) ON DELETE CASCADE;
3561
3561
  END IF;
3562
3562
  END $rb$;
3563
-
3564
-
3565
- -- >>> FROM: 00000000000002_baseline_security_and_grants.sql <<<
3563
+
3564
+
3565
+ -- >>> FROM: 00000000000002_baseline_security_and_grants.sql <<<
3566
3566
  -- AUTO-GENERATED baseline (re-baseline of migrations 000..044). Idempotent; safe to replay.
3567
3567
  -- 02 · row-level security, policies, triggers, grants
3568
3568
  -- Regenerate via tools/scripts/rebaseline-transform.mjs. Do not hand-edit.
@@ -4898,9 +4898,9 @@ CREATE TRIGGER on_auth_user_created
4898
4898
  FOR EACH ROW
4899
4899
  EXECUTE FUNCTION public.handle_new_user();
4900
4900
  REVOKE EXECUTE ON FUNCTION public.handle_new_user() FROM PUBLIC, anon, authenticated;
4901
-
4902
-
4903
- -- >>> FROM: 00000000000003_baseline_seed.sql <<<
4901
+
4902
+
4903
+ -- >>> FROM: 00000000000003_baseline_seed.sql <<<
4904
4904
  -- AUTO-GENERATED baseline (re-baseline of migrations 000..044). Idempotent; safe to replay.
4905
4905
  -- 03 · seed: canonical NextBlock demo content (no users, no secrets)
4906
4906
  -- Regenerate via tools/scripts/rebaseline-transform.mjs. Do not hand-edit.
@@ -5369,9 +5369,9 @@ SELECT pg_catalog.setval('public.languages_id_seq', COALESCE((SELECT MAX(id) FRO
5369
5369
  SELECT pg_catalog.setval('public.navigation_items_id_seq', COALESCE((SELECT MAX(id) FROM public.navigation_items), 1), true);
5370
5370
  SELECT pg_catalog.setval('public.pages_id_seq', COALESCE((SELECT MAX(id) FROM public.pages), 1), true);
5371
5371
  SELECT pg_catalog.setval('public.posts_id_seq', COALESCE((SELECT MAX(id) FROM public.posts), 1), true);
5372
-
5373
-
5374
- -- >>> FROM: 00000000000004_default_logo_email_safe.sql <<<
5372
+
5373
+
5374
+ -- >>> FROM: 00000000000004_default_logo_email_safe.sql <<<
5375
5375
  -- Swap the seeded DEFAULT site logo from the WebP asset to a bundled PNG.
5376
5376
  --
5377
5377
  -- WebP (and AVIF) don't render in Outlook and several other email clients, so the default
@@ -5396,9 +5396,9 @@ UPDATE public.logos
5396
5396
  SET media_id = 'a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d'
5397
5397
  WHERE id = 'd45ef03d-27fc-4099-8b46-1cdf82d658d2'
5398
5398
  AND media_id = 'ea6fdaf5-f8de-416c-9f2b-b689b7a4ed38';
5399
-
5400
-
5401
- -- >>> FROM: 00000000000005_add_custom_canonical.sql <<<
5399
+
5400
+
5401
+ -- >>> FROM: 00000000000005_add_custom_canonical.sql <<<
5402
5402
  -- Add a nullable \`custom_canonical\` column to pages, posts and products.
5403
5403
  --
5404
5404
  -- This is the per-content manual canonical override used by each route's
@@ -5412,9 +5412,9 @@ WHERE id = 'd45ef03d-27fc-4099-8b46-1cdf82d658d2'
5412
5412
  ALTER TABLE public.pages ADD COLUMN IF NOT EXISTS custom_canonical text;
5413
5413
  ALTER TABLE public.posts ADD COLUMN IF NOT EXISTS custom_canonical text;
5414
5414
  ALTER TABLE public.products ADD COLUMN IF NOT EXISTS custom_canonical text;
5415
-
5416
-
5417
- -- >>> FROM: 00000000000006_home_live_demo_promo.sql <<<
5415
+
5416
+
5417
+ -- >>> FROM: 00000000000006_home_live_demo_promo.sql <<<
5418
5418
  -- 00000000000006_home_sandbox_promo.sql
5419
5419
  -- Adds a "Live Demo" promo section to the English (slug 'home') and French
5420
5420
  -- (slug 'accueil') home pages that drives visitors to the public sandbox at
@@ -5482,9 +5482,9 @@ $fr$::jsonb, 1);
5482
5482
  END IF;
5483
5483
  END
5484
5484
  $body$;
5485
-
5486
-
5487
- -- >>> FROM: 00000000000007_home_live_demo_promo_copy_fix.sql <<<
5485
+
5486
+
5487
+ -- >>> FROM: 00000000000007_home_live_demo_promo_copy_fix.sql <<<
5488
5488
  -- 00000000000007_home_live_demo_promo_copy_fix.sql
5489
5489
  -- Corrects the "Live Demo" promo that migration 006 seeded on the EN (slug 'home')
5490
5490
  -- and FR (slug 'accueil') home pages. 006 was already applied to production and the
@@ -5534,9 +5534,9 @@ $fr$::jsonb,
5534
5534
  AND content::text LIKE '%nb-sandbox-promo%';
5535
5535
  END
5536
5536
  $body$;
5537
-
5538
-
5539
- -- >>> FROM: 00000000000008_setup_article_reorder.sql <<<
5537
+
5538
+
5539
+ -- >>> FROM: 00000000000008_setup_article_reorder.sql <<<
5540
5540
  -- 00000000000008_setup_article_reorder.sql
5541
5541
  -- Reorders and rewrites the "How to Install NextBlock" setup guide on BOTH language
5542
5542
  -- twins: EN (post slug 'how-to-setup-nextblock', post_id=3 / block id=40) and FR
@@ -5567,9 +5567,9 @@ $nbfr$::jsonb,
5567
5567
  updated_at = now()
5568
5568
  WHERE post_id = 4
5569
5569
  AND block_type = 'text';
5570
-
5571
-
5572
- -- >>> FROM: 00000000000009_home_live_demo_promo_contrast.sql <<<
5570
+
5571
+
5572
+ -- >>> FROM: 00000000000009_home_live_demo_promo_contrast.sql <<<
5573
5573
  -- 00000000000009_home_live_demo_promo_contrast.sql
5574
5574
  -- Restyles the "Live Demo" promo that migrations 006/007 seeded on the EN (slug 'home')
5575
5575
  -- and FR (slug 'accueil') home pages. 006/007 gave it a fixed dark-navy GRADIENT section
@@ -5622,43 +5622,126 @@ $fr$::jsonb,
5622
5622
  AND content::text LIKE '%nb-sandbox-promo%';
5623
5623
  END
5624
5624
  $body$;
5625
-
5626
-
5627
- -- Step D: Record the applied migrations in history (truncated in Step B) so
5628
- -- \`npm run db:migrate:check\` reports up to date instead of listing every file as pending.
5629
- INSERT INTO supabase_migrations.schema_migrations (version, name) VALUES
5630
- ('00000000000000', 'baseline_schema'),
5631
- ('00000000000001', 'baseline_constraints_and_indexes'),
5632
- ('00000000000002', 'baseline_security_and_grants'),
5633
- ('00000000000003', 'baseline_seed'),
5634
- ('00000000000004', 'default_logo_email_safe'),
5635
- ('00000000000005', 'add_custom_canonical'),
5636
- ('00000000000006', 'home_live_demo_promo'),
5637
- ('00000000000007', 'home_live_demo_promo_copy_fix'),
5638
- ('00000000000008', 'setup_article_reorder'),
5639
- ('00000000000009', 'home_live_demo_promo_contrast')
5640
- ON CONFLICT (version) DO NOTHING;
5641
-
5642
- -- Step E: Anchor preserved profiles
5643
- INSERT INTO public.profiles (id, updated_at, full_name, avatar_url, website, role)
5644
- SELECT preserved_user.id, NULL, NULL, NULL, NULL, 'ADMIN'
5645
- FROM (
5646
- VALUES
5647
- ('63a8aa07-ef6e-4cc7-bafa-ece023c74366'::uuid),
5648
- ('e3a2c121-70a4-4239-a9df-f70ce717f5a1'::uuid)
5649
- ) AS preserved_user(id)
5650
- WHERE EXISTS (SELECT 1 FROM auth.users WHERE id = preserved_user.id)
5651
- AND NOT EXISTS (SELECT 1 FROM public.profiles WHERE id = preserved_user.id);
5652
-
5653
- -- Step F: Re-assert the "first admin exists" flag. The sandbox preserves its demo
5654
- -- ADMIN users across the wipe (Steps A & E), but the reseeded baseline resets
5655
- -- is_admin_created to 'false' and handle_new_user() only flips it on a NEW auth.users
5656
- -- INSERT -- which never fires for preserved users. Without this the proxy setup-gate
5657
- -- funnels ALL sandbox traffic (including this reset cron on its next run) to /setup even
5658
- -- though admins already exist. Guarded on an ADMIN profile actually being present.
5659
- UPDATE public.site_settings
5660
- SET value = 'true'::jsonb
5661
- WHERE key = 'is_admin_created'
5662
- AND EXISTS (SELECT 1 FROM public.profiles WHERE role = 'ADMIN'::public.user_role);
5663
-
5664
- `;
5625
+
5626
+
5627
+ -- >>> FROM: 00000000000010_drop_github_username.sql <<<
5628
+ -- Drop the github_username profile column and remove the GitHub-auth artifacts.
5629
+ --
5630
+ -- GitHub OAuth sign-in / account-linking was removed from the app: it cannot be
5631
+ -- provisioned as part of a one-click deployment (each install needs its own GitHub
5632
+ -- OAuth app + client secret + Supabase's manual-linking toggle, none of which can
5633
+ -- ship in a migration). github_username was only ever populated from that OAuth
5634
+ -- flow, so the column and its seed strings are now dead.
5635
+ --
5636
+ -- NOTE: the self-update "Connect GitHub" flow (lib/updates/*, ConnectGitHubButton)
5637
+ -- is a separate feature and is intentionally left untouched — the connect_github
5638
+ -- translation is kept for it.
5639
+
5640
+ -- 1. Rewrite handle_new_user() so new signups no longer read/populate github_username.
5641
+ CREATE OR REPLACE FUNCTION public.handle_new_user() RETURNS trigger
5642
+ LANGUAGE plpgsql SECURITY DEFINER
5643
+ SET search_path TO 'public'
5644
+ AS $$
5645
+ DECLARE
5646
+ admin_flag_set boolean := false;
5647
+ user_role public.user_role;
5648
+ v_full_name text;
5649
+ v_avatar_url text;
5650
+ BEGIN
5651
+ INSERT INTO public.site_settings (key, value)
5652
+ VALUES ('is_admin_created', 'false'::jsonb)
5653
+ ON CONFLICT (key) DO NOTHING;
5654
+
5655
+ SELECT COALESCE(value::jsonb::boolean, false)
5656
+ INTO admin_flag_set
5657
+ FROM public.site_settings
5658
+ WHERE key = 'is_admin_created'
5659
+ FOR UPDATE;
5660
+
5661
+ IF admin_flag_set = false THEN
5662
+ user_role := 'ADMIN'::public.user_role;
5663
+
5664
+ UPDATE public.site_settings
5665
+ SET value = 'true'::jsonb
5666
+ WHERE key = 'is_admin_created';
5667
+ ELSE
5668
+ user_role := 'USER'::public.user_role;
5669
+ END IF;
5670
+
5671
+ v_full_name := NEW.raw_user_meta_data->>'full_name';
5672
+ v_avatar_url := NEW.raw_user_meta_data->>'avatar_url';
5673
+
5674
+ INSERT INTO public.profiles (
5675
+ id,
5676
+ role,
5677
+ full_name,
5678
+ avatar_url
5679
+ )
5680
+ VALUES (
5681
+ NEW.id,
5682
+ user_role,
5683
+ v_full_name,
5684
+ v_avatar_url
5685
+ )
5686
+ ON CONFLICT (id) DO UPDATE SET
5687
+ full_name = EXCLUDED.full_name,
5688
+ avatar_url = EXCLUDED.avatar_url;
5689
+
5690
+ RETURN NEW;
5691
+ END;
5692
+ $$;
5693
+
5694
+ -- 2. Drop the column now that nothing writes it.
5695
+ ALTER TABLE public.profiles DROP COLUMN IF EXISTS github_username;
5696
+
5697
+ -- 3. Remove the GitHub-auth-only translation strings. connect_github is intentionally
5698
+ -- kept — it is still used by the self-update "Connect GitHub" button.
5699
+ DELETE FROM public.translations
5700
+ WHERE key IN (
5701
+ 'continue_with_github',
5702
+ 'github_username',
5703
+ 'github_username_help',
5704
+ 'github_link_failed',
5705
+ 'github_connected'
5706
+ );
5707
+
5708
+
5709
+ -- Step D: Record the applied migrations in history (truncated in Step B) so
5710
+ -- \`npm run db:migrate:check\` reports up to date instead of listing every file as pending.
5711
+ INSERT INTO supabase_migrations.schema_migrations (version, name) VALUES
5712
+ ('00000000000000', 'baseline_schema'),
5713
+ ('00000000000001', 'baseline_constraints_and_indexes'),
5714
+ ('00000000000002', 'baseline_security_and_grants'),
5715
+ ('00000000000003', 'baseline_seed'),
5716
+ ('00000000000004', 'default_logo_email_safe'),
5717
+ ('00000000000005', 'add_custom_canonical'),
5718
+ ('00000000000006', 'home_live_demo_promo'),
5719
+ ('00000000000007', 'home_live_demo_promo_copy_fix'),
5720
+ ('00000000000008', 'setup_article_reorder'),
5721
+ ('00000000000009', 'home_live_demo_promo_contrast'),
5722
+ ('00000000000010', 'drop_github_username')
5723
+ ON CONFLICT (version) DO NOTHING;
5724
+
5725
+ -- Step E: Anchor preserved profiles
5726
+ INSERT INTO public.profiles (id, updated_at, full_name, avatar_url, website, role)
5727
+ SELECT preserved_user.id, NULL, NULL, NULL, NULL, 'ADMIN'
5728
+ FROM (
5729
+ VALUES
5730
+ ('63a8aa07-ef6e-4cc7-bafa-ece023c74366'::uuid),
5731
+ ('e3a2c121-70a4-4239-a9df-f70ce717f5a1'::uuid)
5732
+ ) AS preserved_user(id)
5733
+ WHERE EXISTS (SELECT 1 FROM auth.users WHERE id = preserved_user.id)
5734
+ AND NOT EXISTS (SELECT 1 FROM public.profiles WHERE id = preserved_user.id);
5735
+
5736
+ -- Step F: Re-assert the "first admin exists" flag. The sandbox preserves its demo
5737
+ -- ADMIN users across the wipe (Steps A & E), but the reseeded baseline resets
5738
+ -- is_admin_created to 'false' and handle_new_user() only flips it on a NEW auth.users
5739
+ -- INSERT -- which never fires for preserved users. Without this the proxy setup-gate
5740
+ -- funnels ALL sandbox traffic (including this reset cron on its next run) to /setup even
5741
+ -- though admins already exist. Guarded on an ADMIN profile actually being present.
5742
+ UPDATE public.site_settings
5743
+ SET value = 'true'::jsonb
5744
+ WHERE key = 'is_admin_created'
5745
+ AND EXISTS (SELECT 1 FROM public.profiles WHERE role = 'ADMIN'::public.user_role);
5746
+
5747
+ `;
@@ -191,7 +191,6 @@ export default function CmsClientLayout({
191
191
 
192
192
  const getInitials = () => {
193
193
  if (profile && profile.full_name) return profile.full_name.split(' ').map((n: string) => n[0]).join('').substring(0,2).toUpperCase();
194
- if (profile && profile.github_username) return profile.github_username.substring(0,2).toUpperCase();
195
194
  if (user && user.email) return user.email.charAt(0).toUpperCase();
196
195
  return "U"; // Default fallback
197
196
  }
@@ -470,11 +469,11 @@ export default function CmsClientLayout({
470
469
  <div className="mt-auto p-3 border-t border-slate-200 dark:border-slate-700/60 shrink-0">
471
470
  <div className="flex items-center gap-3">
472
471
  <Avatar className="h-10 w-10 border">
473
- <AvatarImage src={profile?.avatar_url || undefined} alt={profile?.github_username || user?.email} />
472
+ <AvatarImage src={profile?.avatar_url || undefined} alt={profile?.full_name || user?.email} />
474
473
  <AvatarFallback className="bg-primary/10 text-primary font-medium">{getInitials()}</AvatarFallback>
475
474
  </Avatar>
476
475
  <div className="flex-1 min-w-0">
477
- <p className="text-sm font-medium truncate text-slate-700 dark:text-slate-200">{profile?.full_name || profile?.github_username || user?.email}</p>
476
+ <p className="text-sm font-medium truncate text-slate-700 dark:text-slate-200">{profile?.full_name || user?.email}</p>
478
477
  <div className="flex items-center gap-1.5">
479
478
  <div className={cn("h-2 w-2 rounded-full", getRoleColor())}></div>
480
479
  <p className="text-xs text-slate-500 dark:text-slate-400 capitalize">{role}</p>
@@ -212,7 +212,7 @@ export default function InteractionsModerationClient({
212
212
  <div className="space-y-4">
213
213
  {filteredInteractions.length > 0 ? (
214
214
  filteredInteractions.map((item) => {
215
- const authorName = item.profiles?.full_name || item.profiles?.github_username || "Anonymous";
215
+ const authorName = item.profiles?.full_name || "Anonymous";
216
216
  const dateStr = new Date(item.created_at).toLocaleString();
217
217
  const targetName = item.products?.title || item.posts?.title || "Unknown Target";
218
218
  const targetUrl = item.product_id
@@ -32,7 +32,7 @@ export default async function InteractionsPage() {
32
32
  created_at,
33
33
  product_id,
34
34
  post_id,
35
- profiles(full_name, avatar_url, github_username),
35
+ profiles(full_name, avatar_url),
36
36
  products(title, slug),
37
37
  posts(title, slug)
38
38
  `)
@@ -31,7 +31,7 @@ type RevisionItem = {
31
31
  revision_type: 'snapshot' | 'diff';
32
32
  created_at: string;
33
33
  author_id: string | null;
34
- author?: { full_name?: string | null; github_username?: string | null } | null;
34
+ author?: { full_name?: string | null } | null;
35
35
  };
36
36
 
37
37
  import { Input } from "@nextblock-cms/ui";
@@ -223,7 +223,7 @@ export default function RevisionHistoryButton({ parentType, parentId }: Revision
223
223
  <div className="rounded border divide-y">
224
224
  {revisions.map((rev: RevisionItem, idx) => {
225
225
  const when = rev.created_at ? formatDistanceToNow(new Date(rev.created_at), { addSuffix: true }) : '';
226
- const authorName = rev.author?.full_name || rev.author?.github_username;
226
+ const authorName = rev.author?.full_name;
227
227
  const isCurrent = currentVersion != null && rev.version === currentVersion;
228
228
  const isInitial = rev.version === 1;
229
229
 
@@ -12,7 +12,7 @@ type RevisionListItem = {
12
12
  revision_type: 'snapshot' | 'diff';
13
13
  created_at: string;
14
14
  author_id: string | null;
15
- author?: { full_name?: string | null; github_username?: string | null } | null;
15
+ author?: { full_name?: string | null } | null;
16
16
  };
17
17
 
18
18
  const REVISIONS_PER_PAGE = 10; // Reduced to 10 as requested
@@ -22,7 +22,7 @@ export async function listPageRevisions(pageId: number, page = 1, startDate?: st
22
22
 
23
23
  let query = supabase
24
24
  .from('page_revisions')
25
- .select('id, page_id, author_id, version, revision_type, created_at, content, author:profiles(full_name, github_username)', { count: 'exact' })
25
+ .select('id, page_id, author_id, version, revision_type, created_at, content, author:profiles(full_name)', { count: 'exact' })
26
26
  .eq('page_id', pageId)
27
27
  .order('version', { ascending: false })
28
28
  .limit(1000); // Fetch up to 1000 recent revisions to allow dense pagination after filtering
@@ -105,7 +105,7 @@ export async function listPageRevisions(pageId: number, page = 1, startDate?: st
105
105
  revision_type: 'snapshot',
106
106
  created_at: pageCreatedAt ?? new Date().toISOString(),
107
107
  author_id: null,
108
- author: { full_name: 'System (Initial)', github_username: 'system' },
108
+ author: { full_name: 'System (Initial)' },
109
109
  has_changes: true, // V1 always counts
110
110
  content: null
111
111
  });
@@ -139,7 +139,7 @@ export async function listPostRevisions(postId: number, page = 1, startDate?: st
139
139
 
140
140
  let query = supabase
141
141
  .from('post_revisions')
142
- .select('id, post_id, author_id, version, revision_type, created_at, content, author:profiles(full_name, github_username)', { count: 'exact' })
142
+ .select('id, post_id, author_id, version, revision_type, created_at, content, author:profiles(full_name)', { count: 'exact' })
143
143
  .eq('post_id', postId)
144
144
  .order('version', { ascending: false })
145
145
  .limit(1000);
@@ -221,7 +221,7 @@ export async function listPostRevisions(postId: number, page = 1, startDate?: st
221
221
  revision_type: 'snapshot',
222
222
  created_at: postCreatedAt ?? new Date().toISOString(),
223
223
  author_id: null,
224
- author: { full_name: 'System (Initial)', github_username: 'system' },
224
+ author: { full_name: 'System (Initial)' },
225
225
  has_changes: true,
226
226
  content: null
227
227
  });
@@ -38,7 +38,6 @@ type UpdateUserProfilePayload = {
38
38
  full_name?: string | null;
39
39
  avatar_url?: string | null;
40
40
  website?: string | null;
41
- github_username?: string | null;
42
41
  phone?: string | null;
43
42
  };
44
43
 
@@ -97,7 +96,6 @@ export async function updateUserProfile(userIdToUpdate: string, formData: FormDa
97
96
  full_name: formData.get("full_name") as string || null,
98
97
  avatar_url: formData.get("avatar_url") as string || null,
99
98
  website: formData.get("website") as string || null,
100
- github_username: formData.get("github_username") as string || null,
101
99
  phone: formData.get("phone") as string || null,
102
100
  };
103
101
 
@@ -126,7 +124,6 @@ export async function updateUserProfile(userIdToUpdate: string, formData: FormDa
126
124
  full_name: rawFormData.full_name,
127
125
  avatar_url: rawFormData.avatar_url,
128
126
  website: rawFormData.website,
129
- github_username: rawFormData.github_username,
130
127
  phone: rawFormData.phone,
131
128
  };
132
129
 
@@ -30,7 +30,6 @@ export default function UserForm({ userToEditAuth, userToEditProfile, userToEdit
30
30
  if (data.full_name !== undefined) formData.append('full_name', data.full_name || '');
31
31
  if (data.avatar_url !== undefined) formData.append('avatar_url', data.avatar_url || '');
32
32
  if (data.website !== undefined) formData.append('website', data.website || '');
33
- if (data.github_username !== undefined) formData.append('github_username', data.github_username || '');
34
33
  if (data.phone !== undefined) formData.append('phone', data.phone || '');
35
34
  if (data.role !== undefined) formData.append('role', data.role);
36
35
 
@@ -48,7 +47,6 @@ export default function UserForm({ userToEditAuth, userToEditProfile, userToEdit
48
47
  full_name: userToEditProfile?.full_name || '',
49
48
  avatar_url: userToEditProfile?.avatar_url || '',
50
49
  website: userToEditProfile?.website || '',
51
- github_username: userToEditProfile?.github_username || '',
52
50
  phone: userToEditProfile?.phone || '',
53
51
  role: userToEditProfile?.role || 'USER',
54
52
  billing_address: userToEditAddresses.billingAddress,
@@ -129,7 +129,6 @@ export default async function CmsUsersListPage() {
129
129
  <TableHead className="w-[80px]">Avatar</TableHead>
130
130
  <TableHead>Email</TableHead>
131
131
  <TableHead>Full Name</TableHead>
132
- <TableHead>GitHub</TableHead>
133
132
  <TableHead>Role</TableHead>
134
133
  <TableHead>Joined</TableHead>
135
134
  <TableHead className="text-right w-[80px]">Actions</TableHead>
@@ -146,7 +145,6 @@ export default async function CmsUsersListPage() {
146
145
  </TableCell>
147
146
  <TableCell className="font-medium">{authUser.email}</TableCell>
148
147
  <TableCell className="text-muted-foreground">{profile?.full_name || "N/A"}</TableCell>
149
- <TableCell className="text-muted-foreground">{profile?.github_username || "-"}</TableCell>
150
148
  <TableCell>
151
149
  <Badge variant={
152
150
  profile?.role === "ADMIN" ? "destructive" :
@@ -27,7 +27,7 @@ export function ProfileAccountSidebar({
27
27
  }: ProfileAccountSidebarProps) {
28
28
  const { t } = useTranslations();
29
29
  const displayName =
30
- profile.full_name || profile.github_username || user.email || 'User';
30
+ profile.full_name || user.email || 'User';
31
31
 
32
32
  return (
33
33
  <Card className="h-fit">
@@ -18,7 +18,7 @@ export async function requireProfileAccountContext(redirectTo: string) {
18
18
 
19
19
  const { data: profile } = await supabase
20
20
  .from('profiles')
21
- .select('id, avatar_url, full_name, github_username')
21
+ .select('id, avatar_url, full_name')
22
22
  .eq('id', user.id)
23
23
  .maybeSingle();
24
24
 
@@ -7,5 +7,4 @@ export interface ProfileAccountSummary {
7
7
  id: string;
8
8
  avatar_url: string | null;
9
9
  full_name: string | null;
10
- github_username: string | null;
11
10
  }
@@ -36,7 +36,6 @@ export default async function ProfilePage() {
36
36
  full_name: profile.full_name || '',
37
37
  avatar_url: profile.avatar_url || '',
38
38
  website: profile.website || '',
39
- github_username: profile.github_username || '',
40
39
  phone: profile.phone || '',
41
40
  billing_address: billingAddress,
42
41
  shipping_address: shippingAddress,
@@ -77,7 +77,7 @@ export default function PostCommentsSection({ postId }: PostCommentsSectionProps
77
77
  try {
78
78
  const { data, error: dbError } = await supabase
79
79
  .from("cms_interactions" as any)
80
- .select("*, profiles(full_name, avatar_url, github_username)")
80
+ .select("*, profiles(full_name, avatar_url)")
81
81
  .eq("post_id", postId)
82
82
  .eq("type", "comment")
83
83
  .eq("status", "approved")
@@ -280,7 +280,7 @@ export default function PostCommentsSection({ postId }: PostCommentsSectionProps
280
280
  optimisticComments.map((comment) => {
281
281
  const hasLiked = likedIds.includes(comment.id) || comment.tempHasReacted;
282
282
  const likeCount = (comment.reactions as Record<string, number>)?.likes || 0;
283
- const commenterName = comment.profiles?.full_name || comment.profiles?.github_username || "Anonymous";
283
+ const commenterName = comment.profiles?.full_name || "Anonymous";
284
284
  const dateStr = new Date(comment.created_at).toLocaleDateString(lang, {
285
285
  year: "numeric",
286
286
  month: "long",