@pradip1995/create-storefront-app 0.3.0 → 0.4.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.
@@ -195,7 +195,7 @@ function scaffoldProject(args) {
195
195
  "storefront:build": "npm run storefront:build --prefix storefront",
196
196
  "dev:backend": "npm run dev --prefix backend",
197
197
  "build:backend": "npm run build --prefix backend",
198
- seed: "npm run seed --prefix backend",
198
+ seed: "npm run seed --prefix backend && node scripts/sync-publishable-key.mjs --from-db",
199
199
  },
200
200
  }
201
201
  writeJson(join(projectRoot, "package.json"), rootPkg)
@@ -208,6 +208,12 @@ function scaffoldProject(args) {
208
208
  scaffoldStorefront(join(projectRoot, "storefront"), args)
209
209
  scaffoldBackend(join(projectRoot, "backend"), args)
210
210
  scaffoldDocker(projectRoot, args)
211
+
212
+ mkdirSync(join(projectRoot, "scripts"), { recursive: true })
213
+ cpSync(
214
+ join(PKG_ROOT, "templates/root/scripts/sync-publishable-key.mjs"),
215
+ join(projectRoot, "scripts/sync-publishable-key.mjs")
216
+ )
211
217
  } else {
212
218
  scaffoldStorefront(projectRoot, args, { flat: true })
213
219
  writeFileSync(
@@ -1,16 +1,13 @@
1
1
  import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs"
2
2
  import { join, dirname } from "path"
3
3
  import { fileURLToPath } from "url"
4
- import { buildBackendPackageJson, VERSIONS } from "./build-backend-package.js"
4
+ import { buildBackendDependencies, VERSIONS } from "./build-backend-package.js"
5
5
  import { buildDockerTemplateVars } from "./docker-template-vars.js"
6
- import { createRequire } from "module"
7
-
8
- const require = createRequire(import.meta.url)
9
- const { buildHomepageConfigDefaults } = require("./build-homepage-defaults.cjs")
6
+ import { FRAMEWORK_COMPILER_VERSION } from "./versions.js"
10
7
 
11
8
  const __dirname = dirname(fileURLToPath(import.meta.url))
12
9
  const PKG_ROOT = join(__dirname, "..")
13
- const BACKEND_TEMPLATE = join(PKG_ROOT, "templates", "backend")
10
+ const BACKEND_CURSOR = join(PKG_ROOT, "templates", "backend", ".cursor")
14
11
 
15
12
  const REGION_CURRENCY = {
16
13
  in: { currency: "inr", name: "India" },
@@ -32,6 +29,16 @@ function regionMeta(defaultRegion) {
32
29
  )
33
30
  }
34
31
 
32
+ function buildPluginsConfig(preset) {
33
+ const { medusa, plugins } = buildBackendDependencies(preset)
34
+ return { medusa, plugins }
35
+ }
36
+
37
+ function writeJson(path, data) {
38
+ writeFileSync(path, `${JSON.stringify(data, null, 2)}\n`)
39
+ }
40
+
41
+ /** Scaffold config-only backend/ — Medusa source is generated by backend-build. */
35
42
  export function scaffoldBackend(backendRoot, args) {
36
43
  const { preset, name, backendUrl, baseUrl, defaultRegion } = args
37
44
  const meta = regionMeta(defaultRegion)
@@ -51,56 +58,66 @@ export function scaffoldBackend(backendRoot, args) {
51
58
 
52
59
  mkdirSync(backendRoot, { recursive: true })
53
60
 
54
- const medusaConfigSrc =
55
- preset === "minimal"
56
- ? join(BACKEND_TEMPLATE, "medusa-config.minimal.ts")
57
- : join(BACKEND_TEMPLATE, "medusa-config.full.ts")
58
- writeFileSync(join(backendRoot, "medusa-config.ts"), readFileSync(medusaConfigSrc, "utf8"))
59
-
60
- cpSync(join(BACKEND_TEMPLATE, "tsconfig.json"), join(backendRoot, "tsconfig.json"))
61
- cpSync(join(BACKEND_TEMPLATE, "gitignore"), join(backendRoot, ".gitignore"))
62
- cpSync(join(PKG_ROOT, "templates/shared/npmrc"), join(backendRoot, ".npmrc"))
63
- cpSync(join(BACKEND_TEMPLATE, "config"), join(backendRoot, "config"), { recursive: true })
64
- cpSync(join(BACKEND_TEMPLATE, "src"), join(backendRoot, "src"), { recursive: true })
65
- cpSync(join(BACKEND_TEMPLATE, "scripts"), join(backendRoot, "scripts"), { recursive: true })
66
-
67
- const homepageSchema = JSON.parse(
68
- readFileSync(join(backendRoot, "config/homepage-config.json"), "utf8")
69
- )
70
- writeFileSync(
71
- join(backendRoot, "config/homepage-config.defaults.json"),
72
- `${JSON.stringify(buildHomepageConfigDefaults(homepageSchema), null, 2)}\n`
73
- )
61
+ writeJson(join(backendRoot, "backend.config.json"), {
62
+ preset,
63
+ projectName: name,
64
+ defaultRegion,
65
+ })
66
+
67
+ writeJson(join(backendRoot, "plugins.config.json"), buildPluginsConfig(preset))
74
68
 
75
69
  writeFileSync(
76
- join(backendRoot, "config", "medusa-plugin-versions.json"),
77
- JSON.stringify(VERSIONS, null, 2) + "\n"
70
+ join(backendRoot, ".gitignore"),
71
+ readFileSync(join(PKG_ROOT, "templates/backend/gitignore"), "utf8")
78
72
  )
79
-
80
- const seedContent = applyTemplate(
81
- readFileSync(join(BACKEND_TEMPLATE, "src/scripts/seed.ts"), "utf8"),
82
- templateVars
73
+ writeFileSync(
74
+ join(backendRoot, ".npmrc"),
75
+ readFileSync(join(PKG_ROOT, "templates/shared/npmrc"), "utf8")
83
76
  )
84
- mkdirSync(join(backendRoot, "src/scripts"), { recursive: true })
85
- writeFileSync(join(backendRoot, "src/scripts/seed.ts"), seedContent)
86
77
 
78
+ const envTemplate = join(PKG_ROOT, "templates/backend/env.template")
87
79
  writeFileSync(
88
80
  join(backendRoot, ".env.template"),
89
- applyTemplate(readFileSync(join(BACKEND_TEMPLATE, "env.template"), "utf8"), templateVars)
81
+ applyTemplate(readFileSync(envTemplate, "utf8"), templateVars)
90
82
  )
91
83
  writeFileSync(
92
84
  join(backendRoot, ".env"),
93
- applyTemplate(readFileSync(join(BACKEND_TEMPLATE, "env.template"), "utf8"), templateVars)
85
+ applyTemplate(readFileSync(envTemplate, "utf8"), templateVars)
94
86
  )
95
-
96
87
  writeFileSync(
97
- join(backendRoot, "package.json"),
98
- JSON.stringify(buildBackendPackageJson(preset, name), null, 2) + "\n"
88
+ join(backendRoot, ".env.example"),
89
+ applyTemplate(readFileSync(envTemplate, "utf8"), {
90
+ ...templateVars,
91
+ DATABASE_URL: "postgres://user:pass@localhost:5432/medusa",
92
+ })
99
93
  )
100
94
 
101
- const backendCursor = join(BACKEND_TEMPLATE, ".cursor")
102
- if (existsSync(backendCursor)) {
103
- cpSync(backendCursor, join(backendRoot, ".cursor"), { recursive: true })
95
+ const backendReadme = join(PKG_ROOT, "templates/backend/README.md")
96
+ if (existsSync(backendReadme)) {
97
+ writeFileSync(join(backendRoot, "README.md"), readFileSync(backendReadme, "utf8"))
98
+ }
99
+
100
+ writeJson(join(backendRoot, "package.json"), {
101
+ name: `${name}-backend`,
102
+ private: true,
103
+ scripts: {
104
+ "backend:build": "backend-build",
105
+ dev: "npm run backend:build && cd .generated-backend && npm install && npm run dev",
106
+ build:
107
+ "npm run backend:build && cd .generated-backend && npm install && npm run build",
108
+ start:
109
+ "npm run backend:build && cd .generated-backend && npm install && npm run start",
110
+ seed: "npm run backend:build && cd .generated-backend && npm install && npm run seed",
111
+ "dynamic-config:defaults":
112
+ "npm run backend:build && cd .generated-backend && npm run dynamic-config:defaults",
113
+ },
114
+ dependencies: {
115
+ "@pradip1995/framework-compiler": FRAMEWORK_COMPILER_VERSION,
116
+ },
117
+ })
118
+
119
+ if (existsSync(BACKEND_CURSOR)) {
120
+ cpSync(BACKEND_CURSOR, join(backendRoot, ".cursor"), { recursive: true })
104
121
  }
105
122
 
106
123
  return backendRoot
package/lib/versions.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /** Published package semver ranges — bump when releasing framework/components. */
2
2
  export const COMMERCE_CORE_VERSION = "^4.0.2"
3
3
  export const FRAMEWORK_VERSION = "^0.2.1"
4
- export const FRAMEWORK_COMPILER_VERSION = "^0.2.4"
4
+ export const FRAMEWORK_COMPILER_VERSION = "^0.3.0"
5
5
  export const COMPONENTS_VERSION = ">=0.2.0 <0.4.0"
6
6
 
7
7
  export const FRAMEWORK_PACKAGES = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pradip1995/create-storefront-app",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Scaffold a segment-based Medusa storefront using @pradip1995/framework packages",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,16 +1,21 @@
1
1
  # Seed Medusa backend
2
2
 
3
- From project root:
3
+ From project root (recommended — also syncs publishable key to storefront):
4
+
5
+ ```bash
6
+ npm run seed
7
+ ```
8
+
9
+ Backend only:
4
10
 
5
11
  ```bash
6
12
  cd backend
7
13
  npm run seed
14
+ node ../scripts/sync-publishable-key.mjs --from-db
8
15
  ```
9
16
 
10
17
  Requires PostgreSQL running and `DATABASE_URL` set in `backend/.env`.
11
18
 
12
- After seed:
19
+ ## After seed
13
20
 
14
- 1. Start backend: `npm run dev`
15
- 2. Copy publishable API key from Admin into `storefront/.env.local`
16
- 3. Start storefront: `cd ../storefront && npm run dev`
21
+ `scripts/sync-publishable-key.mjs` reads the publishable key from Postgres and updates `storefront/.env.local`. Then start storefront: `npm run dev:storefront`.
@@ -1,55 +1,49 @@
1
1
  ---
2
- description: Medusa backend — config, plugins, env, seed
2
+ description: Medusa backend — config-only client, generated on dev/build
3
3
  globs: backend/**/*
4
4
  alwaysApply: false
5
5
  ---
6
6
 
7
- # Medusa backend
7
+ # Medusa backend (config client)
8
8
 
9
- ## Key files
9
+ Generated Medusa source lives in `.generated-backend/` (gitignored). Same pattern as `storefront/.generated-app/`.
10
+
11
+ ## Committed source
10
12
 
11
13
  | File | Purpose |
12
14
  |------|---------|
13
- | `medusa-config.ts` | Plugins + modules wiring |
14
- | `config/medusa-plugin-versions.json` | npm version map for all plugins |
15
- | `config/homepage-config.json` | Dynamic-config schema (sync with storefront build) |
15
+ | `plugins.config.json` | Medusa core + plugin npm versions; optional `enabled` array |
16
+ | `backend.config.json` | Preset (`full`/`minimal`), default region |
16
17
  | `.env` | Database, CORS, secrets, integrations |
17
- | `src/scripts/seed.ts` | Region, sales channel, publishable API key, sample product |
18
18
 
19
19
  ## Commands
20
20
 
21
21
  ```bash
22
22
  cd backend
23
- npm run dev # medusa develop
24
- npm run build # medusa build
25
- npm run seed # medusa exec ./src/scripts/seed.ts
23
+ npm run dev # backend-build → medusa develop
24
+ npm run build # backend-build → medusa build
25
+ npm run seed # seed region, API key, sample product
26
+ npm run backend:build # regenerate .generated-backend/ only
26
27
  ```
27
28
 
29
+ **Do not edit** `.generated-backend/` — overwritten every build.
30
+
28
31
  ## Docker
29
32
 
30
- From project root: `make up-infra` then local `npm run dev`, or `make up` for full containerized stack. See `Makefile`.
33
+ From project root: `make up-infra` then local `npm run dev:backend`, or `make up` for full stack. See `Makefile`.
31
34
 
32
35
  ## After seed
33
36
 
34
- 1. Open Medusa Admin Settings Publishable API Keys
35
- 2. Copy token into `storefront/.env.local` as `NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY`
37
+ From project root, `npm run seed` or `make seed` syncs the publishable key into `storefront/.env.local` (via `scripts/sync-publishable-key.mjs`).
36
38
 
37
- ## Plugin versions
39
+ ## Plugins
38
40
 
39
- Edit `config/medusa-plugin-versions.json`, then update `package.json` dependencies to match and run `npm install`.
41
+ Edit `plugins.config.json` add/remove plugin keys or change versions, then run `npm run dev` or `backend:build`.
40
42
 
41
- ## CORS
42
-
43
- `STORE_CORS` and `AUTH_CORS` must include the storefront URL (`STOREFRONT_URL`).
43
+ `medusa-config.ts` is generated from the enabled plugin list.
44
44
 
45
45
  ## Dynamic config
46
46
 
47
- Homepage fields in Admin come from `config/homepage-config.json` (registered in `medusa-config.ts`).
48
-
49
- After editing the schema, run:
50
-
51
- ```bash
52
- npm run dynamic-config:defaults
53
- ```
47
+ Homepage admin fields sync from `storefront/pages.config.json` on `storefront:build` into `.generated-backend/config/homepage-config.json`.
54
48
 
55
- The storefront reads **runtime values** from `GET /store/dynamic-config` — it does not generate or import schema files.
49
+ Restart backend after schema changes. Storefront reads runtime values from `GET /store/dynamic-config`.
@@ -0,0 +1,26 @@
1
+ # Backend (config-only)
2
+
3
+ Medusa source is **generated** into `.generated-backend/` — same pattern as `storefront/.generated-app/`.
4
+
5
+ ## Committed source
6
+
7
+ | File | Edit when |
8
+ |------|-----------|
9
+ | `plugins.config.json` | Add/remove plugins or change npm versions |
10
+ | `backend.config.json` | Change preset (`full`/`minimal`) or default region |
11
+ | `.env` | Database URL, secrets, optional integrations |
12
+
13
+ ## Commands
14
+
15
+ ```bash
16
+ npm run dev # backend-build → medusa develop
17
+ npm run build # backend-build → medusa build
18
+ npm run seed # seed demo data
19
+ npm run backend:build # regenerate .generated-backend/ only
20
+ ```
21
+
22
+ **Do not edit** `.generated-backend/` — changes are overwritten on the next build.
23
+
24
+ ## Homepage admin schema
25
+
26
+ Synced from storefront segments — see `storefront/pages.config.json` and run `npm run storefront:build` in the storefront folder.
@@ -1,21 +1,29 @@
1
- # Dynamic config (backend)
1
+ # Backend config (committed)
2
+
3
+ This folder is **config-only**. Medusa source is generated into `.generated-backend/` on `npm run dev` or `npm run build` — do not commit that folder.
4
+
5
+ ## Source files
2
6
 
3
7
  | File | Purpose |
4
8
  |------|---------|
5
- | `homepage-config.json` | **Schema** field definitions for Medusa Admin (`medusa-plugin-dynamic-config`) |
6
- | `homepage-config.defaults.json` | **Default values** extracted from schema `defaultValue` fields (reference + bootstrap) |
9
+ | `plugins.config.json` | Medusa core + plugin npm versions; optional `enabled` array to override preset |
10
+ | `backend.config.json` | Preset (`full` \| `minimal`), default region, project name |
11
+ | `.env` / `.env.template` | Secrets and service URLs (DATABASE_URL, CORS, optional integrations) |
12
+
13
+ ## Generated (gitignored)
14
+
15
+ Running `npm run backend:build` (or `dev` / `build`) writes:
7
16
 
8
- ## Workflow
17
+ - `.generated-backend/medusa-config.ts` — assembled from `plugins.config.json`
18
+ - `.generated-backend/package.json` — Medusa dependencies
19
+ - `.generated-backend/src/`, `scripts/`, `config/`
9
20
 
10
- 1. Edit `homepage-config.json` when adding/removing admin fields.
11
- 2. Run `npm run dynamic-config:defaults` to refresh default values.
12
- 3. Restart backend — schema is loaded from `medusa-config.ts`.
13
- 4. Set live content in **Medusa Admin** (Dynamic Config).
21
+ ## Dynamic homepage schema
14
22
 
15
- The **storefront does not copy this schema**. It reads runtime values from:
23
+ Homepage admin fields are **not** edited here manually. They sync from the storefront:
16
24
 
17
- ```
18
- GET /store/dynamic-config
19
- ```
25
+ 1. Edit `storefront/pages.config.json` (segments/workflows).
26
+ 2. Run `npm run storefront:build` in `storefront/` — writes filtered schema to `backend/.generated-backend/config/homepage-config.json`.
27
+ 3. Run `npm run backend:build` or `dev:backend` — preserves synced schema when regenerating.
20
28
 
21
- Configure values in Admin after `npm run seed`.
29
+ Restart the backend after schema changes.
@@ -1,4 +1,5 @@
1
1
  node_modules
2
+ .generated-backend
2
3
  .medusa
3
4
  .cache
4
5
  static
@@ -206,7 +206,5 @@ export default async function seedStore({ container }: ExecArgs) {
206
206
  logger.info("=== Seed complete ===")
207
207
  logger.info(`Region: ${SEED_REGION_NAME} (${SEED_COUNTRIES.join(", ")})`)
208
208
  logger.info(`Publishable API key id: ${publishableKey.id}`)
209
- logger.info("Copy the publishable key token from Medusa Admin → Settings → Publishable API Keys")
210
- logger.info("into storefront/.env.local as NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY")
211
209
  logger.info(`Sample product: ${products[0]?.handle}`)
212
210
  }
@@ -72,8 +72,10 @@ health: ## Check backend and storefront HTTP health
72
72
  admin-create: ## Create admin user (ADMIN_EMAIL / ADMIN_PASSWORD)
73
73
  $(COMPOSE) exec backend npx medusa user -e $(ADMIN_EMAIL) -p '$(ADMIN_PASSWORD)'
74
74
 
75
- seed: ## Run Medusa seed script (regions, publishable key, sample product)
75
+ seed: ## Run Medusa seed, then sync publishable key to storefront env
76
76
  $(COMPOSE) exec backend npx medusa exec ./src/scripts/seed.ts
77
+ @token=$$($(COMPOSE) exec -T postgres psql -U {{POSTGRES_USER}} -d {{POSTGRES_DB}} -tAc "SELECT token FROM api_key WHERE type = 'publishable' AND deleted_at IS NULL AND revoked_at IS NULL ORDER BY created_at DESC LIMIT 1" | tr -d '[:space:]'); \
78
+ node scripts/sync-publishable-key.mjs "$$token"
77
79
 
78
80
  db-shell: ## Open psql in postgres container
79
81
  $(COMPOSE) exec postgres psql -U {{POSTGRES_USER}} -d {{POSTGRES_DB}}
@@ -2,10 +2,14 @@ FROM node:24-alpine AS builder
2
2
 
3
3
  WORKDIR /app
4
4
 
5
- COPY package.json package-lock.json* ./
5
+ COPY package.json .npmrc plugins.config.json backend.config.json ./
6
6
  RUN npm install --prefer-offline --no-audit --legacy-peer-deps
7
7
 
8
- COPY . .
8
+ RUN npm run backend:build
9
+
10
+ WORKDIR /app/.generated-backend
11
+
12
+ RUN npm install --prefer-offline --no-audit --legacy-peer-deps
9
13
  RUN npm run build
10
14
 
11
15
  RUN if [ -d templates ]; then cp -r templates .medusa/server/; fi
@@ -16,11 +20,12 @@ WORKDIR /app
16
20
 
17
21
  ENV NODE_ENV=production
18
22
 
19
- COPY --from=builder /app/.medusa/server ./
20
- COPY --from=builder /app/medusa-config.ts ./medusa-config.ts
21
- COPY --from=builder /app/config ./config
22
- COPY --from=builder /app/package.json ./package.json
23
- COPY --from=builder /app/package-lock.json* ./
23
+ COPY --from=builder /app/.generated-backend/.medusa/server ./
24
+ COPY --from=builder /app/.generated-backend/medusa-config.ts ./medusa-config.ts
25
+ COPY --from=builder /app/.generated-backend/config ./config
26
+ COPY --from=builder /app/.generated-backend/src ./src
27
+ COPY --from=builder /app/.generated-backend/package.json ./package.json
28
+ COPY --from=builder /app/.generated-backend/package-lock.json* ./
24
29
 
25
30
  RUN npm install --omit=dev --prefer-offline --no-audit --legacy-peer-deps \
26
31
  && npm install --save-prod ts-node typescript --prefer-offline --no-audit --legacy-peer-deps
@@ -19,14 +19,9 @@ npm run dev:storefront # terminal 2
19
19
 
20
20
  ## After seed
21
21
 
22
- Copy publishable API key into:
22
+ `make seed` runs Medusa seed unchanged, then `scripts/sync-publishable-key.mjs` reads the token from Postgres and updates:
23
23
 
24
- - `storefront/.env.local` (local dev)
25
- - `docker/frontend.build.defaults` + rebuild storefront image (Docker)
26
-
27
- ```bash
28
- make build-storefront
29
- make up-storefront
30
- ```
24
+ - `storefront/.env.local`
25
+ - `docker/.env` and `docker/frontend.build.defaults` (when using Docker)
31
26
 
32
27
  Mailpit: http://localhost:8025
@@ -7,15 +7,17 @@ alwaysApply: true
7
7
 
8
8
  ```
9
9
  <project>/
10
- backend/ Medusa v2 API + Admin
11
- storefront/ Segment-based Next.js (config client)
10
+ backend/ Config-only Medusa client .generated-backend/
11
+ storefront/ Config-only Next.js client → .generated-app/
12
12
  ```
13
13
 
14
+ Both folders are **config clients**. Generated code is gitignored.
15
+
14
16
  ## Dev (local Node)
15
17
 
16
18
  ```bash
17
- npm run dev:backend # terminal 1
18
- npm run dev:storefront # terminal 2
19
+ npm run dev:backend # terminal 1 — backend-build + medusa develop
20
+ npm run dev:storefront # terminal 2 — storefront-build + next dev
19
21
  ```
20
22
 
21
23
  ## Dev (Docker)
@@ -31,9 +33,9 @@ See `Makefile` and command `docker-dev`.
31
33
  ## First-time backend
32
34
 
33
35
  1. Set `DATABASE_URL` in `backend/.env`
34
- 2. `npm run dev:backend` (migrations)
36
+ 2. `npm run dev:backend` (generates backend + migrations)
35
37
  3. `npm run seed` (region, API key, sample product)
36
- 4. Copy publishable key `storefront/.env.local`
38
+ 4. Run `npm run seed` from project root (syncs publishable key to `storefront/.env.local` via Postgres)
37
39
 
38
40
  ## Build
39
41
 
@@ -42,14 +44,16 @@ npm run build:backend
42
44
  npm run build:storefront
43
45
  ```
44
46
 
45
- ## Where to edit
47
+ ## Where to edit (committed source only)
46
48
 
47
49
  | Goal | Path |
48
50
  |------|------|
49
- | Medusa plugins / modules | `backend/medusa-config.ts` |
50
- | Plugin npm versions | `backend/config/medusa-plugin-versions.json` |
51
- | Homepage admin fields | `backend/config/homepage-config.json` (+ `npm run dynamic-config:defaults` in backend) |
51
+ | Medusa plugins + versions | `backend/plugins.config.json` |
52
+ | Backend preset / region | `backend/backend.config.json` |
53
+ | Homepage admin fields | driven by `storefront/pages.config.json` synced on `storefront:build` |
52
54
  | Storefront sections | `storefront/pages.config.json` |
53
55
  | Brand colors | `storefront/theme-overrides.css` |
54
56
 
57
+ **Never edit** `backend/.generated-backend/` or `storefront/.generated-app/` — regenerated every dev/build.
58
+
55
59
  Cursor guides live in `backend/.cursor/` and `storefront/.cursor/`.
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Sync NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY into storefront/.env.local and docker env files.
4
+ * Does not modify Medusa backend — reads the token from Postgres after seed.
5
+ *
6
+ * Usage:
7
+ * node scripts/sync-publishable-key.mjs --from-db
8
+ * node scripts/sync-publishable-key.mjs pk_abc123...
9
+ * echo pk_abc... | node scripts/sync-publishable-key.mjs --stdin
10
+ */
11
+ import { execSync } from "child_process"
12
+ import { existsSync, readFileSync, writeFileSync } from "fs"
13
+ import { dirname, join } from "path"
14
+ import { fileURLToPath } from "url"
15
+
16
+ const __dirname = dirname(fileURLToPath(import.meta.url))
17
+ const projectRoot = join(__dirname, "..")
18
+ const ENV_KEY = "NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY"
19
+
20
+ const SQL = `SELECT token FROM api_key WHERE type = 'publishable' AND deleted_at IS NULL AND revoked_at IS NULL ORDER BY created_at DESC LIMIT 1`
21
+
22
+ function upsertEnvLine(content, key, value) {
23
+ const line = `${key}=${value}`
24
+ const regex = new RegExp(`^${key}=.*$`, "m")
25
+ if (regex.test(content)) return content.replace(regex, line)
26
+ return `${content.replace(/\n?$/, "")}\n${line}\n`
27
+ }
28
+
29
+ function updateEnvFile(filePath, token) {
30
+ if (!existsSync(filePath)) return false
31
+ writeFileSync(filePath, upsertEnvLine(readFileSync(filePath, "utf8"), ENV_KEY, token))
32
+ return true
33
+ }
34
+
35
+ function parseEnvFile(path) {
36
+ if (!existsSync(path)) return {}
37
+ const vars = {}
38
+ for (const line of readFileSync(path, "utf8").split("\n")) {
39
+ const m = line.match(/^([A-Z0-9_]+)=(.*)$/)
40
+ if (m) vars[m[1]] = m[2]
41
+ }
42
+ return vars
43
+ }
44
+
45
+ function readDatabaseUrl() {
46
+ const backendEnv = join(projectRoot, "backend", ".env")
47
+ const vars = parseEnvFile(backendEnv)
48
+ return vars.DATABASE_URL || process.env.DATABASE_URL || null
49
+ }
50
+
51
+ function queryTokenViaPsql(databaseUrl) {
52
+ const out = execSync(`psql "${databaseUrl}" -tAc "${SQL}"`, {
53
+ encoding: "utf8",
54
+ stdio: ["ignore", "pipe", "pipe"],
55
+ })
56
+ return out.trim()
57
+ }
58
+
59
+ function isValidToken(token) {
60
+ return typeof token === "string" && token.startsWith("pk_") && !token.includes("...")
61
+ }
62
+
63
+ function resolveToken() {
64
+ const args = process.argv.slice(2)
65
+
66
+ if (args.includes("--stdin")) {
67
+ const fromStdin = readFileSync(0, "utf8").trim()
68
+ if (isValidToken(fromStdin)) return fromStdin
69
+ }
70
+
71
+ const positional = args.filter((a) => !a.startsWith("--"))
72
+ if (positional[0] && isValidToken(positional[0])) return positional[0]
73
+
74
+ if (args.includes("--from-db") || args.length === 0) {
75
+ const databaseUrl = readDatabaseUrl()
76
+ if (!databaseUrl) {
77
+ throw new Error("DATABASE_URL not found in backend/.env")
78
+ }
79
+ const token = queryTokenViaPsql(databaseUrl)
80
+ if (isValidToken(token)) return token
81
+ throw new Error("No publishable API key in database — run seed first")
82
+ }
83
+
84
+ throw new Error("Pass pk_ token, --stdin, or --from-db")
85
+ }
86
+
87
+ try {
88
+ const token = resolveToken()
89
+ const targets = [
90
+ join(projectRoot, "storefront", ".env.local"),
91
+ join(projectRoot, "docker", ".env"),
92
+ join(projectRoot, "docker", "frontend.build.defaults"),
93
+ ]
94
+ const updated = targets.filter((p) => updateEnvFile(p, token))
95
+
96
+ if (updated.length === 0) {
97
+ console.error("sync-publishable-key: no env files updated (storefront/.env.local missing?)")
98
+ process.exit(1)
99
+ }
100
+
101
+ console.log("sync-publishable-key: set NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY in:")
102
+ for (const p of updated) {
103
+ console.log(` ${p.replace(`${projectRoot}/`, "")}`)
104
+ }
105
+ } catch (error) {
106
+ console.error(`sync-publishable-key: ${error.message}`)
107
+ process.exit(1)
108
+ }
@@ -20,7 +20,13 @@ Each entry in `segments` renders top-to-bottom:
20
20
  ]
21
21
  ```
22
22
 
23
- **Reorder** change array order, then `npm run storefront:build`.
23
+ **Reorder / add / remove sections** — edit `pages.config.json`, then:
24
+
25
+ ```bash
26
+ npm run storefront:build
27
+ ```
28
+
29
+ This regenerates the Next.js app **and** syncs `backend/config/homepage-config.json` (Admin fields for your active segments). Restart the backend after schema changes.
24
30
 
25
31
  **Remove** — delete the line and remove the package from `package.json` if unused elsewhere.
26
32
 
@@ -24,7 +24,7 @@ This folder (`storefront/`) is a **config client**, not a hand-written Next.js a
24
24
  |------|---------|
25
25
  | `.generated-app/` | Full Next.js app from `storefront-build` |
26
26
 
27
- Homepage content (hero, promo, logo, etc.) is **not** generated here — it comes from Medusa `GET /store/dynamic-config`. Schema lives in `../backend/config/homepage-config.json`.
27
+ Homepage content (hero, promo, logo, etc.) comes from Medusa `GET /store/dynamic-config`. The **schema** in `../backend/.generated-backend/config/homepage-config.json` is updated automatically when you run `npm run storefront:build` after changing `pages.config.json`.
28
28
 
29
29
  After any change to config, deps, or `theme-overrides.css`:
30
30
 
@@ -30,7 +30,7 @@ make admin-create
30
30
  | API | {{BACKEND_URL}} |
31
31
  | Mailpit | http://localhost:8025 |
32
32
 
33
- After `make seed`, update `docker/frontend.build.defaults` and `storefront/.env.local` with the publishable key, then `make build-storefront` if using Docker for the storefront.
33
+ After `make seed` or `npm run seed`, the publishable key is read from Postgres and written to `storefront/.env.local` and `docker/` env files (no backend code changes).
34
34
 
35
35
  Default admin: `make admin-create` (see `Makefile` for `ADMIN_EMAIL` / `ADMIN_PASSWORD`).
36
36
 
@@ -93,8 +93,9 @@ make clean # remove containers + volumes
93
93
  |------|--------|
94
94
  | Home sections | `storefront/pages.config.json` |
95
95
  | Colors / fonts | `storefront/theme-overrides.css` |
96
- | Medusa plugins | `backend/medusa-config.ts` + `backend/config/medusa-plugin-versions.json` |
97
- | Homepage admin fields | `backend/config/homepage-config.json` |
96
+ | Medusa plugins | `backend/plugins.config.json` |
97
+ | Plugin preset / region | `backend/backend.config.json` |
98
+ | Homepage admin fields | auto-synced to `backend/.generated-backend/config/` on `storefront:build` |
98
99
  | Docker secrets | `docker/.env` |
99
100
 
100
101
  Cursor guides: `.cursor/` in each folder.
@@ -4,3 +4,5 @@ node_modules
4
4
  .env
5
5
  .env.local
6
6
  docker/.env
7
+ backend/.generated-backend
8
+ storefront/.generated-app