create-nextblock 0.8.11 → 0.9.5

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 (54) hide show
  1. package/bin/create-nextblock.js +101 -35
  2. package/docker-template/.dockerignore +23 -0
  3. package/docker-template/.env.docker.example +56 -0
  4. package/docker-template/Dockerfile +85 -0
  5. package/docker-template/docker/db/init/99-jwt.sql +6 -0
  6. package/docker-template/docker/db/init/99-roles.sql +25 -0
  7. package/docker-template/docker/kong/kong.yml +112 -0
  8. package/docker-template/docker/migrate/run-migrations.sh +51 -0
  9. package/docker-template/docker-compose.yml +219 -0
  10. package/docker-template/scripts/docker-setup.mjs +242 -0
  11. package/package.json +1 -1
  12. package/scripts/sync-template.js +29 -0
  13. package/templates/nextblock-template/.dockerignore +23 -0
  14. package/templates/nextblock-template/Dockerfile +85 -0
  15. package/templates/nextblock-template/app/[slug]/page.tsx +5 -0
  16. package/templates/nextblock-template/app/actions.ts +58 -8
  17. package/templates/nextblock-template/app/api/cron/reset-sandbox/sandboxResetSql.ts +83 -0
  18. package/templates/nextblock-template/app/api/upload/presigned-url/route.ts +9 -9
  19. package/templates/nextblock-template/app/article/[slug]/page.tsx +5 -0
  20. package/templates/nextblock-template/app/cms/settings/security/actions.ts +30 -0
  21. package/templates/nextblock-template/app/cms/settings/security/components/SecurityPanel.tsx +69 -0
  22. package/templates/nextblock-template/app/layout.tsx +57 -3
  23. package/templates/nextblock-template/app/lib/site-settings.ts +22 -7
  24. package/templates/nextblock-template/app/page.tsx +6 -0
  25. package/templates/nextblock-template/app/product/[slug]/page.tsx +5 -0
  26. package/templates/nextblock-template/app/setup/SetupWizard.tsx +771 -0
  27. package/templates/nextblock-template/app/setup/layout.tsx +13 -0
  28. package/templates/nextblock-template/app/setup/page.tsx +103 -0
  29. package/templates/nextblock-template/components/AppShell.tsx +12 -0
  30. package/templates/nextblock-template/components/header-auth.tsx +24 -62
  31. package/templates/nextblock-template/docker/db/init/99-jwt.sql +6 -0
  32. package/templates/nextblock-template/docker/db/init/99-roles.sql +25 -0
  33. package/templates/nextblock-template/docker/kong/kong.yml +112 -0
  34. package/templates/nextblock-template/docker/migrate/run-migrations.sh +51 -0
  35. package/templates/nextblock-template/docker-compose.yml +219 -0
  36. package/templates/nextblock-template/docs/11-SELF-HOSTED-DOCKER.md +173 -0
  37. package/templates/nextblock-template/docs/12-VERCEL-DEPLOYMENT.md +67 -0
  38. package/templates/nextblock-template/docs/README.md +2 -0
  39. package/templates/nextblock-template/lib/blocks/FeaturedProductBlock.tsx +1 -1
  40. package/templates/nextblock-template/lib/blocks/ProductGridBlock.tsx +1 -1
  41. package/templates/nextblock-template/lib/custom-block-r2-upload.test.ts +5 -5
  42. package/templates/nextblock-template/lib/custom-block-r2-upload.ts +2 -2
  43. package/templates/nextblock-template/lib/setup/actions.ts +370 -0
  44. package/templates/nextblock-template/lib/setup/env-status.ts +86 -0
  45. package/templates/nextblock-template/lib/setup/env-write.ts +111 -0
  46. package/templates/nextblock-template/lib/setup/provisioning.ts +59 -0
  47. package/templates/nextblock-template/lib/setup/schema-apply.ts +379 -0
  48. package/templates/nextblock-template/lib/setup/system-config.ts +105 -0
  49. package/templates/nextblock-template/lib/setup/types.ts +18 -0
  50. package/templates/nextblock-template/next.config.js +9 -0
  51. package/templates/nextblock-template/package.json +6 -2
  52. package/templates/nextblock-template/proxy.ts +143 -49
  53. package/templates/nextblock-template/scripts/docker-setup.mjs +242 -0
  54. package/templates/nextblock-template/tsconfig.tsbuildinfo +1 -1
