create-turbo-mono 1.0.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/.claude/settings.local.json +14 -0
- package/README.md +182 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +118 -0
- package/dist/index.js.map +1 -0
- package/dist/scaffold.d.ts +8 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +42 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/templates/backend.d.ts +2 -0
- package/dist/templates/backend.d.ts.map +1 -0
- package/dist/templates/backend.js +424 -0
- package/dist/templates/backend.js.map +1 -0
- package/dist/templates/cicd.d.ts +3 -0
- package/dist/templates/cicd.d.ts.map +1 -0
- package/dist/templates/cicd.js +307 -0
- package/dist/templates/cicd.js.map +1 -0
- package/dist/templates/docker.d.ts +3 -0
- package/dist/templates/docker.d.ts.map +1 -0
- package/dist/templates/docker.js +458 -0
- package/dist/templates/docker.js.map +1 -0
- package/dist/templates/docs.d.ts +3 -0
- package/dist/templates/docs.d.ts.map +1 -0
- package/dist/templates/docs.js +71 -0
- package/dist/templates/docs.js.map +1 -0
- package/dist/templates/frontend.d.ts +2 -0
- package/dist/templates/frontend.d.ts.map +1 -0
- package/dist/templates/frontend.js +441 -0
- package/dist/templates/frontend.js.map +1 -0
- package/dist/templates/root.d.ts +3 -0
- package/dist/templates/root.d.ts.map +1 -0
- package/dist/templates/root.js +210 -0
- package/dist/templates/root.js.map +1 -0
- package/dist/templates/shared.d.ts +2 -0
- package/dist/templates/shared.d.ts.map +1 -0
- package/dist/templates/shared.js +696 -0
- package/dist/templates/shared.js.map +1 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +34 -0
- package/dist/utils.js.map +1 -0
- package/package.json +40 -0
- package/src/index.ts +138 -0
- package/src/scaffold.ts +51 -0
- package/src/templates/backend.ts +460 -0
- package/src/templates/cicd.ts +334 -0
- package/src/templates/docker.ts +503 -0
- package/src/templates/docs.ts +74 -0
- package/src/templates/frontend.ts +469 -0
- package/src/templates/root.ts +216 -0
- package/src/templates/shared.ts +820 -0
- package/src/utils.ts +31 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { ProjectConfig } from '../scaffold';
|
|
4
|
+
|
|
5
|
+
export async function createDockerFiles(config: ProjectConfig): Promise<void> {
|
|
6
|
+
const { targetDir, backends, frontends } = config;
|
|
7
|
+
|
|
8
|
+
await createDockerfileBase(targetDir, backends, frontends);
|
|
9
|
+
await createDockerComposeLocal(targetDir);
|
|
10
|
+
await createDockerComposeDev(targetDir, backends, frontends);
|
|
11
|
+
await createDockerComposeProd(targetDir, backends, frontends);
|
|
12
|
+
await createDockerIgnore(targetDir);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function createDockerfileBase(
|
|
16
|
+
targetDir: string,
|
|
17
|
+
backends: string[],
|
|
18
|
+
frontends: string[]
|
|
19
|
+
): Promise<void> {
|
|
20
|
+
const backendTargets = backends
|
|
21
|
+
.map(
|
|
22
|
+
(name) => `# ============================================
|
|
23
|
+
# ${name.toUpperCase()} Production
|
|
24
|
+
# ============================================
|
|
25
|
+
FROM build-base AS ${name}-production
|
|
26
|
+
WORKDIR /app/apps/backend/${name}
|
|
27
|
+
|
|
28
|
+
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
|
29
|
+
|
|
30
|
+
RUN --mount=type=cache,id=nest,target=/app/node_modules/.cache \\
|
|
31
|
+
echo "📦 Building ${name}..." && \\
|
|
32
|
+
pnpm prisma generate && \\
|
|
33
|
+
pnpm run build
|
|
34
|
+
|
|
35
|
+
ENV NODE_OPTIONS="--max-old-space-size=2048"
|
|
36
|
+
|
|
37
|
+
EXPOSE 3000
|
|
38
|
+
|
|
39
|
+
CMD ["sh", "-c", "pnpm prisma generate && until pnpm prisma migrate deploy; do echo 'Waiting for DB...'; sleep 2; done && pnpm run start:prod"]
|
|
40
|
+
|
|
41
|
+
# ============================================
|
|
42
|
+
# ${name.toUpperCase()} Development
|
|
43
|
+
# ============================================
|
|
44
|
+
FROM shared-libs AS ${name}-development
|
|
45
|
+
WORKDIR /app/apps/backend/${name}
|
|
46
|
+
ENV NODE_ENV=development
|
|
47
|
+
|
|
48
|
+
EXPOSE 3000
|
|
49
|
+
CMD ["sh", "-c", "pnpm prisma generate && until pnpm prisma migrate deploy; do echo 'Waiting for DB...'; sleep 2; done && pnpm run dev"]`
|
|
50
|
+
)
|
|
51
|
+
.join('\n\n');
|
|
52
|
+
|
|
53
|
+
const frontendTargets = frontends
|
|
54
|
+
.map(
|
|
55
|
+
(name, index) => `# ============================================
|
|
56
|
+
# ${name.toUpperCase()} Production
|
|
57
|
+
# ============================================
|
|
58
|
+
FROM build-base AS ${name}-production
|
|
59
|
+
WORKDIR /app/apps/frontend/${name}
|
|
60
|
+
|
|
61
|
+
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
|
62
|
+
|
|
63
|
+
RUN --mount=type=cache,id=next,target=/app/node_modules/.cache \\
|
|
64
|
+
echo "🌐 Building ${name}..." && \\
|
|
65
|
+
NEXT_TELEMETRY_DISABLED=1 pnpm next build
|
|
66
|
+
|
|
67
|
+
ENV NODE_OPTIONS="--max-old-space-size=2048"
|
|
68
|
+
|
|
69
|
+
EXPOSE ${3000 + index}
|
|
70
|
+
CMD ["pnpm", "run", "start"]`
|
|
71
|
+
)
|
|
72
|
+
.join('\n\n');
|
|
73
|
+
|
|
74
|
+
const dockerfile = `# syntax=docker/dockerfile:1
|
|
75
|
+
|
|
76
|
+
# ============================================
|
|
77
|
+
# Global Build Arguments
|
|
78
|
+
# ============================================
|
|
79
|
+
ARG NODE_VERSION=20
|
|
80
|
+
ARG DATABASE_URL
|
|
81
|
+
|
|
82
|
+
# ============================================
|
|
83
|
+
# Base Stage – Node + pnpm + Workspace setup
|
|
84
|
+
# ============================================
|
|
85
|
+
FROM node:\${NODE_VERSION}-alpine AS base
|
|
86
|
+
|
|
87
|
+
# Install pnpm
|
|
88
|
+
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
89
|
+
|
|
90
|
+
# Essentials
|
|
91
|
+
RUN apk add --no-cache curl bash postgresql-client
|
|
92
|
+
|
|
93
|
+
WORKDIR /app
|
|
94
|
+
|
|
95
|
+
# Workspace files
|
|
96
|
+
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./
|
|
97
|
+
COPY turbo.json tsconfig.json ./
|
|
98
|
+
COPY packages/ packages/
|
|
99
|
+
|
|
100
|
+
# App-level package.json
|
|
101
|
+
${backends.map((name) => `COPY apps/backend/${name}/package.json apps/backend/${name}/`).join('\n')}
|
|
102
|
+
${frontends.map((name) => `COPY apps/frontend/${name}/package.json apps/frontend/${name}/`).join('\n')}
|
|
103
|
+
|
|
104
|
+
# ============================================
|
|
105
|
+
# Dependencies Stage
|
|
106
|
+
# ============================================
|
|
107
|
+
FROM base AS dependencies
|
|
108
|
+
|
|
109
|
+
RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store \\
|
|
110
|
+
pnpm install --frozen-lockfile
|
|
111
|
+
|
|
112
|
+
# ============================================
|
|
113
|
+
# Shared Libraries + Prisma Clients
|
|
114
|
+
# ============================================
|
|
115
|
+
FROM dependencies AS shared-libs
|
|
116
|
+
|
|
117
|
+
ARG DATABASE_URL
|
|
118
|
+
ENV DATABASE_URL=\${DATABASE_URL}
|
|
119
|
+
|
|
120
|
+
# Prisma schema
|
|
121
|
+
COPY packages/database/prisma/ packages/database/prisma/
|
|
122
|
+
|
|
123
|
+
# Generate Prisma client
|
|
124
|
+
RUN echo "🧩 Generating Prisma client..." && \\
|
|
125
|
+
cd packages/database && pnpm prisma generate
|
|
126
|
+
|
|
127
|
+
# Build shared packages
|
|
128
|
+
RUN --mount=type=cache,id=turbo,target=/app/node_modules/.cache \\
|
|
129
|
+
pnpm turbo run build --filter="@repo/shared-*"
|
|
130
|
+
|
|
131
|
+
# ============================================
|
|
132
|
+
# Common Build Base
|
|
133
|
+
# ============================================
|
|
134
|
+
FROM shared-libs AS build-base
|
|
135
|
+
|
|
136
|
+
# Source
|
|
137
|
+
COPY apps/ apps/
|
|
138
|
+
|
|
139
|
+
ENV NODE_ENV=production
|
|
140
|
+
ENV NEXT_TELEMETRY_DISABLED=1
|
|
141
|
+
|
|
142
|
+
${backendTargets}
|
|
143
|
+
|
|
144
|
+
${frontendTargets}
|
|
145
|
+
`;
|
|
146
|
+
|
|
147
|
+
await fs.writeFile(path.join(targetDir, 'Dockerfile'), dockerfile);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function createDockerComposeLocal(targetDir: string): Promise<void> {
|
|
151
|
+
const dockerComposeLocal = `version: '3.8'
|
|
152
|
+
|
|
153
|
+
services:
|
|
154
|
+
# PostgreSQL Database
|
|
155
|
+
postgres:
|
|
156
|
+
image: postgres:15-alpine
|
|
157
|
+
container_name: \${PROJECT_NAME:-app}-postgres
|
|
158
|
+
restart: unless-stopped
|
|
159
|
+
environment:
|
|
160
|
+
POSTGRES_USER: \${DATABASE_USER:-postgres}
|
|
161
|
+
POSTGRES_PASSWORD: \${DATABASE_PASSWORD:-postgres}
|
|
162
|
+
POSTGRES_DB: \${DATABASE_NAME:-app_db}
|
|
163
|
+
ports:
|
|
164
|
+
- '\${DATABASE_PORT:-5432}:5432'
|
|
165
|
+
volumes:
|
|
166
|
+
- postgres_data:/var/lib/postgresql/data
|
|
167
|
+
healthcheck:
|
|
168
|
+
test: ['CMD-SHELL', 'pg_isready -U \${DATABASE_USER:-postgres}']
|
|
169
|
+
interval: 10s
|
|
170
|
+
timeout: 5s
|
|
171
|
+
retries: 5
|
|
172
|
+
networks:
|
|
173
|
+
- app-network
|
|
174
|
+
|
|
175
|
+
# Redis Cache
|
|
176
|
+
redis:
|
|
177
|
+
image: redis:7-alpine
|
|
178
|
+
container_name: \${PROJECT_NAME:-app}-redis
|
|
179
|
+
restart: unless-stopped
|
|
180
|
+
ports:
|
|
181
|
+
- '\${REDIS_PORT:-6379}:6379'
|
|
182
|
+
volumes:
|
|
183
|
+
- redis_data:/data
|
|
184
|
+
healthcheck:
|
|
185
|
+
test: ['CMD', 'redis-cli', 'ping']
|
|
186
|
+
interval: 10s
|
|
187
|
+
timeout: 5s
|
|
188
|
+
retries: 5
|
|
189
|
+
networks:
|
|
190
|
+
- app-network
|
|
191
|
+
|
|
192
|
+
networks:
|
|
193
|
+
app-network:
|
|
194
|
+
driver: bridge
|
|
195
|
+
|
|
196
|
+
volumes:
|
|
197
|
+
postgres_data:
|
|
198
|
+
name: \${PROJECT_NAME:-app}-postgres-data
|
|
199
|
+
redis_data:
|
|
200
|
+
name: \${PROJECT_NAME:-app}-redis-data
|
|
201
|
+
`;
|
|
202
|
+
|
|
203
|
+
await fs.writeFile(
|
|
204
|
+
path.join(targetDir, 'docker-compose.local.yml'),
|
|
205
|
+
dockerComposeLocal
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function createDockerComposeDev(
|
|
210
|
+
targetDir: string,
|
|
211
|
+
backends: string[],
|
|
212
|
+
frontends: string[]
|
|
213
|
+
): Promise<void> {
|
|
214
|
+
const backendServices = backends
|
|
215
|
+
.map(
|
|
216
|
+
(name, index) => ` # ${name} Backend
|
|
217
|
+
${name}:
|
|
218
|
+
build:
|
|
219
|
+
context: .
|
|
220
|
+
dockerfile: Dockerfile
|
|
221
|
+
target: ${name}-development
|
|
222
|
+
container_name: \${PROJECT_NAME:-app}-${name}
|
|
223
|
+
restart: unless-stopped
|
|
224
|
+
env_file: .env
|
|
225
|
+
environment:
|
|
226
|
+
NODE_ENV: development
|
|
227
|
+
DATABASE_URL: postgresql://\${DATABASE_USER:-postgres}:\${DATABASE_PASSWORD:-postgres}@postgres:5432/\${DATABASE_NAME:-app_db}
|
|
228
|
+
REDIS_URL: redis://redis:6379
|
|
229
|
+
volumes:
|
|
230
|
+
- ./apps/backend/${name}:/app/apps/backend/${name}
|
|
231
|
+
- ./packages:/app/packages
|
|
232
|
+
- /app/node_modules
|
|
233
|
+
- /app/apps/backend/${name}/node_modules
|
|
234
|
+
depends_on:
|
|
235
|
+
postgres:
|
|
236
|
+
condition: service_healthy
|
|
237
|
+
redis:
|
|
238
|
+
condition: service_healthy
|
|
239
|
+
ports:
|
|
240
|
+
- '\${${name.toUpperCase()}_PORT:-${4000 + index}}:3000'
|
|
241
|
+
networks:
|
|
242
|
+
- app-network`
|
|
243
|
+
)
|
|
244
|
+
.join('\n\n');
|
|
245
|
+
|
|
246
|
+
const frontendServices = frontends
|
|
247
|
+
.map(
|
|
248
|
+
(name, index) => ` # ${name} Frontend
|
|
249
|
+
${name}:
|
|
250
|
+
build:
|
|
251
|
+
context: .
|
|
252
|
+
dockerfile: Dockerfile
|
|
253
|
+
target: ${name}-production
|
|
254
|
+
container_name: \${PROJECT_NAME:-app}-${name}
|
|
255
|
+
restart: unless-stopped
|
|
256
|
+
env_file: .env
|
|
257
|
+
environment:
|
|
258
|
+
NODE_ENV: development
|
|
259
|
+
NEXT_PUBLIC_API_URL: \${NEXT_PUBLIC_API_URL:-http://localhost:4000}
|
|
260
|
+
volumes:
|
|
261
|
+
- ./apps/frontend/${name}:/app/apps/frontend/${name}
|
|
262
|
+
- ./packages:/app/packages
|
|
263
|
+
- /app/node_modules
|
|
264
|
+
- /app/apps/frontend/${name}/node_modules
|
|
265
|
+
- /app/apps/frontend/${name}/.next
|
|
266
|
+
ports:
|
|
267
|
+
- '\${${name.toUpperCase()}_PORT:-${3000 + index}}:${3000 + index}'
|
|
268
|
+
networks:
|
|
269
|
+
- app-network`
|
|
270
|
+
)
|
|
271
|
+
.join('\n\n');
|
|
272
|
+
|
|
273
|
+
const dockerComposeDev = `version: '3.8'
|
|
274
|
+
|
|
275
|
+
services:
|
|
276
|
+
# PostgreSQL Database
|
|
277
|
+
postgres:
|
|
278
|
+
image: postgres:15-alpine
|
|
279
|
+
container_name: \${PROJECT_NAME:-app}-postgres-dev
|
|
280
|
+
restart: unless-stopped
|
|
281
|
+
environment:
|
|
282
|
+
POSTGRES_USER: \${DATABASE_USER:-postgres}
|
|
283
|
+
POSTGRES_PASSWORD: \${DATABASE_PASSWORD:-postgres}
|
|
284
|
+
POSTGRES_DB: \${DATABASE_NAME:-app_db}
|
|
285
|
+
ports:
|
|
286
|
+
- '\${DATABASE_PORT:-5432}:5432'
|
|
287
|
+
volumes:
|
|
288
|
+
- postgres_dev_data:/var/lib/postgresql/data
|
|
289
|
+
healthcheck:
|
|
290
|
+
test: ['CMD-SHELL', 'pg_isready -U \${DATABASE_USER:-postgres}']
|
|
291
|
+
interval: 10s
|
|
292
|
+
timeout: 5s
|
|
293
|
+
retries: 5
|
|
294
|
+
networks:
|
|
295
|
+
- app-network
|
|
296
|
+
|
|
297
|
+
# Redis Cache
|
|
298
|
+
redis:
|
|
299
|
+
image: redis:7-alpine
|
|
300
|
+
container_name: \${PROJECT_NAME:-app}-redis-dev
|
|
301
|
+
restart: unless-stopped
|
|
302
|
+
ports:
|
|
303
|
+
- '\${REDIS_PORT:-6379}:6379'
|
|
304
|
+
healthcheck:
|
|
305
|
+
test: ['CMD', 'redis-cli', 'ping']
|
|
306
|
+
interval: 10s
|
|
307
|
+
timeout: 5s
|
|
308
|
+
retries: 5
|
|
309
|
+
networks:
|
|
310
|
+
- app-network
|
|
311
|
+
|
|
312
|
+
${backendServices}
|
|
313
|
+
|
|
314
|
+
${frontendServices}
|
|
315
|
+
|
|
316
|
+
networks:
|
|
317
|
+
app-network:
|
|
318
|
+
driver: bridge
|
|
319
|
+
|
|
320
|
+
volumes:
|
|
321
|
+
postgres_dev_data:
|
|
322
|
+
name: \${PROJECT_NAME:-app}-postgres-dev-data
|
|
323
|
+
redis_dev_data:
|
|
324
|
+
name: \${PROJECT_NAME:-app}-redis-dev-data
|
|
325
|
+
`;
|
|
326
|
+
|
|
327
|
+
await fs.writeFile(
|
|
328
|
+
path.join(targetDir, 'docker-compose.dev.yml'),
|
|
329
|
+
dockerComposeDev
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
async function createDockerComposeProd(
|
|
334
|
+
targetDir: string,
|
|
335
|
+
backends: string[],
|
|
336
|
+
frontends: string[]
|
|
337
|
+
): Promise<void> {
|
|
338
|
+
const backendServices = backends
|
|
339
|
+
.map(
|
|
340
|
+
(name, index) => ` # ${name} Backend
|
|
341
|
+
${name}:
|
|
342
|
+
image: ghcr.io/\${GITHUB_REPOSITORY:-your-org/your-repo}/${name}:\${RELEASE_TAG:-latest}
|
|
343
|
+
container_name: \${PROJECT_NAME:-app}-${name}
|
|
344
|
+
restart: unless-stopped
|
|
345
|
+
env_file: .env
|
|
346
|
+
environment:
|
|
347
|
+
NODE_ENV: production
|
|
348
|
+
DATABASE_URL: postgresql://\${DATABASE_USER:-postgres}:\${DATABASE_PASSWORD:-postgres}@postgres:5432/\${DATABASE_NAME:-app_db}
|
|
349
|
+
REDIS_URL: redis://redis:6379
|
|
350
|
+
PORT: 3000
|
|
351
|
+
depends_on:
|
|
352
|
+
postgres:
|
|
353
|
+
condition: service_healthy
|
|
354
|
+
redis:
|
|
355
|
+
condition: service_healthy
|
|
356
|
+
ports:
|
|
357
|
+
- '\${${name.toUpperCase()}_PORT:-${4000 + index}}:3000'
|
|
358
|
+
healthcheck:
|
|
359
|
+
test: ['CMD-SHELL', 'wget -qO- http://127.0.0.1:3000/health || exit 1']
|
|
360
|
+
interval: 15s
|
|
361
|
+
timeout: 5s
|
|
362
|
+
retries: 10
|
|
363
|
+
start_period: 90s
|
|
364
|
+
networks:
|
|
365
|
+
- app-network`
|
|
366
|
+
)
|
|
367
|
+
.join('\n\n');
|
|
368
|
+
|
|
369
|
+
const frontendServices = frontends
|
|
370
|
+
.map(
|
|
371
|
+
(name, index) => ` # ${name} Frontend
|
|
372
|
+
${name}:
|
|
373
|
+
image: ghcr.io/\${GITHUB_REPOSITORY:-your-org/your-repo}/${name}:\${RELEASE_TAG:-latest}
|
|
374
|
+
container_name: \${PROJECT_NAME:-app}-${name}
|
|
375
|
+
restart: unless-stopped
|
|
376
|
+
env_file: .env
|
|
377
|
+
environment:
|
|
378
|
+
NODE_ENV: production
|
|
379
|
+
HOST: 0.0.0.0
|
|
380
|
+
PORT: ${3000 + index}
|
|
381
|
+
NEXT_PUBLIC_API_URL: \${NEXT_PUBLIC_API_URL:-http://localhost:4000}
|
|
382
|
+
ports:
|
|
383
|
+
- '\${${name.toUpperCase()}_PORT:-${3000 + index}}:${3000 + index}'
|
|
384
|
+
healthcheck:
|
|
385
|
+
test: ['CMD-SHELL', 'wget -qO- http://127.0.0.1:${3000 + index}/ || exit 1']
|
|
386
|
+
interval: 20s
|
|
387
|
+
timeout: 5s
|
|
388
|
+
retries: 6
|
|
389
|
+
start_period: 60s
|
|
390
|
+
networks:
|
|
391
|
+
- app-network`
|
|
392
|
+
)
|
|
393
|
+
.join('\n\n');
|
|
394
|
+
|
|
395
|
+
const dockerComposeProd = `name: \${PROJECT_NAME:-app}-deploy
|
|
396
|
+
|
|
397
|
+
services:
|
|
398
|
+
# PostgreSQL Database
|
|
399
|
+
postgres:
|
|
400
|
+
image: postgres:15-alpine
|
|
401
|
+
restart: unless-stopped
|
|
402
|
+
env_file: .env
|
|
403
|
+
environment:
|
|
404
|
+
POSTGRES_USER: \${DATABASE_USER:-postgres}
|
|
405
|
+
POSTGRES_PASSWORD: \${DATABASE_PASSWORD:-postgres}
|
|
406
|
+
POSTGRES_DB: \${DATABASE_NAME:-app_db}
|
|
407
|
+
volumes:
|
|
408
|
+
- postgres_data:/var/lib/postgresql/data
|
|
409
|
+
healthcheck:
|
|
410
|
+
test: ['CMD-SHELL', 'pg_isready -U \${DATABASE_USER:-postgres} -d \${DATABASE_NAME:-app_db}']
|
|
411
|
+
interval: 10s
|
|
412
|
+
timeout: 5s
|
|
413
|
+
retries: 5
|
|
414
|
+
networks:
|
|
415
|
+
- app-network
|
|
416
|
+
|
|
417
|
+
# Redis Cache
|
|
418
|
+
redis:
|
|
419
|
+
image: redis:7-alpine
|
|
420
|
+
restart: unless-stopped
|
|
421
|
+
healthcheck:
|
|
422
|
+
test: ['CMD', 'redis-cli', 'ping']
|
|
423
|
+
interval: 10s
|
|
424
|
+
timeout: 5s
|
|
425
|
+
retries: 5
|
|
426
|
+
networks:
|
|
427
|
+
- app-network
|
|
428
|
+
|
|
429
|
+
${backendServices}
|
|
430
|
+
|
|
431
|
+
${frontendServices}
|
|
432
|
+
|
|
433
|
+
networks:
|
|
434
|
+
app-network:
|
|
435
|
+
driver: bridge
|
|
436
|
+
|
|
437
|
+
volumes:
|
|
438
|
+
postgres_data:
|
|
439
|
+
external: true
|
|
440
|
+
name: \${PROJECT_NAME:-app}-postgres-prod-data
|
|
441
|
+
redis_data:
|
|
442
|
+
name: \${PROJECT_NAME:-app}-redis-prod-data
|
|
443
|
+
`;
|
|
444
|
+
|
|
445
|
+
await fs.writeFile(
|
|
446
|
+
path.join(targetDir, 'docker-compose.prod.yml'),
|
|
447
|
+
dockerComposeProd
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
async function createDockerIgnore(targetDir: string): Promise<void> {
|
|
452
|
+
const dockerignore = `# Dependencies
|
|
453
|
+
node_modules
|
|
454
|
+
npm-debug.log*
|
|
455
|
+
yarn-debug.log*
|
|
456
|
+
yarn-error.log*
|
|
457
|
+
pnpm-debug.log*
|
|
458
|
+
|
|
459
|
+
# Build outputs
|
|
460
|
+
dist
|
|
461
|
+
.next
|
|
462
|
+
out
|
|
463
|
+
build
|
|
464
|
+
*.tsbuildinfo
|
|
465
|
+
|
|
466
|
+
# Testing
|
|
467
|
+
coverage
|
|
468
|
+
.nyc_output
|
|
469
|
+
|
|
470
|
+
# Environment
|
|
471
|
+
.env
|
|
472
|
+
.env.*
|
|
473
|
+
!.env.example
|
|
474
|
+
|
|
475
|
+
# Misc
|
|
476
|
+
.DS_Store
|
|
477
|
+
*.pem
|
|
478
|
+
.vscode
|
|
479
|
+
.idea
|
|
480
|
+
|
|
481
|
+
# Git
|
|
482
|
+
.git
|
|
483
|
+
.gitignore
|
|
484
|
+
|
|
485
|
+
# Docker
|
|
486
|
+
Dockerfile*
|
|
487
|
+
docker-compose*.yml
|
|
488
|
+
.dockerignore
|
|
489
|
+
|
|
490
|
+
# Documentation
|
|
491
|
+
*.md
|
|
492
|
+
!README.md
|
|
493
|
+
|
|
494
|
+
# CI/CD
|
|
495
|
+
.github
|
|
496
|
+
.gitlab-ci.yml
|
|
497
|
+
|
|
498
|
+
# Turborepo
|
|
499
|
+
.turbo
|
|
500
|
+
`;
|
|
501
|
+
|
|
502
|
+
await fs.writeFile(path.join(targetDir, '.dockerignore'), dockerignore);
|
|
503
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { ProjectConfig } from '../scaffold';
|
|
4
|
+
|
|
5
|
+
export async function createDocumentation(config: ProjectConfig): Promise<void> {
|
|
6
|
+
const { targetDir, backends, frontends } = config;
|
|
7
|
+
const docsDir = path.join(targetDir, 'docs');
|
|
8
|
+
|
|
9
|
+
await fs.ensureDir(docsDir);
|
|
10
|
+
|
|
11
|
+
await createEnvGuide(docsDir, backends, frontends);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function createEnvGuide(
|
|
15
|
+
docsDir: string,
|
|
16
|
+
backends: string[],
|
|
17
|
+
frontends: string[]
|
|
18
|
+
): Promise<void> {
|
|
19
|
+
const content = `# Environment Variables
|
|
20
|
+
|
|
21
|
+
## Required
|
|
22
|
+
|
|
23
|
+
\`\`\`bash
|
|
24
|
+
# Database
|
|
25
|
+
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/app_db"
|
|
26
|
+
|
|
27
|
+
# Redis
|
|
28
|
+
REDIS_URL="redis://localhost:6379"
|
|
29
|
+
\`\`\`
|
|
30
|
+
|
|
31
|
+
## Optional
|
|
32
|
+
|
|
33
|
+
\`\`\`bash
|
|
34
|
+
# JWT
|
|
35
|
+
JWT_SECRET="your-secret-key"
|
|
36
|
+
JWT_EXPIRES_IN="7d"
|
|
37
|
+
|
|
38
|
+
# CORS
|
|
39
|
+
CORS_ORIGINS="http://localhost:3000"
|
|
40
|
+
|
|
41
|
+
# Email (for CI/CD notifications)
|
|
42
|
+
SMTP_SERVER=smtp.gmail.com
|
|
43
|
+
SMTP_PORT=587
|
|
44
|
+
SMTP_USERNAME=your-email@gmail.com
|
|
45
|
+
SMTP_PASSWORD=your-app-password
|
|
46
|
+
\`\`\`
|
|
47
|
+
|
|
48
|
+
## Docker
|
|
49
|
+
|
|
50
|
+
\`\`\`bash
|
|
51
|
+
NODE_VERSION=20
|
|
52
|
+
REGISTRY=ghcr.io
|
|
53
|
+
IMAGE_TAG=latest
|
|
54
|
+
\`\`\`
|
|
55
|
+
|
|
56
|
+
## GitHub Actions Secrets
|
|
57
|
+
|
|
58
|
+
| Secret | Description |
|
|
59
|
+
|--------|-------------|
|
|
60
|
+
| \`SSH_HOST\` | Server IP/domain |
|
|
61
|
+
| \`SSH_USER\` | SSH username |
|
|
62
|
+
| \`SSH_KEY\` | Private SSH key |
|
|
63
|
+
| \`DEPLOY_PATH\` | Deployment directory |
|
|
64
|
+
|
|
65
|
+
## Service Ports
|
|
66
|
+
|
|
67
|
+
| Service | Port |
|
|
68
|
+
|---------|------|
|
|
69
|
+
${backends.map((name, i) => `| ${name} | ${4000 + i} |`).join('\n')}
|
|
70
|
+
${frontends.map((name, i) => `| ${name} | ${3000 + i} |`).join('\n')}
|
|
71
|
+
`;
|
|
72
|
+
|
|
73
|
+
await fs.writeFile(path.join(docsDir, 'ENVIRONMENT.md'), content);
|
|
74
|
+
}
|