@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.
- package/bin/create-storefront-app.js +7 -1
- package/lib/scaffold-backend.js +59 -42
- package/lib/versions.js +1 -1
- package/package.json +1 -1
- package/templates/backend/.cursor/commands/seed-backend.md +10 -5
- package/templates/backend/.cursor/rules/medusa-backend.mdc +20 -26
- package/templates/backend/README.md +26 -0
- package/templates/backend/config/README.md +21 -13
- package/templates/backend/gitignore +1 -0
- package/templates/backend/src/scripts/seed.ts +0 -2
- package/templates/docker/Makefile +3 -1
- package/templates/docker/backend/Dockerfile +12 -7
- package/templates/root/.cursor/commands/docker-dev.md +3 -8
- package/templates/root/.cursor/rules/monorepo-layout.mdc +14 -10
- package/templates/root/scripts/sync-publishable-key.mjs +108 -0
- package/templates/shared/.cursor/rules/customize-sections.mdc +7 -1
- package/templates/shared/.cursor/rules/storefront-architecture.mdc +1 -1
- package/templates/shared/README.md +4 -3
- package/templates/shared/root-gitignore +2 -0
|
@@ -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(
|
package/lib/scaffold-backend.js
CHANGED
|
@@ -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 {
|
|
4
|
+
import { buildBackendDependencies, VERSIONS } from "./build-backend-package.js"
|
|
5
5
|
import { buildDockerTemplateVars } from "./docker-template-vars.js"
|
|
6
|
-
import {
|
|
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
|
|
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
|
-
|
|
55
|
-
preset
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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, "
|
|
77
|
-
|
|
70
|
+
join(backendRoot, ".gitignore"),
|
|
71
|
+
readFileSync(join(PKG_ROOT, "templates/backend/gitignore"), "utf8")
|
|
78
72
|
)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
readFileSync(join(
|
|
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(
|
|
81
|
+
applyTemplate(readFileSync(envTemplate, "utf8"), templateVars)
|
|
90
82
|
)
|
|
91
83
|
writeFileSync(
|
|
92
84
|
join(backendRoot, ".env"),
|
|
93
|
-
applyTemplate(readFileSync(
|
|
85
|
+
applyTemplate(readFileSync(envTemplate, "utf8"), templateVars)
|
|
94
86
|
)
|
|
95
|
-
|
|
96
87
|
writeFileSync(
|
|
97
|
-
join(backendRoot, "
|
|
98
|
-
|
|
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
|
|
102
|
-
if (existsSync(
|
|
103
|
-
|
|
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.
|
|
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,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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
| `
|
|
14
|
-
| `config
|
|
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
|
|
24
|
-
npm run build
|
|
25
|
-
npm run seed
|
|
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
|
|
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
|
-
|
|
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
|
-
##
|
|
39
|
+
## Plugins
|
|
38
40
|
|
|
39
|
-
Edit `config
|
|
41
|
+
Edit `plugins.config.json` — add/remove plugin keys or change versions, then run `npm run dev` or `backend:build`.
|
|
40
42
|
|
|
41
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
#
|
|
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
|
-
| `
|
|
6
|
-
| `
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
+
Homepage admin fields are **not** edited here manually. They sync from the storefront:
|
|
16
24
|
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
29
|
+
Restart the backend after schema changes.
|
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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/
|
|
23
|
-
COPY --from=builder /app/package
|
|
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
|
-
|
|
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`
|
|
25
|
-
- `docker/frontend.build.defaults`
|
|
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
|
|
11
|
-
storefront/
|
|
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.
|
|
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
|
|
50
|
-
|
|
|
51
|
-
| Homepage admin fields | `
|
|
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
|
|
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.)
|
|
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
|
|
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/
|
|
97
|
-
|
|
|
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.
|