@@ -83,30 +83,46 @@ async function handleCommand(projectDirectory, options) {
83
83
  try {
84
84
  console.log(chalk.bold.cyan(`\n🧱 create-nextblock v${CLI_VERSION}\n`));
85
85
 
86
- // Prerequisites gate (interactive only) — shown BEFORE we ask for a name, scaffold, or
87
- // install, so anyone who isn't ready can cancel without creating anything.
86
+ // Pick the hosting profile up front (interactive only). Cloud = Vercel + Supabase Cloud;
87
+ // Docker = a fully local self-hosted sandbox that needs no cloud accounts.
88
+ let hostingMode = 'cloud';
88
89
  if (!yes) {
90
+ const modeChoice = await clack.select({
91
+ message: 'Select your target hosting environment profile:',
92
+ options: [
93
+ { value: 'cloud', label: 'Managed Cloud Mode (Vercel + Supabase Cloud)' },
94
+ {
95
+ value: 'docker',
96
+ label: 'Local Self-Hosted Docker Mode (One-Click Local Sandbox)',
97
+ },
98
+ ],
99
+ initialValue: 'cloud',
100
+ });
101
+ if (clack.isCancel(modeChoice)) {
102
+ handleWizardCancel('Setup cancelled.');
103
+ }
104
+ hostingMode = modeChoice;
105
+ }
106
+
107
+ // Cloud / local configuration moved to the browser First-Boot Setup Wizard (/setup), so the
108
+ // CLI no longer asks for Supabase / R2 / SMTP credentials here. Docker still preflights below.
109
+ if (!yes && hostingMode === 'docker') {
89
110
  clack.note(
90
111
  [
91
- '1. A Supabase project https://supabase.com/dashboard',
92
- ' • Reference ID — Project Settings > General > "Reference ID"',
93
- ' • Connection string — Connect (top bar) > Direct connection > URI',
94
- ' • anon + service_role keys — Project Settings > API Keys',
95
- ' • Personal Access Token — Account > Access Tokens > Generate new token',
112
+ 'Local Self-Hosted Docker Mode runs everything on your machine — no cloud accounts needed.',
96
113
  '',
97
- '2. A Cloudflare R2 bucket https://dash.cloudflare.com > R2',
98
- ' • Create a bucket, then enable its Public Development URL (Bucket > Settings > General)',
99
- ' • Create an R2 API token (Object Read & Write); copy the Access Key ID + Secret (shown once)',
114
+ 'Requirement:',
115
+ ' • Docker Desktop installed and running (https://www.docker.com/products/docker-desktop)',
100
116
  '',
101
- '3. SMTP credentials SMTP2GO works very well: https://www.smtp2go.com',
102
- ' • Required so Supabase can email the confirmation link your first admin needs to sign in',
117
+ 'Optional (you can skip both at the prompts):',
118
+ ' • Cloudflare Turnstile keys (bot protection)',
119
+ ' • SMTP credentials (otherwise sign-ups auto-confirm with no email)',
103
120
  ].join('\n'),
104
- 'Before you continue, have all of the following ready',
121
+ 'One-click local sandbox',
105
122
  );
106
123
 
107
124
  const ready = await clack.confirm({
108
- message:
109
- 'Do you have your Supabase, Cloudflare R2, and SMTP details ready?',
125
+ message: 'Is Docker Desktop installed and running?',
110
126
  initialValue: true,
111
127
  });
112
128
  if (clack.isCancel(ready)) {
@@ -114,7 +130,7 @@ async function handleCommand(projectDirectory, options) {
114
130
  }
115
131
  if (!ready) {
116
132
  clack.note(
117
- 'No problem — nothing was created. Gather the items above, then run\n`npm create nextblock` again. Full guide: docs/05-DEVELOPER-GUIDE.md',
133
+ 'No problem — nothing was created. Install & start Docker Desktop, then run\n`npm create nextblock` again.',
118
134
  'Come back when ready',
119
135
  );
120
136
  return;
@@ -214,26 +230,13 @@ async function handleCommand(projectDirectory, options) {
214
230
  console.log(chalk.yellow('Skipping dependency installation.'));
215
231
  }
216
232
 
217
- // Run the setup wizard after dependencies are installed so package assets are available.
218
- // When it runs, its own "next steps" outro (cd + npm run dev) is the final message, so we
219
- // don't print a second closing block here — the whole flow completes in this one command.
220
- if (!yes) {
221
- await runSetupWizard(projectDir, projectName);
233
+ // Run the post-scaffold flow after dependencies are installed so package assets are available.
234
+ // Docker boots the local stack; everything else just materializes Supabase assets and points
235
+ // the user at the browser First-Boot Setup Wizard at /setup (no terminal credential prompts).
236
+ if (!yes && hostingMode === 'docker') {
237
+ await runDockerSetup(projectDir, projectName);
222
238
  } else {
223
- // Non-interactive path: nothing was configured, so point the user at their env file.
224
- console.log(
225
- chalk.green(
226
- `\nSuccess! Your NextBlockā„¢ CMS project "${projectName}" is scaffolded.\n`,
227
- ),
228
- );
229
- console.log(chalk.cyan('Next steps:'));
230
- console.log(chalk.cyan(` 1. cd ${projectName}`));
231
- console.log(
232
- chalk.gray(
233
- ' 2. Add your Supabase / R2 / SMTP values to .env.local (template in .env.example)',
234
- ),
235
- );
236
- console.log(chalk.cyan(' 3. npm run dev'));
239
+ await runCloudScaffold(projectDir, projectName);
237
240
  }
238
241
  } catch (error) {
239
242
  console.error(
@@ -898,6 +901,65 @@ async function runSetupWizard(projectDir, projectName) {
898
901
  );
899
902
  }
900
903
 
904
+ // Local Self-Hosted Docker Mode: materialize the supabase migrations out of the installed
905
+ // @nextblock-cms/db package (the migration-runner container applies them on boot), then hand off
906
+ // to the project's own zero-dependency Docker setup script (prompts + .env + `docker compose up`).
907
+ async function runCloudScaffold(projectDir, projectName) {
908
+ const projectPath = resolve(projectDir);
909
+
910
+ // Materialize the Supabase assets (migrations + config) so `npm run db:migrate` works later,
911
+ // then hand off entirely to the browser First-Boot Setup Wizard for configuration. No
912
+ // credentials are collected in the terminal.
913
+ try {
914
+ await ensureSupabaseAssets(projectPath, { required: false });
915
+ } catch {
916
+ // Non-fatal: the wizard still works; db:migrate just needs these assets present.
917
+ }
918
+
919
+ console.log(
920
+ chalk.green(
921
+ `\nSuccess! Your NextBlockā„¢ CMS project "${projectName}" is scaffolded.\n`,
922
+ ),
923
+ );
924
+ console.log(chalk.cyan('Next steps:'));
925
+ console.log(chalk.cyan(` 1. cd ${projectName}`));
926
+ console.log(chalk.cyan(' 2. npm run dev'));
927
+ console.log(
928
+ ` 3. Open ${chalk.cyan('/setup')} ${chalk.gray('in your browser (the URL npm run dev prints, e.g. http://localhost:3000/setup)')}`,
929
+ );
930
+ console.log(
931
+ chalk.gray(' Connect Supabase, configure storage / email, and create your administrator.'),
932
+ );
933
+ console.log('');
934
+ console.log(chalk.gray(' Self-hosted Docker instead? npm run docker:setup'));
935
+ console.log(chalk.gray(' One-click cloud deploy: see docs/12-VERCEL-DEPLOYMENT.md'));
936
+ }
937
+
938
+ async function runDockerSetup(projectDir, projectName) {
939
+ const projectPath = resolve(projectDir);
940
+ process.chdir(projectPath);
941
+
942
+ clack.intro('🐳 NextBlockā„¢ CMS — Local Self-Hosted Docker setup');
943
+
944
+ await ensureSupabaseAssets(projectPath, { required: true });
945
+
946
+ const setupScript = resolve(projectPath, 'scripts', 'docker-setup.mjs');
947
+ if (!(await fs.pathExists(setupScript))) {
948
+ clack.note(
949
+ 'scripts/docker-setup.mjs is missing from the template. Run `npm run sync:create-nextblock` and try again.',
950
+ 'Docker setup unavailable',
951
+ );
952
+ return;
953
+ }
954
+
955
+ // The script drives docker compose interactively; inherit stdio so its prompts work.
956
+ await runCommand('node', ['scripts/docker-setup.mjs'], { cwd: projectPath });
957
+
958
+ clack.outro(
959
+ `šŸŽ‰ Your NextBlockā„¢ project ${projectName ? `"${projectName}" ` : ''}is running in Docker.\nApp: http://localhost:3000 (first sign-up becomes ADMIN)`,
960
+ );
961
+ }
962
+
901
963
  async function configureHostedSupabaseAuth(
902
964
  projectDir,
903
965
  { projectId, siteUrl, accessToken, smtpValues },
@@ -1924,7 +1986,11 @@ function buildNextConfigContent(editorUtilNames) {
1924
1986
  '/**',
1925
1987
  " * @type {import('next').NextConfig}",
1926
1988
  ' **/',
1989
+ // Self-hosted Docker builds emit a standalone server (`node server.js`); gated on
1990
+ // DOCKER_BUILD so a normal `next build` / Vercel deploy is unaffected.
1991
+ "const isDockerStandalone = process.env.DOCKER_BUILD === 'true';",
1927
1992
  'const nextConfig = {',
1993
+ " ...(isDockerStandalone ? { output: 'standalone' } : {}),",
1928
1994
  ' outputFileTracingRoot: path.join(__dirname),',
1929
1995
  ' env: {',
1930
1996
  ' NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL,',
@@ -0,0 +1,23 @@
1
+ # Keep the build context lean; deps are reinstalled in the image and secrets stay out.
2
+ node_modules
3
+ .git
4
+ .github
5
+ .next
6
+ out
7
+ dist
8
+ coverage
9
+ tmp
10
+ backup
11
+ backups
12
+ *.log
13
+ npm-debug.log*
14
+
15
+ # Secrets never enter the image (NEXT_PUBLIC_* arrive as build args).
16
+ .env
17
+ .env.*
18
+ !.env.docker.example
19
+
20
+ # Not needed inside the app image (mounted/used by other compose services instead).
21
+ docker
22
+ supabase/.branches
23
+ supabase/.temp
@@ -0,0 +1,56 @@
1
+ # ──────────────────────────────────────────────────────────────────────────────
2
+ # Reference for the Docker (self-hosted) .env that `npm run docker:setup` generates.
3
+ # You normally do NOT edit this by hand — run `npm run docker:setup` and it writes a
4
+ # `.env` with secure random secrets. This file documents every key the stack reads.
5
+ # ──────────────────────────────────────────────────────────────────────────────
6
+
7
+ # --- Auto-generated secrets (crypto-random) --------------------------------------------------
8
+ POSTGRES_PASSWORD=replace-me-generated
9
+ JWT_SECRET=replace-me-at-least-32-chars
10
+ JWT_EXP=3600
11
+ # anon + service_role keys are HS256 JWTs SIGNED with JWT_SECRET (generated for you):
12
+ ANON_KEY=replace-me-anon-jwt
13
+ SERVICE_ROLE_KEY=replace-me-service-role-jwt
14
+ CRON_SECRET=replace-me-generated
15
+ DRAFT_MODE_SECRET=replace-me-generated
16
+ REVALIDATE_SECRET_TOKEN=replace-me-generated
17
+
18
+ # --- Supabase wiring (internal container network vs browser) ---------------------------------
19
+ NEXT_PUBLIC_SUPABASE_URL=http://localhost:8000
20
+ NEXT_PUBLIC_SUPABASE_ANON_KEY=${ANON_KEY}
21
+ SUPABASE_SERVICE_ROLE_KEY=${SERVICE_ROLE_KEY}
22
+ API_EXTERNAL_URL=http://localhost:8000
23
+ SITE_URL=http://localhost:3000
24
+ POSTGRES_DB=postgres
25
+
26
+ # --- App ------------------------------------------------------------------------------------
27
+ NEXT_PUBLIC_URL=http://localhost:3000
28
+ NEXT_PUBLIC_IS_SANDBOX=true
29
+ CORTEX_AI_ENCRYPTION_KEY=
30
+
31
+ # --- MinIO object storage (S3-compatible) ----------------------------------------------------
32
+ MINIO_ROOT_USER=nextblock
33
+ MINIO_ROOT_PASSWORD=replace-me-generated
34
+ STORAGE_BUCKET=nextblock
35
+ R2_ACCOUNT_ID=minio
36
+ R2_REGION=us-east-1
37
+ R2_S3_ENDPOINT=http://minio:9000
38
+ # 127.0.0.1 (not localhost) so the browser never sends the app's localhost cookies to MinIO,
39
+ # which would otherwise 400 (MetadataTooLarge) once the Supabase auth cookies grow.
40
+ R2_S3_PUBLIC_ENDPOINT=http://127.0.0.1:9000
41
+ R2_FORCE_PATH_STYLE=true
42
+ NEXT_PUBLIC_R2_BASE_URL=http://127.0.0.1:9000/nextblock
43
+ NEXT_PUBLIC_R2_PUBLIC_URL=http://127.0.0.1:9000/nextblock
44
+
45
+ # --- Cloudflare Turnstile (optional). Press Enter at setup to skip and use the dev sandbox. ---
46
+ NEXT_PUBLIC_TURNSTILE_SITE_KEY=
47
+ TURNSTILE_SECRET_KEY=
48
+
49
+ # --- SMTP (optional). Blank => GoTrue auto-confirms sign-ups (loopback mode). -----------------
50
+ GOTRUE_MAILER_AUTOCONFIRM=true
51
+ SMTP_HOST=
52
+ SMTP_PORT=
53
+ SMTP_USER=
54
+ SMTP_PASS=
55
+ SMTP_FROM_EMAIL=
56
+ SMTP_FROM_NAME=
@@ -0,0 +1,85 @@
1
+ # syntax=docker/dockerfile:1.7
2
+ #
3
+ # Production image for a standalone NextBlock CMS project (single Next.js app, not a monorepo).
4
+ # Built with Next.js standalone output, gated on DOCKER_BUILD so a normal `npm run build` / a
5
+ # Vercel deploy is unaffected. Driven by docker-compose.yml (`npm run docker:setup`).
6
+
7
+ ###############################################################################
8
+ # Stage 1 — deps
9
+ ###############################################################################
10
+ FROM node:22-alpine AS deps
11
+ WORKDIR /app
12
+ RUN apk add --no-cache libc6-compat
13
+ COPY package.json ./
14
+ RUN npm install --no-audit --no-fund
15
+
16
+ ###############################################################################
17
+ # Stage 2 — builder (Next.js standalone output)
18
+ ###############################################################################
19
+ FROM node:22-alpine AS builder
20
+ WORKDIR /app
21
+ RUN apk add --no-cache libc6-compat
22
+ ENV NEXT_TELEMETRY_DISABLED=1 \
23
+ CI=true \
24
+ DOCKER_BUILD=true \
25
+ NODE_ENV=production
26
+ COPY --from=deps /app/node_modules ./node_modules
27
+ COPY . .
28
+
29
+ # NEXT_PUBLIC_* values are inlined into the browser bundle at build time. Server-only secrets are
30
+ # NOT baked in — they arrive at runtime from docker-compose.yml.
31
+ ARG NEXT_PUBLIC_SUPABASE_URL
32
+ ARG NEXT_PUBLIC_SUPABASE_ANON_KEY
33
+ ARG NEXT_PUBLIC_URL
34
+ ARG NEXT_PUBLIC_R2_PUBLIC_URL
35
+ ARG NEXT_PUBLIC_R2_BASE_URL
36
+ ARG NEXT_PUBLIC_TURNSTILE_SITE_KEY
37
+ ARG NEXT_PUBLIC_IS_SANDBOX
38
+ ENV NEXT_PUBLIC_SUPABASE_URL=$NEXT_PUBLIC_SUPABASE_URL \
39
+ NEXT_PUBLIC_SUPABASE_ANON_KEY=$NEXT_PUBLIC_SUPABASE_ANON_KEY \
40
+ NEXT_PUBLIC_URL=$NEXT_PUBLIC_URL \
41
+ NEXT_PUBLIC_R2_PUBLIC_URL=$NEXT_PUBLIC_R2_PUBLIC_URL \
42
+ NEXT_PUBLIC_R2_BASE_URL=$NEXT_PUBLIC_R2_BASE_URL \
43
+ NEXT_PUBLIC_TURNSTILE_SITE_KEY=$NEXT_PUBLIC_TURNSTILE_SITE_KEY \
44
+ NEXT_PUBLIC_IS_SANDBOX=$NEXT_PUBLIC_IS_SANDBOX
45
+
46
+ RUN npm run build
47
+
48
+ ###############################################################################
49
+ # Stage 3 — runner (hardened, non-root)
50
+ ###############################################################################
51
+ FROM node:22-alpine AS runner
52
+ WORKDIR /app
53
+ RUN apk add --no-cache libc6-compat socat \
54
+ && addgroup -g 1001 -S nodejs \
55
+ && adduser -u 1001 -S nextjs -G nodejs
56
+ ENV NODE_ENV=production \
57
+ NEXT_TELEMETRY_DISABLED=1 \
58
+ PORT=3000 \
59
+ HOSTNAME=0.0.0.0 \
60
+ # ipv4first so localhost resolves to the socat IPv4 listener; the larger header limit absorbs
61
+ # Supabase auth cookies (on localhost they aren't port-scoped, so they pile onto every request).
62
+ NODE_OPTIONS="--dns-result-order=ipv4first --max-http-header-size=65536"
63
+
64
+ # A single-app project traces to its own root, so server.js sits at the top of .next/standalone.
65
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
66
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
67
+ COPY --from=builder --chown=nextjs:nodejs /app/public ./public
68
+
69
+ # Loopback proxy: the browser-facing localhost URLs (Supabase :8000, MinIO :9000) are inlined into
70
+ # the build, but server-side code + next/image run INSIDE this container. Forward those ports to the
71
+ # compose services so SSR + image optimization work, and so the Supabase auth cookie key (derived
72
+ # from the URL host) matches between browser and server. Override targets with LOOPBACK_PROXIES.
73
+ RUN printf '%s\n' \
74
+ '#!/bin/sh' \
75
+ 'set -e' \
76
+ ': "${LOOPBACK_PROXIES:=8000:kong:8000 9000:minio:9000}"' \
77
+ 'for p in $LOOPBACK_PROXIES; do' \
78
+ ' socat "TCP4-LISTEN:${p%%:*},fork,reuseaddr,bind=127.0.0.1" "TCP:${p#*:}" 2>/dev/null &' \
79
+ 'done' \
80
+ 'exec node "$(cat .server-entry 2>/dev/null || echo server.js)"' \
81
+ > /app/docker-entrypoint.sh && chmod +x /app/docker-entrypoint.sh
82
+
83
+ USER nextjs
84
+ EXPOSE 3000
85
+ ENTRYPOINT ["/app/docker-entrypoint.sh"]
@@ -0,0 +1,6 @@
1
+ -- Expose the JWT secret to Postgres as a database GUC, matching Supabase's setup. Runs once.
2
+ \set jwt_secret `echo "$JWT_SECRET"`
3
+ \set jwt_exp `echo "$JWT_EXP"`
4
+
5
+ alter database postgres set "app.settings.jwt_secret" to :'jwt_secret';
6
+ alter database postgres set "app.settings.jwt_exp" to :'jwt_exp';
@@ -0,0 +1,25 @@
1
+ -- Self-hosted Supabase role passwords. The supabase/postgres image creates the platform roles;
2
+ -- GoTrue logs in as supabase_auth_admin and PostgREST as authenticator. Give the roles that exist
3
+ -- the generated POSTGRES_PASSWORD (the set varies by image tag, so each is guarded; the password
4
+ -- is passed via a session GUC because psql variables aren't interpolated inside a dollar block).
5
+ -- Runs once on first init.
6
+ \set pgpass `echo "$POSTGRES_PASSWORD"`
7
+ select set_config('nextblock.pgpass', :'pgpass', false);
8
+
9
+ do $$
10
+ declare
11
+ role_name text;
12
+ begin
13
+ foreach role_name in array array[
14
+ 'authenticator',
15
+ 'pgbouncer',
16
+ 'supabase_auth_admin',
17
+ 'supabase_storage_admin',
18
+ 'supabase_functions_admin',
19
+ 'supabase_read_only_user'
20
+ ] loop
21
+ if exists (select 1 from pg_roles where rolname = role_name) then
22
+ execute format('alter role %I with password %L', role_name, current_setting('nextblock.pgpass'));
23
+ end if;
24
+ end loop;
25
+ end $$;
@@ -0,0 +1,112 @@
1
+ # Kong declarative (DB-less) gateway config for the NextBlock self-hosted stack.
2
+ #
3
+ # $SUPABASE_ANON_KEY and $SUPABASE_SERVICE_KEY are substituted at container start by the kong
4
+ # service entrypoint (docker-compose.yml) from the generated ANON_KEY / SERVICE_ROLE_KEY env.
5
+ # Trimmed to just the Auth (GoTrue) and PostgREST routes the app uses.
6
+ _format_version: '2.1'
7
+ _transform: true
8
+
9
+ consumers:
10
+ - username: anon
11
+ keyauth_credentials:
12
+ - key: $SUPABASE_ANON_KEY
13
+ - username: service_role
14
+ keyauth_credentials:
15
+ - key: $SUPABASE_SERVICE_KEY
16
+
17
+ acls:
18
+ - consumer: anon
19
+ group: anon
20
+ - consumer: service_role
21
+ group: admin
22
+
23
+ services:
24
+ - name: auth-v1-open
25
+ url: http://auth:9999/verify
26
+ routes:
27
+ - name: auth-v1-open
28
+ strip_path: true
29
+ paths:
30
+ - /auth/v1/verify
31
+ plugins:
32
+ - name: cors
33
+ - name: auth-v1-open-callback
34
+ url: http://auth:9999/callback
35
+ routes:
36
+ - name: auth-v1-open-callback
37
+ strip_path: true
38
+ paths:
39
+ - /auth/v1/callback
40
+ plugins:
41
+ - name: cors
42
+ - name: auth-v1-open-authorize
43
+ url: http://auth:9999/authorize
44
+ routes:
45
+ - name: auth-v1-open-authorize
46
+ strip_path: true
47
+ paths:
48
+ - /auth/v1/authorize
49
+ plugins:
50
+ - name: cors
51
+
52
+ - name: auth-v1
53
+ url: http://auth:9999/
54
+ routes:
55
+ - name: auth-v1-all
56
+ strip_path: true
57
+ paths:
58
+ - /auth/v1/
59
+ plugins:
60
+ - name: cors
61
+ - name: key-auth
62
+ config:
63
+ hide_credentials: false
64
+ - name: acl
65
+ config:
66
+ hide_groups_header: true
67
+ allow:
68
+ - admin
69
+ - anon
70
+
71
+ - name: rest-v1
72
+ url: http://rest:3000/
73
+ routes:
74
+ - name: rest-v1-all
75
+ strip_path: true
76
+ paths:
77
+ - /rest/v1/
78
+ plugins:
79
+ - name: cors
80
+ - name: key-auth
81
+ config:
82
+ hide_credentials: true
83
+ - name: acl
84
+ config:
85
+ hide_groups_header: true
86
+ allow:
87
+ - admin
88
+ - anon
89
+
90
+ - name: graphql-v1
91
+ url: http://rest:3000/rpc/graphql
92
+ routes:
93
+ - name: graphql-v1-all
94
+ strip_path: true
95
+ paths:
96
+ - /graphql/v1
97
+ plugins:
98
+ - name: cors
99
+ - name: key-auth
100
+ config:
101
+ hide_credentials: false
102
+ - name: request-transformer
103
+ config:
104
+ add:
105
+ headers:
106
+ - 'Content-Profile: graphql_public'
107
+ - name: acl
108
+ config:
109
+ hide_groups_header: true
110
+ allow:
111
+ - admin
112
+ - anon
@@ -0,0 +1,51 @@
1
+ #!/bin/sh
2
+ # Applies the NextBlock SQL migrations (mounted at /migrations) in chronological filename order,
3
+ # once GoTrue has provisioned the auth schema (profiles etc. FK to auth.users). Idempotent: a
4
+ # tracking table records applied versions so restarts never re-run a migration. Each file runs
5
+ # in a single transaction (ON_ERROR_STOP).
6
+ set -eu
7
+
8
+ PGHOST="${POSTGRES_HOST:-db}"
9
+ PGPORT="${POSTGRES_PORT:-5432}"
10
+ PGUSER="postgres"
11
+ PGDATABASE="${POSTGRES_DB:-postgres}"
12
+ export PGPASSWORD="${POSTGRES_PASSWORD}"
13
+
14
+ psql_cmd() {
15
+ psql -v ON_ERROR_STOP=1 -h "$PGHOST" -p "$PGPORT" -U "$PGUSER" -d "$PGDATABASE" "$@"
16
+ }
17
+
18
+ echo "[migrate] waiting for Postgres at ${PGHOST}:${PGPORT}..."
19
+ until psql_cmd -c 'select 1;' >/dev/null 2>&1; do
20
+ sleep 2
21
+ done
22
+
23
+ echo "[migrate] waiting for GoTrue to create auth.users..."
24
+ until [ "$(psql_cmd -tAc "select to_regclass('auth.users') is not null;")" = "t" ]; do
25
+ sleep 2
26
+ done
27
+
28
+ psql_cmd -c "create table if not exists public._nextblock_docker_migrations (
29
+ version text primary key,
30
+ applied_at timestamptz not null default now()
31
+ );"
32
+
33
+ applied_any=0
34
+ for file in $(ls /migrations/*.sql | sort); do
35
+ version="$(basename "$file" .sql)"
36
+ already="$(psql_cmd -tAc "select 1 from public._nextblock_docker_migrations where version = '${version}';")"
37
+ if [ "$already" = "1" ]; then
38
+ echo "[migrate] skip ${version} (already applied)"
39
+ continue
40
+ fi
41
+ echo "[migrate] applying ${version}"
42
+ psql_cmd --single-transaction -f "$file"
43
+ psql_cmd -c "insert into public._nextblock_docker_migrations (version) values ('${version}');"
44
+ applied_any=1
45
+ done
46
+
47
+ if [ "$applied_any" = "1" ]; then
48
+ echo "[migrate] migrations applied successfully."
49
+ else
50
+ echo "[migrate] database already up to date."
51
+ fi