create-nextblock 0.8.11 → 0.9.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.
- package/bin/create-nextblock.js +91 -5
- package/docker-template/.dockerignore +23 -0
- package/docker-template/.env.docker.example +55 -0
- package/docker-template/Dockerfile +68 -0
- package/docker-template/docker/db/init/99-jwt.sql +6 -0
- package/docker-template/docker/db/init/99-roles.sql +25 -0
- package/docker-template/docker/kong/kong.yml +112 -0
- package/docker-template/docker/migrate/run-migrations.sh +51 -0
- package/docker-template/docker-compose.yml +215 -0
- package/docker-template/scripts/docker-setup.mjs +227 -0
- package/package.json +1 -1
- package/scripts/sync-template.js +29 -0
- package/templates/nextblock-template/.dockerignore +23 -0
- package/templates/nextblock-template/Dockerfile +68 -0
- package/templates/nextblock-template/app/api/upload/presigned-url/route.ts +9 -9
- package/templates/nextblock-template/docker/db/init/99-jwt.sql +6 -0
- package/templates/nextblock-template/docker/db/init/99-roles.sql +25 -0
- package/templates/nextblock-template/docker/kong/kong.yml +112 -0
- package/templates/nextblock-template/docker/migrate/run-migrations.sh +51 -0
- package/templates/nextblock-template/docker-compose.yml +215 -0
- package/templates/nextblock-template/lib/custom-block-r2-upload.test.ts +5 -5
- package/templates/nextblock-template/lib/custom-block-r2-upload.ts +2 -2
- package/templates/nextblock-template/next.config.js +9 -0
- package/templates/nextblock-template/package.json +6 -2
- package/templates/nextblock-template/scripts/docker-setup.mjs +227 -0
package/bin/create-nextblock.js
CHANGED
|
@@ -83,9 +83,30 @@ 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
|
+
// 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';
|
|
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
|
+
|
|
86
107
|
// Prerequisites gate (interactive only) — shown BEFORE we ask for a name, scaffold, or
|
|
87
108
|
// install, so anyone who isn't ready can cancel without creating anything.
|
|
88
|
-
if (!yes) {
|
|
109
|
+
if (!yes && hostingMode === 'cloud') {
|
|
89
110
|
clack.note(
|
|
90
111
|
[
|
|
91
112
|
'1. A Supabase project https://supabase.com/dashboard',
|
|
@@ -119,6 +140,35 @@ async function handleCommand(projectDirectory, options) {
|
|
|
119
140
|
);
|
|
120
141
|
return;
|
|
121
142
|
}
|
|
143
|
+
} else if (!yes && hostingMode === 'docker') {
|
|
144
|
+
clack.note(
|
|
145
|
+
[
|
|
146
|
+
'Local Self-Hosted Docker Mode runs everything on your machine — no cloud accounts needed.',
|
|
147
|
+
'',
|
|
148
|
+
'Requirement:',
|
|
149
|
+
' • Docker Desktop installed and running (https://www.docker.com/products/docker-desktop)',
|
|
150
|
+
'',
|
|
151
|
+
'Optional (you can skip both at the prompts):',
|
|
152
|
+
' • Cloudflare Turnstile keys (bot protection)',
|
|
153
|
+
' • SMTP credentials (otherwise sign-ups auto-confirm with no email)',
|
|
154
|
+
].join('\n'),
|
|
155
|
+
'One-click local sandbox',
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
const ready = await clack.confirm({
|
|
159
|
+
message: 'Is Docker Desktop installed and running?',
|
|
160
|
+
initialValue: true,
|
|
161
|
+
});
|
|
162
|
+
if (clack.isCancel(ready)) {
|
|
163
|
+
handleWizardCancel('Setup cancelled.');
|
|
164
|
+
}
|
|
165
|
+
if (!ready) {
|
|
166
|
+
clack.note(
|
|
167
|
+
'No problem — nothing was created. Install & start Docker Desktop, then run\n`npm create nextblock` again.',
|
|
168
|
+
'Come back when ready',
|
|
169
|
+
);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
122
172
|
}
|
|
123
173
|
|
|
124
174
|
let projectName = projectDirectory;
|
|
@@ -214,11 +264,15 @@ async function handleCommand(projectDirectory, options) {
|
|
|
214
264
|
console.log(chalk.yellow('Skipping dependency installation.'));
|
|
215
265
|
}
|
|
216
266
|
|
|
217
|
-
// Run the setup
|
|
218
|
-
// When it runs, its own "next steps" outro
|
|
219
|
-
//
|
|
267
|
+
// Run the setup flow after dependencies are installed so package assets are available.
|
|
268
|
+
// When it runs, its own "next steps" outro is the final message, so we don't print a second
|
|
269
|
+
// closing block here — the whole flow completes in this one command.
|
|
220
270
|
if (!yes) {
|
|
221
|
-
|
|
271
|
+
if (hostingMode === 'docker') {
|
|
272
|
+
await runDockerSetup(projectDir, projectName);
|
|
273
|
+
} else {
|
|
274
|
+
await runSetupWizard(projectDir, projectName);
|
|
275
|
+
}
|
|
222
276
|
} else {
|
|
223
277
|
// Non-interactive path: nothing was configured, so point the user at their env file.
|
|
224
278
|
console.log(
|
|
@@ -898,6 +952,34 @@ async function runSetupWizard(projectDir, projectName) {
|
|
|
898
952
|
);
|
|
899
953
|
}
|
|
900
954
|
|
|
955
|
+
// Local Self-Hosted Docker Mode: materialize the supabase migrations out of the installed
|
|
956
|
+
// @nextblock-cms/db package (the migration-runner container applies them on boot), then hand off
|
|
957
|
+
// to the project's own zero-dependency Docker setup script (prompts + .env + `docker compose up`).
|
|
958
|
+
async function runDockerSetup(projectDir, projectName) {
|
|
959
|
+
const projectPath = resolve(projectDir);
|
|
960
|
+
process.chdir(projectPath);
|
|
961
|
+
|
|
962
|
+
clack.intro('🐳 NextBlock™ CMS — Local Self-Hosted Docker setup');
|
|
963
|
+
|
|
964
|
+
await ensureSupabaseAssets(projectPath, { required: true });
|
|
965
|
+
|
|
966
|
+
const setupScript = resolve(projectPath, 'scripts', 'docker-setup.mjs');
|
|
967
|
+
if (!(await fs.pathExists(setupScript))) {
|
|
968
|
+
clack.note(
|
|
969
|
+
'scripts/docker-setup.mjs is missing from the template. Run `npm run sync:create-nextblock` and try again.',
|
|
970
|
+
'Docker setup unavailable',
|
|
971
|
+
);
|
|
972
|
+
return;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// The script drives docker compose interactively; inherit stdio so its prompts work.
|
|
976
|
+
await runCommand('node', ['scripts/docker-setup.mjs'], { cwd: projectPath });
|
|
977
|
+
|
|
978
|
+
clack.outro(
|
|
979
|
+
`🎉 Your NextBlock™ project ${projectName ? `"${projectName}" ` : ''}is running in Docker.\nApp: http://localhost:3000 (first sign-up becomes ADMIN)`,
|
|
980
|
+
);
|
|
981
|
+
}
|
|
982
|
+
|
|
901
983
|
async function configureHostedSupabaseAuth(
|
|
902
984
|
projectDir,
|
|
903
985
|
{ projectId, siteUrl, accessToken, smtpValues },
|
|
@@ -1924,7 +2006,11 @@ function buildNextConfigContent(editorUtilNames) {
|
|
|
1924
2006
|
'/**',
|
|
1925
2007
|
" * @type {import('next').NextConfig}",
|
|
1926
2008
|
' **/',
|
|
2009
|
+
// Self-hosted Docker builds emit a standalone server (`node server.js`); gated on
|
|
2010
|
+
// DOCKER_BUILD so a normal `next build` / Vercel deploy is unaffected.
|
|
2011
|
+
"const isDockerStandalone = process.env.DOCKER_BUILD === 'true';",
|
|
1927
2012
|
'const nextConfig = {',
|
|
2013
|
+
" ...(isDockerStandalone ? { output: 'standalone' } : {}),",
|
|
1928
2014
|
' outputFileTracingRoot: path.join(__dirname),',
|
|
1929
2015
|
' env: {',
|
|
1930
2016
|
' 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,55 @@
|
|
|
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
|
+
SUPABASE_INTERNAL_URL=http://kong:8000
|
|
21
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY=${ANON_KEY}
|
|
22
|
+
SUPABASE_SERVICE_ROLE_KEY=${SERVICE_ROLE_KEY}
|
|
23
|
+
API_EXTERNAL_URL=http://localhost:8000
|
|
24
|
+
SITE_URL=http://localhost:3000
|
|
25
|
+
POSTGRES_DB=postgres
|
|
26
|
+
|
|
27
|
+
# --- App ------------------------------------------------------------------------------------
|
|
28
|
+
NEXT_PUBLIC_URL=http://localhost:3000
|
|
29
|
+
NEXT_PUBLIC_IS_SANDBOX=true
|
|
30
|
+
CORTEX_AI_ENCRYPTION_KEY=
|
|
31
|
+
|
|
32
|
+
# --- MinIO object storage (S3-compatible) ----------------------------------------------------
|
|
33
|
+
MINIO_ROOT_USER=nextblock
|
|
34
|
+
MINIO_ROOT_PASSWORD=replace-me-generated
|
|
35
|
+
STORAGE_BUCKET=nextblock
|
|
36
|
+
R2_ACCOUNT_ID=minio
|
|
37
|
+
R2_REGION=us-east-1
|
|
38
|
+
R2_S3_ENDPOINT=http://minio:9000
|
|
39
|
+
R2_S3_PUBLIC_ENDPOINT=http://localhost:9000
|
|
40
|
+
R2_FORCE_PATH_STYLE=true
|
|
41
|
+
NEXT_PUBLIC_R2_BASE_URL=http://localhost:9000/nextblock
|
|
42
|
+
NEXT_PUBLIC_R2_PUBLIC_URL=http://localhost:9000/nextblock
|
|
43
|
+
|
|
44
|
+
# --- Cloudflare Turnstile (optional). Press Enter at setup to skip and use the dev sandbox. ---
|
|
45
|
+
NEXT_PUBLIC_TURNSTILE_SITE_KEY=
|
|
46
|
+
TURNSTILE_SECRET_KEY=
|
|
47
|
+
|
|
48
|
+
# --- SMTP (optional). Blank => GoTrue auto-confirms sign-ups (loopback mode). -----------------
|
|
49
|
+
GOTRUE_MAILER_AUTOCONFIRM=true
|
|
50
|
+
SMTP_HOST=
|
|
51
|
+
SMTP_PORT=
|
|
52
|
+
SMTP_USER=
|
|
53
|
+
SMTP_PASS=
|
|
54
|
+
SMTP_FROM_EMAIL=
|
|
55
|
+
SMTP_FROM_NAME=
|
|
@@ -0,0 +1,68 @@
|
|
|
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 \
|
|
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
|
+
|
|
61
|
+
# A single-app project traces to its own root, so server.js sits at the top of .next/standalone.
|
|
62
|
+
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
|
63
|
+
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
|
64
|
+
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
|
|
65
|
+
|
|
66
|
+
USER nextjs
|
|
67
|
+
EXPOSE 3000
|
|
68
|
+
CMD ["node", "server.js"]
|
|
@@ -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
|