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.
- package/LICENSE +21 -0
- package/README.md +140 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +313 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +33 -0
- package/dist/scaffolder.d.ts +2 -0
- package/dist/scaffolder.js +89 -0
- package/dist/types.d.ts +28 -0
- package/dist/types.js +2 -0
- package/dist/utils/shell.d.ts +6 -0
- package/dist/utils/shell.js +53 -0
- package/dist/utils/template.d.ts +2 -0
- package/dist/utils/template.js +51 -0
- package/dist/utils/validation.d.ts +6 -0
- package/dist/utils/validation.js +59 -0
- package/package.json +40 -0
- package/templates/backend/nestjs/.dockerignore +3 -0
- package/templates/backend/nestjs/.eslintrc.cjs.ejs +19 -0
- package/templates/backend/nestjs/.prettierrc +6 -0
- package/templates/backend/nestjs/Dockerfile +44 -0
- package/templates/backend/nestjs/nest-cli.json +8 -0
- package/templates/backend/nestjs/package.json.ejs +52 -0
- package/templates/backend/nestjs/src/app.controller.ts +17 -0
- package/templates/backend/nestjs/src/app.module.ts.ejs +20 -0
- package/templates/backend/nestjs/src/app.service.ts +4 -0
- package/templates/backend/nestjs/src/auth/auth.controller.ts +19 -0
- package/templates/backend/nestjs/src/auth/auth.guard.ts +5 -0
- package/templates/backend/nestjs/src/auth/auth.module.ts +28 -0
- package/templates/backend/nestjs/src/auth/auth.service.ts +50 -0
- package/templates/backend/nestjs/src/auth/dto/login.dto.ts +10 -0
- package/templates/backend/nestjs/src/auth/dto/register.dto.ts +10 -0
- package/templates/backend/nestjs/src/auth/entities/user.entity.ts +25 -0
- package/templates/backend/nestjs/src/auth/jwt.strategy.ts +19 -0
- package/templates/backend/nestjs/src/config/data-source.ts +9 -0
- package/templates/backend/nestjs/src/config/typeorm.config.ts.ejs +8 -0
- package/templates/backend/nestjs/src/main.ts.ejs +20 -0
- package/templates/backend/nestjs/src/migrations/.gitkeep +0 -0
- package/templates/backend/nestjs/tsconfig.build.json +4 -0
- package/templates/backend/nestjs/tsconfig.json +21 -0
- package/templates/frontend/nuxt/.dockerignore +5 -0
- package/templates/frontend/nuxt/.eslintrc.cjs.ejs +5 -0
- package/templates/frontend/nuxt/.prettierrc +6 -0
- package/templates/frontend/nuxt/Dockerfile +38 -0
- package/templates/frontend/nuxt/app/app.vue +3 -0
- package/templates/frontend/nuxt/app/pages/index.vue.ejs +25 -0
- package/templates/frontend/nuxt/nuxt.config.ts.ejs +19 -0
- package/templates/frontend/nuxt/package.json.ejs +25 -0
- package/templates/frontend/nuxt/tsconfig.json +3 -0
- package/templates/frontend/vue/.dockerignore +3 -0
- package/templates/frontend/vue/.eslintrc.cjs.ejs +14 -0
- package/templates/frontend/vue/.prettierrc +6 -0
- package/templates/frontend/vue/Dockerfile +33 -0
- package/templates/frontend/vue/index.html.ejs +13 -0
- package/templates/frontend/vue/package.json.ejs +28 -0
- package/templates/frontend/vue/src/App.vue.ejs +22 -0
- package/templates/frontend/vue/src/main.ts +4 -0
- package/templates/frontend/vue/src/vite-env.d.ts +1 -0
- package/templates/frontend/vue/tsconfig.json +20 -0
- package/templates/frontend/vue/vite.config.ts.ejs +14 -0
- package/templates/root/.env.ejs +24 -0
- package/templates/root/.env.example.ejs +24 -0
- package/templates/root/.env.production.ejs +17 -0
- package/templates/root/Makefile.ejs +116 -0
- package/templates/root/README.md.ejs +158 -0
- package/templates/root/docker-compose.prod.yml.ejs +45 -0
- package/templates/root/docker-compose.yml.ejs +77 -0
- package/templates/root/gitignore +8 -0
|
@@ -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,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,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 @@
|
|
|
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.
|