qhttpx 2.0.1 → 2.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.
@@ -0,0 +1,798 @@
1
+ # QHttpX Production Deployment Guide
2
+
3
+ **Complete Guide to Deploying QHttpX in Production**
4
+
5
+ ---
6
+
7
+ ## Pre-Deployment Checklist
8
+
9
+ - [ ] All tests passing (`npm test`)
10
+ - [ ] Zero TypeScript errors (`npm run build`)
11
+ - [ ] No npm security vulnerabilities (`npm audit`)
12
+ - [ ] Environment variables documented
13
+ - [ ] Database migrations applied
14
+ - [ ] Backups scheduled
15
+ - [ ] Monitoring configured
16
+ - [ ] Alerting setup
17
+ - [ ] Logging aggregation ready
18
+ - [ ] Load testing completed
19
+ - [ ] Security review done
20
+ - [ ] Team trained on deployment
21
+ - [ ] Rollback plan documented
22
+ - [ ] Incident response plan ready
23
+
24
+ ---
25
+
26
+ ## Environment Setup
27
+
28
+ ### Environment Variables
29
+
30
+ Create `.env.production`:
31
+
32
+ ```bash
33
+ # Server
34
+ NODE_ENV=production
35
+ PORT=3000
36
+ WORKERS=4
37
+
38
+ # Database
39
+ DATABASE_URL=postgresql://user:pass@prod-db:5432/qhttpx_prod
40
+ DB_POOL_MIN=5
41
+ DB_POOL_MAX=20
42
+
43
+ # Authentication
44
+ JWT_SECRET=your-super-secret-key-min-32-chars
45
+ JWT_EXPIRES_IN=7d
46
+ BCRYPT_ROUNDS=12
47
+
48
+ # Security
49
+ CORS_ORIGIN=https://example.com,https://www.example.com
50
+ RATE_LIMIT_WINDOW_MS=900000
51
+ RATE_LIMIT_MAX=100
52
+
53
+ # HTTPS
54
+ TLS_KEY_PATH=/etc/ssl/private/key.pem
55
+ TLS_CERT_PATH=/etc/ssl/certs/cert.pem
56
+
57
+ # Logging
58
+ LOG_LEVEL=info
59
+ LOG_FORMAT=json
60
+
61
+ # Monitoring
62
+ SENTRY_DSN=https://xxx@xxx.ingest.sentry.io/123456
63
+ DATADOG_API_KEY=your-datadog-key
64
+ PROMETHEUS_PORT=9090
65
+
66
+ # Secrets
67
+ VAULT_ADDR=https://vault.example.com
68
+ VAULT_TOKEN=s.xxxxxx
69
+ ```
70
+
71
+ ### Environment Validation
72
+
73
+ ```typescript
74
+ // src/env.ts
75
+ import { z } from 'zod';
76
+
77
+ const envSchema = z.object({
78
+ NODE_ENV: z.enum(['development', 'production', 'test']),
79
+ PORT: z.coerce.number().min(1).max(65535),
80
+ DATABASE_URL: z.string().url(),
81
+ JWT_SECRET: z.string().min(32),
82
+ CORS_ORIGIN: z.string(),
83
+ TLS_KEY_PATH: z.string().optional(),
84
+ TLS_CERT_PATH: z.string().optional(),
85
+ LOG_LEVEL: z.enum(['debug', 'info', 'warn', 'error']),
86
+ SENTRY_DSN: z.string().url().optional(),
87
+ });
88
+
89
+ export const env = envSchema.parse(process.env);
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Deployment Strategies
95
+
96
+ ### 1. Traditional VPS/EC2 Deployment
97
+
98
+ #### Initial Setup
99
+
100
+ ```bash
101
+ # SSH into server
102
+ ssh ubuntu@your-server.com
103
+
104
+ # Update system
105
+ sudo apt update && sudo apt upgrade -y
106
+
107
+ # Install Node.js 18+
108
+ curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
109
+ sudo apt install -y nodejs
110
+
111
+ # Install PM2 (process manager)
112
+ sudo npm install -g pm2
113
+
114
+ # Clone repository
115
+ git clone https://github.com/yourusername/qhttpx-app.git
116
+ cd qhttpx-app
117
+
118
+ # Install dependencies
119
+ npm ci --production
120
+
121
+ # Build
122
+ npm run build
123
+ ```
124
+
125
+ #### PM2 Configuration
126
+
127
+ Create `ecosystem.config.js`:
128
+
129
+ ```javascript
130
+ module.exports = {
131
+ apps: [
132
+ {
133
+ name: 'qhttpx-app',
134
+ script: 'dist/index.js',
135
+ instances: 4,
136
+ exec_mode: 'cluster',
137
+ env: {
138
+ NODE_ENV: 'production',
139
+ PORT: 3000,
140
+ },
141
+ // Restart if memory exceeds 500MB
142
+ max_memory_restart: '500M',
143
+ // Restart crashed apps
144
+ autorestart: true,
145
+ // Max restarts before waiting
146
+ max_restarts: 5,
147
+ min_uptime: '10s',
148
+ // Logging
149
+ out_file: '/var/log/qhttpx/out.log',
150
+ error_file: '/var/log/qhttpx/error.log',
151
+ log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
152
+ },
153
+ ],
154
+ };
155
+ ```
156
+
157
+ #### Startup
158
+
159
+ ```bash
160
+ # Start with PM2
161
+ pm2 start ecosystem.config.js
162
+
163
+ # Make it start on reboot
164
+ pm2 startup
165
+ pm2 save
166
+
167
+ # Monitor
168
+ pm2 monit
169
+ pm2 logs
170
+ ```
171
+
172
+ #### Nginx Reverse Proxy
173
+
174
+ ```nginx
175
+ upstream qhttpx {
176
+ server 127.0.0.1:3000;
177
+ server 127.0.0.1:3001;
178
+ server 127.0.0.1:3002;
179
+ server 127.0.0.1:3003;
180
+ }
181
+
182
+ server {
183
+ listen 443 ssl http2;
184
+ server_name api.example.com;
185
+
186
+ ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
187
+ ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
188
+
189
+ # Strong SSL config
190
+ ssl_protocols TLSv1.2 TLSv1.3;
191
+ ssl_ciphers HIGH:!aNULL:!MD5;
192
+ ssl_prefer_server_ciphers on;
193
+
194
+ # Security headers
195
+ add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
196
+ add_header X-Content-Type-Options "nosniff" always;
197
+ add_header X-Frame-Options "DENY" always;
198
+ add_header X-XSS-Protection "1; mode=block" always;
199
+
200
+ # Proxy settings
201
+ location / {
202
+ proxy_pass http://qhttpx;
203
+ proxy_set_header Host $host;
204
+ proxy_set_header X-Real-IP $remote_addr;
205
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
206
+ proxy_set_header X-Forwarded-Proto $scheme;
207
+
208
+ # WebSocket support
209
+ proxy_http_version 1.1;
210
+ proxy_set_header Upgrade $http_upgrade;
211
+ proxy_set_header Connection "upgrade";
212
+
213
+ # Timeouts
214
+ proxy_connect_timeout 60s;
215
+ proxy_send_timeout 60s;
216
+ proxy_read_timeout 60s;
217
+ }
218
+
219
+ # Health check endpoint
220
+ location /health {
221
+ access_log off;
222
+ proxy_pass http://qhttpx;
223
+ }
224
+ }
225
+
226
+ # Redirect HTTP to HTTPS
227
+ server {
228
+ listen 80;
229
+ server_name api.example.com;
230
+ return 301 https://$server_name$request_uri;
231
+ }
232
+ ```
233
+
234
+ #### Certbot for SSL
235
+
236
+ ```bash
237
+ # Install Let's Encrypt
238
+ sudo apt install certbot python3-certbot-nginx
239
+
240
+ # Get certificate
241
+ sudo certbot certonly --standalone -d api.example.com
242
+
243
+ # Auto-renewal
244
+ 0 12 * * * certbot renew --quiet && nginx -s reload
245
+ ```
246
+
247
+ ---
248
+
249
+ ### 2. Docker Deployment
250
+
251
+ #### Dockerfile
252
+
253
+ ```dockerfile
254
+ # Build stage
255
+ FROM node:18-alpine AS builder
256
+
257
+ WORKDIR /app
258
+
259
+ COPY package*.json ./
260
+ RUN npm ci
261
+
262
+ COPY . .
263
+ RUN npm run build
264
+
265
+ # Production stage
266
+ FROM node:18-alpine
267
+
268
+ WORKDIR /app
269
+
270
+ # Install dumb-init to handle signals properly
271
+ RUN apk add --no-cache dumb-init
272
+
273
+ # Copy from builder
274
+ COPY --from=builder /app/dist ./dist
275
+ COPY --from=builder /app/node_modules ./node_modules
276
+ COPY package*.json ./
277
+
278
+ # Non-root user
279
+ RUN addgroup -g 1001 -S nodejs && \
280
+ adduser -S nodejs -u 1001
281
+
282
+ USER nodejs
283
+
284
+ # Health check
285
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
286
+ CMD node -e "require('http').get('http://localhost:3000/health', (r) => {if (r.statusCode !== 200) throw new Error(r.statusCode)})"
287
+
288
+ # Use dumb-init to forward signals
289
+ ENTRYPOINT ["dumb-init", "--"]
290
+
291
+ # Start app
292
+ CMD ["node", "dist/index.js"]
293
+ ```
294
+
295
+ #### docker-compose.yml
296
+
297
+ ```yaml
298
+ version: '3.8'
299
+
300
+ services:
301
+ app:
302
+ build: .
303
+ ports:
304
+ - "3000:3000"
305
+ environment:
306
+ NODE_ENV: production
307
+ DATABASE_URL: postgresql://user:pass@postgres:5432/qhttpx
308
+ JWT_SECRET: ${JWT_SECRET}
309
+ depends_on:
310
+ postgres:
311
+ condition: service_healthy
312
+ restart: unless-stopped
313
+ healthcheck:
314
+ test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
315
+ interval: 30s
316
+ timeout: 10s
317
+ retries: 3
318
+ start_period: 5s
319
+ networks:
320
+ - qhttpx-network
321
+
322
+ postgres:
323
+ image: postgres:15-alpine
324
+ environment:
325
+ POSTGRES_PASSWORD: ${DB_PASSWORD}
326
+ POSTGRES_DB: qhttpx
327
+ volumes:
328
+ - postgres_data:/var/lib/postgresql/data
329
+ restart: unless-stopped
330
+ healthcheck:
331
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
332
+ interval: 10s
333
+ timeout: 5s
334
+ retries: 5
335
+ networks:
336
+ - qhttpx-network
337
+
338
+ nginx:
339
+ image: nginx:alpine
340
+ ports:
341
+ - "80:80"
342
+ - "443:443"
343
+ volumes:
344
+ - ./nginx.conf:/etc/nginx/nginx.conf:ro
345
+ - ./ssl:/etc/nginx/ssl:ro
346
+ depends_on:
347
+ - app
348
+ restart: unless-stopped
349
+ networks:
350
+ - qhttpx-network
351
+
352
+ volumes:
353
+ postgres_data:
354
+
355
+ networks:
356
+ qhttpx-network:
357
+ driver: bridge
358
+ ```
359
+
360
+ #### Build and Deploy
361
+
362
+ ```bash
363
+ # Build image
364
+ docker build -t qhttpx:latest .
365
+
366
+ # Run with compose
367
+ docker-compose up -d
368
+
369
+ # Monitor
370
+ docker-compose logs -f app
371
+
372
+ # Scale
373
+ docker-compose up -d --scale app=4
374
+ ```
375
+
376
+ ---
377
+
378
+ ### 3. Kubernetes Deployment
379
+
380
+ #### Deployment
381
+
382
+ ```yaml
383
+ apiVersion: apps/v1
384
+ kind: Deployment
385
+ metadata:
386
+ name: qhttpx-app
387
+ labels:
388
+ app: qhttpx
389
+ spec:
390
+ replicas: 3
391
+ selector:
392
+ matchLabels:
393
+ app: qhttpx
394
+ template:
395
+ metadata:
396
+ labels:
397
+ app: qhttpx
398
+ spec:
399
+ containers:
400
+ - name: qhttpx
401
+ image: qhttpx:latest
402
+ imagePullPolicy: Always
403
+ ports:
404
+ - name: http
405
+ containerPort: 3000
406
+ env:
407
+ - name: NODE_ENV
408
+ value: production
409
+ - name: DATABASE_URL
410
+ valueFrom:
411
+ secretKeyRef:
412
+ name: qhttpx-secrets
413
+ key: database-url
414
+ - name: JWT_SECRET
415
+ valueFrom:
416
+ secretKeyRef:
417
+ name: qhttpx-secrets
418
+ key: jwt-secret
419
+ livenessProbe:
420
+ httpGet:
421
+ path: /health
422
+ port: 3000
423
+ initialDelaySeconds: 10
424
+ periodSeconds: 30
425
+ timeoutSeconds: 5
426
+ failureThreshold: 3
427
+ readinessProbe:
428
+ httpGet:
429
+ path: /health
430
+ port: 3000
431
+ initialDelaySeconds: 5
432
+ periodSeconds: 10
433
+ timeoutSeconds: 5
434
+ resources:
435
+ requests:
436
+ memory: "256Mi"
437
+ cpu: "250m"
438
+ limits:
439
+ memory: "512Mi"
440
+ cpu: "500m"
441
+ ```
442
+
443
+ #### Service
444
+
445
+ ```yaml
446
+ apiVersion: v1
447
+ kind: Service
448
+ metadata:
449
+ name: qhttpx-service
450
+ spec:
451
+ type: LoadBalancer
452
+ selector:
453
+ app: qhttpx
454
+ ports:
455
+ - name: http
456
+ port: 80
457
+ targetPort: 3000
458
+ - name: https
459
+ port: 443
460
+ targetPort: 3000
461
+ ```
462
+
463
+ #### Deploy
464
+
465
+ ```bash
466
+ # Create secrets
467
+ kubectl create secret generic qhttpx-secrets \
468
+ --from-literal=database-url=$DATABASE_URL \
469
+ --from-literal=jwt-secret=$JWT_SECRET
470
+
471
+ # Deploy
472
+ kubectl apply -f deployment.yaml
473
+ kubectl apply -f service.yaml
474
+
475
+ # Monitor
476
+ kubectl get pods
477
+ kubectl logs -f deployment/qhttpx-app
478
+ kubectl describe service qhttpx-service
479
+ ```
480
+
481
+ ---
482
+
483
+ ## Database Migrations
484
+
485
+ ### Migration Script
486
+
487
+ ```typescript
488
+ // scripts/migrate.ts
489
+ import { Pool } from 'pg';
490
+
491
+ const pool = new Pool({
492
+ connectionString: process.env.DATABASE_URL,
493
+ });
494
+
495
+ const migrations = [
496
+ {
497
+ name: '001_create_users_table',
498
+ up: async (client) => {
499
+ await client.query(`
500
+ CREATE TABLE users (
501
+ id SERIAL PRIMARY KEY,
502
+ email VARCHAR(255) UNIQUE NOT NULL,
503
+ password VARCHAR(255) NOT NULL,
504
+ name VARCHAR(255),
505
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
506
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
507
+ )
508
+ `);
509
+ },
510
+ down: async (client) => {
511
+ await client.query('DROP TABLE users');
512
+ },
513
+ },
514
+ ];
515
+
516
+ async function migrate(direction: 'up' | 'down') {
517
+ const client = await pool.connect();
518
+
519
+ try {
520
+ // Create migrations table
521
+ await client.query(`
522
+ CREATE TABLE IF NOT EXISTS migrations (
523
+ id SERIAL PRIMARY KEY,
524
+ name VARCHAR(255) UNIQUE NOT NULL,
525
+ executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
526
+ )
527
+ `);
528
+
529
+ for (const migration of migrations) {
530
+ const executed = await client.query(
531
+ 'SELECT * FROM migrations WHERE name = $1',
532
+ [migration.name]
533
+ );
534
+
535
+ if (direction === 'up' && executed.rows.length === 0) {
536
+ console.log(`Running: ${migration.name}`);
537
+ await migration.up(client);
538
+ await client.query(
539
+ 'INSERT INTO migrations (name) VALUES ($1)',
540
+ [migration.name]
541
+ );
542
+ } else if (direction === 'down' && executed.rows.length > 0) {
543
+ console.log(`Rolling back: ${migration.name}`);
544
+ await migration.down(client);
545
+ await client.query(
546
+ 'DELETE FROM migrations WHERE name = $1',
547
+ [migration.name]
548
+ );
549
+ }
550
+ }
551
+
552
+ console.log('Done!');
553
+ } finally {
554
+ await client.end();
555
+ }
556
+ }
557
+
558
+ const direction = process.argv[2] as 'up' | 'down' || 'up';
559
+ migrate(direction).catch(console.error);
560
+ ```
561
+
562
+ #### Run Migrations
563
+
564
+ ```bash
565
+ # Run migrations before deployment
566
+ npm run migrate:up
567
+
568
+ # Test rollback
569
+ npm run migrate:down
570
+
571
+ # Run migrations again
572
+ npm run migrate:up
573
+ ```
574
+
575
+ ---
576
+
577
+ ## Monitoring & Observability
578
+
579
+ ### Health Check Endpoint
580
+
581
+ ```typescript
582
+ app.get('/health', ({ json }) => {
583
+ json({
584
+ status: 'healthy',
585
+ uptime: process.uptime(),
586
+ timestamp: new Date().toISOString(),
587
+ }, 200);
588
+ });
589
+
590
+ app.get('/readiness', ({ json, db }) => {
591
+ try {
592
+ // Test database
593
+ await db?.query('SELECT 1');
594
+ json({ ready: true }, 200);
595
+ } catch {
596
+ json({ ready: false }, 503);
597
+ }
598
+ });
599
+ ```
600
+
601
+ ### Metrics Endpoint
602
+
603
+ ```typescript
604
+ import client from 'prom-client';
605
+
606
+ app.get('/metrics', ({ send }) => {
607
+ const metrics = client.register.metrics();
608
+ send(metrics, 200);
609
+ });
610
+ ```
611
+
612
+ ### Logging Aggregation
613
+
614
+ ```typescript
615
+ import pino from 'pino';
616
+
617
+ const logger = pino({
618
+ transport: {
619
+ target: 'pino-loki',
620
+ options: {
621
+ host: 'logs-server.example.com',
622
+ basicAuth: process.env.LOKI_AUTH,
623
+ },
624
+ },
625
+ });
626
+
627
+ app.use(async ({ req, next }) => {
628
+ logger.info({ method: req.method, path: req.url });
629
+ if (next) await next();
630
+ });
631
+ ```
632
+
633
+ ### Error Tracking
634
+
635
+ ```typescript
636
+ import * as Sentry from "@sentry/node";
637
+
638
+ Sentry.init({
639
+ dsn: process.env.SENTRY_DSN,
640
+ environment: process.env.NODE_ENV,
641
+ tracesSampleRate: 0.1,
642
+ });
643
+
644
+ app.onError(({ error, json }) => {
645
+ Sentry.captureException(error);
646
+ json({ error: 'Internal server error' }, 500);
647
+ });
648
+ ```
649
+
650
+ ---
651
+
652
+ ## Performance Tuning
653
+
654
+ ### Node.js Flags
655
+
656
+ ```bash
657
+ # Increase memory limit
658
+ node --max-old-space-size=4096 dist/index.js
659
+
660
+ # Enable profiling
661
+ node --prof dist/index.js
662
+ node --prof-process isolate-*.log > profile.txt
663
+ ```
664
+
665
+ ### PM2 Cluster Mode
666
+
667
+ ```javascript
668
+ module.exports = {
669
+ apps: [
670
+ {
671
+ name: 'qhttpx',
672
+ script: 'dist/index.js',
673
+ instances: 'max', // Use all CPU cores
674
+ exec_mode: 'cluster',
675
+ merge_logs: true,
676
+ },
677
+ ],
678
+ };
679
+ ```
680
+
681
+ ---
682
+
683
+ ## Backup & Disaster Recovery
684
+
685
+ ### Database Backups
686
+
687
+ ```bash
688
+ # Daily backup
689
+ 0 2 * * * pg_dump $DATABASE_URL > /backups/db-$(date +%Y%m%d).sql
690
+
691
+ # Weekly full backup to S3
692
+ 0 3 * * 0 pg_dump $DATABASE_URL | gzip | aws s3 cp - s3://backups/db-$(date +%Y%m%d).sql.gz
693
+ ```
694
+
695
+ ### Restore from Backup
696
+
697
+ ```bash
698
+ # From local backup
699
+ psql $DATABASE_URL < /backups/db-20240121.sql
700
+
701
+ # From S3 backup
702
+ aws s3 cp s3://backups/db-20240121.sql.gz - | gunzip | psql $DATABASE_URL
703
+ ```
704
+
705
+ ---
706
+
707
+ ## Rollback Procedure
708
+
709
+ ```bash
710
+ # Tag release
711
+ git tag v1.0.0
712
+
713
+ # If something goes wrong
714
+ git checkout v0.9.9
715
+ npm install
716
+ npm run build
717
+
718
+ # Restart with PM2
719
+ pm2 restart qhttpx
720
+ ```
721
+
722
+ ---
723
+
724
+ ## Post-Deployment Checklist
725
+
726
+ - [ ] Server responding to health checks
727
+ - [ ] Database connections working
728
+ - [ ] Logging aggregation receiving logs
729
+ - [ ] Monitoring collecting metrics
730
+ - [ ] Alerting configured and tested
731
+ - [ ] SSL certificate valid
732
+ - [ ] All endpoints tested
733
+ - [ ] Performance acceptable
734
+ - [ ] Load testing completed
735
+ - [ ] Security scan passed
736
+ - [ ] Team notified of deployment
737
+ - [ ] Documentation updated
738
+ - [ ] Rollback plan confirmed
739
+
740
+ ---
741
+
742
+ ## Troubleshooting
743
+
744
+ ### High Memory Usage
745
+
746
+ ```bash
747
+ # Check memory
748
+ ps aux | grep node
749
+
750
+ # Profile memory
751
+ node --inspect dist/index.js
752
+ # Then open chrome://inspect in Chrome DevTools
753
+
754
+ # Check for memory leaks
755
+ npm install clinic
756
+ clinic doctor -- node dist/index.js
757
+ ```
758
+
759
+ ### Database Connection Issues
760
+
761
+ ```bash
762
+ # Test connection
763
+ psql $DATABASE_URL
764
+
765
+ # Check pool status
766
+ app.get('/debug/db', ({ json }) => {
767
+ json({
768
+ poolSize: pool.totalCount,
769
+ idle: pool.idleCount,
770
+ waiting: pool.waitingCount,
771
+ });
772
+ });
773
+ ```
774
+
775
+ ### High Latency
776
+
777
+ ```bash
778
+ # Check slow endpoints
779
+ app.use(async ({ path, next }) => {
780
+ const start = Date.now();
781
+ if (next) await next();
782
+ const duration = Date.now() - start;
783
+ if (duration > 1000) {
784
+ console.warn(`Slow: ${path} (${duration}ms)`);
785
+ }
786
+ });
787
+ ```
788
+
789
+ ---
790
+
791
+ ## Support
792
+
793
+ For deployment issues:
794
+ 1. Check logs: `pm2 logs` or `kubectl logs`
795
+ 2. Check metrics: Visit `/metrics` endpoint
796
+ 3. Check health: Visit `/health` endpoint
797
+ 4. Read security guide: `docs/SECURITY.md`
798
+ 5. Consult API reference: `docs/API_REFERENCE.md`