create-apollo-monorepo 0.9.786 → 0.9.787
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/README.md +1 -1
- package/index.mjs +30 -137
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ collide:
|
|
|
45
45
|
| ------------------------------------ | -------------------- |
|
|
46
46
|
| `/`, your custom routes | `apps/frontend` |
|
|
47
47
|
| `/admin/*` | `apps/backend` (admin pages **and** their JS chunks) |
|
|
48
|
-
| `/api/auth/*`, `/api/v1/*`, `/api/admin/*`, `/api/email/*`, `/api/
|
|
48
|
+
| `/api/auth/*`, `/api/v1/*`, `/api/admin/*`, `/api/email/*`, `/api/health`, `/api/mcp`, `/api/editing-presence/*` | `apps/backend` |
|
|
49
49
|
| `/uploads/*` | `apps/backend` (media) |
|
|
50
50
|
|
|
51
51
|
Apollo CMS reads `APOLLO_ASSET_PREFIX` (default `/admin`) and serves its built
|
package/index.mjs
CHANGED
|
@@ -227,14 +227,14 @@ function preflight(flags) {
|
|
|
227
227
|
success(`Node.js ${process.versions.node}`);
|
|
228
228
|
|
|
229
229
|
// bun is required at runtime by apps/backend (apollo-cms): db:push, db:seed,
|
|
230
|
-
//
|
|
230
|
+
// plugins:build, the upgrade pipeline, and pre-commit hooks all
|
|
231
231
|
// shell out to `bun`. The scaffold itself works without bun, but `pnpm dev`,
|
|
232
232
|
// `pnpm backend:setup`, and `pnpm backend:upgrade` will fail without it.
|
|
233
233
|
const TOOLS = [
|
|
234
234
|
{ cmd: "git", required: true, missing: "git is required but not found.\n Install: https://git-scm.com/" },
|
|
235
235
|
{ cmd: "pnpm", required: false, missing: "pnpm not found — install with: npm i -g pnpm (required for workspaces)" },
|
|
236
236
|
{ cmd: "bun", required: false, missing:
|
|
237
|
-
"bun not found — required by apps/backend for db:push, db:seed
|
|
237
|
+
"bun not found — required by apps/backend for db:push, db:seed,\n" +
|
|
238
238
|
" plugins:build, and the upgrade pipeline.\n" +
|
|
239
239
|
" Install: curl -fsSL https://bun.sh/install | bash (or: brew install bun)" },
|
|
240
240
|
];
|
|
@@ -318,12 +318,11 @@ function writeRootPackageJson(targetDir, dirName) {
|
|
|
318
318
|
description: `${dirName} monorepo (frontend + apollo-cms backend submodule)`,
|
|
319
319
|
scripts: {
|
|
320
320
|
// We bypass apollo-cms's own `dev` script ("bun install && bun run
|
|
321
|
-
// plugins:build && next dev
|
|
322
|
-
//
|
|
323
|
-
//
|
|
324
|
-
//
|
|
325
|
-
//
|
|
326
|
-
// (production uses Vercel Cron via apps/backend/vercel.json).
|
|
321
|
+
// plugins:build && next dev") because the bash-`&`-then-foreground
|
|
322
|
+
// pattern exits with SIGHUP (129) under pnpm's parallel runner. Instead
|
|
323
|
+
// we explicitly run plugins:build (both ours and apollo-cms's) and
|
|
324
|
+
// `next dev` directly. Nothing extra to start for scheduled jobs — the
|
|
325
|
+
// backend drives its scheduler queue in-process (dev and production).
|
|
327
326
|
"predev:setup":
|
|
328
327
|
"pnpm cms-plugins:build && pnpm --filter ./apps/backend exec bun run plugins:build",
|
|
329
328
|
// `pnpm dev` runs FE + BE Next.js dev servers (which watch their own
|
|
@@ -343,7 +342,6 @@ function writeRootPackageJson(targetDir, dirName) {
|
|
|
343
342
|
"node scripts/with-env.mjs --port=FRONTEND_PORT pnpm --filter ./apps/frontend exec next dev",
|
|
344
343
|
"dev:backend":
|
|
345
344
|
"pnpm predev:setup && node scripts/with-env.mjs --port=BACKEND_PORT pnpm --filter ./apps/backend exec next dev",
|
|
346
|
-
"dev:cron": "pnpm --filter ./apps/backend exec bun scripts/dev-cron.ts",
|
|
347
345
|
// Build pipeline: cms-plugins → apollo-cms's own plugins → backend → frontend.
|
|
348
346
|
// `prebuild` runs first (npm/pnpm convention) and fails fast if the
|
|
349
347
|
// backend's required env vars are missing.
|
|
@@ -366,12 +364,6 @@ function writeRootPackageJson(targetDir, dirName) {
|
|
|
366
364
|
"node scripts/with-env.mjs --port=FRONTEND_PORT pnpm --filter ./apps/frontend exec next start",
|
|
367
365
|
"start:backend":
|
|
368
366
|
"node scripts/with-env.mjs --port=BACKEND_PORT pnpm --filter ./apps/backend exec next start",
|
|
369
|
-
// Self-hosted scheduler fallback. Vercel deploys use Vercel Cron via
|
|
370
|
-
// apps/backend/vercel.json — skip this on Vercel. For Docker / VPS
|
|
371
|
-
// you can either run \`pnpm start:cron\` alongside \`pnpm start\` (PM2
|
|
372
|
-
// handles this via ecosystem.config.cjs), or use system cron / k8s
|
|
373
|
-
// CronJob to hit /api/cron with CRON_SECRET.
|
|
374
|
-
"start:cron": "pnpm --filter ./apps/backend exec bun scripts/dev-cron.ts",
|
|
375
367
|
"cms-plugins:build":
|
|
376
368
|
"pnpm --filter './apps/cms-plugins/*' --parallel --if-present build",
|
|
377
369
|
"cms-plugins:dev":
|
|
@@ -522,7 +514,7 @@ server {
|
|
|
522
514
|
# Backend APIs (mirror the list in apps/frontend/next.config.ts).
|
|
523
515
|
# \`editing-presence\` is included so the bare path also routes to backend;
|
|
524
516
|
# streaming sub-paths still match the \`^~\` block above first.
|
|
525
|
-
location ~ ^/api/(auth|v1|
|
|
517
|
+
location ~ ^/api/(auth|v1|email|health|mcp|admin|editing-presence)(/|$) {
|
|
526
518
|
proxy_pass http://apollo_backend;
|
|
527
519
|
}
|
|
528
520
|
|
|
@@ -546,7 +538,7 @@ function writeProxyApp(targetDir, dirName, adminPrefix) {
|
|
|
546
538
|
// Routes (in order):
|
|
547
539
|
// ${prefix}, ${prefix}/*, /uploads/* → backend
|
|
548
540
|
// /api/editing-presence/* → backend (SSE)
|
|
549
|
-
// /api/{auth,v1,
|
|
541
|
+
// /api/{auth,v1,email,health,mcp,admin,...}/* → backend
|
|
550
542
|
// /__proxy/health → 200 OK (proxy itself)
|
|
551
543
|
// everything else → frontend
|
|
552
544
|
//
|
|
@@ -597,7 +589,7 @@ const ADMIN_PREFIX = process.env.ADMIN_PREFIX ?? "${prefix}";
|
|
|
597
589
|
// Pipe-separated list of /api/<segment> paths that route to the backend.
|
|
598
590
|
const BACKEND_API_SEGMENTS = (
|
|
599
591
|
process.env.BACKEND_API_PATHS ??
|
|
600
|
-
"auth|v1|
|
|
592
|
+
"auth|v1|email|health|mcp|admin|editing-presence"
|
|
601
593
|
).trim();
|
|
602
594
|
const BACKEND_API = new RegExp(\`^/api/(\${BACKEND_API_SEGMENTS})(/|$)\`);
|
|
603
595
|
|
|
@@ -840,7 +832,7 @@ single-origin URL.
|
|
|
840
832
|
| \`BACKEND\` | \`http://127.0.0.1:3000\` | apps/backend dev server |
|
|
841
833
|
| \`FRONTEND\` | \`http://127.0.0.1:3001\` | apps/frontend dev server |
|
|
842
834
|
| \`ADMIN_PREFIX\` | \`${prefix}\` | Backend admin path prefix |
|
|
843
|
-
| \`BACKEND_API_PATHS\` | \`auth\\|v1\\|
|
|
835
|
+
| \`BACKEND_API_PATHS\` | \`auth\\|v1\\|email\\|health\\|mcp\\|admin\\|editing-presence\` | Pipe-separated /api/* segments routed to backend |
|
|
844
836
|
| \`MAX_BODY_BYTES\` | \`52428800\` (50MB) | Reject larger bodies. \`0\` = unlimited |
|
|
845
837
|
| \`UPSTREAM_TIMEOUT_MS\` | \`60000\` | Request timeout to upstream |
|
|
846
838
|
| \`TRUST_PROXY\` | \`1\` | Honors inbound X-Forwarded-* from upstream TLS terminator (nginx/Caddy/CF/LB). Set \`0\` if proxy is directly internet-facing |
|
|
@@ -1065,10 +1057,9 @@ const config: NextConfig = {
|
|
|
1065
1057
|
return [
|
|
1066
1058
|
// Apollo CMS admin UI (and its chunks when APOLLO_ASSET_PREFIX is /admin)
|
|
1067
1059
|
{ source: "/admin/:path*", destination: \`\${BACKEND}/admin/:path*\` },
|
|
1068
|
-
// Apollo CMS APIs (REST + auth +
|
|
1060
|
+
// Apollo CMS APIs (REST + auth + email + health + mcp + …)
|
|
1069
1061
|
{ source: "/api/auth/:path*", destination: \`\${BACKEND}/api/auth/:path*\` },
|
|
1070
1062
|
{ source: "/api/v1/:path*", destination: \`\${BACKEND}/api/v1/:path*\` },
|
|
1071
|
-
{ source: "/api/cron", destination: \`\${BACKEND}/api/cron\` },
|
|
1072
1063
|
{ source: "/api/email/:path*", destination: \`\${BACKEND}/api/email/:path*\` },
|
|
1073
1064
|
{ source: "/api/health", destination: \`\${BACKEND}/api/health\` },
|
|
1074
1065
|
{ source: "/api/mcp", destination: \`\${BACKEND}/api/mcp\` },
|
|
@@ -1123,20 +1114,6 @@ export default config;
|
|
|
1123
1114
|
// .env.local is intentionally committed — it only contains dev port hints.
|
|
1124
1115
|
["node_modules", ".next", "dist", ""].join("\n"),
|
|
1125
1116
|
);
|
|
1126
|
-
|
|
1127
|
-
// Vercel project config for the frontend. Skip cron + region pinning is
|
|
1128
|
-
// optional; configure Root Directory + "Include all submodules" in the
|
|
1129
|
-
// Vercel UI when linking the project.
|
|
1130
|
-
const vercelJson = {
|
|
1131
|
-
$schema: "https://openapi.vercel.sh/vercel.json",
|
|
1132
|
-
regions: ["sin1"],
|
|
1133
|
-
};
|
|
1134
|
-
writeFileSync(resolve(dir, "vercel.json"), JSON.stringify(vercelJson, null, 2) + "\n");
|
|
1135
|
-
|
|
1136
|
-
writeFileSync(
|
|
1137
|
-
resolve(dir, ".vercelignore"),
|
|
1138
|
-
["node_modules", ".next", ".env.local", ""].join("\n"),
|
|
1139
|
-
);
|
|
1140
1117
|
}
|
|
1141
1118
|
|
|
1142
1119
|
// ─── Custom plugins scaffold ─────────────────────────────────────────────────
|
|
@@ -1233,7 +1210,7 @@ if (watch) {
|
|
|
1233
1210
|
// → dist/server.mjs (built by \`pnpm cms-plugins:build\`).
|
|
1234
1211
|
//
|
|
1235
1212
|
// In dev under Bun the loader can also import index.ts directly (with a
|
|
1236
|
-
// warning); in production (
|
|
1213
|
+
// warning); in production (next start) the dist file is required.
|
|
1237
1214
|
|
|
1238
1215
|
import type { PluginDefinition } from "@/lib/plugins/types";
|
|
1239
1216
|
|
|
@@ -1441,7 +1418,7 @@ function writeCheckEnvScript(targetDir) {
|
|
|
1441
1418
|
// Pre-build sanity check. Reads apps/backend/.env.local and verifies that
|
|
1442
1419
|
// required vars are present and non-empty. Skipped when SKIP_ENV_CHECK=1.
|
|
1443
1420
|
//
|
|
1444
|
-
// In CI
|
|
1421
|
+
// In CI where envs come from the platform (not files), set
|
|
1445
1422
|
// SKIP_ENV_CHECK=1 and rely on the platform's own validation.
|
|
1446
1423
|
|
|
1447
1424
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -1486,7 +1463,7 @@ console.log("✓ env check passed");
|
|
|
1486
1463
|
}
|
|
1487
1464
|
|
|
1488
1465
|
// PM2 ecosystem config — production process supervision for self-hosted deploys.
|
|
1489
|
-
// Includes FE, BE,
|
|
1466
|
+
// Includes FE, BE, and optional proxy in fork mode (no clustering
|
|
1490
1467
|
// since Next.js handles that internally and the proxy is single-threaded by design).
|
|
1491
1468
|
function writePm2Config(targetDir, dirName, adminPrefix) {
|
|
1492
1469
|
const singleOrigin = !!adminPrefix;
|
|
@@ -1510,9 +1487,8 @@ function writePm2Config(targetDir, dirName, adminPrefix) {
|
|
|
1510
1487
|
// pnpm install
|
|
1511
1488
|
// pnpm backend:upgrade
|
|
1512
1489
|
// pnpm build
|
|
1513
|
-
// pm2 start ecosystem.config.cjs # FE + BE + proxy
|
|
1490
|
+
// pm2 start ecosystem.config.cjs # FE + BE + proxy
|
|
1514
1491
|
// pm2 start ecosystem.config.cjs --only proxy # only the reverse proxy
|
|
1515
|
-
// pm2 start ecosystem.config.cjs --only cron # self-hosted cron fallback
|
|
1516
1492
|
// pm2 stop \${PM2_NAMESPACE} # stop everything in this project
|
|
1517
1493
|
// pm2 save && pm2 startup # persist across reboots
|
|
1518
1494
|
|
|
@@ -1538,9 +1514,9 @@ const FRONTEND_PORT = Number(process.env.FRONTEND_PORT) || 3001;
|
|
|
1538
1514
|
const BACKEND_PORT = Number(process.env.BACKEND_PORT) || 3002;
|
|
1539
1515
|
const PROXY_PORT = Number(process.env.PROXY_PORT) || 3030;
|
|
1540
1516
|
|
|
1541
|
-
// Backend's internal URL (loopback).
|
|
1542
|
-
// frontend — going through the FE just adds a failure
|
|
1543
|
-
//
|
|
1517
|
+
// Backend's internal URL (loopback). Server-to-server callers MUST target
|
|
1518
|
+
// this, never the proxy or frontend — going through the FE just adds a failure
|
|
1519
|
+
// mode (an HTML error page where JSON was expected).
|
|
1544
1520
|
const BACKEND_INTERNAL_URL = process.env.BACKEND_INTERNAL_URL || \`http://127.0.0.1:\${BACKEND_PORT}\`;
|
|
1545
1521
|
|
|
1546
1522
|
module.exports = {
|
|
@@ -1582,21 +1558,6 @@ ${proxyEnv}
|
|
|
1582
1558
|
max_memory_restart: '256M',
|
|
1583
1559
|
autorestart: true,
|
|
1584
1560
|
},
|
|
1585
|
-
{
|
|
1586
|
-
name: 'cron',
|
|
1587
|
-
namespace: NAMESPACE,
|
|
1588
|
-
cwd: './apps/backend',
|
|
1589
|
-
script: 'scripts/dev-cron.ts',
|
|
1590
|
-
interpreter: 'bun',
|
|
1591
|
-
env: {
|
|
1592
|
-
NODE_ENV: 'production',
|
|
1593
|
-
// dev-cron.ts derives its target from NEXT_PUBLIC_SITE_URL. Override
|
|
1594
|
-
// it to the backend loopback so cron skips the FE/proxy entirely.
|
|
1595
|
-
NEXT_PUBLIC_SITE_URL: BACKEND_INTERNAL_URL,
|
|
1596
|
-
CRON_SECRET: process.env.CRON_SECRET,
|
|
1597
|
-
},
|
|
1598
|
-
autorestart: true,
|
|
1599
|
-
},
|
|
1600
1561
|
],
|
|
1601
1562
|
};
|
|
1602
1563
|
`;
|
|
@@ -1612,7 +1573,7 @@ function writeClaudeMd(targetDir, adminPrefix) {
|
|
|
1612
1573
|
Frontend is the public entry point. It rewrites these paths to the backend (do **not** create matching routes in the frontend):
|
|
1613
1574
|
|
|
1614
1575
|
- \`/admin/*\`, \`/uploads/*\`
|
|
1615
|
-
- \`/api/auth/*\`, \`/api/v1/*\`, \`/api/email/*\`, \`/api/
|
|
1576
|
+
- \`/api/auth/*\`, \`/api/v1/*\`, \`/api/email/*\`, \`/api/health\`, \`/api/mcp\`, \`/api/admin/*\`, \`/api/editing-presence/*\`
|
|
1616
1577
|
- \`/_next/static\` under \`APOLLO_ASSET_PREFIX=${prefix}\` (backend chunks)
|
|
1617
1578
|
|
|
1618
1579
|
Custom frontend APIs must be namespaced (e.g. \`/api/internal/*\`).
|
|
@@ -1687,7 +1648,6 @@ Proxy reads \`PORT\`, \`BACKEND\`, \`FRONTEND\`, \`ADMIN_PREFIX\` from env. When
|
|
|
1687
1648
|
## Deploy
|
|
1688
1649
|
|
|
1689
1650
|
- Self-hosted: build on a runner, rsync to the server, then \`pm2 startOrReload ecosystem.config.cjs --update-env\`. If the backend submodule is private, the deploy runner needs a token with read access.
|
|
1690
|
-
- Vercel: two projects per repo (\`apps/backend\` and \`apps/frontend\` root dirs). Backend's Build Command must copy \`apps/cms-plugins/*/dist\` into \`apps/backend/plugins/\` before \`next build\` because Turbopack rejects \`outputFileTracingIncludes\` globs above the project root. "Include all submodules" must be ON in both projects.
|
|
1691
1651
|
|
|
1692
1652
|
## Submodule discipline
|
|
1693
1653
|
|
|
@@ -1710,7 +1670,7 @@ paths to the backend so /_next/* doesn't collide:
|
|
|
1710
1670
|
| --------------------------- | ------------------ |
|
|
1711
1671
|
| \`/\` and other frontend routes | \`apps/frontend\` |
|
|
1712
1672
|
| \`/admin/*\` | \`apps/backend\` |
|
|
1713
|
-
| \`/api/auth/*\`, \`/api/v1/*\`, \`/api/email/*\`, \`/api/
|
|
1673
|
+
| \`/api/auth/*\`, \`/api/v1/*\`, \`/api/email/*\`, \`/api/health\`, \`/api/mcp\`, \`/api/admin/*\`, \`/api/editing-presence/*\` | \`apps/backend\` |
|
|
1714
1674
|
| \`/uploads/*\` | \`apps/backend\` (media) |
|
|
1715
1675
|
| \`${adminPrefix}/*\` | \`apps/backend\` (chunks via \`APOLLO_ASSET_PREFIX\`) |
|
|
1716
1676
|
|
|
@@ -1727,7 +1687,7 @@ Two options ship out of the box:
|
|
|
1727
1687
|
deploys. Configure via \`PORT\`, \`BACKEND\`, \`FRONTEND\`, \`ADMIN_PREFIX\`.
|
|
1728
1688
|
- **\`nginx.conf.sample\`** (production) — same routing baked in (frontend on
|
|
1729
1689
|
:3001, backend on :3000). Use it when fronting both apps behind a single
|
|
1730
|
-
TLS-terminating proxy
|
|
1690
|
+
TLS-terminating proxy — drop in your domain and SSL certs.
|
|
1731
1691
|
|
|
1732
1692
|
To **disable** single-origin and run the backend on its own subdomain,
|
|
1733
1693
|
delete \`APOLLO_ASSET_PREFIX\` from \`apps/backend/.env.local\` and remove the
|
|
@@ -1865,14 +1825,13 @@ processes without re-running \`drizzle-kit push\` every time. Always run
|
|
|
1865
1825
|
| \`pnpm start\` | FE + BE (parallel \`next start\`) |
|
|
1866
1826
|
| \`pnpm start:rp\` | FE + BE + reverse proxy on :3030 |
|
|
1867
1827
|
| \`pnpm start:proxy\` | Reverse proxy alone (already running FE/BE separately) |
|
|
1868
|
-
| \`pnpm start:cron\` | Self-hosted cron fallback — only if not using Vercel Cron / k8s |
|
|
1869
1828
|
|
|
1870
1829
|
### PM2 (recommended for VPS)
|
|
1871
1830
|
|
|
1872
1831
|
The installer scaffolds \`ecosystem.config.cjs\`. To supervise everything:
|
|
1873
1832
|
|
|
1874
1833
|
\`\`\`bash
|
|
1875
|
-
pm2 start ecosystem.config.cjs # FE + BE + proxy
|
|
1834
|
+
pm2 start ecosystem.config.cjs # FE + BE + proxy (all three)
|
|
1876
1835
|
pm2 start ecosystem.config.cjs --only proxy # only the reverse proxy
|
|
1877
1836
|
pm2 save && pm2 startup # persist across reboots
|
|
1878
1837
|
pm2 logs \${PM2_NAMESPACE:-${dirName}} # tail just this project's logs
|
|
@@ -1880,13 +1839,14 @@ pm2 reload \${PM2_NAMESPACE:-${dirName}} # zero-downtime restart (nam
|
|
|
1880
1839
|
pm2 stop \${PM2_NAMESPACE:-${dirName}} # stop only this project
|
|
1881
1840
|
\`\`\`
|
|
1882
1841
|
|
|
1883
|
-
All
|
|
1842
|
+
All three processes are grouped under the \`PM2_NAMESPACE\` set in \`.env.local\`
|
|
1884
1843
|
(defaults to \`${dirName}\`), so namespace commands target only this project
|
|
1885
1844
|
even when other PM2 apps share the host. Process names embed the bound port
|
|
1886
1845
|
(\`frontend:3001\`, \`backend:3002\`, \`proxy:3030\`) for quick \`pm2 ls\` triage.
|
|
1887
1846
|
|
|
1888
|
-
The proxy
|
|
1889
|
-
|
|
1847
|
+
The proxy process can be omitted with \`--only\` if you front the apps with
|
|
1848
|
+
nginx/Caddy. Scheduled jobs need no extra process — the backend drives its
|
|
1849
|
+
scheduler queue in-process.
|
|
1890
1850
|
|
|
1891
1851
|
### Docker / k8s
|
|
1892
1852
|
|
|
@@ -1894,74 +1854,7 @@ For containerized deploys:
|
|
|
1894
1854
|
- Bake the build into the image (\`pnpm install && pnpm build\`)
|
|
1895
1855
|
- Set entrypoint to \`pnpm start\` or \`pnpm start:rp\`
|
|
1896
1856
|
- Run \`pnpm backend:upgrade\` as a separate init container / Job before app pods start
|
|
1897
|
-
-
|
|
1898
|
-
|
|
1899
|
-
## Deploy on Vercel
|
|
1900
|
-
|
|
1901
|
-
Two Vercel projects, one repo. Each project picks up its own Root Directory.
|
|
1902
|
-
|
|
1903
|
-
### 1) Backend project
|
|
1904
|
-
|
|
1905
|
-
- **Import** this repo into Vercel as a new project.
|
|
1906
|
-
- **Root Directory**: \`apps/backend\`
|
|
1907
|
-
- **Build Command**: \`cd ../.. && pnpm install --frozen-lockfile && pnpm cms-plugins:build && cp -r ../cms-plugins/* apps/backend/plugins/ 2>/dev/null || true && pnpm --filter ./apps/backend build\`
|
|
1908
|
-
- **Install Command**: leave empty (handled in build)
|
|
1909
|
-
- **Settings → Git → Include all submodules: ON** (Vercel checks out an empty \`apps/backend\` otherwise)
|
|
1910
|
-
- **Environment variables**:
|
|
1911
|
-
\`\`\`
|
|
1912
|
-
DATABASE_URL=postgresql://…
|
|
1913
|
-
APOLLO_SECRET=<openssl rand -hex 32>
|
|
1914
|
-
NEXT_PUBLIC_SITE_URL=https://yourdomain.com # the PUBLIC origin
|
|
1915
|
-
NEXT_PUBLIC_DEFAULT_LOCALE=en
|
|
1916
|
-
${singleOrigin ? `APOLLO_ASSET_PREFIX=${adminPrefix}` : "# APOLLO_ASSET_PREFIX=/admin # only when single-origin"}
|
|
1917
|
-
CRON_SECRET=<random> # protects /api/cron
|
|
1918
|
-
APOLLO_EXTRA_PLUGINS_DIR=./plugins # picks up copied cms-plugins (see Build Command)
|
|
1919
|
-
# Storage on Vercel cannot use local FS — pick one:
|
|
1920
|
-
# Vercel Blob: BLOB_READ_WRITE_TOKEN=…
|
|
1921
|
-
# S3 / R2 / Spaces: S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY, S3_BUCKET, S3_REGION, S3_ENDPOINT
|
|
1922
|
-
APOLLO_DISABLE_LOCAL_STORAGE=1
|
|
1923
|
-
\`\`\`
|
|
1924
|
-
- **Cron**: \`apps/backend/vercel.json\` declares \`/api/cron\` on a 5-minute schedule (from apollo-cms upstream).
|
|
1925
|
-
- **About the \`cp\` step**: Turbopack rejects \`outputFileTracingIncludes\` globs that walk above the project root, so the Build Command copies built \`apps/cms-plugins/*/dist\` into \`apps/backend/plugins/\` before \`next build\`. NFT then traces them normally as if they were built-in plugins. Locally this isn't needed because \`APOLLO_EXTRA_PLUGINS_DIR=../cms-plugins\` reads them directly.
|
|
1926
|
-
|
|
1927
|
-
### 2) Frontend project
|
|
1928
|
-
|
|
1929
|
-
- **Import the same repo** as a separate Vercel project.
|
|
1930
|
-
- **Root Directory**: \`apps/frontend\`
|
|
1931
|
-
- **Build Command**: \`cd ../.. && pnpm install --frozen-lockfile && pnpm --filter ./apps/frontend build\`
|
|
1932
|
-
- **Install Command**: leave empty
|
|
1933
|
-
- **Settings → Git → Include all submodules: ON**
|
|
1934
|
-
- **Environment variables**:
|
|
1935
|
-
\`\`\`
|
|
1936
|
-
NEXT_PUBLIC_SITE_URL=https://yourdomain.com
|
|
1937
|
-
${singleOrigin
|
|
1938
|
-
? ` BACKEND_INTERNAL_URL=https://<your-backend>.vercel.app`
|
|
1939
|
-
: ` NEXT_PUBLIC_BACKEND_URL=https://<your-backend>.vercel.app`
|
|
1940
|
-
}
|
|
1941
|
-
\`\`\`
|
|
1942
|
-
|
|
1943
|
-
### 3) Custom domain
|
|
1944
|
-
|
|
1945
|
-
Attach \`yourdomain.com\` to the **frontend** project${singleOrigin ? " (in single-origin mode it's the public entry point)" : ""}. The backend stays on its \`*.vercel.app\` URL${singleOrigin ? " — that's what the rewrite proxies to" : ""}.
|
|
1946
|
-
|
|
1947
|
-
### 4) Skip duplicate builds (optional)
|
|
1948
|
-
|
|
1949
|
-
Each push triggers both projects to rebuild. Add an **Ignored Build Step** in
|
|
1950
|
-
each project's Settings → Git:
|
|
1951
|
-
|
|
1952
|
-
- **Backend**: \`git diff HEAD^ HEAD --quiet -- apps/backend\` (exits 0 → skip build)
|
|
1953
|
-
- **Frontend**: \`git diff HEAD^ HEAD --quiet -- apps/frontend\`
|
|
1954
|
-
|
|
1955
|
-
### Gotchas
|
|
1956
|
-
|
|
1957
|
-
- **Submodule must be initialized** on Vercel — the "Include all submodules"
|
|
1958
|
-
toggle is the most common reason builds fail with a missing \`apps/backend\`.
|
|
1959
|
-
- **OAuth callbacks** for email providers must use the public domain:
|
|
1960
|
-
\`https://yourdomain.com/api/email/oauth/callback\`.${singleOrigin ? " The frontend rewrite forwards it to the backend." : ""}
|
|
1961
|
-
- **Cron** runs on the backend project only. Vercel sends \`Authorization:
|
|
1962
|
-
Bearer $CRON_SECRET\` automatically when \`CRON_SECRET\` is set.${singleOrigin ? `
|
|
1963
|
-
- **Better Auth** uses \`trustedProxyHeaders: true\` so the rewrite proxy's
|
|
1964
|
-
\`x-forwarded-host\` lands cookies at the public origin without extra config.` : ""}
|
|
1857
|
+
- No cron sidecar needed — the backend drives its scheduler queue in-process
|
|
1965
1858
|
`;
|
|
1966
1859
|
writeFileSync(resolve(targetDir, "README.md"), readme);
|
|
1967
1860
|
}
|
|
@@ -1993,8 +1886,8 @@ async function main() {
|
|
|
1993
1886
|
const adminPrefix =normalizeAdminPrefix(flags.adminPrefix);
|
|
1994
1887
|
const { dbUrl, siteUrl, locale } = await gatherEnv(flags);
|
|
1995
1888
|
const authSecret = randomBytes(48).toString("base64");
|
|
1996
|
-
//
|
|
1997
|
-
// Without it
|
|
1889
|
+
// Authenticates trusted callers of apollo-cms's /api/health?check=ready.
|
|
1890
|
+
// Without it that readiness check answers 403 "CRON_SECRET not configured".
|
|
1998
1891
|
const cronSecret = randomBytes(24).toString("hex");
|
|
1999
1892
|
const backendInternalUrl = `http://localhost:${DEFAULT_BACKEND_PORT}`;
|
|
2000
1893
|
success(`Frontend pkg name: ${frontendName}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-apollo-monorepo",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.787",
|
|
4
4
|
"description": "Scaffold a monorepo with a frontend app and Apollo CMS as a git submodule backend (single-origin via Next.js rewrites + assetPrefix)",
|
|
5
5
|
"bin": {
|
|
6
6
|
"create-apollo-monorepo": "index.mjs"
|