create-tigra 1.0.0 → 1.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.
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  import { Command } from 'commander';
4
4
  import prompts from 'prompts';
@@ -80,7 +80,6 @@ const filesToProcess = [
80
80
  'CLAUDE.md',
81
81
  // Server files
82
82
  'server/package.json',
83
- 'server/.env',
84
83
  'server/.env.example',
85
84
  'server/docker-compose.yml',
86
85
  'server/README.md',
@@ -100,6 +99,11 @@ const excludePatterns = [
100
99
  'coverage',
101
100
  'build-errors.txt',
102
101
  '.env.local',
102
+ '.env',
103
+ '*.tsbuildinfo',
104
+ 'tsconfig.build.tsbuildinfo',
105
+ '.DS_Store',
106
+ 'Thumbs.db',
103
107
  ];
104
108
 
105
109
  // ==============================================
@@ -278,7 +282,7 @@ program
278
282
  console.log();
279
283
  console.log(chalk.gray(' URLs after starting:'));
280
284
  console.log(chalk.gray(' API Server: http://localhost:3000'));
281
- console.log(chalk.gray(' API Docs: http://localhost:3000/docs'));
285
+ console.log(chalk.gray(' Health Check: http://localhost:3000/health'));
282
286
  console.log(chalk.gray(' phpMyAdmin: http://localhost:8080 (root / password)'));
283
287
  console.log(chalk.gray(' Redis UI: http://localhost:8081'));
284
288
  console.log();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-tigra",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "description": "Create a production-ready Fastify + TypeScript + Prisma API server",
6
6
  "keywords": [
@@ -1,148 +1,69 @@
1
- # ==============================================
2
- # SERVER CONFIGURATION
3
- # ==============================================
4
-
5
- # Environment: development | production | test
6
- NODE_ENV=development
7
-
8
- # Server port
9
- PORT=3000
10
-
11
- # API base path (e.g., /api/v1)
12
- API_PREFIX=/api/v1
13
-
14
- # Logging level: trace | debug | info | warn | error | fatal
15
- LOG_LEVEL=info
16
-
17
- # ==============================================
18
- # DATABASE CONFIGURATION (MySQL)
19
- # ==============================================
20
-
21
- # MySQL connection string format:
22
- # mysql://USER:PASSWORD@HOST:PORT/DATABASE
23
- # For local development with Docker:
24
- DATABASE_URL="mysql://root:{{DATABASE_PASSWORD}}@localhost:3306/{{DATABASE_NAME}}"
25
-
26
- # For production (example):
27
- # DATABASE_URL="mysql://user:secure_password@db.example.com:3306/{{DATABASE_NAME}}_production?connection_limit=10&pool_timeout=20"
28
-
29
- # ==============================================
30
- # REDIS CONFIGURATION
31
- # ==============================================
32
-
33
- # Redis host
34
- REDIS_HOST=localhost
35
-
36
- # Redis port
37
- REDIS_PORT=6379
38
-
39
- # Redis password (leave empty for local development)
40
- REDIS_PASSWORD=
41
-
42
- # Redis database number (0-15)
43
- REDIS_DB=0
44
-
45
- # ==============================================
46
- # JWT AUTHENTICATION
47
- # ==============================================
48
-
49
- # JWT secret key - CHANGE THIS IN PRODUCTION!
50
- # Generate with: openssl rand -base64 32
51
- JWT_SECRET=123412341234123412341234123412341234123412341234123412341234
52
-
53
- # Access token expiration (e.g., 15m, 1h, 7d)
54
- JWT_ACCESS_EXPIRATION=15m
55
-
56
- # Refresh token expiration (e.g., 7d, 30d, 90d)
57
- JWT_REFRESH_EXPIRATION=7d
58
-
59
- # JWT issuer (optional)
60
- JWT_ISSUER={{PROJECT_NAME}}
61
-
62
- # ==============================================
63
- # CORS CONFIGURATION
64
- # ==============================================
65
-
66
- # Allowed origins (comma-separated)
67
- # For development:
68
- CORS_ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000
69
-
70
- # For production:
71
- # CORS_ALLOWED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com
72
-
73
-
74
- # ==============================================
75
- # FILE UPLOAD CONFIGURATION
76
- # ==============================================
77
-
78
- # Maximum file size in bytes (10MB default)
79
- MAX_FILE_SIZE=10485760
80
-
81
- # Allowed file types (comma-separated)
82
- ALLOWED_FILE_TYPES=image/jpeg,image/png,image/webp,application/pdf
83
-
84
- # Upload directory
85
- UPLOAD_DIR=./uploads
86
-
87
- # ==============================================
88
- # RATE LIMITING
89
- # ==============================================
90
-
91
- # Global rate limit (requests per time window)
92
- RATE_LIMIT_MAX=100
93
-
94
- # Time window in milliseconds (15 minutes = 900000)
95
- RATE_LIMIT_WINDOW=900000
96
-
97
- # ==============================================
98
- # MONITORING & ERROR TRACKING
99
- # ==============================================
100
-
101
- # ==============================================
102
- # API DOCUMENTATION (Swagger)
103
- # ==============================================
104
-
105
- # Enable Swagger UI (true/false)
106
- # Set to false in production for security
107
- SWAGGER_ENABLED=true
108
-
109
- # Swagger route prefix
110
- SWAGGER_ROUTE=/docs
111
-
112
- # ==============================================
113
- # EXTERNAL SERVICES (Optional)
114
- # ==============================================
115
-
116
- # AWS S3 (for file storage)
117
- # AWS_ACCESS_KEY_ID=
118
- # AWS_SECRET_ACCESS_KEY=
119
- # AWS_REGION=us-east-1
120
- # AWS_S3_BUCKET=
121
-
122
- # Stripe (for payments)
123
- # STRIPE_SECRET_KEY=
124
- # STRIPE_WEBHOOK_SECRET=
125
-
126
-
127
-
128
- # ==============================================
129
- # DEVELOPMENT TOOLS
130
- # ==============================================
131
-
132
- # Enable query logging in development
133
- PRISMA_QUERY_LOG=false
134
-
135
- # Enable slow query warnings (milliseconds)
136
- SLOW_QUERY_THRESHOLD=1000
137
-
138
- # ==============================================
139
- # ADMIN SEED (Initial Setup)
140
- # ==============================================
141
-
142
- # Admin user email for database seeding
143
- # Change this before running npm run prisma:seed
144
- ADMIN_EMAIL=admin@{{PROJECT_NAME}}.dev
145
-
146
- # Admin user password for database seeding
147
- # CHANGE THIS IMMEDIATELY AFTER FIRST LOGIN!
148
- ADMIN_PASSWORD=ChangeThisPassword123!
1
+ # ==============================================
2
+ # SERVER CONFIGURATION
3
+ # ==============================================
4
+
5
+ NODE_ENV=development
6
+ PORT=3000
7
+ API_PREFIX=/api/v1
8
+ LOG_LEVEL=info
9
+
10
+ # ==============================================
11
+ # DATABASE CONFIGURATION (MySQL)
12
+ # ==============================================
13
+
14
+ DATABASE_URL="mysql://root:{{DATABASE_PASSWORD}}@localhost:3306/{{DATABASE_NAME}}"
15
+
16
+
17
+ # ==============================================
18
+ # REDIS CONFIGURATION
19
+ # ==============================================
20
+
21
+ REDIS_HOST=localhost
22
+ REDIS_PORT=6379
23
+ REDIS_PASSWORD=
24
+ REDIS_DB=0
25
+
26
+ # ==============================================
27
+ # JWT AUTHENTICATION
28
+ # =============================================='
29
+
30
+ # enter strong jwt secret in production !
31
+ JWT_SECRET=123412341234123412341234123412341234123412341234123412341234
32
+ JWT_ACCESS_EXPIRATION=15m
33
+ JWT_REFRESH_EXPIRATION=7d
34
+ JWT_ISSUER={{PROJECT_NAME}}
35
+
36
+ # ==============================================
37
+ # CORS CONFIGURATION
38
+ # ==============================================
39
+
40
+ CORS_ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000
41
+
42
+ # ==============================================
43
+ # FILE UPLOAD CONFIGURATION
44
+ # ==============================================
45
+
46
+ MAX_FILE_SIZE=10485760
47
+ ALLOWED_FILE_TYPES=image/jpeg,image/png,image/webp,application/pdf
48
+ UPLOAD_DIR=./uploads
49
+
50
+ # ==============================================
51
+ # RATE LIMITING
52
+ # ==============================================
53
+
54
+ RATE_LIMIT_MAX=100
55
+ RATE_LIMIT_WINDOW=900000
56
+
57
+ # ==============================================
58
+ # DEVELOPMENT TOOLS
59
+ # ==============================================
60
+
61
+ PRISMA_QUERY_LOG=false
62
+ SLOW_QUERY_THRESHOLD=1000
63
+
64
+ # ==============================================
65
+ # ADMIN SEED (Initial Setup)
66
+ # ==============================================
67
+
68
+ ADMIN_EMAIL=admin@{{PROJECT_NAME}}.dev
69
+ ADMIN_PASSWORD=Admin123!
@@ -9,7 +9,6 @@
9
9
  - Pagination with filters
10
10
  - Rate limiting with Redis
11
11
  - Background job processing (BullMQ)
12
- - API documentation (Swagger/OpenAPI)
13
12
  - Comprehensive error handling
14
13
  - Request logging and monitoring
15
14
  - Health checks
@@ -22,7 +21,6 @@
22
21
  - **Validation**: Zod
23
22
  - **Authentication**: JWT (jsonwebtoken)
24
23
  - **Testing**: Vitest + Supertest
25
- - **Documentation**: Swagger/OpenAPI 3.0
26
24
 
27
25
  ## Prerequisites
28
26
  - Node.js 20 or higher
@@ -35,33 +33,43 @@
35
33
  # 1. Install dependencies
36
34
  npm install
37
35
 
38
- # 2. Copy environment variables
39
- cp .env.example .env
40
- # Edit .env and set required variables (DATABASE_URL, JWT_SECRET, etc.)
41
-
42
- # 3. Start Docker services (MySQL + Redis)
36
+ # 2. Start Docker services (MySQL + Redis)
43
37
  docker-compose up -d
44
38
 
45
- # 4. Generate Prisma Client
46
- npm run prisma:generate
47
-
48
- # 5. Run database migrations
49
- npx prisma migrate dev --name init
39
+ # 3. Initialize database (creates .env, waits for DB, runs migrations)
40
+ npm run db:init
50
41
 
51
- # 6. Seed the database
42
+ # 4. Seed the database
52
43
  npm run prisma:seed
53
44
 
54
- # 7. Start development server
45
+ # 5. Start development server
55
46
  npm run dev
56
47
  ```
57
48
 
58
49
  Server will start at http://localhost:3000
59
50
 
60
- ## API Documentation
51
+ ### Production Installation
52
+
53
+ For production deployments, install only production dependencies:
54
+
55
+ ```bash
56
+ # Install production dependencies only (faster, smaller)
57
+ npm install --omit=dev
58
+
59
+ # Generate Prisma Client
60
+ npx prisma generate
61
+
62
+ # Run migrations
63
+ npx prisma migrate deploy
64
+ ```
65
+
66
+ ## API Endpoints
61
67
 
62
- Once running, visit:
63
- - Swagger UI: http://localhost:3000/docs
68
+ Once running, you can access:
64
69
  - Health Check: http://localhost:3000/health
70
+ - Auth endpoints: http://localhost:3000/api/v1/auth/*
71
+ - Resources endpoints: http://localhost:3000/api/v1/resources/*
72
+ - Admin endpoints: http://localhost:3000/api/v1/admin/*
65
73
 
66
74
  ## Available Scripts
67
75
 
@@ -3,7 +3,7 @@
3
3
  "version": "1.0.0",
4
4
  "description": "{{PROJECT_DESCRIPTION}}",
5
5
  "main": "dist/server.js",
6
- "type": "commonjs",
6
+ "type": "module",
7
7
  "engines": {
8
8
  "node": ">=20.0.0"
9
9
  },
@@ -19,8 +19,9 @@
19
19
  "test:watch": "vitest",
20
20
  "test:coverage": "vitest run --coverage",
21
21
  "test:e2e": "vitest run --config vitest.e2e.config.ts",
22
+ "env:setup": "node scripts/setup-env.js",
22
23
  "db:wait": "node scripts/wait-for-db.js",
23
- "db:init": "npm run db:wait && npx prisma migrate dev --name init",
24
+ "db:init": "npm run env:setup && npm run db:wait && npx prisma migrate dev --name init",
24
25
  "prisma:generate": "prisma generate",
25
26
  "prisma:migrate": "prisma migrate dev",
26
27
  "prisma:migrate:deploy": "prisma migrate deploy",
@@ -48,8 +49,6 @@
48
49
  "@fastify/helmet": "^13.0.0",
49
50
  "@fastify/multipart": "^9.0.0",
50
51
  "@fastify/rate-limit": "^10.2.0",
51
- "@fastify/swagger": "^9.4.0",
52
- "@fastify/swagger-ui": "^5.2.0",
53
52
  "@prisma/client": "^6.2.0",
54
53
  "bcryptjs": "^2.4.3",
55
54
  "bullmq": "^5.34.0",
@@ -59,8 +58,7 @@
59
58
  "jsonwebtoken": "^9.0.2",
60
59
  "mysql2": "^3.11.5",
61
60
  "pino": "^9.6.0",
62
- "zod": "^3.24.0",
63
- "zod-to-json-schema": "^3.24.0"
61
+ "zod": "^3.24.0"
64
62
  },
65
63
  "devDependencies": {
66
64
  "@biomejs/biome": "^1.9.0",
@@ -68,9 +66,9 @@
68
66
  "@types/jsonwebtoken": "^9.0.5",
69
67
  "@types/node": "^22.10.0",
70
68
  "@types/supertest": "^6.0.2",
69
+ "@vitest/coverage-v8": "^3.0.0",
71
70
  "pino-pretty": "^13.0.0",
72
71
  "prisma": "^6.2.0",
73
- "@vitest/coverage-v8": "^3.0.0",
74
72
  "supertest": "^7.1.0",
75
73
  "tsc-alias": "^1.8.16",
76
74
  "tsx": "^4.19.0",
@@ -8,7 +8,8 @@
8
8
  // ==============================================
9
9
 
10
10
  generator client {
11
- provider = "prisma-client-js"
11
+ provider = "prisma-client-js"
12
+ binaryTargets = ["native"]
12
13
  }
13
14
 
14
15
  datasource db {
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Setup environment file
5
+ * Copies .env.example to .env if .env doesn't exist
6
+ */
7
+
8
+ import fs from 'fs';
9
+ import path from 'path';
10
+ import { fileURLToPath } from 'url';
11
+ import { dirname } from 'path';
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = dirname(__filename);
15
+
16
+ const ROOT_DIR = path.resolve(__dirname, '..');
17
+ const ENV_FILE = path.join(ROOT_DIR, '.env');
18
+ const EXAMPLE_ENV_FILE = path.join(ROOT_DIR, '.env.example');
19
+
20
+ function setupEnv() {
21
+ // Check if .env already exists
22
+ if (fs.existsSync(ENV_FILE)) {
23
+ console.log('✓ .env file already exists');
24
+ return;
25
+ }
26
+
27
+ // Check if .env.example exists
28
+ if (!fs.existsSync(EXAMPLE_ENV_FILE)) {
29
+ console.error('✗ .env.example file not found!');
30
+ console.error(' Please create a .env.example file first');
31
+ process.exit(1);
32
+ }
33
+
34
+ // Copy .env.example to .env
35
+ try {
36
+ fs.copyFileSync(EXAMPLE_ENV_FILE, ENV_FILE);
37
+ console.log('✓ Created .env file from .env.example');
38
+ console.log('');
39
+ console.log('⚠ IMPORTANT: Please update the following values in .env:');
40
+ console.log(' - DATABASE_URL (replace {{DATABASE_PASSWORD}} and {{DATABASE_NAME}})');
41
+ console.log(' - JWT_SECRET (use a strong secret in production)');
42
+ console.log(' - ADMIN_EMAIL and ADMIN_PASSWORD');
43
+ console.log('');
44
+ } catch (error) {
45
+ console.error('✗ Failed to create .env file:', error.message);
46
+ process.exit(1);
47
+ }
48
+ }
49
+
50
+ setupEnv();
@@ -5,9 +5,9 @@
5
5
  * This script polls the database connection until it's available
6
6
  */
7
7
 
8
- const { createConnection } = require('mysql2/promise');
9
- const { config } = require('dotenv');
10
- const { resolve } = require('path');
8
+ import { createConnection } from 'mysql2/promise';
9
+ import { config } from 'dotenv';
10
+ import { resolve } from 'path';
11
11
 
12
12
  // Load environment variables
13
13
  config({ path: resolve(process.cwd(), '.env') });
@@ -7,7 +7,6 @@
7
7
  * @see /mnt/project/02-general-rules.md
8
8
  * @see /mnt/project/06-response-handling.md
9
9
  * @see /mnt/project/08-observability.md
10
- * @see /mnt/project/09-api-documentation-v2.md
11
10
  * @see /mnt/project/11-rate-limiting-v2.md
12
11
  */
13
12
 
@@ -19,7 +18,6 @@ import logger from '@/libs/logger';
19
18
  import { setupErrorHandler } from '@/libs/error-handler';
20
19
  import { registerSecurityPlugins } from '@/plugins/security.plugin';
21
20
  import { registerRateLimit } from '@/plugins/rate-limit.plugin';
22
- import { registerSwagger } from '@/plugins/swagger.plugin';
23
21
  import { registerRequestHooks } from '@/hooks/request-timing.hook';
24
22
  import { authenticateMiddleware } from '@/libs/auth/authenticate.middleware';
25
23
  import { healthRoutes } from '@/routes/health.routes';
@@ -47,7 +45,6 @@ const buildApp = async () => {
47
45
  // 1. Plugins
48
46
  await registerSecurityPlugins(app);
49
47
  await registerRateLimit(app);
50
- await registerSwagger(app);
51
48
 
52
49
  // 2. Decorators
53
50
  app.decorate('authenticate', authenticateMiddleware);
@@ -54,13 +54,6 @@ const envSchema = z.object({
54
54
  RATE_LIMIT_MAX: z.coerce.number().int().positive().default(100),
55
55
  RATE_LIMIT_WINDOW: z.coerce.number().int().positive().default(900000), // 15 minutes
56
56
 
57
- // API Documentation Configuration
58
- SWAGGER_ENABLED: z
59
- .string()
60
- .transform((val) => val === 'true')
61
- .default('true'),
62
- SWAGGER_ROUTE: z.string().default('/docs'),
63
-
64
57
  // Development Tools
65
58
  PRISMA_QUERY_LOG: z
66
59
  .string()
@@ -1,7 +1,5 @@
1
1
  import { FastifyInstance } from 'fastify';
2
- import { toJsonSchema } from '@/libs/swagger-schemas';
3
2
  import * as adminController from './admin.controller';
4
- import * as schemas from './admin.schemas';
5
3
 
6
4
  /**
7
5
  * Admin Routes
@@ -13,12 +11,6 @@ export async function adminRoutes(app: FastifyInstance) {
13
11
  * List all users with pagination
14
12
  */
15
13
  app.get('/users', {
16
- schema: {
17
- description: 'List all users (admin only)',
18
- tags: ['admin'],
19
- querystring: toJsonSchema(schemas.ListUsersSchema, 'ListUsersSchema'),
20
- security: [{ bearerAuth: [] }],
21
- },
22
14
  preHandler: [app.authenticate, app.requireAdmin()],
23
15
  handler: adminController.listAllUsers,
24
16
  });
@@ -28,12 +20,6 @@ export async function adminRoutes(app: FastifyInstance) {
28
20
  * Get user details
29
21
  */
30
22
  app.get('/users/:id', {
31
- schema: {
32
- description: 'Get user by ID (admin only)',
33
- tags: ['admin'],
34
- params: toJsonSchema(schemas.UserIdSchema, 'UserIdSchema'),
35
- security: [{ bearerAuth: [] }],
36
- },
37
23
  preHandler: [app.authenticate, app.requireAdmin()],
38
24
  handler: adminController.getUserById,
39
25
  });
@@ -43,12 +29,6 @@ export async function adminRoutes(app: FastifyInstance) {
43
29
  * Delete user
44
30
  */
45
31
  app.delete('/users/:id', {
46
- schema: {
47
- description: 'Delete user (admin only)',
48
- tags: ['admin'],
49
- params: toJsonSchema(schemas.UserIdSchema, 'UserIdSchema'),
50
- security: [{ bearerAuth: [] }],
51
- },
52
32
  preHandler: [app.authenticate, app.requireAdmin()],
53
33
  handler: adminController.deleteUser,
54
34
  });
@@ -58,13 +38,6 @@ export async function adminRoutes(app: FastifyInstance) {
58
38
  * Change user role
59
39
  */
60
40
  app.post('/users/:id/change-role', {
61
- schema: {
62
- description: 'Change user role (admin only)',
63
- tags: ['admin'],
64
- params: toJsonSchema(schemas.UserIdSchema, 'UserIdSchema'),
65
- body: toJsonSchema(schemas.ChangeRoleSchema, 'ChangeRoleSchema'),
66
- security: [{ bearerAuth: [] }],
67
- },
68
41
  preHandler: [app.authenticate, app.requireAdmin()],
69
42
  handler: adminController.changeUserRole,
70
43
  });
@@ -74,12 +47,6 @@ export async function adminRoutes(app: FastifyInstance) {
74
47
  * Manually verify user email
75
48
  */
76
49
  app.post('/users/:id/verify-email', {
77
- schema: {
78
- description: 'Verify user email manually (admin only)',
79
- tags: ['admin'],
80
- params: toJsonSchema(schemas.UserIdSchema, 'UserIdSchema'),
81
- security: [{ bearerAuth: [] }],
82
- },
83
50
  preHandler: [app.authenticate, app.requireAdmin()],
84
51
  handler: adminController.verifyUserEmail,
85
52
  });
@@ -89,11 +56,6 @@ export async function adminRoutes(app: FastifyInstance) {
89
56
  * Get system statistics
90
57
  */
91
58
  app.get('/stats', {
92
- schema: {
93
- description: 'Get system statistics (admin only)',
94
- tags: ['admin'],
95
- security: [{ bearerAuth: [] }],
96
- },
97
59
  preHandler: [app.authenticate, app.requireAdmin()],
98
60
  handler: adminController.getSystemStats,
99
61
  });