create-apollo-monorepo 0.1.0 → 0.2.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/README.md +35 -1
- package/index.mjs +204 -31
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -33,6 +33,34 @@ thamc-new/
|
|
|
33
33
|
└── .gitignore
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
## Routing modes
|
|
37
|
+
|
|
38
|
+
### Single-origin (default)
|
|
39
|
+
|
|
40
|
+
Both apps share one public origin (the frontend). The frontend's
|
|
41
|
+
`next.config.ts` rewrites these paths to the backend so `/_next/*` doesn't
|
|
42
|
+
collide:
|
|
43
|
+
|
|
44
|
+
| Browser path | Goes to |
|
|
45
|
+
| ------------------------------------ | -------------------- |
|
|
46
|
+
| `/`, your custom routes | `apps/frontend` |
|
|
47
|
+
| `/admin/*` | `apps/backend` |
|
|
48
|
+
| `/api/auth/*`, `/api/v1/*`, `/api/admin/*`, `/api/email/*`, `/api/cron`, `/api/health`, `/api/mcp`, `/api/editing-presence/*` | `apps/backend` |
|
|
49
|
+
| `/uploads/*` | `apps/backend` (media) |
|
|
50
|
+
| `/cms-assets/*` | `apps/backend` chunks (via `APOLLO_ASSET_PREFIX`) |
|
|
51
|
+
| `/monitoring` | `apps/backend` (Sentry tunnel, no-op without DSN) |
|
|
52
|
+
|
|
53
|
+
Apollo CMS picks up `APOLLO_ASSET_PREFIX=/cms-assets` and serves its built JS
|
|
54
|
+
under that namespace, sidestepping the `/_next/*` collision. The frontend
|
|
55
|
+
**must not** define routes at `/admin`, `/api/auth`, `/api/v1`, etc.
|
|
56
|
+
|
|
57
|
+
### Separate origins (fallback)
|
|
58
|
+
|
|
59
|
+
Pass `--asset-prefix none` (or `off`/`false`/`disabled`) to skip the rewrite
|
|
60
|
+
wiring. The backend runs at `http://localhost:3000` and the frontend at
|
|
61
|
+
`http://localhost:3001`. Useful when you'd rather deploy them on separate
|
|
62
|
+
subdomains (e.g. `cms.example.com` + `example.com`).
|
|
63
|
+
|
|
36
64
|
## Flags
|
|
37
65
|
|
|
38
66
|
| Flag | Default | Description |
|
|
@@ -41,8 +69,9 @@ thamc-new/
|
|
|
41
69
|
| `--backend-url <url>` | `https://github.com/5Lab-Group-Co-Ltd/apollo-cms.git` | Submodule git URL |
|
|
42
70
|
| `--backend-branch <name>` | `main` | Submodule branch to track |
|
|
43
71
|
| `-d, --db <url>` | _(prompted)_ | `DATABASE_URL` for backend |
|
|
44
|
-
| `-u, --url <url>` | `
|
|
72
|
+
| `-u, --url <url>` | `:3001` single-origin / `:3000` separate | `NEXT_PUBLIC_SITE_URL` |
|
|
45
73
|
| `-l, --locale <code>` | `en` | `NEXT_PUBLIC_DEFAULT_LOCALE` |
|
|
74
|
+
| `--asset-prefix <path>` | `/cms-assets` | Single-origin asset namespace; `none` to disable |
|
|
46
75
|
| `--skip-install` | off | Don't run `pnpm install` |
|
|
47
76
|
| `--skip-submodule` | off | Don't add the git submodule |
|
|
48
77
|
| `-h, --help` | — | Show help |
|
|
@@ -55,6 +84,11 @@ pnpm backend:setup # push schema + seed apollo-cms
|
|
|
55
84
|
pnpm dev # frontend :3001 + backend :3000 in parallel
|
|
56
85
|
```
|
|
57
86
|
|
|
87
|
+
In single-origin mode open `http://localhost:3001/admin` (the frontend port);
|
|
88
|
+
the rewrite proxies to the backend and Better Auth's cookies/origins line up
|
|
89
|
+
because `NEXT_PUBLIC_SITE_URL` and the backend's `trustedProxyHeaders` are wired
|
|
90
|
+
to the public origin.
|
|
91
|
+
|
|
58
92
|
## Updating the backend
|
|
59
93
|
|
|
60
94
|
```bash
|
package/index.mjs
CHANGED
|
@@ -54,11 +54,21 @@ ${COLORS.bold}Flags:${COLORS.reset}
|
|
|
54
54
|
--backend-url <url> Backend git URL (default: ${BACKEND_REPO_URL})
|
|
55
55
|
--backend-branch <name> Backend branch to track (default: ${BACKEND_BRANCH})
|
|
56
56
|
-d, --db <url> DATABASE_URL for backend
|
|
57
|
-
-u, --url <url> NEXT_PUBLIC_SITE_URL (default: http://localhost:3000)
|
|
57
|
+
-u, --url <url> NEXT_PUBLIC_SITE_URL (default: http://localhost:3001 single-origin / 3000 separate)
|
|
58
58
|
-l, --locale <code> NEXT_PUBLIC_DEFAULT_LOCALE (default: en)
|
|
59
|
+
--asset-prefix <path> Single-origin asset namespace (default: /cms-assets, or "none" to disable)
|
|
59
60
|
--skip-install Skip dependency installation
|
|
60
61
|
--skip-submodule Skip git submodule add (you'll add it later)
|
|
61
62
|
-h, --help Show this help message
|
|
63
|
+
|
|
64
|
+
${COLORS.bold}Single-origin model:${COLORS.reset}
|
|
65
|
+
The frontend is the public entry point. It rewrites these paths to the
|
|
66
|
+
backend so both apps live under one domain without /_next/* collisions:
|
|
67
|
+
/admin/* → backend
|
|
68
|
+
/api/* → backend (REST API + auth)
|
|
69
|
+
/uploads/* → backend (media)
|
|
70
|
+
<asset-prefix>/* → backend (Next.js chunks via APOLLO_ASSET_PREFIX)
|
|
71
|
+
Frontend MUST NOT define routes at /admin or /api/auth or /api/v1.
|
|
62
72
|
`;
|
|
63
73
|
|
|
64
74
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
@@ -112,6 +122,7 @@ function parseArgs(argv) {
|
|
|
112
122
|
db: null,
|
|
113
123
|
url: null,
|
|
114
124
|
locale: null,
|
|
125
|
+
assetPrefix: "/cms-assets",
|
|
115
126
|
skipInstall: false,
|
|
116
127
|
skipSubmodule: false,
|
|
117
128
|
help: false,
|
|
@@ -146,6 +157,9 @@ function parseArgs(argv) {
|
|
|
146
157
|
case "--locale":
|
|
147
158
|
flags.locale = args[++i];
|
|
148
159
|
break;
|
|
160
|
+
case "--asset-prefix":
|
|
161
|
+
flags.assetPrefix = args[++i];
|
|
162
|
+
break;
|
|
149
163
|
case "--skip-install":
|
|
150
164
|
flags.skipInstall = true;
|
|
151
165
|
break;
|
|
@@ -181,6 +195,23 @@ function isValidLocale(code) {
|
|
|
181
195
|
return /^[a-z]{2,5}$/i.test(code);
|
|
182
196
|
}
|
|
183
197
|
|
|
198
|
+
// Normalize an asset prefix:
|
|
199
|
+
// - empty string / "none" / "off" / "false" → "" (single-origin disabled, fallback to separate origins)
|
|
200
|
+
// - "/foo" → "/foo"
|
|
201
|
+
// - "foo" → "/foo"
|
|
202
|
+
// - "/foo/" → "/foo"
|
|
203
|
+
function normalizeAssetPrefix(value) {
|
|
204
|
+
if (!value) return "";
|
|
205
|
+
const trimmed = String(value).trim();
|
|
206
|
+
if (trimmed === "" || /^(none|off|false|disabled)$/i.test(trimmed)) return "";
|
|
207
|
+
let p = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
|
|
208
|
+
p = p.replace(/\/+$/, "");
|
|
209
|
+
if (!/^\/[a-z0-9._-]+(\/[a-z0-9._-]+)*$/i.test(p)) {
|
|
210
|
+
fatal(`Invalid --asset-prefix: ${value}\n Use a path like /cms-assets, or "none" to disable single-origin.`);
|
|
211
|
+
}
|
|
212
|
+
return p;
|
|
213
|
+
}
|
|
214
|
+
|
|
184
215
|
// ─── Pre-flight ──────────────────────────────────────────────────────────────
|
|
185
216
|
|
|
186
217
|
function preflight(flags) {
|
|
@@ -223,9 +254,12 @@ function createPrompt() {
|
|
|
223
254
|
return { ask, close };
|
|
224
255
|
}
|
|
225
256
|
|
|
226
|
-
async function gatherEnv(flags) {
|
|
257
|
+
async function gatherEnv(flags, { singleOrigin }) {
|
|
227
258
|
let dbUrl = flags.db;
|
|
228
|
-
|
|
259
|
+
// In single-origin mode the frontend is the public entry → default :3001.
|
|
260
|
+
// In separate-origins mode the backend is the public CMS URL → default :3000.
|
|
261
|
+
const defaultSiteUrl = singleOrigin ? "http://localhost:3001" : "http://localhost:3000";
|
|
262
|
+
let siteUrl = flags.url ?? defaultSiteUrl;
|
|
229
263
|
let locale = flags.locale ?? "en";
|
|
230
264
|
|
|
231
265
|
if (flags.db && !isValidDbUrl(flags.db)) fatal("--db must start with postgresql:// or postgres://");
|
|
@@ -321,23 +355,53 @@ function writeRootGitignore(targetDir) {
|
|
|
321
355
|
writeFileSync(resolve(targetDir, ".gitignore"), ignore);
|
|
322
356
|
}
|
|
323
357
|
|
|
324
|
-
function writeRootEnv(targetDir, { dbUrl, siteUrl, locale, authSecret }) {
|
|
358
|
+
function writeRootEnv(targetDir, { dbUrl, siteUrl, locale, authSecret, assetPrefix, backendInternalUrl }) {
|
|
359
|
+
const header = assetPrefix
|
|
360
|
+
? [
|
|
361
|
+
"# Single-origin monorepo dev env",
|
|
362
|
+
"# Public origin = frontend (apps/frontend on :3001). Frontend rewrites /admin/*,",
|
|
363
|
+
"# /api/*, /uploads/*, and the asset prefix to the backend (apps/backend on :3000).",
|
|
364
|
+
]
|
|
365
|
+
: [
|
|
366
|
+
"# Separate-origins monorepo dev env",
|
|
367
|
+
"# Backend runs at http://localhost:3000, frontend at http://localhost:3001.",
|
|
368
|
+
"# To switch to single-origin: set APOLLO_ASSET_PREFIX, BACKEND_INTERNAL_URL,",
|
|
369
|
+
"# NEXT_PUBLIC_APOLLO_ASSET_PREFIX, and add rewrites() to apps/frontend/next.config.ts.",
|
|
370
|
+
];
|
|
371
|
+
|
|
325
372
|
const lines = [
|
|
326
|
-
|
|
373
|
+
...header,
|
|
374
|
+
"",
|
|
375
|
+
"# ── Shared (consumed by both apps) ───────────────────────────────",
|
|
327
376
|
`DATABASE_URL=${dbUrl}`,
|
|
328
377
|
`APOLLO_SECRET=${authSecret}`,
|
|
329
|
-
`BETTER_AUTH_SECRET=${authSecret}`,
|
|
330
378
|
`NEXT_PUBLIC_SITE_URL=${siteUrl}`,
|
|
331
379
|
`NEXT_PUBLIC_DEFAULT_LOCALE=${locale}`,
|
|
332
380
|
"",
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
381
|
+
];
|
|
382
|
+
|
|
383
|
+
if (assetPrefix) {
|
|
384
|
+
lines.push(
|
|
385
|
+
"# ── Backend (apps/backend) ───────────────────────────────────────",
|
|
386
|
+
`APOLLO_ASSET_PREFIX=${assetPrefix}`,
|
|
387
|
+
"",
|
|
388
|
+
"# ── Frontend (apps/frontend) ─────────────────────────────────────",
|
|
389
|
+
`BACKEND_INTERNAL_URL=${backendInternalUrl}`,
|
|
390
|
+
`NEXT_PUBLIC_APOLLO_ASSET_PREFIX=${assetPrefix}`,
|
|
391
|
+
"",
|
|
392
|
+
);
|
|
393
|
+
} else {
|
|
394
|
+
lines.push(
|
|
395
|
+
"# ── Frontend (apps/frontend) ─────────────────────────────────────",
|
|
396
|
+
`NEXT_PUBLIC_BACKEND_URL=${siteUrl}`,
|
|
397
|
+
"",
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
writeFileSync(resolve(targetDir, ".env.local"), lines.join("\n"));
|
|
338
402
|
}
|
|
339
403
|
|
|
340
|
-
function writeFrontendApp(targetDir, frontendName, siteUrl) {
|
|
404
|
+
function writeFrontendApp(targetDir, frontendName, siteUrl, assetPrefix, backendInternalUrl) {
|
|
341
405
|
const dir = resolve(targetDir, FRONTEND_PATH);
|
|
342
406
|
mkdirSync(dir, { recursive: true });
|
|
343
407
|
mkdirSync(resolve(dir, "src/app"), { recursive: true });
|
|
@@ -397,15 +461,70 @@ function writeFrontendApp(targetDir, frontendName, siteUrl) {
|
|
|
397
461
|
) + "\n",
|
|
398
462
|
);
|
|
399
463
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
464
|
+
// Frontend Next.js config:
|
|
465
|
+
// - Single-origin mode (assetPrefix set): rewrite all backend paths.
|
|
466
|
+
// - Separate-origins mode (assetPrefix empty): no rewrites; the two apps
|
|
467
|
+
// are reachable on their own ports/subdomains.
|
|
468
|
+
const nextConfigContent = assetPrefix
|
|
469
|
+
? `import type { NextConfig } from "next";
|
|
470
|
+
|
|
471
|
+
const BACKEND = process.env.BACKEND_INTERNAL_URL ?? "http://localhost:3000";
|
|
472
|
+
const ASSET_PREFIX = process.env.NEXT_PUBLIC_APOLLO_ASSET_PREFIX ?? "${assetPrefix}";
|
|
473
|
+
|
|
474
|
+
const config: NextConfig = {
|
|
475
|
+
reactStrictMode: true,
|
|
476
|
+
async rewrites() {
|
|
477
|
+
return [
|
|
478
|
+
// Apollo CMS admin UI
|
|
479
|
+
{ source: "/admin/:path*", destination: \`\${BACKEND}/admin/:path*\` },
|
|
480
|
+
// Apollo CMS APIs (REST + auth + cron + email + health + mcp + …)
|
|
481
|
+
{ source: "/api/auth/:path*", destination: \`\${BACKEND}/api/auth/:path*\` },
|
|
482
|
+
{ source: "/api/v1/:path*", destination: \`\${BACKEND}/api/v1/:path*\` },
|
|
483
|
+
{ source: "/api/cron", destination: \`\${BACKEND}/api/cron\` },
|
|
484
|
+
{ source: "/api/email/:path*", destination: \`\${BACKEND}/api/email/:path*\` },
|
|
485
|
+
{ source: "/api/health", destination: \`\${BACKEND}/api/health\` },
|
|
486
|
+
{ source: "/api/mcp", destination: \`\${BACKEND}/api/mcp\` },
|
|
487
|
+
{ source: "/api/editing-presence/:path*", destination: \`\${BACKEND}/api/editing-presence/:path*\` },
|
|
488
|
+
{ source: "/api/admin/:path*", destination: \`\${BACKEND}/api/admin/:path*\` },
|
|
489
|
+
// Media uploads
|
|
490
|
+
{ source: "/uploads/:path*", destination: \`\${BACKEND}/uploads/:path*\` },
|
|
491
|
+
// Sentry tunnel (no-op if SENTRY_DSN isn't set on the backend)
|
|
492
|
+
{ source: "/monitoring", destination: \`\${BACKEND}/monitoring\` },
|
|
493
|
+
// Backend's namespaced Next.js chunks (set via APOLLO_ASSET_PREFIX)
|
|
494
|
+
{ source: \`\${ASSET_PREFIX}/:path*\`, destination: \`\${BACKEND}\${ASSET_PREFIX}/:path*\` },
|
|
495
|
+
];
|
|
496
|
+
},
|
|
497
|
+
};
|
|
404
498
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
`
|
|
408
|
-
|
|
499
|
+
export default config;
|
|
500
|
+
`
|
|
501
|
+
: `import type { NextConfig } from "next";
|
|
502
|
+
|
|
503
|
+
// Separate-origins mode: backend runs at http://localhost:3000, frontend here.
|
|
504
|
+
// Switch to single-origin by setting APOLLO_ASSET_PREFIX in apps/backend/.env.local
|
|
505
|
+
// and adding rewrites() — see this monorepo's README for the recipe.
|
|
506
|
+
const config: NextConfig = {
|
|
507
|
+
reactStrictMode: true,
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
export default config;
|
|
511
|
+
`;
|
|
512
|
+
writeFileSync(resolve(dir, "next.config.ts"), nextConfigContent);
|
|
513
|
+
|
|
514
|
+
const envLocalLines = assetPrefix
|
|
515
|
+
? [
|
|
516
|
+
`# Internal backend URL (used by Next.js rewrites — not exposed to browser)`,
|
|
517
|
+
`BACKEND_INTERNAL_URL=${backendInternalUrl}`,
|
|
518
|
+
`# Mirror of backend's APOLLO_ASSET_PREFIX`,
|
|
519
|
+
`NEXT_PUBLIC_APOLLO_ASSET_PREFIX=${assetPrefix}`,
|
|
520
|
+
"",
|
|
521
|
+
]
|
|
522
|
+
: [
|
|
523
|
+
`# Public URL of the backend (used by your client code)`,
|
|
524
|
+
`NEXT_PUBLIC_BACKEND_URL=${siteUrl}`,
|
|
525
|
+
"",
|
|
526
|
+
];
|
|
527
|
+
writeFileSync(resolve(dir, ".env.local.example"), envLocalLines.join("\n"));
|
|
409
528
|
|
|
410
529
|
writeFileSync(
|
|
411
530
|
resolve(dir, "src/app/layout.tsx"),
|
|
@@ -414,7 +533,7 @@ function writeFrontendApp(targetDir, frontendName, siteUrl) {
|
|
|
414
533
|
|
|
415
534
|
writeFileSync(
|
|
416
535
|
resolve(dir, "src/app/page.tsx"),
|
|
417
|
-
`export default function Page() {\n return (\n <main style={{ padding: 40, fontFamily: "system-ui" }}>\n <h1>Frontend</h1>\n <p
|
|
536
|
+
`export default function Page() {\n return (\n <main style={{ padding: 40, fontFamily: "system-ui" }}>\n <h1>Frontend</h1>\n <p>\n Admin: <a href="/admin">/admin</a>\n </p>\n </main>\n );\n}\n`,
|
|
418
537
|
);
|
|
419
538
|
|
|
420
539
|
writeFileSync(
|
|
@@ -423,7 +542,54 @@ function writeFrontendApp(targetDir, frontendName, siteUrl) {
|
|
|
423
542
|
);
|
|
424
543
|
}
|
|
425
544
|
|
|
426
|
-
function writeReadme(targetDir, dirName, frontendName) {
|
|
545
|
+
function writeReadme(targetDir, dirName, frontendName, assetPrefix) {
|
|
546
|
+
const singleOrigin = !!assetPrefix;
|
|
547
|
+
|
|
548
|
+
const originSection = singleOrigin
|
|
549
|
+
? `## Routing model — single origin
|
|
550
|
+
|
|
551
|
+
Both apps share one public origin (the frontend). The frontend rewrites these
|
|
552
|
+
paths to the backend so /_next/* doesn't collide:
|
|
553
|
+
|
|
554
|
+
| Path | Goes to |
|
|
555
|
+
| --------------------------- | ------------------ |
|
|
556
|
+
| \`/\` and other frontend routes | \`apps/frontend\` |
|
|
557
|
+
| \`/admin/*\` | \`apps/backend\` |
|
|
558
|
+
| \`/api/auth/*\`, \`/api/v1/*\`, \`/api/email/*\`, \`/api/cron\`, \`/api/health\`, \`/api/mcp\`, \`/api/admin/*\`, \`/api/editing-presence/*\` | \`apps/backend\` |
|
|
559
|
+
| \`/uploads/*\` | \`apps/backend\` (media) |
|
|
560
|
+
| \`${assetPrefix}/*\` | \`apps/backend\` (chunks via \`APOLLO_ASSET_PREFIX\`) |
|
|
561
|
+
|
|
562
|
+
The frontend MUST NOT define routes at \`/admin\`, \`/api/auth\`, \`/api/v1\`, etc.
|
|
563
|
+
If you need your own API, namespace it under \`/api/internal/*\` or similar.
|
|
564
|
+
|
|
565
|
+
To **disable** single-origin and run the backend on its own subdomain,
|
|
566
|
+
delete \`APOLLO_ASSET_PREFIX\` from \`apps/backend/.env.local\` and remove the
|
|
567
|
+
\`rewrites()\` block from \`apps/frontend/next.config.ts\`.
|
|
568
|
+
|
|
569
|
+
### Known limitations
|
|
570
|
+
|
|
571
|
+
- The backend's \`/_next/image\` (Next.js Image optimization endpoint) is **not**
|
|
572
|
+
rewritten — only \`/_next/static\` moves under \`APOLLO_ASSET_PREFIX\`. If your
|
|
573
|
+
backend admin uses \`<Image>\` to render \`/uploads/*\` media, those will fall
|
|
574
|
+
back to the frontend's image optimizer and 404. Apollo CMS's stock admin uses
|
|
575
|
+
plain \`<img>\` for uploaded media so this rarely matters; if you customize the
|
|
576
|
+
admin and need optimized images, switch to separate-origins mode or set
|
|
577
|
+
\`unoptimized\` on those \`<Image>\` instances.
|
|
578
|
+
`
|
|
579
|
+
: `## Routing model — separate origins
|
|
580
|
+
|
|
581
|
+
The two apps run on separate origins. Default ports:
|
|
582
|
+
|
|
583
|
+
- Backend (apps/backend): http://localhost:3000
|
|
584
|
+
- Frontend (apps/frontend): http://localhost:3001
|
|
585
|
+
|
|
586
|
+
\`NEXT_PUBLIC_SITE_URL\` should point at whichever origin you treat as the
|
|
587
|
+
public CMS URL (typically the backend). To switch to **single-origin** later,
|
|
588
|
+
re-run the installer with \`--asset-prefix /cms-assets\` or set
|
|
589
|
+
\`APOLLO_ASSET_PREFIX\` in \`apps/backend/.env.local\` and add the matching
|
|
590
|
+
\`rewrites()\` block to \`apps/frontend/next.config.ts\`.
|
|
591
|
+
`;
|
|
592
|
+
|
|
427
593
|
const readme = `# ${dirName}
|
|
428
594
|
|
|
429
595
|
Monorepo with a custom frontend and Apollo CMS as a git submodule backend.
|
|
@@ -447,6 +613,7 @@ pnpm backend:setup # push schema + seed apollo-cms
|
|
|
447
613
|
pnpm dev # runs frontend (3001) + backend (3000) in parallel
|
|
448
614
|
\`\`\`
|
|
449
615
|
|
|
616
|
+
${originSection}
|
|
450
617
|
## Updating the backend
|
|
451
618
|
|
|
452
619
|
Apollo CMS is tracked as a git submodule. Pull the latest:
|
|
@@ -490,9 +657,13 @@ async function main() {
|
|
|
490
657
|
|
|
491
658
|
// ── Step 2: Gather env ──
|
|
492
659
|
step(2, "Configuring environment");
|
|
493
|
-
const
|
|
660
|
+
const assetPrefix = normalizeAssetPrefix(flags.assetPrefix);
|
|
661
|
+
const singleOrigin = !!assetPrefix;
|
|
662
|
+
const { dbUrl, siteUrl, locale } = await gatherEnv(flags, { singleOrigin });
|
|
494
663
|
const authSecret = randomBytes(48).toString("base64");
|
|
664
|
+
const backendInternalUrl = "http://localhost:3000";
|
|
495
665
|
success(`Frontend pkg name: ${frontendName}`);
|
|
666
|
+
success(`Asset prefix: ${assetPrefix || "(disabled — separate origins)"}`);
|
|
496
667
|
|
|
497
668
|
// ── Step 3: Scaffold root ──
|
|
498
669
|
step(3, "Scaffolding monorepo root");
|
|
@@ -501,8 +672,8 @@ async function main() {
|
|
|
501
672
|
writeRootPackageJson(targetDir, dirName);
|
|
502
673
|
writePnpmWorkspace(targetDir);
|
|
503
674
|
writeRootGitignore(targetDir);
|
|
504
|
-
writeRootEnv(targetDir, { dbUrl, siteUrl, locale, authSecret });
|
|
505
|
-
writeReadme(targetDir, dirName, frontendName);
|
|
675
|
+
writeRootEnv(targetDir, { dbUrl, siteUrl, locale, authSecret, assetPrefix, backendInternalUrl });
|
|
676
|
+
writeReadme(targetDir, dirName, frontendName, assetPrefix);
|
|
506
677
|
success("package.json, pnpm-workspace.yaml, .gitignore, .env.local, README.md");
|
|
507
678
|
|
|
508
679
|
// ── Step 4: git init ──
|
|
@@ -512,7 +683,7 @@ async function main() {
|
|
|
512
683
|
|
|
513
684
|
// ── Step 5: Frontend skeleton ──
|
|
514
685
|
step(5, "Creating frontend app");
|
|
515
|
-
writeFrontendApp(targetDir, frontendName, siteUrl);
|
|
686
|
+
writeFrontendApp(targetDir, frontendName, siteUrl, assetPrefix, backendInternalUrl);
|
|
516
687
|
success(`apps/frontend (${frontendName})`);
|
|
517
688
|
|
|
518
689
|
// ── Step 6: Backend submodule ──
|
|
@@ -537,15 +708,17 @@ async function main() {
|
|
|
537
708
|
}
|
|
538
709
|
|
|
539
710
|
// Mirror env to backend
|
|
540
|
-
const
|
|
711
|
+
const backendEnvLines = [
|
|
541
712
|
`DATABASE_URL=${dbUrl}`,
|
|
542
713
|
`APOLLO_SECRET=${authSecret}`,
|
|
543
|
-
`BETTER_AUTH_SECRET=${authSecret}`,
|
|
544
714
|
`NEXT_PUBLIC_SITE_URL=${siteUrl}`,
|
|
545
715
|
`NEXT_PUBLIC_DEFAULT_LOCALE=${locale}`,
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
716
|
+
];
|
|
717
|
+
if (assetPrefix) {
|
|
718
|
+
backendEnvLines.push(`APOLLO_ASSET_PREFIX=${assetPrefix}`);
|
|
719
|
+
}
|
|
720
|
+
backendEnvLines.push("");
|
|
721
|
+
writeFileSync(resolve(targetDir, BACKEND_PATH, ".env.local"), backendEnvLines.join("\n"));
|
|
549
722
|
success("apps/backend/.env.local");
|
|
550
723
|
}
|
|
551
724
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-apollo-monorepo",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Scaffold a monorepo with a frontend app and Apollo CMS as a git submodule backend",
|
|
5
|
-
"bin":
|
|
3
|
+
"version": "0.2.0",
|
|
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
|
+
"bin": {
|
|
6
|
+
"create-apollo-monorepo": "index.mjs"
|
|
7
|
+
},
|
|
6
8
|
"type": "module",
|
|
7
9
|
"engines": {
|
|
8
10
|
"node": ">=20"
|