create-tigra 2.8.0 → 3.0.2
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/README.md +10 -3
- package/bin/create-tigra.js +77 -37
- package/package.json +5 -5
- package/template/_claude/commands/create-server.md +8 -2
- package/template/_claude/rules/client/01-project-structure.md +12 -0
- package/template/_claude/rules/client/03-data-and-state.md +1 -1
- package/template/_claude/rules/client/04-design-system.md +23 -0
- package/template/_claude/rules/client/07-deployment.md +99 -0
- package/template/_claude/rules/client/08-lockfile-cross-platform.md +79 -0
- package/template/_claude/rules/client/core.md +1 -0
- package/template/_claude/rules/global/core.md +20 -1
- package/template/_claude/rules/global/investigation-before-conclusions.md +57 -0
- package/template/_claude/rules/server/core.md +2 -0
- package/template/_claude/rules/server/deployment.md +78 -0
- package/template/client/next.config.ts +12 -2
- package/template/client/package-lock.json +12345 -0
- package/template/client/package.json +3 -2
- package/template/client/src/components/common/SafeImage.tsx +2 -1
- package/template/client/src/lib/api/axios.config.ts +19 -4
- package/template/client/src/middleware.ts +7 -0
- package/template/gitignore +1 -0
- package/template/server/.env.example +248 -194
- package/template/server/.env.example.production +221 -168
- package/template/server/Dockerfile +29 -5
- package/template/server/docker-compose.yml +32 -4
- package/template/server/package-lock.json +6544 -6823
- package/template/server/package.json +76 -75
- package/template/server/prisma/seed.ts +20 -4
- package/template/server/src/app.ts +316 -271
- package/template/server/src/config/env.ts +150 -99
- package/template/server/src/config/rate-limit.config.ts +16 -0
- package/template/server/src/libs/__tests__/auth-path.test.ts +24 -0
- package/template/server/src/libs/__tests__/client-ip.test.ts +121 -0
- package/template/server/src/libs/__tests__/http.test.ts +23 -9
- package/template/server/src/libs/__tests__/ip-block.test.ts +62 -0
- package/template/server/src/libs/__tests__/origin-check.test.ts +53 -0
- package/template/server/src/libs/__tests__/url-safety.test.ts +80 -0
- package/template/server/src/libs/auth-path.ts +14 -0
- package/template/server/src/libs/auth.ts +6 -16
- package/template/server/src/libs/client-ip.ts +77 -0
- package/template/server/src/libs/cookies.ts +1 -1
- package/template/server/src/libs/duration.ts +30 -0
- package/template/server/src/libs/ip-block.ts +220 -206
- package/template/server/src/libs/origin-check.ts +38 -0
- package/template/server/src/libs/query-counter.ts +59 -0
- package/template/server/src/libs/redis.ts +1 -1
- package/template/server/src/libs/url-safety.ts +121 -0
- package/template/server/src/modules/auth/__tests__/auth.service.test.ts +274 -44
- package/template/server/src/modules/auth/auth.controller.ts +128 -127
- package/template/server/src/modules/auth/auth.repo.ts +2 -0
- package/template/server/src/modules/auth/auth.service.ts +103 -12
- package/template/server/src/test/setup.ts +22 -2
- package/template/server/vitest.config.ts +43 -43
|
@@ -1,168 +1,221 @@
|
|
|
1
|
-
# ===================================================================
|
|
2
|
-
# PRODUCTION ENVIRONMENT CONFIGURATION
|
|
3
|
-
# ===================================================================
|
|
4
|
-
# This file contains production-optimized settings for high-traffic
|
|
5
|
-
# deployments (10K-100K users/day). Copy to .env and customize.
|
|
6
|
-
#
|
|
7
|
-
# COOLIFY DEPLOYMENT NOTE:
|
|
8
|
-
# When adding environment variables in Coolify, do NOT check
|
|
9
|
-
# "Available at Buildtime" for any variable unless explicitly noted.
|
|
10
|
-
# The server Dockerfile handles build-time config internally.
|
|
11
|
-
# Secrets (DATABASE_URL, JWT_SECRET, COOKIE_SECRET, REDIS_URL) must
|
|
12
|
-
# NEVER be available at buildtime — they get baked into the image layer.
|
|
13
|
-
#
|
|
14
|
-
# ===================================================================
|
|
15
|
-
|
|
16
|
-
# ===================================================================
|
|
17
|
-
# APPLICATION
|
|
18
|
-
# ===================================================================
|
|
19
|
-
|
|
20
|
-
# COOLIFY: Do NOT check "Available at Buildtime" — the Dockerfile
|
|
21
|
-
# sets NODE_ENV=development during build to ensure devDependencies install.
|
|
22
|
-
NODE_ENV=production
|
|
23
|
-
PORT=3000
|
|
24
|
-
HOST=0.0.0.0
|
|
25
|
-
|
|
26
|
-
# ===================================================================
|
|
27
|
-
#
|
|
28
|
-
# ===================================================================
|
|
29
|
-
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
#
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# ===================================================================
|
|
42
|
-
|
|
43
|
-
#
|
|
44
|
-
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
#
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
# ===================================================================
|
|
55
|
-
#
|
|
56
|
-
# ===================================================================
|
|
57
|
-
|
|
58
|
-
#
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
#
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
#
|
|
84
|
-
#
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
#
|
|
95
|
-
#
|
|
96
|
-
#
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
#
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
#
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
#
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
#
|
|
112
|
-
#
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
#
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
#
|
|
119
|
-
#
|
|
120
|
-
#
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
#
|
|
126
|
-
|
|
127
|
-
#
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
#
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
# Production
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
#
|
|
146
|
-
#
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
#
|
|
150
|
-
#
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
#
|
|
154
|
-
#
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
#
|
|
158
|
-
#
|
|
159
|
-
#
|
|
160
|
-
#
|
|
161
|
-
#
|
|
162
|
-
#
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
#
|
|
166
|
-
#
|
|
167
|
-
#
|
|
168
|
-
|
|
1
|
+
# ===================================================================
|
|
2
|
+
# PRODUCTION ENVIRONMENT CONFIGURATION
|
|
3
|
+
# ===================================================================
|
|
4
|
+
# This file contains production-optimized settings for high-traffic
|
|
5
|
+
# deployments (10K-100K users/day). Copy to .env and customize.
|
|
6
|
+
#
|
|
7
|
+
# COOLIFY DEPLOYMENT NOTE:
|
|
8
|
+
# When adding environment variables in Coolify, do NOT check
|
|
9
|
+
# "Available at Buildtime" for any variable unless explicitly noted.
|
|
10
|
+
# The server Dockerfile handles build-time config internally.
|
|
11
|
+
# Secrets (DATABASE_URL, JWT_SECRET, COOKIE_SECRET, REDIS_URL) must
|
|
12
|
+
# NEVER be available at buildtime — they get baked into the image layer.
|
|
13
|
+
#
|
|
14
|
+
# ===================================================================
|
|
15
|
+
|
|
16
|
+
# ===================================================================
|
|
17
|
+
# APPLICATION
|
|
18
|
+
# ===================================================================
|
|
19
|
+
|
|
20
|
+
# COOLIFY: Do NOT check "Available at Buildtime" — the Dockerfile
|
|
21
|
+
# sets NODE_ENV=development during build to ensure devDependencies install.
|
|
22
|
+
NODE_ENV=production
|
|
23
|
+
PORT=3000
|
|
24
|
+
HOST=0.0.0.0
|
|
25
|
+
|
|
26
|
+
# ===================================================================
|
|
27
|
+
# SERVER TIMEOUTS
|
|
28
|
+
# ===================================================================
|
|
29
|
+
|
|
30
|
+
# Fastify request timeout in milliseconds (default: 30000 = 30s)
|
|
31
|
+
# Long-running routes (LLM calls, big exports) may need 180000+ (180s).
|
|
32
|
+
# CRITICAL: the reverse proxy (Nginx/Coolify) timeout must be raised to
|
|
33
|
+
# match, or the proxy cuts the connection before the server does.
|
|
34
|
+
REQUEST_TIMEOUT_MS=30000
|
|
35
|
+
|
|
36
|
+
# Fastify connection timeout in milliseconds (default: 60000 = 60s)
|
|
37
|
+
CONNECTION_TIMEOUT_MS=60000
|
|
38
|
+
|
|
39
|
+
# ===================================================================
|
|
40
|
+
# DATABASE (MySQL 8.0+)
|
|
41
|
+
# ===================================================================
|
|
42
|
+
|
|
43
|
+
# Production database connection
|
|
44
|
+
# CRITICAL: Use secure credentials, SSL/TLS, and private network
|
|
45
|
+
# COOLIFY: Runtime only. Do NOT check "Available at Buildtime".
|
|
46
|
+
DATABASE_URL="mysql://prod_user:SECURE_PASSWORD@db.internal:3306/prod_db?ssl=true"
|
|
47
|
+
|
|
48
|
+
# Connection pool for high traffic (10K-100K users/day)
|
|
49
|
+
# Recommended: 20-50 connections for production
|
|
50
|
+
# Monitor and adjust based on load testing results
|
|
51
|
+
DATABASE_POOL_MIN=10
|
|
52
|
+
DATABASE_POOL_MAX=50
|
|
53
|
+
|
|
54
|
+
# ===================================================================
|
|
55
|
+
# REDIS
|
|
56
|
+
# ===================================================================
|
|
57
|
+
|
|
58
|
+
# Production Redis instance
|
|
59
|
+
# CRITICAL: Use authentication and private network
|
|
60
|
+
# COOLIFY: Runtime only. Do NOT check "Available at Buildtime".
|
|
61
|
+
REDIS_URL="redis://:REDIS_PASSWORD@redis.internal:6379"
|
|
62
|
+
|
|
63
|
+
# Production retry settings
|
|
64
|
+
REDIS_MAX_RETRIES=5
|
|
65
|
+
REDIS_CONNECT_TIMEOUT=5000
|
|
66
|
+
|
|
67
|
+
# ===================================================================
|
|
68
|
+
# RATE LIMITING
|
|
69
|
+
# ===================================================================
|
|
70
|
+
|
|
71
|
+
# Always enabled in production
|
|
72
|
+
RATE_LIMIT_ENABLED=true
|
|
73
|
+
|
|
74
|
+
# Production: keep at 1 (default limits are tuned for production)
|
|
75
|
+
RATE_LIMIT_MULTIPLIER=1
|
|
76
|
+
|
|
77
|
+
# Tight limits for sensitive auth routes in production
|
|
78
|
+
RATE_LIMIT_AUTH_LOGIN_MAX=10
|
|
79
|
+
RATE_LIMIT_AUTH_REGISTER_MAX=5
|
|
80
|
+
|
|
81
|
+
# Trust Cloudflare's CF-Connecting-IP header for the real client IP (default: false)
|
|
82
|
+
# Used ONLY for rate-limiting and IP auto-block decisions. Behind Cloudflare,
|
|
83
|
+
# request.ip is a CF edge IP — without this, all users behind one edge collapse
|
|
84
|
+
# onto a single IP and can rate-limit or auto-ban each other.
|
|
85
|
+
# COOLIFY: Runtime only. Do NOT check "Available at Buildtime".
|
|
86
|
+
# SECURITY: the header is client-spoofable. Set true ONLY when this origin
|
|
87
|
+
# accepts traffic exclusively via Cloudflare (direct origin access is blocked
|
|
88
|
+
# at the firewall/network so clients cannot reach it bypassing CF).
|
|
89
|
+
# Note: the left-most X-Forwarded-For entry is now trusted as the client IP
|
|
90
|
+
# regardless of this flag (covers grey-cloud / DNS-only), so the origin must be
|
|
91
|
+
# proxy-locked (firewall direct access) in production either way.
|
|
92
|
+
TRUST_CLOUDFLARE=false
|
|
93
|
+
|
|
94
|
+
# ===================================================================
|
|
95
|
+
# IP AUTO-BLOCK
|
|
96
|
+
# ===================================================================
|
|
97
|
+
|
|
98
|
+
# Rate-limit violations before an IP is auto-blocked for every route.
|
|
99
|
+
# Keep HIGH enough that a retry-looping legitimate client or a NAT'd
|
|
100
|
+
# office sharing one egress IP cannot self-ban (sustained abuse only).
|
|
101
|
+
# See src/config/rate-limit.config.ts before lowering.
|
|
102
|
+
IP_AUTO_BLOCK_THRESHOLD=20
|
|
103
|
+
|
|
104
|
+
# Sliding window for counting violations, in seconds (5 minutes)
|
|
105
|
+
IP_AUTO_BLOCK_WINDOW_SECONDS=300
|
|
106
|
+
|
|
107
|
+
# How long an auto-blocked IP stays blocked, in seconds (1 hour)
|
|
108
|
+
IP_AUTO_BLOCK_DURATION_SECONDS=3600
|
|
109
|
+
|
|
110
|
+
# ===================================================================
|
|
111
|
+
# ACCOUNT ACTIVATION
|
|
112
|
+
# ===================================================================
|
|
113
|
+
|
|
114
|
+
# Require account verification before users can log in
|
|
115
|
+
# Set to true in production for security
|
|
116
|
+
REQUIRE_USER_VERIFICATION=true
|
|
117
|
+
|
|
118
|
+
# ===================================================================
|
|
119
|
+
# FILE UPLOAD
|
|
120
|
+
# ===================================================================
|
|
121
|
+
|
|
122
|
+
# Maximum file upload size in MB
|
|
123
|
+
MAX_FILE_SIZE_MB=10
|
|
124
|
+
|
|
125
|
+
# COOLIFY PERSISTENT STORAGE (required for uploads to survive redeployments):
|
|
126
|
+
# Go to your service in Coolify → Storages → Add Volume Mount:
|
|
127
|
+
# Name: uploads (or <project-name>-uploads)
|
|
128
|
+
# Source Path: (leave empty — Coolify manages the Docker volume)
|
|
129
|
+
# Destination Path: /app/uploads
|
|
130
|
+
# Without this, all uploaded files are lost on every redeployment.
|
|
131
|
+
|
|
132
|
+
# ===================================================================
|
|
133
|
+
# JWT AUTHENTICATION
|
|
134
|
+
# ===================================================================
|
|
135
|
+
|
|
136
|
+
# CRITICAL: Generate a cryptographically secure secret
|
|
137
|
+
# Example: openssl rand -base64 48
|
|
138
|
+
# COOLIFY: Runtime only. Do NOT check "Available at Buildtime" — this is a secret!
|
|
139
|
+
JWT_SECRET="YOUR_PRODUCTION_JWT_SECRET_MUST_BE_AT_LEAST_32_CHARS_LONG"
|
|
140
|
+
|
|
141
|
+
# Production token expiry (tighter security)
|
|
142
|
+
JWT_ACCESS_EXPIRY="15m"
|
|
143
|
+
JWT_REFRESH_EXPIRY="7d"
|
|
144
|
+
|
|
145
|
+
# Cookie signing secret (separate from JWT for defense-in-depth)
|
|
146
|
+
# COOLIFY: Runtime only. Do NOT check "Available at Buildtime" — this is a secret!
|
|
147
|
+
COOKIE_SECRET="YOUR_PRODUCTION_COOKIE_SECRET_MUST_BE_AT_LEAST_32_CHARS"
|
|
148
|
+
|
|
149
|
+
# ===================================================================
|
|
150
|
+
# CORS
|
|
151
|
+
# ===================================================================
|
|
152
|
+
|
|
153
|
+
# REQUIRED in production - your frontend domain(s)
|
|
154
|
+
# Multiple origins separated by commas
|
|
155
|
+
CORS_ORIGIN="https://yourdomain.com,https://app.yourdomain.com"
|
|
156
|
+
|
|
157
|
+
# Cookie domain for cross-origin deployments
|
|
158
|
+
# REQUIRED when client and API are on different subdomains:
|
|
159
|
+
# Client: https://app.example.com | API: https://api.example.com
|
|
160
|
+
# → Set COOKIE_DOMAIN=".example.com" (leading dot = all subdomains)
|
|
161
|
+
# This enables cookies to be shared between client and API subdomains.
|
|
162
|
+
# Without it, login will silently fail (server returns 200 but browser drops cookies).
|
|
163
|
+
COOKIE_DOMAIN=".yourdomain.com"
|
|
164
|
+
|
|
165
|
+
# ===================================================================
|
|
166
|
+
# EMAIL (Resend)
|
|
167
|
+
# ===================================================================
|
|
168
|
+
|
|
169
|
+
# Resend API key for transactional emails
|
|
170
|
+
# COOLIFY: Runtime only. Do NOT check "Available at Buildtime" — this is a secret!
|
|
171
|
+
RESEND_API_KEY="re_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
172
|
+
|
|
173
|
+
# Sender email — must be from a verified domain in Resend
|
|
174
|
+
RESEND_FROM_EMAIL="noreply@yourdomain.com"
|
|
175
|
+
|
|
176
|
+
# Frontend URL for email links (password reset, verification, etc.)
|
|
177
|
+
CLIENT_URL="https://yourdomain.com"
|
|
178
|
+
|
|
179
|
+
# ===================================================================
|
|
180
|
+
# LOGGING
|
|
181
|
+
# ===================================================================
|
|
182
|
+
|
|
183
|
+
# Production: info or warn (reduces log volume)
|
|
184
|
+
# Use warn to reduce costs in high-traffic scenarios
|
|
185
|
+
LOG_LEVEL=info
|
|
186
|
+
|
|
187
|
+
# ===================================================================
|
|
188
|
+
# DATABASE SEEDING
|
|
189
|
+
# ===================================================================
|
|
190
|
+
|
|
191
|
+
# DO NOT SEED PRODUCTION. The seed script (npm run prisma:seed) creates
|
|
192
|
+
# well-known demo accounts (admin@example.com) and hard-refuses to run when
|
|
193
|
+
# NODE_ENV=production. These variables exist only to keep .env files in sync
|
|
194
|
+
# across environments — leave them commented out in production.
|
|
195
|
+
# SEED_ADMIN_PASSWORD=
|
|
196
|
+
# SEED_USER_PASSWORD=
|
|
197
|
+
|
|
198
|
+
# ===================================================================
|
|
199
|
+
# ERROR TRACKING
|
|
200
|
+
# ===================================================================
|
|
201
|
+
|
|
202
|
+
# Sentry for production error monitoring
|
|
203
|
+
# Get your DSN from: https://sentry.io/settings/projects/
|
|
204
|
+
SENTRY_DSN="https://YOUR_PUBLIC_KEY@o0.ingest.sentry.io/YOUR_PROJECT_ID"
|
|
205
|
+
|
|
206
|
+
# ===================================================================
|
|
207
|
+
# PRODUCTION CHECKLIST
|
|
208
|
+
# ===================================================================
|
|
209
|
+
# [ ] DATABASE_URL uses secure credentials and SSL
|
|
210
|
+
# [ ] JWT_SECRET is a strong random string (48+ chars)
|
|
211
|
+
# [ ] CORS_ORIGIN is set to your exact frontend URL(s)
|
|
212
|
+
# [ ] COOKIE_DOMAIN is set if client and API are on different subdomains
|
|
213
|
+
# [ ] REDIS_URL uses authentication if Redis is exposed
|
|
214
|
+
# [ ] LOG_LEVEL is set to info or warn
|
|
215
|
+
# [ ] SENTRY_DSN is configured for error tracking
|
|
216
|
+
# [ ] Database connection pool tested under load
|
|
217
|
+
# [ ] All secrets are stored in environment variables, not in code
|
|
218
|
+
# [ ] SSL/TLS certificates are configured
|
|
219
|
+
# [ ] Firewall rules restrict access to database and Redis
|
|
220
|
+
# [ ] In Coolify: NO secrets have "Available at Buildtime" checked
|
|
221
|
+
# [ ] In Coolify: NODE_ENV does NOT have "Available at Buildtime" checked
|
|
@@ -23,6 +23,18 @@ RUN if [ -f pnpm-lock.yaml ]; then \
|
|
|
23
23
|
npm ci --omit=dev; \
|
|
24
24
|
fi
|
|
25
25
|
|
|
26
|
+
# Add the Prisma CLI to the production node_modules so the runtime image can
|
|
27
|
+
# run `npx prisma migrate deploy` at container start (see CMD in stage 3).
|
|
28
|
+
# `prisma` is a devDependency (correct for local dev), so the prod-only install
|
|
29
|
+
# above does NOT include it — without this step, npx would try to download the
|
|
30
|
+
# CLI from the registry on every container boot. Pinned to the exact
|
|
31
|
+
# @prisma/client version the lockfile installed, so CLI and client never drift.
|
|
32
|
+
# --no-save keeps package.json and the lockfile untouched. Because this stage
|
|
33
|
+
# runs on the same Alpine (musl) base as the runtime stage, the engine binaries
|
|
34
|
+
# downloaded here are the correct ones for production.
|
|
35
|
+
RUN npm install --no-save --no-audit --no-fund \
|
|
36
|
+
"prisma@$(node -p "require('@prisma/client/package.json').version")"
|
|
37
|
+
|
|
26
38
|
# ===================================================================
|
|
27
39
|
# Stage 2: Build (compile TypeScript)
|
|
28
40
|
# ===================================================================
|
|
@@ -69,7 +81,9 @@ RUN addgroup -g 1001 -S nodejs && \
|
|
|
69
81
|
|
|
70
82
|
WORKDIR /app
|
|
71
83
|
|
|
72
|
-
# Copy production dependencies from dependencies stage
|
|
84
|
+
# Copy production dependencies from dependencies stage.
|
|
85
|
+
# Includes the Prisma CLI (+ its engines) installed in stage 1, which the
|
|
86
|
+
# CMD below needs for `npx prisma migrate deploy`.
|
|
73
87
|
COPY --from=dependencies --chown=nodejs:nodejs /app/node_modules ./node_modules
|
|
74
88
|
|
|
75
89
|
# Copy generated Prisma client from builder stage (not present in prod deps)
|
|
@@ -91,12 +105,22 @@ USER nodejs
|
|
|
91
105
|
# Expose port (matches default PORT in env.ts; override via PORT env var)
|
|
92
106
|
EXPOSE 8000
|
|
93
107
|
|
|
94
|
-
# Health check for container orchestration
|
|
95
|
-
|
|
108
|
+
# Health check for container orchestration.
|
|
109
|
+
# start-period is 60s because CMD runs `prisma migrate deploy` BEFORE the server
|
|
110
|
+
# starts listening — a slow migration must not be counted as unhealthy, or
|
|
111
|
+
# Coolify restart-loops the container mid-migration.
|
|
112
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
|
|
96
113
|
CMD node -e "const p=process.env.PORT||8000;require('http').get('http://localhost:'+p+'/api/v1/live',(r)=>{process.exit(r.statusCode===200?0:1)})"
|
|
97
114
|
|
|
98
115
|
# Use dumb-init to handle signals properly
|
|
99
116
|
ENTRYPOINT ["dumb-init", "--"]
|
|
100
117
|
|
|
101
|
-
# Start the application
|
|
102
|
-
|
|
118
|
+
# Start the application — apply any pending Prisma migrations first, then boot.
|
|
119
|
+
# Runs inside Coolify on every container start, where DATABASE_URL is reachable
|
|
120
|
+
# (it isn't from CI, which is why migrations belong here and not in a workflow).
|
|
121
|
+
# `migrate deploy` is idempotent: it applies only pending migrations and exits 0
|
|
122
|
+
# when there is nothing to do. Requirements, all satisfied above:
|
|
123
|
+
# - prisma CLI in ./node_modules (installed in stage 1, copied with node_modules)
|
|
124
|
+
# - ./prisma/schema.prisma + ./prisma/migrations (copied from builder)
|
|
125
|
+
# `exec` replaces the shell so node receives signals from dumb-init directly.
|
|
126
|
+
CMD ["sh", "-c", "npx prisma migrate deploy && exec node dist/server.js"]
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# WARNING: These credentials are for LOCAL DEVELOPMENT ONLY.
|
|
2
2
|
# Change all passwords and secrets before using in any shared or production environment.
|
|
3
|
+
#
|
|
4
|
+
# Security posture:
|
|
5
|
+
# - All published ports are bound to 127.0.0.1 — nothing is reachable from the
|
|
6
|
+
# network (an unpassworded Redis or root-password MySQL on 0.0.0.0 is an open
|
|
7
|
+
# door on shared networks/VPS dev boxes). Don't remove the 127.0.0.1 prefix.
|
|
8
|
+
# - The admin UIs (phpMyAdmin, Redis Commander) are behind the "tools" profile
|
|
9
|
+
# and do NOT start by default:
|
|
10
|
+
# docker compose up -d → MySQL + Redis only
|
|
11
|
+
# docker compose --profile tools up -d → also phpMyAdmin + Redis Commander
|
|
3
12
|
|
|
4
13
|
name: {{PROJECT_NAME}}
|
|
5
14
|
|
|
@@ -8,8 +17,19 @@ services:
|
|
|
8
17
|
image: mysql:8.0
|
|
9
18
|
container_name: {{PROJECT_NAME}}-mysql
|
|
10
19
|
restart: unless-stopped
|
|
20
|
+
# Local-dev resource bounds. mem_limit is a cgroup ceiling (a runaway container is
|
|
21
|
+
# OOM-killed in isolation instead of wedging the whole WSL2/Docker engine); the flags
|
|
22
|
+
# dev-tune MySQL 8 to a small, fast-warming footprint. Production reaches MySQL as a
|
|
23
|
+
# separate Coolify resource via DATABASE_URL — none of this touches prod.
|
|
24
|
+
mem_limit: 900m
|
|
25
|
+
command:
|
|
26
|
+
- --innodb-buffer-pool-size=128M # default is fine for dev; keeps RSS small
|
|
27
|
+
- --performance-schema=OFF # saves ~hundreds of MB on its own
|
|
28
|
+
- --skip-name-resolve # no reverse-DNS per connection (faster, leaner)
|
|
29
|
+
- --max-connections=60 # well above DATABASE_POOL_MAX (10) + headroom
|
|
30
|
+
- --innodb-flush-log-at-trx-commit=2 # dev durability/throughput trade — not for prod
|
|
11
31
|
ports:
|
|
12
|
-
- '
|
|
32
|
+
- '127.0.0.1:${MYSQL_PORT:-{{MYSQL_PORT}}}:3306'
|
|
13
33
|
environment:
|
|
14
34
|
MYSQL_ROOT_PASSWORD: rootpassword
|
|
15
35
|
MYSQL_DATABASE: {{DATABASE_NAME}}
|
|
@@ -27,8 +47,9 @@ services:
|
|
|
27
47
|
image: phpmyadmin:latest
|
|
28
48
|
container_name: {{PROJECT_NAME}}-phpmyadmin
|
|
29
49
|
restart: unless-stopped
|
|
50
|
+
profiles: ["tools"]
|
|
30
51
|
ports:
|
|
31
|
-
- '
|
|
52
|
+
- '127.0.0.1:${PHPMYADMIN_PORT:-{{PHPMYADMIN_PORT}}}:80'
|
|
32
53
|
environment:
|
|
33
54
|
PMA_HOST: mysql
|
|
34
55
|
PMA_PORT: 3306
|
|
@@ -47,8 +68,14 @@ services:
|
|
|
47
68
|
image: redis:7-alpine
|
|
48
69
|
container_name: {{PROJECT_NAME}}-redis
|
|
49
70
|
restart: unless-stopped
|
|
71
|
+
# Local-dev resource bounds. Layer two ceilings: Redis's own --maxmemory (150mb) trips
|
|
72
|
+
# FIRST and refuses writes loudly (noeviction — create-tigra also keeps rate-limit/queue
|
|
73
|
+
# keys in Redis, so silent LRU eviction would be a dev data-loss trap), while the 200m
|
|
74
|
+
# cgroup mem_limit sits above it as a hard backstop. --save "" drops RDB snapshot fork/IO.
|
|
75
|
+
mem_limit: 200m
|
|
76
|
+
command: ['redis-server', '--maxmemory', '150mb', '--maxmemory-policy', 'noeviction', '--save', '']
|
|
50
77
|
ports:
|
|
51
|
-
- '
|
|
78
|
+
- '127.0.0.1:${REDIS_PORT:-{{REDIS_PORT}}}:6379'
|
|
52
79
|
volumes:
|
|
53
80
|
- redis_data:/data
|
|
54
81
|
networks:
|
|
@@ -63,8 +90,9 @@ services:
|
|
|
63
90
|
image: rediscommander/redis-commander:latest
|
|
64
91
|
container_name: {{PROJECT_NAME}}-redis-commander
|
|
65
92
|
restart: unless-stopped
|
|
93
|
+
profiles: ["tools"]
|
|
66
94
|
ports:
|
|
67
|
-
- '
|
|
95
|
+
- '127.0.0.1:${REDIS_COMMANDER_PORT:-{{REDIS_COMMANDER_PORT}}}:8081'
|
|
68
96
|
environment:
|
|
69
97
|
REDIS_HOSTS: local:redis:6379
|
|
70
98
|
depends_on:
|