boiling-fullstack 0.1.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.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +140 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +313 -0
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.js +33 -0
  7. package/dist/scaffolder.d.ts +2 -0
  8. package/dist/scaffolder.js +89 -0
  9. package/dist/types.d.ts +28 -0
  10. package/dist/types.js +2 -0
  11. package/dist/utils/shell.d.ts +6 -0
  12. package/dist/utils/shell.js +53 -0
  13. package/dist/utils/template.d.ts +2 -0
  14. package/dist/utils/template.js +51 -0
  15. package/dist/utils/validation.d.ts +6 -0
  16. package/dist/utils/validation.js +59 -0
  17. package/package.json +40 -0
  18. package/templates/backend/nestjs/.dockerignore +3 -0
  19. package/templates/backend/nestjs/.eslintrc.cjs.ejs +19 -0
  20. package/templates/backend/nestjs/.prettierrc +6 -0
  21. package/templates/backend/nestjs/Dockerfile +44 -0
  22. package/templates/backend/nestjs/nest-cli.json +8 -0
  23. package/templates/backend/nestjs/package.json.ejs +52 -0
  24. package/templates/backend/nestjs/src/app.controller.ts +17 -0
  25. package/templates/backend/nestjs/src/app.module.ts.ejs +20 -0
  26. package/templates/backend/nestjs/src/app.service.ts +4 -0
  27. package/templates/backend/nestjs/src/auth/auth.controller.ts +19 -0
  28. package/templates/backend/nestjs/src/auth/auth.guard.ts +5 -0
  29. package/templates/backend/nestjs/src/auth/auth.module.ts +28 -0
  30. package/templates/backend/nestjs/src/auth/auth.service.ts +50 -0
  31. package/templates/backend/nestjs/src/auth/dto/login.dto.ts +10 -0
  32. package/templates/backend/nestjs/src/auth/dto/register.dto.ts +10 -0
  33. package/templates/backend/nestjs/src/auth/entities/user.entity.ts +25 -0
  34. package/templates/backend/nestjs/src/auth/jwt.strategy.ts +19 -0
  35. package/templates/backend/nestjs/src/config/data-source.ts +9 -0
  36. package/templates/backend/nestjs/src/config/typeorm.config.ts.ejs +8 -0
  37. package/templates/backend/nestjs/src/main.ts.ejs +20 -0
  38. package/templates/backend/nestjs/src/migrations/.gitkeep +0 -0
  39. package/templates/backend/nestjs/tsconfig.build.json +4 -0
  40. package/templates/backend/nestjs/tsconfig.json +21 -0
  41. package/templates/frontend/nuxt/.dockerignore +5 -0
  42. package/templates/frontend/nuxt/.eslintrc.cjs.ejs +5 -0
  43. package/templates/frontend/nuxt/.prettierrc +6 -0
  44. package/templates/frontend/nuxt/Dockerfile +38 -0
  45. package/templates/frontend/nuxt/app/app.vue +3 -0
  46. package/templates/frontend/nuxt/app/pages/index.vue.ejs +25 -0
  47. package/templates/frontend/nuxt/nuxt.config.ts.ejs +19 -0
  48. package/templates/frontend/nuxt/package.json.ejs +25 -0
  49. package/templates/frontend/nuxt/tsconfig.json +3 -0
  50. package/templates/frontend/vue/.dockerignore +3 -0
  51. package/templates/frontend/vue/.eslintrc.cjs.ejs +14 -0
  52. package/templates/frontend/vue/.prettierrc +6 -0
  53. package/templates/frontend/vue/Dockerfile +33 -0
  54. package/templates/frontend/vue/index.html.ejs +13 -0
  55. package/templates/frontend/vue/package.json.ejs +28 -0
  56. package/templates/frontend/vue/src/App.vue.ejs +22 -0
  57. package/templates/frontend/vue/src/main.ts +4 -0
  58. package/templates/frontend/vue/src/vite-env.d.ts +1 -0
  59. package/templates/frontend/vue/tsconfig.json +20 -0
  60. package/templates/frontend/vue/vite.config.ts.ejs +14 -0
  61. package/templates/root/.env.ejs +24 -0
  62. package/templates/root/.env.example.ejs +24 -0
  63. package/templates/root/.env.production.ejs +17 -0
  64. package/templates/root/Makefile.ejs +116 -0
  65. package/templates/root/README.md.ejs +158 -0
  66. package/templates/root/docker-compose.prod.yml.ejs +45 -0
  67. package/templates/root/docker-compose.yml.ejs +77 -0
  68. package/templates/root/gitignore +8 -0
