create-tigra 2.1.5 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create-tigra.js +2 -0
- package/package.json +4 -1
- package/template/_claude/commands/create-client.md +1 -4
- package/template/_claude/commands/create-server.md +0 -1
- package/template/_claude/rules/client/01-project-structure.md +0 -3
- package/template/_claude/rules/client/03-data-and-state.md +1 -1
- package/template/_claude/rules/server/project-conventions.md +13 -0
- package/template/client/package.json +2 -1
- package/template/server/.env.example +23 -0
- package/template/server/.env.example.production +21 -0
- package/template/server/package.json +2 -1
- package/template/server/postman/collection.json +524 -0
- package/template/server/postman/environment.json +31 -0
- package/template/server/prisma/schema.prisma +17 -1
- package/template/server/src/app.ts +43 -10
- package/template/server/src/config/env.ts +9 -0
- package/template/server/src/config/rate-limit.config.ts +114 -0
- package/template/server/src/jobs/cleanup-deleted-accounts.job.ts +80 -0
- package/template/server/src/{libs/cleanup.ts → jobs/cleanup-expired-auth.job.ts} +10 -4
- package/template/server/src/jobs/index.ts +20 -0
- package/template/server/src/libs/auth.ts +45 -1
- package/template/server/src/libs/ip-block.ts +206 -0
- package/template/server/src/libs/requestLogger.ts +1 -1
- package/template/server/src/libs/storage/file-storage.service.ts +65 -18
- package/template/server/src/libs/storage/file-validator.ts +4 -11
- package/template/server/src/libs/storage/image-optimizer.service.ts +1 -1
- package/template/server/src/modules/admin/admin.controller.ts +42 -0
- package/template/server/src/modules/admin/admin.routes.ts +45 -0
- package/template/server/src/modules/auth/auth.repo.ts +18 -0
- package/template/server/src/modules/auth/auth.routes.ts +10 -30
- package/template/server/src/modules/auth/auth.service.ts +52 -26
- package/template/server/src/modules/users/users.controller.ts +92 -5
- package/template/server/src/modules/users/users.repo.ts +27 -0
- package/template/server/src/modules/users/users.routes.ts +210 -19
- package/template/server/src/modules/users/users.schemas.ts +62 -4
- package/template/server/src/modules/users/users.service.ts +124 -3
- package/template/server/src/shared/types/index.ts +2 -0
- package/template/server/tsconfig.json +2 -1
- package/template/server/uploads/avatars/.gitkeep +0 -1
package/bin/create-tigra.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-tigra",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Create a production-ready full-stack app with Next.js 16 + Fastify 5 + Prisma + Redis",
|
|
6
6
|
"bin": {
|
|
@@ -39,6 +39,9 @@
|
|
|
39
39
|
"url": "https://github.com/blessandsoul/create-tigra/issues"
|
|
40
40
|
},
|
|
41
41
|
"homepage": "https://github.com/blessandsoul/create-tigra#readme",
|
|
42
|
+
"scripts": {
|
|
43
|
+
"create:test": "node -e \"import('fs').then(f=>f.rmSync('testapp',{recursive:true,force:true}))\" && node bin/create-tigra.js testapp"
|
|
44
|
+
},
|
|
42
45
|
"dependencies": {
|
|
43
46
|
"chalk": "^5.4.1",
|
|
44
47
|
"commander": "^13.1.0",
|
|
@@ -180,8 +180,6 @@ export const API_ENDPOINTS = {
|
|
|
180
180
|
LOGOUT: '/auth/logout',
|
|
181
181
|
REFRESH: '/auth/refresh',
|
|
182
182
|
ME: '/auth/me',
|
|
183
|
-
VERIFY_EMAIL: '/auth/verify-email',
|
|
184
|
-
RESEND_VERIFICATION: '/auth/resend-verification',
|
|
185
183
|
REQUEST_PASSWORD_RESET: '/auth/request-password-reset',
|
|
186
184
|
RESET_PASSWORD: '/auth/reset-password',
|
|
187
185
|
},
|
|
@@ -199,7 +197,6 @@ export const ROUTES = {
|
|
|
199
197
|
HOME: '/',
|
|
200
198
|
LOGIN: '/login',
|
|
201
199
|
REGISTER: '/register',
|
|
202
|
-
VERIFY_EMAIL: '/verify-email',
|
|
203
200
|
RESET_PASSWORD: '/reset-password',
|
|
204
201
|
DASHBOARD: '/dashboard',
|
|
205
202
|
PROFILE: '/profile',
|
|
@@ -369,7 +366,7 @@ Auth types from `02-components-and-types.md`:
|
|
|
369
366
|
|
|
370
367
|
#### `src/features/auth/services/auth.service.ts`
|
|
371
368
|
Auth service class from `03-data-and-state.md`:
|
|
372
|
-
- `register`, `login`, `logout`, `refreshToken`, `getMe`, `
|
|
369
|
+
- `register`, `login`, `logout`, `refreshToken`, `getMe`, `requestPasswordReset`, `resetPassword`
|
|
373
370
|
- Uses `apiClient` and `API_ENDPOINTS`
|
|
374
371
|
- Singleton export: `export const authService = new AuthService();`
|
|
375
372
|
|
|
@@ -78,8 +78,6 @@ export const API_ENDPOINTS = {
|
|
|
78
78
|
LOGOUT: '/auth/logout',
|
|
79
79
|
REFRESH: '/auth/refresh',
|
|
80
80
|
ME: '/auth/me',
|
|
81
|
-
VERIFY_EMAIL: '/auth/verify-email',
|
|
82
|
-
RESEND_VERIFICATION: '/auth/resend-verification',
|
|
83
81
|
REQUEST_PASSWORD_RESET: '/auth/request-password-reset',
|
|
84
82
|
RESET_PASSWORD: '/auth/reset-password',
|
|
85
83
|
},
|
|
@@ -103,7 +101,6 @@ export const ROUTES = {
|
|
|
103
101
|
HOME: '/',
|
|
104
102
|
LOGIN: '/login',
|
|
105
103
|
REGISTER: '/register',
|
|
106
|
-
VERIFY_EMAIL: '/verify-email',
|
|
107
104
|
RESET_PASSWORD: '/reset-password',
|
|
108
105
|
DASHBOARD: '/dashboard',
|
|
109
106
|
PROFILE: '/profile',
|
|
@@ -92,7 +92,7 @@ class ItemService {
|
|
|
92
92
|
export const itemService = new ItemService();
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
Auth service methods: `register`, `login`, `logout`, `refreshToken`, `getMe`, `
|
|
95
|
+
Auth service methods: `register`, `login`, `logout`, `refreshToken`, `getMe`, `requestPasswordReset`, `resetPassword`.
|
|
96
96
|
|
|
97
97
|
---
|
|
98
98
|
|
|
@@ -212,3 +212,16 @@ const apiClient = axios.create({ ...httpClient.defaults, baseURL: 'https://api.e
|
|
|
212
212
|
- Always use `httpClient` — never `fetch`, `node:http`, or inline `axios.create()`.
|
|
213
213
|
- Never add auth headers, cookies, or credentials to the singleton itself.
|
|
214
214
|
- Never log response bodies (may contain PII or secrets).
|
|
215
|
+
|
|
216
|
+
## File Storage
|
|
217
|
+
|
|
218
|
+
Upload directory structure: `uploads/users/{userId}/<media-type>/`
|
|
219
|
+
|
|
220
|
+
| Media type | Path | Example |
|
|
221
|
+
|---|---|---|
|
|
222
|
+
| Avatar | `uploads/users/{userId}/avatar/` | `uploads/users/abc123/avatar/john-doe-avatar.webp` |
|
|
223
|
+
|
|
224
|
+
- All user media lives under `uploads/users/{userId}/` for easy per-user cleanup.
|
|
225
|
+
- On account purge, delete the entire `uploads/users/{userId}/` directory via `deleteUserMedia()`.
|
|
226
|
+
- Public URL pattern: `/uploads/users/{userId}/<media-type>/{filename}`
|
|
227
|
+
- New media types follow the same pattern: add a subfolder under the user directory.
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
"dev": "next dev",
|
|
7
7
|
"build": "next build",
|
|
8
8
|
"start": "next start",
|
|
9
|
-
"lint": "eslint src/"
|
|
9
|
+
"lint": "eslint src/",
|
|
10
|
+
"generate:env": "node -e \"import('fs').then(f=>{if(f.existsSync('.env'))console.log('.env already exists, skipping');else{f.copyFileSync('.env.example','.env');console.log('.env created from .env.example')}})\""
|
|
10
11
|
},
|
|
11
12
|
"dependencies": {
|
|
12
13
|
"@hookform/resolvers": "^5.2.2",
|
|
@@ -39,6 +39,29 @@ REDIS_MAX_RETRIES=3
|
|
|
39
39
|
# Connection timeout in milliseconds
|
|
40
40
|
REDIS_CONNECT_TIMEOUT=10000
|
|
41
41
|
|
|
42
|
+
# ===================================================================
|
|
43
|
+
# RATE LIMITING
|
|
44
|
+
# ===================================================================
|
|
45
|
+
|
|
46
|
+
# Master switch to enable/disable rate limiting (default: true)
|
|
47
|
+
# Set to false in development to disable all rate limits
|
|
48
|
+
RATE_LIMIT_ENABLED=true
|
|
49
|
+
|
|
50
|
+
# Multiply all rate limit max values by this factor (default: 1)
|
|
51
|
+
# Set to 10 in development for 10x headroom, or 0.5 for tighter limits
|
|
52
|
+
RATE_LIMIT_MULTIPLIER=1
|
|
53
|
+
|
|
54
|
+
# Optional: Override specific critical route limits (uses defaults if not set)
|
|
55
|
+
# RATE_LIMIT_AUTH_LOGIN_MAX=10
|
|
56
|
+
# RATE_LIMIT_AUTH_REGISTER_MAX=5
|
|
57
|
+
|
|
58
|
+
# ===================================================================
|
|
59
|
+
# FILE UPLOAD
|
|
60
|
+
# ===================================================================
|
|
61
|
+
|
|
62
|
+
# Maximum file upload size in MB (default: 10)
|
|
63
|
+
MAX_FILE_SIZE_MB=10
|
|
64
|
+
|
|
42
65
|
# ===================================================================
|
|
43
66
|
# DOCKER PORTS (auto-generated, unique per project)
|
|
44
67
|
# ===================================================================
|
|
@@ -39,6 +39,27 @@ REDIS_URL="redis://:REDIS_PASSWORD@redis.internal:6379"
|
|
|
39
39
|
REDIS_MAX_RETRIES=5
|
|
40
40
|
REDIS_CONNECT_TIMEOUT=5000
|
|
41
41
|
|
|
42
|
+
# ===================================================================
|
|
43
|
+
# RATE LIMITING
|
|
44
|
+
# ===================================================================
|
|
45
|
+
|
|
46
|
+
# Always enabled in production
|
|
47
|
+
RATE_LIMIT_ENABLED=true
|
|
48
|
+
|
|
49
|
+
# Production: keep at 1 (default limits are tuned for production)
|
|
50
|
+
RATE_LIMIT_MULTIPLIER=1
|
|
51
|
+
|
|
52
|
+
# Tight limits for sensitive auth routes in production
|
|
53
|
+
RATE_LIMIT_AUTH_LOGIN_MAX=10
|
|
54
|
+
RATE_LIMIT_AUTH_REGISTER_MAX=5
|
|
55
|
+
|
|
56
|
+
# ===================================================================
|
|
57
|
+
# FILE UPLOAD
|
|
58
|
+
# ===================================================================
|
|
59
|
+
|
|
60
|
+
# Maximum file upload size in MB
|
|
61
|
+
MAX_FILE_SIZE_MB=10
|
|
62
|
+
|
|
42
63
|
# ===================================================================
|
|
43
64
|
# JWT AUTHENTICATION
|
|
44
65
|
# ===================================================================
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"redis:flush": "tsx scripts/flush-redis.ts",
|
|
23
23
|
"docker:up": "docker compose up -d",
|
|
24
24
|
"docker:down": "docker compose down",
|
|
25
|
-
"docker:logs": "docker compose logs -f"
|
|
25
|
+
"docker:logs": "docker compose logs -f",
|
|
26
|
+
"generate:env": "node -e \"import('fs').then(f=>{if(f.existsSync('.env'))console.log('.env already exists, skipping');else{f.copyFileSync('.env.example','.env');console.log('.env created from .env.example')}})\""
|
|
26
27
|
},
|
|
27
28
|
"prisma": {
|
|
28
29
|
"seed": "tsx prisma/seed.ts"
|