@@ -0,0 +1,6 @@
1
+ {
2
+ "semi": false,
3
+ "singleQuote": true,
4
+ "trailingComma": "all",
5
+ "printWidth": 100
6
+ }
@@ -0,0 +1,38 @@
1
+ # Stage 1: Development
2
+ FROM node:20-alpine AS development
3
+
4
+ WORKDIR /app
5
+
6
+ COPY package*.json ./
7
+ RUN npm install
8
+
9
+ COPY . .
10
+
11
+ EXPOSE 3000 24678
12
+
13
+ CMD ["npm", "run", "dev"]
14
+
15
+ # Stage 2: Build
16
+ FROM node:20-alpine AS build
17
+
18
+ WORKDIR /app
19
+
20
+ COPY package*.json ./
21
+ RUN npm install
22
+
23
+ COPY . .
24
+ RUN npm run build
25
+
26
+ # Stage 3: Production
27
+ FROM node:20-alpine AS production
28
+
29
+ WORKDIR /app
30
+
31
+ COPY --from=build /app/.output /app/.output
32
+
33
+ ENV NUXT_HOST=0.0.0.0
34
+ ENV NUXT_PORT=3000
35
+
36
+ EXPOSE 3000
37
+
38
+ CMD ["node", ".output/server/index.mjs"]
@@ -0,0 +1,3 @@
1
+ <template>
2
+ <NuxtPage />
3
+ </template>
@@ -0,0 +1,25 @@
1
+ <template>
2
+ <div class="container">
3
+ <h1>Welcome to <%= name %></h1>
4
+ <p>Your Nuxt 4 frontend is running!</p>
5
+ </div>
6
+ </template>
7
+
8
+ <script setup lang="ts">
9
+ useHead({
10
+ title: '<%= name %>',
11
+ })
12
+ </script>
13
+
14
+ <<% if (styling === 'sass') { %>style lang="scss" scoped<% } else { %>style scoped<% } %>>
15
+ .container {
16
+ max-width: 800px;
17
+ margin: 0 auto;
18
+ padding: 2rem;
19
+ text-align: center;
20
+ }
21
+
22
+ h1 {
23
+ color: #00dc82;
24
+ }
25
+ </<% if (styling === 'sass') { %>style<% } else { %>style<% } %>>
@@ -0,0 +1,19 @@
1
+ // https://nuxt.com/docs/api/configuration/nuxt-config
2
+ export default defineNuxtConfig({
3
+ compatibilityDate: '2025-01-01',
4
+ devtools: { enabled: true },
5
+ vite: {
6
+ server: {
7
+ hmr: {
8
+ port: 24678,
9
+ },
10
+ },
11
+ },
12
+ devServer: {
13
+ host: '0.0.0.0',
14
+ port: 3000,
15
+ },
16
+ watch: {
17
+ usePolling: true,
18
+ },
19
+ })
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "<%= name %>",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "nuxt build",
7
+ "dev": "nuxt dev",
8
+ "generate": "nuxt generate",
9
+ "preview": "nuxt preview",
10
+ "lint": "eslint .",
11
+ "format": "prettier --write ."
12
+ },
13
+ "dependencies": {
14
+ "nuxt": "^4.0.0",
15
+ "vue": "^3.5.0",
16
+ "vue-router": "^4.4.0"
17
+ },
18
+ "devDependencies": {
19
+ "@nuxt/eslint-config": "^0.6.0",
20
+ "eslint": "^8.57.0",
21
+ "prettier": "^3.4.0",
22
+ "typescript": "^5.7.0"<% if (styling === 'sass') { %>,
23
+ "sass": "^1.80.0"<% } %>
24
+ }
25
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "extends": "./.nuxt/tsconfig.json"
3
+ }
@@ -0,0 +1,3 @@
1
+ node_modules
2
+ dist
3
+ .git
@@ -0,0 +1,14 @@
1
+ module.exports = {
2
+ root: true,
3
+ env: {
4
+ node: true,
5
+ },
6
+ extends: [
7
+ 'plugin:vue/vue3-recommended',
8
+ 'plugin:@typescript-eslint/recommended',
9
+ ],
10
+ parserOptions: {
11
+ parser: '@typescript-eslint/parser',
12
+ },
13
+ rules: {},
14
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "semi": false,
3
+ "singleQuote": true,
4
+ "trailingComma": "all",
5
+ "printWidth": 100
6
+ }
@@ -0,0 +1,33 @@
1
+ # Stage 1: Development
2
+ FROM node:20-alpine AS development
3
+
4
+ WORKDIR /app
5
+
6
+ COPY package*.json ./
7
+ RUN npm install
8
+
9
+ COPY . .
10
+
11
+ EXPOSE 5173
12
+
13
+ CMD ["npm", "run", "dev"]
14
+
15
+ # Stage 2: Build
16
+ FROM node:20-alpine AS build
17
+
18
+ WORKDIR /app
19
+
20
+ COPY package*.json ./
21
+ RUN npm install
22
+
23
+ COPY . .
24
+ RUN npm run build
25
+
26
+ # Stage 3: Production
27
+ FROM nginx:alpine AS production
28
+
29
+ COPY --from=build /app/dist /usr/share/nginx/html
30
+
31
+ EXPOSE 80
32
+
33
+ CMD ["nginx", "-g", "daemon off;"]
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title><%= name %></title>
8
+ </head>
9
+ <body>
10
+ <div id="app"></div>
11
+ <script type="module" src="/src/main.ts"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "<%= name %>",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vue-tsc && vite build",
9
+ "preview": "vite preview",
10
+ "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts",
11
+ "format": "prettier --write ."
12
+ },
13
+ "dependencies": {
14
+ "vue": "^3.5.0"
15
+ },
16
+ "devDependencies": {
17
+ "@vitejs/plugin-vue": "^5.2.0",
18
+ "eslint": "^8.57.0",
19
+ "eslint-plugin-vue": "^9.30.0",
20
+ "@typescript-eslint/eslint-plugin": "^7.0.0",
21
+ "@typescript-eslint/parser": "^7.0.0",
22
+ "prettier": "^3.4.0",
23
+ "typescript": "^5.7.0",
24
+ "vite": "^6.0.0",
25
+ "vue-tsc": "^2.1.0"<% if (styling === 'sass') { %>,
26
+ "sass": "^1.80.0"<% } %>
27
+ }
28
+ }
@@ -0,0 +1,22 @@
1
+ <template>
2
+ <div class="container">
3
+ <h1>Welcome to <%= name %></h1>
4
+ <p>Your Vue 3 + Vite frontend is running!</p>
5
+ </div>
6
+ </template>
7
+
8
+ <script setup lang="ts">
9
+ </script>
10
+
11
+ <<% if (styling === 'sass') { %>style lang="scss" scoped<% } else { %>style scoped<% } %>>
12
+ .container {
13
+ max-width: 800px;
14
+ margin: 0 auto;
15
+ padding: 2rem;
16
+ text-align: center;
17
+ }
18
+
19
+ h1 {
20
+ color: #42b883;
21
+ }
22
+ </<% if (styling === 'sass') { %>style<% } else { %>style<% } %>>
@@ -0,0 +1,4 @@
1
+ import { createApp } from 'vue'
2
+ import App from './App.vue'
3
+
4
+ createApp(App).mount('#app')
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "module": "ESNext",
6
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
7
+ "skipLibCheck": true,
8
+ "moduleResolution": "bundler",
9
+ "allowImportingTsExtensions": true,
10
+ "isolatedModules": true,
11
+ "moduleDetection": "force",
12
+ "noEmit": true,
13
+ "jsx": "preserve",
14
+ "strict": true,
15
+ "noUnusedLocals": true,
16
+ "noUnusedParameters": true,
17
+ "noFallthroughCasesInSwitch": true
18
+ },
19
+ "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
20
+ }
@@ -0,0 +1,14 @@
1
+ import { defineConfig } from 'vite'
2
+ import vue from '@vitejs/plugin-vue'
3
+
4
+ // https://vite.dev/config/
5
+ export default defineConfig({
6
+ plugins: [vue()],
7
+ server: {
8
+ host: '0.0.0.0',
9
+ port: 5173,
10
+ watch: {
11
+ usePolling: true,
12
+ },
13
+ },
14
+ })
@@ -0,0 +1,24 @@
1
+ # Development environment
2
+
3
+ # Database
4
+ DB_NAME=<%= dbName %>
5
+ DB_USER=<%= dbUser %>
6
+ DB_PASSWORD=<%= dbPassword %>
7
+
8
+ # Backend
9
+ BACKEND_PORT=<%= backendPort %>
10
+ JWT_SECRET=<%= jwtSecret || 'change-me-in-production' %>
11
+
12
+ # Frontend ports
13
+ <% for (const fe of frontends) { -%>
14
+ <%= fe.name.toUpperCase().replace(/-/g, '_') %>_PORT=<%= fe.port %>
15
+ <% } -%>
16
+ <%_ if (dbAdmin && dbAdmin.tool === 'pgadmin') { -%>
17
+ # pgAdmin
18
+ PGADMIN_EMAIL=<%= dbAdmin.email %>
19
+ PGADMIN_PASSWORD=<%= dbAdmin.password %>
20
+ PGADMIN_PORT=<%= dbAdmin.port %>
21
+ <%_ } else if (dbAdmin && dbAdmin.tool === 'adminer') { -%>
22
+ # Adminer
23
+ ADMINER_PORT=<%= dbAdmin.port %>
24
+ <%_ } -%>
@@ -0,0 +1,24 @@
1
+ # Copy this file to .env and fill in the values
2
+
3
+ # Database
4
+ DB_NAME=mydb
5
+ DB_USER=myuser
6
+ DB_PASSWORD=changeme
7
+
8
+ # Backend
9
+ BACKEND_PORT=<%= backendPort %>
10
+ JWT_SECRET=change-me-in-production
11
+
12
+ # Frontend ports
13
+ <% for (const fe of frontends) { -%>
14
+ <%= fe.name.toUpperCase().replace(/-/g, '_') %>_PORT=<%= fe.port %>
15
+ <% } -%>
16
+ <%_ if (dbAdmin && dbAdmin.tool === 'pgadmin') { -%>
17
+ # pgAdmin
18
+ PGADMIN_EMAIL=admin@example.com
19
+ PGADMIN_PASSWORD=changeme
20
+ PGADMIN_PORT=<%= dbAdmin.port %>
21
+ <%_ } else if (dbAdmin && dbAdmin.tool === 'adminer') { -%>
22
+ # Adminer
23
+ ADMINER_PORT=<%= dbAdmin.port %>
24
+ <%_ } -%>
@@ -0,0 +1,17 @@
1
+ # Production environment
2
+ # IMPORTANT: Change all default values before deploying!
3
+
4
+ # Database
5
+ DB_NAME=<%= dbName %>
6
+ DB_USER=<%= dbUser %>
7
+ DB_PASSWORD=change-me-use-strong-password
8
+
9
+ # Backend
10
+ BACKEND_PORT=<%= backendPort %>
11
+ JWT_SECRET=generate-a-secure-random-string-min-32-chars
12
+ NODE_ENV=production
13
+
14
+ # Frontend ports
15
+ <% for (const fe of frontends) { -%>
16
+ <%= fe.name.toUpperCase().replace(/-/g, '_') %>_PORT=<%= fe.port %>
17
+ <% } -%>
@@ -0,0 +1,116 @@
1
+ .PHONY: up down build logs restart clean up-prod down-prod build-prod logs-prod restart-prod clean-prod db-logs db-shell lint-back format-back migration-generate migration-run migration-revert migration-run-prod<%_ if (dbAdmin && dbAdmin.tool === 'pgadmin') { %> pgadmin-logs pgadmin-restart<%_ } else if (dbAdmin && dbAdmin.tool === 'adminer') { %> adminer-logs adminer-restart<%_ } %>
2
+
3
+ # --- Development ---
4
+
5
+ # Start all services (dev)
6
+ up:
7
+ docker compose up -d
8
+
9
+ # Stop all services (dev)
10
+ down:
11
+ docker compose down
12
+
13
+ # Build all services (dev)
14
+ build:
15
+ docker compose build
16
+
17
+ # View logs (dev)
18
+ logs:
19
+ docker compose logs -f
20
+
21
+ # Restart all services (dev)
22
+ restart:
23
+ docker compose restart
24
+
25
+ # Clean everything (dev, volumes included)
26
+ clean:
27
+ docker compose down -v
28
+
29
+ # --- Production ---
30
+
31
+ # Start all services (prod)
32
+ up-prod:
33
+ docker compose -f docker-compose.prod.yml --env-file .env.production up -d
34
+
35
+ # Stop all services (prod)
36
+ down-prod:
37
+ docker compose -f docker-compose.prod.yml --env-file .env.production down
38
+
39
+ # Build all services (prod)
40
+ build-prod:
41
+ docker compose -f docker-compose.prod.yml --env-file .env.production build
42
+
43
+ # View logs (prod)
44
+ logs-prod:
45
+ docker compose -f docker-compose.prod.yml --env-file .env.production logs -f
46
+
47
+ # Restart all services (prod)
48
+ restart-prod:
49
+ docker compose -f docker-compose.prod.yml --env-file .env.production restart
50
+
51
+ # Clean everything (prod, volumes included)
52
+ clean-prod:
53
+ docker compose -f docker-compose.prod.yml --env-file .env.production down -v
54
+
55
+ # --- Service commands ---
56
+
57
+ # Database commands
58
+ db-logs:
59
+ docker compose logs -f postgres
60
+
61
+ db-shell:
62
+ docker compose exec postgres psql -U <%= dbUser %> -d <%= dbName %>
63
+
64
+ # Backend commands
65
+ backend-logs:
66
+ docker compose logs -f backend
67
+
68
+ backend-shell:
69
+ docker compose exec backend sh
70
+
71
+ lint-back:
72
+ docker compose exec backend npm run lint
73
+
74
+ format-back:
75
+ docker compose exec backend npm run format
76
+
77
+ # --- Migrations ---
78
+
79
+ # Generate a migration (usage: make migration-generate name=AddPostTable)
80
+ migration-generate:
81
+ docker compose exec backend npm run migration:generate -- src/migrations/$(name)
82
+
83
+ # Run pending migrations (dev)
84
+ migration-run:
85
+ docker compose exec backend npm run migration:run
86
+
87
+ # Revert last migration (dev)
88
+ migration-revert:
89
+ docker compose exec backend npm run migration:revert
90
+
91
+ # Run pending migrations (prod)
92
+ migration-run-prod:
93
+ docker compose -f docker-compose.prod.yml --env-file .env.production run --rm backend npx typeorm migration:run -d dist/config/data-source.js
94
+ <%_ for (const fe of frontends) { %>
95
+ # <%= fe.name %> commands
96
+ <%= fe.name %>-logs:
97
+ docker compose logs -f <%= fe.name %>
98
+
99
+ <%= fe.name %>-shell:
100
+ docker compose exec <%= fe.name %> sh
101
+ <%_ } %>
102
+ <%_ if (dbAdmin && dbAdmin.tool === 'pgadmin') { %>
103
+ # pgAdmin commands
104
+ pgadmin-logs:
105
+ docker compose logs -f pgadmin
106
+
107
+ pgadmin-restart:
108
+ docker compose restart pgadmin
109
+ <%_ } else if (dbAdmin && dbAdmin.tool === 'adminer') { %>
110
+ # Adminer commands
111
+ adminer-logs:
112
+ docker compose logs -f adminer
113
+
114
+ adminer-restart:
115
+ docker compose restart adminer
116
+ <%_ } %>
@@ -0,0 +1,158 @@
1
+ # <%= projectName %>
2
+
3
+ Fullstack application scaffolded with [boiling-fullstack](https://github.com/boiling-fullstack).
4
+
5
+ ## Architecture
6
+
7
+ - **Backend**: NestJS + TypeORM + PostgreSQL (port <%= backendPort %>)
8
+ <%_ for (const fe of frontends) { -%>
9
+ - **<%= fe.name %>**: <%= fe.framework === 'nuxt' ? 'Nuxt 4' : 'Vue 3 + Vite' %> (port <%= fe.port %>)
10
+ <%_ } -%>
11
+ <%_ if (dbAdmin && dbAdmin.tool === 'pgadmin') { -%>
12
+ - **Admin DB**: pgAdmin (port <%= dbAdmin.port %>)
13
+ <%_ } else if (dbAdmin && dbAdmin.tool === 'adminer') { -%>
14
+ - **Admin DB**: Adminer (port <%= dbAdmin.port %>)
15
+ <%_ } -%>
16
+ - **Database**: PostgreSQL (`<%= dbName %>`, user: `<%= dbUser %>`)
17
+
18
+ ## Getting started
19
+
20
+ ### Prerequisites
21
+
22
+ - [Docker](https://docs.docker.com/get-docker/) & Docker Compose
23
+ - [Node.js](https://nodejs.org/) >= 20 (for local development)
24
+
25
+ ### Start the project (development)
26
+
27
+ ```bash
28
+ # Start all services
29
+ make up
30
+
31
+ # View logs
32
+ make logs
33
+ ```
34
+
35
+ ### Available services (development)
36
+
37
+ | Service | URL |
38
+ |---------|-----|
39
+ | Backend API | http://localhost:<%= backendPort %> |
40
+ <%_ for (const fe of frontends) { -%>
41
+ | <%= fe.name %> | http://localhost:<%= fe.port %> |
42
+ <%_ } -%>
43
+ <%_ if (dbAdmin && dbAdmin.tool === 'pgadmin') { -%>
44
+ | pgAdmin | http://localhost:<%= dbAdmin.port %> |
45
+ <%_ } else if (dbAdmin && dbAdmin.tool === 'adminer') { -%>
46
+ | Adminer | http://localhost:<%= dbAdmin.port %> |
47
+ <%_ } -%>
48
+ | PostgreSQL | localhost:5432 |
49
+
50
+ ### API Endpoints
51
+
52
+ | Method | Endpoint | Description |
53
+ |--------|----------|-------------|
54
+ | GET | `/` | Status check |
55
+ | GET | `/health` | Healthcheck |
56
+ | POST | `/auth/register` | Register a new user |
57
+ | POST | `/auth/login` | Login |
58
+
59
+ ### Useful commands (development)
60
+
61
+ ```bash
62
+ make up # Start all services
63
+ make down # Stop all services
64
+ make build # Rebuild all services
65
+ make logs # View all logs
66
+ make restart # Restart all services
67
+ make clean # Stop & remove volumes
68
+ ```
69
+
70
+ ## Production
71
+
72
+ ### Setup
73
+
74
+ 1. Edit `.env.production` and replace all placeholder values:
75
+ - `DB_PASSWORD` — use a strong, unique password
76
+ - `JWT_SECRET` — generate a random string (min 32 characters)
77
+
78
+ 2. Build and start:
79
+
80
+ ```bash
81
+ make build-prod
82
+ make up-prod
83
+ ```
84
+
85
+ ### Commands
86
+
87
+ ```bash
88
+ make build-prod # Build production images
89
+ make up-prod # Start all services (prod)
90
+ make down-prod # Stop all services (prod)
91
+ make logs-prod # View all logs (prod)
92
+ make restart-prod # Restart all services (prod)
93
+ make clean-prod # Stop & remove volumes (prod)
94
+ ```
95
+
96
+ ### Dev vs Production comparison
97
+
98
+ | Aspect | Development | Production |
99
+ |--------|-------------|------------|
100
+ | Compose file | `docker-compose.yml` | `docker-compose.prod.yml` |
101
+ | Env file | `.env` | `.env.production` |
102
+ | Build target | `development` | `production` |
103
+ | Restart policy | `unless-stopped` | `always` |
104
+ | Source volumes | Mounted (hot-reload) | None (code built in image) |
105
+ | Backend NODE_ENV | `development` | `production` |
106
+ <%_ for (const fe of frontends) { -%>
107
+ <%_ if (fe.framework === 'vue') { -%>
108
+ | <%= fe.name %> server | Vite dev (port 5173) | nginx (port 80) |
109
+ <%_ } else { -%>
110
+ | <%= fe.name %> server | Nuxt dev (port 3000) | Nuxt production (port 3000) |
111
+ <%_ } -%>
112
+ <%_ } -%>
113
+ | PostgreSQL | Exposed (5432) | Internal only |
114
+ <%_ if (dbAdmin) { -%>
115
+ | DB Admin | <%= dbAdmin.tool === 'pgadmin' ? 'pgAdmin' : 'Adminer' %> | Not included |
116
+ <%_ } -%>
117
+
118
+ ## Migrations
119
+
120
+ TypeORM migrations are used to manage the database schema in production. In development, `synchronize: true` handles schema changes automatically.
121
+
122
+ ### Workflow
123
+
124
+ 1. **Generate** a migration after modifying entities:
125
+
126
+ ```bash
127
+ make migration-generate name=AddPostTable
128
+ ```
129
+
130
+ 2. **Review** the generated file in `backend/src/migrations/`.
131
+
132
+ 3. **Run** the migration:
133
+
134
+ ```bash
135
+ # Development
136
+ make migration-run
137
+
138
+ # Production
139
+ make migration-run-prod
140
+ ```
141
+
142
+ ### Commands
143
+
144
+ | Command | Description |
145
+ |---------|-------------|
146
+ | `make migration-generate name=Name` | Generate a migration from entity changes |
147
+ | `make migration-run` | Run pending migrations (dev) |
148
+ | `make migration-revert` | Revert last migration (dev) |
149
+ | `make migration-run-prod` | Run pending migrations (prod) |
150
+
151
+ ## Environment variables
152
+
153
+ Configuration is managed via two env files at the project root:
154
+
155
+ - **`.env`** — Development environment. Used by `docker-compose.yml`.
156
+ - **`.env.production`** — Production environment. Used by `docker-compose.prod.yml`. Contains placeholder values that **must** be changed before deploying.
157
+
158
+ Both files are listed in `.gitignore` and should never be committed.