diff --git a/.env.database.example b/.env.database.example new file mode 100644 index 000000000..aeaf206ff --- /dev/null +++ b/.env.database.example @@ -0,0 +1,44 @@ +# Database Connection Pool Optimization - Environment Configuration + +# Enhanced Connection Pool Settings +# Production Environment (Recommended values) +DATABASE_POOL_MAX=20 +DATABASE_POOL_MIN=2 +DATABASE_IDLE_TIMEOUT=30000 +DATABASE_CONNECTION_TIMEOUT=10000 +DATABASE_ACQUIRE_TIMEOUT=60000 +DATABASE_CREATE_TIMEOUT=30000 +DATABASE_DESTROY_TIMEOUT=5000 +DATABASE_REAP_INTERVAL=1000 +DATABASE_CREATE_RETRY_INTERVAL=200 + +# Connection Validation and Health Checks +DATABASE_VALIDATE_CONNECTIONS=true +DATABASE_RUN_ON_CONNECT=true +DATABASE_CONNECTION_MAX_USES=10000 + +# Performance Tuning +DATABASE_STATEMENT_TIMEOUT=30000 +DATABASE_QUERY_TIMEOUT=30000 +DATABASE_APP_NAME=strellerminds-backend + +# SSL Configuration (Production) +DATABASE_SSL_REJECT_UNAUTHORIZED=true +# DATABASE_SSL_CERT=/path/to/cert.pem +# DATABASE_SSL_KEY=/path/to/key.pem +# DATABASE_SSL_CA=/path/to/ca.pem + +# Pool Monitoring (Development Only) +DATABASE_POOL_LOG=true + +# Development Environment (Lower values for testing) +# DATABASE_POOL_MAX=5 +# DATABASE_POOL_MIN=1 +# DATABASE_IDLE_TIMEOUT=15000 +# DATABASE_CONNECTION_TIMEOUT=5000 + +# High Load Environment (For traffic spikes) +# DATABASE_POOL_MAX=50 +# DATABASE_POOL_MIN=5 +# DATABASE_IDLE_TIMEOUT=60000 +# DATABASE_CONNECTION_TIMEOUT=15000 diff --git a/.env.development b/.env.development index a199873c7..df28c9dc5 100644 --- a/.env.development +++ b/.env.development @@ -3,18 +3,18 @@ PORT=3000 DATABASE_HOST=localhost DATABASE_PORT=5432 -DATABASE_USER=Lagodxy -DATABASE_PASSWORD=4633922uw8w8w8w -DATABASE_NAME=StrellerMinds +DATABASE_USER=your_database_user_here +DATABASE_PASSWORD=your_database_password_here +DATABASE_NAME=your_database_name_here # Authentication -JWT_SECRET=mysecretJourney +JWT_SECRET=your_jwt_secret_here -#Claudinary Credentials -CLOUDINARY_CLOUD_NAME=ds3czwdtg -CLOUDINARY_API_KEY=377545627931675 -CLOUDINARY_API_SECRET=pFrg1_s63mhEQ_v9w-IVdDFq3jE -STELLAR_SECRET_KEY="jgjxvsjxvwjxsjxgskjxksmxjswkxwgxwdcj" +#Cloudinary Credentials +CLOUDINARY_CLOUD_NAME=your_cloud_name_here +CLOUDINARY_API_KEY=your_cloudinary_api_key_here +CLOUDINARY_API_SECRET=your_cloudinary_api_secret_here +STELLAR_SECRET_KEY=your_stellar_secret_key_here NODE_ENV=development EMAIL_ENABLED=false diff --git a/.env.example b/.env.example index 4afa477cd..e5b6e7c16 100644 --- a/.env.example +++ b/.env.example @@ -1,12 +1,15 @@ +# WARNING: Never commit real credentials to version control. +# Copy this file to .env and fill in your actual values. +# Use a secrets manager (e.g., AWS Secrets Manager, Vault) in production. DATABASE_HOST=localhost DATABASE_PORT=5432 -DATABASE_USER=postgres -DATABASE_PASSWORD=adetomi.54 -DATABASE_NAME=streller_minds +DATABASE_USER=your_database_user_here +DATABASE_PASSWORD=your_database_password_here +DATABASE_NAME=your_database_name_here # JWT Configuration -JWT_SECRET=your_super_secret_jwt_key_here_change_in_production -JWT_REFRESH_SECRET=your_super_secret_refresh_key_here_change_in_production +JWT_SECRET=secure_placeholder_32_characters_minimum_1 +JWT_REFRESH_SECRET=secure_placeholder_32_characters_minimum_2 JWT_EXPIRES_IN=15m JWT_REFRESH_EXPIRES_IN=7d JWT_EMAIL_EXPIRES_IN=24h @@ -33,12 +36,37 @@ CLOUDINARY_API_KEY=your_api_key CLOUDINARY_API_SECRET=your_api_secret # Stellar Soroban Configuration +# SECURITY: Never commit real credentials. Use a secrets manager in production. SOROBAN_RPC_URL=https://soroban-testnet.stellar.org STELLAR_NETWORK=TESTNET -CREDENTIAL_CONTRACT_ID=CC...your_contract_id_here... - -# Key Management (In production, use a secure key management service) -SIGNER_SECRET_KEY=S...your_secret_key_here... +CREDENTIAL_CONTRACT_ID= + +# Key Management - CRITICAL SECURITY NOTICE +# ⚠️ NEVER use production keys in development environments +# ⚠️ Store keys in a secure key management service (AWS Secrets Manager, HashiCorp Vault, etc.) +# ⚠️ Use separate key pairs for different environments (dev, staging, prod) +# ⚠️ Rotate keys regularly and monitor for unauthorized access +# ⚠️ For testnet: Generate test keys at https://laboratory.stellar.org +# +# STELLAR BLOCKCHAIN SECURITY BEST PRACTICES +# ===================================================== +# 1. NEVER commit real secret keys to version control +# 2. Use a secrets manager: AWS Secrets Manager, HashiCorp Vault, Azure Key Vault +# 3. Generate separate key pairs for each environment (dev, staging, prod) +# 4. Implement key rotation policies (recommended: every 90 days) +# 5. Monitor key usage and set up alerts for unauthorized access +# 6. Use hardware security modules (HSM) for production when possible +# 7. Implement multi-signature schemes for high-value operations +# 8. For testnet development: https://laboratory.stellar.org +# 9. Regular security audits of key storage and access patterns +# 10. Have a key compromise response plan in place +# +# Key Format: S + 55 uppercase alphanumeric characters (56 total) +# Example (DO NOT USE): SABCDEFGHIJKLMNOPQRSTUVWXYZ234567ABCDEFGHIJKLMNOPQRSTUV +# +# Security Validation: The application validates key format on startup. +# If you see validation errors, ensure you're using a properly formatted key from a secure source. +SIGNER_SECRET_KEY= # Connection Pool Settings DATABASE_POOL_MAX=10 @@ -55,6 +83,36 @@ BACKUP_RETENTION_DAYS=30 BACKUP_MONTHLY_RETENTION_MONTHS=12 BACKUP_VERIFICATION_ENABLED=true +# Backup Encryption +BACKUP_ENCRYPTION_ENABLED=true +BACKUP_ENCRYPTION_KEY=your-32-byte-base64-encoded-encryption-key-here + +# Backup Cloud Storage (S3) +BACKUP_CLOUD_UPLOAD_ENABLED=true +AWS_BACKUP_BUCKET=strellerminds-backups +AWS_BACKUP_REPLICA_BUCKET=strellerminds-backups-replica +AWS_BACKUP_REPLICA_REGION=us-west-2 +BACKUP_CROSS_REGION_REPLICATION=true + +# Backup Retention Policies +BACKUP_DAILY_RETENTION_DAYS=30 +BACKUP_WEEKLY_RETENTION_WEEKS=12 +BACKUP_MONTHLY_RETENTION_MONTHS=12 +BACKUP_YEARLY_RETENTION_YEARS=7 + +# Backup Scheduling +BACKUP_SCHEDULING_ENABLED=true + +# Recovery Testing +BACKUP_RECOVERY_TEST_ENABLED=true +BACKUP_RECOVERY_TEST_DATABASE=strellerminds_recovery_test + +# Backup Alerts +BACKUP_ALERT_ON_SUCCESS=false +BACKUP_ALERT_ON_FAILURE=true +BACKUP_ALERT_ON_RECOVERY_TEST=true +BACKUP_STORAGE_WARNING_THRESHOLD_GB=500 + # Logging Configuration LOG_LEVEL=info LOG_FORMAT=json @@ -118,12 +176,6 @@ VIDEO_PROCESSING_TEMP_DIR=./temp/video-processing FFMPEG_PATH=/usr/bin/ffmpeg FFPROBE_PATH=/usr/bin/ffprobe -AWS_ACCESS_KEY_ID=test -AWS_SECRET_ACCESS_KEY=test -AWS_REGION=us-east-1 -S3_ENDPOINT=http://localhost:4566 -S3_BUCKET_NAME=media-bucket - # Video Security VIDEO_TOKEN_EXPIRY=3600 VIDEO_DRM_ENABLED=false @@ -159,4 +211,33 @@ OTEL_RESOURCE_ATTRIBUTES=service.version=1.0 OTEL_EXPORTER=jaeger OTEL_COLLECTOR_URL=http://localhost:14268/api/traces +ELASTICSEARCH_NODE=http://localhost:9200 +ELASTICSEARCH_USERNAME=elastic +ELASTICSEARCH_PASSWORD=your_elasticsearch_password_here + +# Webhook Security Configuration +# Stripe Webhook Configuration +STRIPE_WEBHOOK_SECRET=whsec_your_stripe_webhook_secret_here + +# PayPal Webhook Configuration +PAYPAL_WEBHOOK_SECRET=your_paypal_webhook_secret_here + +# Zoom Webhook Configuration +ZOOM_WEBHOOK_SECRET=your_zoom_webhook_secret_here + +# Custom Webhook Configuration +CUSTOM_WEBHOOK_SECRET=your_custom_webhook_secret_here + +# Webhook Rate Limiting (per minute) +WEBHOOK_RATE_LIMIT_STRIPE=100 +WEBHOOK_RATE_LIMIT_PAYPAL=50 +WEBHOOK_RATE_LIMIT_ZOOM=200 +WEBHOOK_RATE_LIMIT_CUSTOM=100 + +# Webhook Replay Protection (window in milliseconds) +WEBHOOK_REPLAY_WINDOW=300000 +# Webhook Logging Configuration +WEBHOOK_LOG_RETENTION_DAYS=30 +WEBHOOK_LOG_INCLUDE_PAYLOAD=false +WEBHOOK_LOG_INCLUDE_HEADERS=true \ No newline at end of file diff --git a/.env.locale b/.env.locale new file mode 100644 index 000000000..df28c9dc5 --- /dev/null +++ b/.env.locale @@ -0,0 +1,27 @@ +# Server Configuration +PORT=3000 + +DATABASE_HOST=localhost +DATABASE_PORT=5432 +DATABASE_USER=your_database_user_here +DATABASE_PASSWORD=your_database_password_here +DATABASE_NAME=your_database_name_here + +# Authentication +JWT_SECRET=your_jwt_secret_here + +#Cloudinary Credentials +CLOUDINARY_CLOUD_NAME=your_cloud_name_here +CLOUDINARY_API_KEY=your_cloudinary_api_key_here +CLOUDINARY_API_SECRET=your_cloudinary_api_secret_here +STELLAR_SECRET_KEY=your_stellar_secret_key_here + +NODE_ENV=development +EMAIL_ENABLED=false +EMAIL_TRACKING_ENABLED=true +EMAIL_TRACKING_SECRET= +EMAIL_TRACKING_BASE_URL=http://localhost:3000 + +# Redis Configuration +REDIS_HOST=localhost +REDIS_PORT=6379 \ No newline at end of file diff --git a/.env.production b/.env.production new file mode 100644 index 000000000..3fd844834 --- /dev/null +++ b/.env.production @@ -0,0 +1,185 @@ +# Production Environment Configuration +NODE_ENV=production +PORT=3000 + +# Database Configuration +DATABASE_HOST=prod-db.strellerminds.com +DATABASE_PORT=5432 +DATABASE_USER=strellerminds_prod +DATABASE_PASSWORD=CHANGE_THIS_TO_A_STRONG_PASSWORD_32_CHARS_MIN +DATABASE_NAME=strellerminds_prod + +# Database Pool Settings +DATABASE_POOL_MAX=20 +DATABASE_POOL_MIN=5 +DATABASE_IDLE_TIMEOUT=60000 +DATABASE_RETRY_ATTEMPTS=5 +DATABASE_RETRY_DELAY=5000 + +# JWT Configuration (Production - Use secrets manager) +JWT_SECRET=CHANGE_THIS_TO_A_STRONG_SECRET_32_CHARS_MIN +JWT_REFRESH_SECRET=CHANGE_THIS_TO_A_STRONG_REFRESH_SECRET_32_CHARS +JWT_EXPIRES_IN=15m +JWT_REFRESH_EXPIRES_IN=7d +JWT_EMAIL_EXPIRES_IN=24h +JWT_PASSWORD_RESET_EXPIRES_IN=1h + +# Email Configuration (Production) +SMTP_HOST=smtp.sendgrid.net +SMTP_PORT=587 +SMTP_SECURE=false +SMTP_USER=apikey +SMTP_PASS=CHANGE_THIS_TO_PRODUCTION_SENDGRID_API_KEY +SMTP_FROM=noreply@strellerminds.com + +# Rate Limiting (Production) +RATE_LIMIT_TTL=60000 +RATE_LIMIT_MAX=20 + +# File Uploads +UPLOAD_DIR=./uploads + +# Cloudinary Configuration (Production) +CLOUDINARY_CLOUD_NAME=CHANGE_THIS_TO_PRODUCTION_CLOUD_NAME +CLOUDINARY_API_KEY=CHANGE_THIS_TO_PRODUCTION_API_KEY +CLOUDINARY_API_SECRET=CHANGE_THIS_TO_PRODUCTION_API_SECRET_32_CHARS + +# Stellar Configuration (Production - Mainnet) +SOROBAN_RPC_URL=https://soroban.stellar.org +STELLAR_NETWORK=MAINNET +CREDENTIAL_CONTRACT_ID=CHANGE_THIS_TO_PRODUCTION_CONTRACT_ID +SIGNER_SECRET_KEY=CHANGE_THIS_TO_PRODUCTION_SECRET_KEY_56_CHARS + +# AWS Configuration (Production) +AWS_ACCESS_KEY_ID=CHANGE_THIS_TO_PRODUCTION_ACCESS_KEY_ID +AWS_SECRET_ACCESS_KEY=CHANGE_THIS_TO_PRODUCTION_SECRET_ACCESS_KEY +AWS_REGION=us-east-1 +AWS_BACKUP_BUCKET=strellerminds-prod-backups +AWS_BACKUP_REPLICA_BUCKET=strellerminds-prod-backups-replica +AWS_BACKUP_REPLICA_REGION=us-west-2 +AWS_CLOUDFRONT_DISTRIBUTION_ID=CHANGE_THIS_TO_PRODUCTION_CLOUDFRONT_ID +AWS_CLOUDFRONT_DOMAIN=CHANGE_THIS_TO_PRODUCTION_CLOUDFRONT_DOMAIN +AWS_S3_BUCKET=strellerminds-prod-videos +AWS_SIGNED_URL_EXPIRY=3600 + +# Backup Configuration (Production) +BACKUP_DIR=./backups +BACKUP_RETENTION_DAYS=90 +BACKUP_MONTHLY_RETENTION_MONTHS=24 +BACKUP_VERIFICATION_ENABLED=true +BACKUP_ENCRYPTION_ENABLED=true +BACKUP_ENCRYPTION_KEY=CHANGE_THIS_TO_PRODUCTION_BACKUP_ENCRYPTION_KEY +BACKUP_CLOUD_UPLOAD_ENABLED=true +BACKUP_CROSS_REGION_REPLICATION=true +BACKUP_DAILY_RETENTION_DAYS=90 +BACKUP_WEEKLY_RETENTION_WEEKS=52 +BACKUP_MONTHLY_RETENTION_MONTHS=24 +BACKUP_YEARLY_RETENTION_YEARS=10 +BACKUP_SCHEDULING_ENABLED=true +BACKUP_RECOVERY_TEST_ENABLED=true +BACKUP_RECOVERY_TEST_DATABASE=strellerminds_prod_recovery_test +BACKUP_ALERT_ON_SUCCESS=false +BACKUP_ALERT_ON_FAILURE=true +BACKUP_ALERT_ON_RECOVERY_TEST=true +BACKUP_STORAGE_WARNING_THRESHOLD_GB=1000 + +# Logging Configuration (Production) +LOG_LEVEL=warn +LOG_FORMAT=json +LOG_FILE_ENABLED=true +LOG_FILE_PATH=logs/prod/app-%DATE%.log +LOG_FILE_MAX_SIZE=50m +LOG_FILE_MAX_FILES=30 +LOG_CONSOLE_ENABLED=false +LOG_CONSOLE_COLORIZE=false +LOG_ERROR_FILE_ENABLED=true +LOG_ERROR_FILE_PATH=logs/prod/error-%DATE%.log +LOG_ERROR_FILE_MAX_SIZE=50m +LOG_ERROR_FILE_MAX_FILES=60 + +# Sentry Configuration (Production) +SENTRY_ENABLED=true +SENTRY_DSN=CHANGE_THIS_TO_PRODUCTION_SENTRY_DSN +SENTRY_TRACES_SAMPLE_RATE=0.1 +SENTRY_DEBUG=false +SENTRY_HTTP_INTEGRATION=true +SENTRY_EXPRESS_INTEGRATION=true +SENTRY_CONSOLE_INTEGRATION=true + +# Alerting Configuration (Production) +ALERTING_ENABLED=true +EMAIL_ALERTS_ENABLED=true +EMAIL_ALERT_RECIPIENTS=alerts@strellerminds.com,devops@strellerminds.com +SLACK_ALERTS_ENABLED=true +SLACK_WEBHOOK_URL=CHANGE_THIS_TO_PRODUCTION_SLACK_WEBHOOK_URL +SLACK_ALERT_CHANNEL=#production-alerts +WEBHOOK_ALERTS_ENABLED=true +WEBHOOK_ALERT_URL=CHANGE_THIS_TO_PRODUCTION_WEBHOOK_URL +WEBHOOK_ALERT_HEADERS={"Authorization":"Bearer CHANGE_THIS_TO_PRODUCTION_TOKEN"} +ERROR_RATE_THRESHOLD=0.02 +RESPONSE_TIME_THRESHOLD=3000 +CRITICAL_ERROR_CODES=INTERNAL_ERROR,DATABASE_ERROR,EXTERNAL_SERVICE_ERROR +ALERT_RATE_LIMITING_ENABLED=true +MAX_ALERTS_PER_HOUR=10 +ALERT_COOLDOWN_MINUTES=5 + +# Video Configuration (Production) +VIDEO_PROCESSING_ENABLED=true +VIDEO_PROCESSING_CONCURRENT_JOBS=4 +VIDEO_PROCESSING_TEMP_DIR=./temp/prod/video-processing +FFMPEG_PATH=/usr/bin/ffmpeg +FFPROBE_PATH=/usr/bin/ffprobe +VIDEO_TOKEN_EXPIRY=3600 +VIDEO_DRM_ENABLED=true +DRM_WIDEVINE_LICENSE_URL=CHANGE_THIS_TO_PRODUCTION_WIDEVINE_LICENSE_URL +DRM_WIDEVINE_CERT_URL=CHANGE_THIS_TO_PRODUCTION_WIDEVINE_CERT_URL +DRM_FAIRPLAY_LICENSE_URL=CHANGE_THIS_TO_PRODUCTION_FAIRPLAY_LICENSE_URL +DRM_FAIRPLAY_CERT_URL=CHANGE_THIS_TO_PRODUCTION_FAIRPLAY_CERT_URL +VIDEO_ANALYTICS_ENABLED=true +VIDEO_ANALYTICS_BATCH_SIZE=100 +VIDEO_ANALYTICS_RETENTION_DAYS=365 +VIDEO_DEFAULT_QUALITIES=240p,360p,480p,720p,1080p,4K +VIDEO_ADAPTIVE_STREAMING_ENABLED=true +VIDEO_HLS_ENABLED=true +VIDEO_DASH_ENABLED=true +VIDEO_THUMBNAIL_COUNT=10 +VIDEO_PREVIEW_ENABLED=true +VIDEO_MAX_FILE_SIZE=5368709120 +VIDEO_ALLOWED_FORMATS=mp4,webm,mov,avi,mkv +VIDEO_MAX_DURATION=7200 +VIDEO_MIN_DURATION=1 + +# OpenTelemetry Configuration (Production) +OTEL_SERVICE_NAME=streller-minds-backend-prod +OTEL_COLLECTOR_URL=http://prod-collector:4318/v1/traces +OTEL_EXPORTER=jaeger +OTEL_SAMPLER_PROBABILITY=0.1 +OTEL_RESOURCE_ATTRIBUTES=service.version=production,environment=production + +# Elasticsearch Configuration (Production) +ELASTICSEARCH_NODE=https://prod-elasticsearch.strellerminds.com:9200 +ELASTICSEARCH_USERNAME=prod_elastic +ELASTICSEARCH_PASSWORD=CHANGE_THIS_TO_PRODUCTION_ELASTICSEARCH_PASSWORD + +# Webhook Security Configuration (Production) +STRIPE_WEBHOOK_SECRET=CHANGE_THIS_TO_PRODUCTION_STRIPE_WEBHOOK_SECRET +PAYPAL_WEBHOOK_SECRET=CHANGE_THIS_TO_PRODUCTION_PAYPAL_WEBHOOK_SECRET +ZOOM_WEBHOOK_SECRET=CHANGE_THIS_TO_PRODUCTION_ZOOM_WEBHOOK_SECRET +CUSTOM_WEBHOOK_SECRET=CHANGE_THIS_TO_PRODUCTION_CUSTOM_WEBHOOK_SECRET +WEBHOOK_RATE_LIMIT_STRIPE=100 +WEBHOOK_RATE_LIMIT_PAYPAL=50 +WEBHOOK_RATE_LIMIT_ZOOM=200 +WEBHOOK_RATE_LIMIT_CUSTOM=100 +WEBHOOK_REPLAY_WINDOW=300000 +WEBHOOK_LOG_RETENTION_DAYS=90 +WEBHOOK_LOG_INCLUDE_PAYLOAD=false +WEBHOOK_LOG_INCLUDE_HEADERS=true + +# Redis Configuration (Production) +REDIS_HOST=prod-redis.strellerminds.com +REDIS_PORT=6379 + +# Feature Flags (Production) +FEATURE_NEW_DASHBOARD=false +FEATURE_DEBUG_MODE=false +FEATURE_EXPERIMENTAL_FEATURES=false diff --git a/.env.production.example b/.env.production.example index c23e22070..676914a97 100644 --- a/.env.production.example +++ b/.env.production.example @@ -14,7 +14,7 @@ JWT_SECRET=change_this_to_a_strong_secret_in_production ENCRYPTION_KEY=change_this_to_a_32_character_string # Monitoring -GRAFANA_PASSWORD=admin123 +GRAFANA_PASSWORD=your_grafana_password_here # Docker Registry DOCKER_REGISTRY=localhost:5000 \ No newline at end of file diff --git a/.env.staging b/.env.staging new file mode 100644 index 000000000..8b215931d --- /dev/null +++ b/.env.staging @@ -0,0 +1,185 @@ +# Staging Environment Configuration +NODE_ENV=staging +PORT=3000 + +# Database Configuration +DATABASE_HOST=staging-db.strellerminds.com +DATABASE_PORT=5432 +DATABASE_USER=strellerminds_staging +DATABASE_PASSWORD=staging_secure_password_32_chars_minimum +DATABASE_NAME=strellerminds_staging + +# Database Pool Settings +DATABASE_POOL_MAX=15 +DATABASE_POOL_MIN=2 +DATABASE_IDLE_TIMEOUT=60000 +DATABASE_RETRY_ATTEMPTS=5 +DATABASE_RETRY_DELAY=3000 + +# JWT Configuration (Staging) +JWT_SECRET=staging_jwt_secret_key_32_chars_minimum_secure +JWT_REFRESH_SECRET=staging_refresh_secret_key_32_chars_secure +JWT_EXPIRES_IN=15m +JWT_REFRESH_EXPIRES_IN=7d +JWT_EMAIL_EXPIRES_IN=24h +JWT_PASSWORD_RESET_EXPIRES_IN=1h + +# Email Configuration (Staging) +SMTP_HOST=smtp.sendgrid.net +SMTP_PORT=587 +SMTP_SECURE=false +SMTP_USER=apikey +SMTP_PASS=staging_sendgrid_api_key_32_chars_min +SMTP_FROM=noreply@strellerminds.com + +# Rate Limiting (Staging) +RATE_LIMIT_TTL=60000 +RATE_LIMIT_MAX=50 + +# File Uploads +UPLOAD_DIR=./uploads/staging + +# Cloudinary Configuration (Staging) +CLOUDINARY_CLOUD_NAME=staging_cloud_name +CLOUDINARY_API_KEY=staging_api_key_123456789 +CLOUDINARY_API_SECRET=staging_api_secret_32_chars_minimum + +# Stellar Configuration (Staging - Testnet) +SOROBAN_RPC_URL=https://soroban-testnet.stellar.org +STELLAR_NETWORK=TESTNET +CREDENTIAL_CONTRACT_ID=staging_test_contract_id_here +SIGNER_SECRET_KEY=SSGTSTKEY123456789ABCDEFGHIJKLMNOPQRSTUVWXYZSTG + +# AWS Configuration (Staging) +AWS_ACCESS_KEY_ID=AKIASTG1234567890123 +AWS_SECRET_ACCESS_KEY=staging_aws_secret_32_characters_minimum +AWS_REGION=us-east-1 +AWS_BACKUP_BUCKET=strellerminds-staging-backups +AWS_BACKUP_REPLICA_BUCKET=strellerminds-staging-backups-replica +AWS_BACKUP_REPLICA_REGION=us-west-2 +AWS_CLOUDFRONT_DISTRIBUTION_ID=STG1234567890ABC +AWS_CLOUDFRONT_DOMAIN=d1234567890abc.cloudfront.net +AWS_S3_BUCKET=strellerminds-staging-videos +AWS_SIGNED_URL_EXPIRY=3600 + +# Backup Configuration (Staging) +BACKUP_DIR=./backups/staging +BACKUP_RETENTION_DAYS=30 +BACKUP_MONTHLY_RETENTION_MONTHS=6 +BACKUP_VERIFICATION_ENABLED=true +BACKUP_ENCRYPTION_ENABLED=true +BACKUP_ENCRYPTION_KEY=staging_backup_encryption_key_32_chars +BACKUP_CLOUD_UPLOAD_ENABLED=true +BACKUP_CROSS_REGION_REPLICATION=true +BACKUP_DAILY_RETENTION_DAYS=30 +BACKUP_WEEKLY_RETENTION_WEEKS=8 +BACKUP_MONTHLY_RETENTION_MONTHS=6 +BACKUP_YEARLY_RETENTION_YEARS=2 +BACKUP_SCHEDULING_ENABLED=true +BACKUP_RECOVERY_TEST_ENABLED=true +BACKUP_RECOVERY_TEST_DATABASE=strellerminds_staging_recovery_test +BACKUP_ALERT_ON_SUCCESS=false +BACKUP_ALERT_ON_FAILURE=true +BACKUP_ALERT_ON_RECOVERY_TEST=true +BACKUP_STORAGE_WARNING_THRESHOLD_GB=100 + +# Logging Configuration (Staging) +LOG_LEVEL=info +LOG_FORMAT=json +LOG_FILE_ENABLED=true +LOG_FILE_PATH=logs/staging/app-%DATE%.log +LOG_FILE_MAX_SIZE=20m +LOG_FILE_MAX_FILES=14 +LOG_CONSOLE_ENABLED=true +LOG_CONSOLE_COLORIZE=false +LOG_ERROR_FILE_ENABLED=true +LOG_ERROR_FILE_PATH=logs/staging/error-%DATE%.log +LOG_ERROR_FILE_MAX_SIZE=20m +LOG_ERROR_FILE_MAX_FILES=30 + +# Sentry Configuration (Staging) +SENTRY_ENABLED=true +SENTRY_DSN=https://staging-sentry-dsn@sentry.io/project-id +SENTRY_TRACES_SAMPLE_RATE=0.5 +SENTRY_DEBUG=false +SENTRY_HTTP_INTEGRATION=true +SENTRY_EXPRESS_INTEGRATION=true +SENTRY_CONSOLE_INTEGRATION=true + +# Alerting Configuration (Staging) +ALERTING_ENABLED=true +EMAIL_ALERTS_ENABLED=true +EMAIL_ALERT_RECIPIENTS=staging@example.com,devops@example.com +SLACK_ALERTS_ENABLED=true +SLACK_WEBHOOK_URL=https://hooks.slack.com/services/STG/SLACK/WEBHOOK +SLACK_ALERT_CHANNEL=#staging-alerts +WEBHOOK_ALERTS_ENABLED=false +WEBHOOK_ALERT_URL=https://staging-webhook-url.com/alerts +WEBHOOK_ALERT_HEADERS={"Authorization":"Bearer staging-token"} +ERROR_RATE_THRESHOLD=0.05 +RESPONSE_TIME_THRESHOLD=5000 +CRITICAL_ERROR_CODES=INTERNAL_ERROR,DATABASE_ERROR,EXTERNAL_SERVICE_ERROR +ALERT_RATE_LIMITING_ENABLED=true +MAX_ALERTS_PER_HOUR=15 +ALERT_COOLDOWN_MINUTES=3 + +# Video Configuration (Staging) +VIDEO_PROCESSING_ENABLED=true +VIDEO_PROCESSING_CONCURRENT_JOBS=2 +VIDEO_PROCESSING_TEMP_DIR=./temp/staging/video-processing +FFMPEG_PATH=/usr/bin/ffmpeg +FFPROBE_PATH=/usr/bin/ffprobe +VIDEO_TOKEN_EXPIRY=3600 +VIDEO_DRM_ENABLED=false +DRM_WIDEVINE_LICENSE_URL=https://staging-drm-provider.com/widevine/license +DRM_WIDEVINE_CERT_URL=https://staging-drm-provider.com/widevine/cert +DRM_FAIRPLAY_LICENSE_URL=https://staging-drm-provider.com/fairplay/license +DRM_FAIRPLAY_CERT_URL=https://staging-drm-provider.com/fairplay/cert +VIDEO_ANALYTICS_ENABLED=true +VIDEO_ANALYTICS_BATCH_SIZE=50 +VIDEO_ANALYTICS_RETENTION_DAYS=90 +VIDEO_DEFAULT_QUALITIES=240p,360p,480p,720p,1080p +VIDEO_ADAPTIVE_STREAMING_ENABLED=true +VIDEO_HLS_ENABLED=true +VIDEO_DASH_ENABLED=true +VIDEO_THUMBNAIL_COUNT=5 +VIDEO_PREVIEW_ENABLED=true +VIDEO_MAX_FILE_SIZE=1073741824 +VIDEO_ALLOWED_FORMATS=mp4,webm,mov,avi +VIDEO_MAX_DURATION=3600 +VIDEO_MIN_DURATION=1 + +# OpenTelemetry Configuration (Staging) +OTEL_SERVICE_NAME=streller-minds-backend-staging +OTEL_COLLECTOR_URL=http://staging-collector:4318/v1/traces +OTEL_EXPORTER=jaeger +OTEL_SAMPLER_PROBABILITY=0.5 +OTEL_RESOURCE_ATTRIBUTES=service.version=staging,environment=staging + +# Elasticsearch Configuration (Staging) +ELASTICSEARCH_NODE=https://staging-elasticsearch.strellerminds.com:9200 +ELASTICSEARCH_USERNAME=staging_elastic +ELASTICSEARCH_PASSWORD=staging_elasticsearch_password_secure + +# Webhook Security Configuration (Staging) +STRIPE_WEBHOOK_SECRET=whsec_staging_stripe_webhook_secret_32_chars +PAYPAL_WEBHOOK_SECRET=staging_paypal_webhook_secret_32_chars +ZOOM_WEBHOOK_SECRET=staging_zoom_webhook_secret_32_chars +CUSTOM_WEBHOOK_SECRET=staging_custom_webhook_secret_32_chars +WEBHOOK_RATE_LIMIT_STRIPE=100 +WEBHOOK_RATE_LIMIT_PAYPAL=50 +WEBHOOK_RATE_LIMIT_ZOOM=200 +WEBHOOK_RATE_LIMIT_CUSTOM=100 +WEBHOOK_REPLAY_WINDOW=300000 +WEBHOOK_LOG_RETENTION_DAYS=30 +WEBHOOK_LOG_INCLUDE_PAYLOAD=false +WEBHOOK_LOG_INCLUDE_HEADERS=true + +# Redis Configuration (Staging) +REDIS_HOST=staging-redis.strellerminds.com +REDIS_PORT=6379 + +# Feature Flags (Staging) +FEATURE_NEW_DASHBOARD=true +FEATURE_DEBUG_MODE=false +FEATURE_EXPERIMENTAL_FEATURES=true diff --git a/.eslintrc.js b/.eslintrc.js index d252b0cbb..8200c098e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { tsconfigRootDir: __dirname, sourceType: 'module', }, - plugins: ['@typescript-eslint', 'prettier'], + plugins: ['@typescript-eslint', 'prettier', 'import', 'strellerminds'], extends: [ 'eslint:recommended', 'plugin:@typescript-eslint/recommended', @@ -28,6 +28,9 @@ module.exports = { '@typescript-eslint/consistent-type-imports': 'off', '@typescript-eslint/no-require-imports': 'off', + // Duplicate import / module registration + 'import/no-duplicates': 'error', + // Prettier 'prettier/prettier': [ 'error', @@ -39,4 +42,12 @@ module.exports = { }, ], }, + overrides: [ + { + files: ['**/*.module.ts'], + rules: { + 'strellerminds/no-duplicate-nest-module-imports': 'error', + }, + }, + ], }; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..8a55c34f6 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,46 @@ + # Build and Deploy + build-and-deploy: + runs-on: ubuntu-latest + name: Build & Deploy + needs: [code-quality, test, security-scan] + if: always() + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Install dependencies + run: npm ci --legacy-peer-deps + + - name: Build application + run: npm run build + + - name: Build Docker image + run: | + docker build -t strellerminds-backend:${{ github.sha }} . + docker tag strellerminds-backend:${{ github.sha }} strellerminds-backend:latest + + - name: Log in to Docker Hub + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Push to Docker Hub + if: github.event_name != 'pull_request' + run: | + docker push strellerminds-backend:${{ github.sha }} + docker push strellerminds-backend:latest + + - name: Deploy to production + if: github.event_name != 'pull_request' + run: | + echo "Deployment step would go here" + # Add your deployment logic here \ No newline at end of file diff --git a/.github/workflows/enhanced-security.yml b/.github/workflows/enhanced-security.yml new file mode 100644 index 000000000..93178f7b7 --- /dev/null +++ b/.github/workflows/enhanced-security.yml @@ -0,0 +1,402 @@ +name: Enhanced Security Scanning + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + schedule: + # Run comprehensive security scan daily at 3 AM UTC + - cron: '0 3 * * *' + workflow_dispatch: + +env: + NODE_VERSION: '20.x' + NODE_OPTIONS: '--max-old-space-size=4096' + +jobs: + # Dependency Security Audit + dependency-audit: + runs-on: ubuntu-latest + name: Dependency Security Audit + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Install dependencies + run: npm ci --legacy-peer-deps + + - name: Run npm audit + id: npm-audit + run: | + npm audit --audit-level=high --json > audit-report.json || true + if [ -s audit-report.json ]; then + VULNERABILITIES=$(cat audit-report.json | jq '.metadata.vulnerabilities | if . then .high + .critical else 0 end') + echo "vulnerabilities=$VULNERABILITIES" >> $GITHUB_OUTPUT + if [ "$VULNERABILITIES" -gt 0 ]; then + echo "::warning::Found $VULNERABILITIES high/critical vulnerabilities" + cat audit-report.json | jq '.vulnerabilities' + fi + fi + continue-on-error: true + + - name: Upload audit report + if: always() + uses: actions/upload-artifact@v4 + with: + name: npm-audit-report + path: audit-report.json + retention-days: 30 + + # Snyk Security Scan + snyk-scan: + runs-on: ubuntu-latest + name: Snyk Security Scan + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Install dependencies + run: npm ci --legacy-peer-deps + + - name: Run Snyk to check for vulnerabilities + uses: snyk/actions/node@master + continue-on-error: true + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + args: --severity-threshold=high --all-projects + if: env.SNYK_TOKEN != '' + + - name: Upload Snyk report + if: always() && env.SNYK_TOKEN != '' + uses: actions/upload-artifact@v4 + with: + name: snyk-report + path: snyk-report.html + retention-days: 30 + + # CodeQL Analysis + codeql: + runs-on: ubuntu-latest + name: CodeQL Analysis + needs: [dependency-audit] + if: always() # Run CodeQL on all events + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + continue-on-error: true + + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + continue-on-error: true + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" + continue-on-error: true + + # Container Security Scan + container-scan: + runs-on: ubuntu-latest + name: Container Security Scan + needs: [dependency-audit] + if: always() + permissions: + contents: read + security-events: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + run: | + docker build -t strellerminds-backend:test . || exit 0 + continue-on-error: true + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + image-ref: 'strellerminds-backend:test' + format: 'sarif' + output: 'trivy-results.sarif' + exit-code: '0' + severity: 'CRITICAL,HIGH' + ignore-unfixed: true + continue-on-error: true + + - name: Run Trivy vulnerability scanner (table output) + uses: aquasecurity/trivy-action@master + with: + image-ref: 'strellerminds-backend:test' + format: 'table' + exit-code: '0' + severity: 'CRITICAL,HIGH' + ignore-unfixed: true + continue-on-error: true + + - name: Upload Trivy scan results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + if: always() + with: + sarif_file: 'trivy-results.sarif' + continue-on-error: true + + - name: Upload Trivy report + if: always() + uses: actions/upload-artifact@v4 + with: + name: trivy-report + path: trivy-results.sarif + retention-days: 30 + + # OWASP ZAP Baseline Scan + zap-baseline: + runs-on: ubuntu-latest + name: OWASP ZAP Baseline Scan + needs: [dependency-audit] + if: always() # Run ZAP scan on all events + + services: + postgres: + image: postgres:15 + env: + POSTGRES_PASSWORD: test + POSTGRES_USER: test + POSTGRES_DB: test_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + redis: + image: redis:7 + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 6379:6379 + + env: + NODE_ENV: test + DATABASE_HOST: localhost + DATABASE_PORT: 5432 + DATABASE_USER: test + DATABASE_PASSWORD: test + DATABASE_NAME: test_db + REDIS_HOST: localhost + REDIS_PORT: 6379 + JWT_SECRET: test-jwt-secret-for-ci-only + JWT_REFRESH_SECRET: test-refresh-secret-for-ci-only + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: 'npm' + + - name: Install dependencies + run: npm ci --legacy-peer-deps + + - name: Build application + run: npm run build + + - name: Start application + run: | + npm run start:prod & + APP_PID=$! + echo "App PID: $APP_PID" + sleep 10 + + - name: Wait for application to be ready + run: | + echo "Waiting for application to be ready..." + for i in {1..30}; do + if curl -f http://localhost:3000/api/health; then + echo "Application is ready!" + exit 0 + fi + echo "Attempt $i: Application not ready yet, waiting..." + sleep 2 + done + echo "Application failed to start" + exit 1 + + - name: Run OWASP ZAP Baseline Scan + uses: zaproxy/action-baseline@v0.7.0 + with: + target: 'http://localhost:3000/api' + rules_file_name: '.zap/rules.tsv' + cmd_options: '-a' + continue-on-error: true + + - name: Upload ZAP report + if: always() + uses: actions/upload-artifact@v4 + with: + name: zap-report + path: report_html.html + retention-days: 30 + + # Secret Scanning + secret-scan: + runs-on: ubuntu-latest + name: Secret Scanning + if: always() # Run secret scan on all events + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Run Gitleaks + uses: gitleaks/gitleaks-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} + with: + config-path: '.gitleaks.toml' + report-path: 'gitleaks-report.json' + continue-on-error: true + + - name: Upload Gitleaks report + if: always() + uses: actions/upload-artifact@v4 + with: + name: gitleaks-report + path: gitleaks-report.json + retention-days: 30 + + # Security Scorecard + security-scorecard: + runs-on: ubuntu-latest + name: OSSF Security Scorecard + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' + permissions: + security-events: write + id-token: write + contents: read + actions: read + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Run OSSF Scorecard + uses: ossf/scorecard-action@v2.3.1 + with: + results_file: results.sarif + results_format: sarif + publish_results: true + continue-on-error: true + + - name: Upload Scorecard results to GitHub Security tab + uses: github/codeql-action/upload-sarif@v2 + if: always() + with: + sarif_file: results.sarif + continue-on-error: true + + - name: Upload Scorecard results + uses: actions/upload-artifact@v4 + if: always() + with: + name: security-scorecard + path: results.sarif + retention-days: 30 + + # Security Summary Report + security-summary: + runs-on: ubuntu-latest + name: Security Summary + needs: [dependency-audit, snyk-scan, codeql, container-scan, zap-baseline, secret-scan] + if: always() + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + + - name: Create Security Summary + run: | + echo "# Security Scan Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "## Scan Results" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Dependency Audit + if [ -f npm-audit-report/audit-report.json ]; then + VULNS=$(cat npm-audit-report/audit-report.json | jq '.metadata.vulnerabilities.total // 0') + echo "- **Dependency Audit**: $VULNS vulnerabilities found" >> $GITHUB_STEP_SUMMARY + else + echo "- **Dependency Audit**: ✅ Passed" >> $GITHUB_STEP_SUMMARY + fi + + # Container Scan + if [ -f trivy-report/trivy-results.sarif ]; then + CRITICAL=$(cat trivy-report/trivy-results.sarif | jq '.runs[0].results[] | select(.level == "error") | .level' | wc -l) + HIGH=$(cat trivy-report/trivy-results.sarif | jq '.runs[0].results[] | select(.level == "warning") | .level' | wc -l) + echo "- **Container Scan**: $CRITICAL critical, $HIGH high vulnerabilities" >> $GITHUB_STEP_SUMMARY + else + echo "- **Container Scan**: ✅ Passed" >> $GITHUB_STEP_SUMMARY + fi + + # Secret Scan + if [ -f gitleaks-report/gitleaks-report.json ]; then + SECRETS=$(cat gitleaks-report/gitleaks-report.json | jq '. | length') + if [ "$SECRETS" -gt 0 ]; then + echo "- **Secret Scan**: ❌ $SECRETS secrets found" >> $GITHUB_STEP_SUMMARY + else + echo "- **Secret Scan**: ✅ No secrets found" >> $GITHUB_STEP_SUMMARY + fi + else + echo "- **Secret Scan**: ✅ Passed" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "## Recommendations" >> $GITHUB_STEP_SUMMARY + echo "- Review and fix any high/critical vulnerabilities" >> $GITHUB_STEP_SUMMARY + echo "- Update dependencies to latest secure versions" >> $GITHUB_STEP_SUMMARY + echo "- Implement proper secret management" >> $GITHUB_STEP_SUMMARY + echo "- Regular security scanning and monitoring" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/security-audit.yml b/.github/workflows/security-audit.yml index d7521879f..bed123c74 100644 --- a/.github/workflows/security-audit.yml +++ b/.github/workflows/security-audit.yml @@ -3,8 +3,6 @@ name: Security Audit on: push: branches: [ main, develop ] - pull_request: - branches: [ main, develop ] schedule: # Run security audit daily at 2 AM UTC - cron: '0 2 * * *' diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 574927f66..4177f9769 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -3,37 +3,39 @@ name: Security Scan on: push: branches: [ main, develop ] - pull_request: - branches: [ main ] schedule: - cron: '0 0 * * 0' # Weekly scan + workflow_dispatch: + +env: + NODE_VERSION: '20.x' jobs: security: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Node.js - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: '18' + node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: Install dependencies - run: npm ci + run: npm ci --legacy-peer-deps - - name: Run security scan - run: | - chmod +x scripts/security-scan.sh - ./scripts/security-scan.sh + - name: Run npm audit + run: npm audit --audit-level=high + continue-on-error: true - name: Upload scan results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: name: security-scan-results path: | trivy-report.json - npm-audit-report.json \ No newline at end of file + npm-audit-report.json + retention-days: 7 \ No newline at end of file diff --git a/.github/workflows/test-coverage.yml b/.github/workflows/test-coverage.yml new file mode 100644 index 000000000..f170a3fa9 --- /dev/null +++ b/.github/workflows/test-coverage.yml @@ -0,0 +1,215 @@ +name: Test Coverage and Quality Checks + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main ] + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18.x, 20.x] + + services: + postgres: + image: postgres:15 + env: + POSTGRES_PASSWORD: postgres + POSTGRES_DB: test_strellerminds + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + redis: + image: redis:7 + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 6379:6379 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run linting + run: npm run lint + + - name: Run type checking + run: npm run build + + - name: Run unit tests with coverage + run: npm run test:cov + env: + NODE_ENV: test + DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_strellerminds + REDIS_URL: redis://localhost:6379 + + - name: Run integration tests + run: npm run test:integration:cov + env: + NODE_ENV: test + DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_strellerminds + REDIS_URL: redis://localhost:6379 + + - name: Run E2E tests + run: npm run test:e2e + env: + NODE_ENV: test + DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_strellerminds + REDIS_URL: redis://localhost:6379 + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + with: + file: ./coverage/lcov.info + flags: unittests + name: codecov-umbrella + fail_ci_if_error: true + + - name: Generate coverage report + run: | + npm run test:cov + npm run test:integration:cov + npx nyc merge coverage coverage/merged-coverage.json + npx nyc report --reporter=text-summary --reporter=html --reporter=json + + - name: Coverage comment + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const coverage = JSON.parse(fs.readFileSync('coverage/coverage-summary.json', 'utf8')); + + const total = coverage.total; + const lines = `${total.lines.pct}%`; + const functions = `${total.functions.pct}%`; + const branches = `${total.branches.pct}%`; + const statements = `${total.statements.pct}%`; + + const comment = ` + ## 📊 Test Coverage Report + + | Metric | Coverage | + |--------|----------| + | Lines | ${lines} | + | Functions | ${functions} | + | Branches | ${branches} | + | Statements | ${statements} | + + **Overall Coverage: ${statements}** + + ${total.statements.pct >= 80 ? '✅ Coverage threshold met!' : '❌ Coverage below 80% threshold'} + `; + + if (context.eventName === 'pull_request') { + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: comment + }); + } + + security: + runs-on: ubuntu-latest + needs: test + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20.x' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run security audit + run: npm audit --audit-level=moderate + + - name: Run security scan + run: npm run security:scan + + performance: + runs-on: ubuntu-latest + needs: test + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20.x' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Build application + run: npm run build + + - name: Start application + run: npm run start:prod & + env: + NODE_ENV: test + DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test_strellerminds + REDIS_URL: redis://localhost:6379 + + - name: Wait for application + run: sleep 30 + + - name: Run performance tests + run: npm run perf:load + + - name: Run stress tests + run: npm run perf:stress + + quality-gate: + runs-on: ubuntu-latest + needs: [test, security, performance] + if: always() + + steps: + - name: Quality gate check + run: | + if [[ "${{ needs.test.result }}" != "success" ]]; then + echo "❌ Tests failed" + exit 1 + fi + + if [[ "${{ needs.security.result }}" != "success" ]]; then + echo "❌ Security checks failed" + exit 1 + fi + + if [[ "${{ needs.performance.result }}" != "success" ]]; then + echo "⚠️ Performance tests failed" + exit 1 + fi + + echo "✅ All quality checks passed!" diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 000000000..3e2c0d153 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,192 @@ +# Gitleaks configuration for secret scanning + +title = "Gitleaks Config" + +# Extend the base (if this config is in a different location) +# extend = path/to/base/config + +[allowlist] +description = "Global allowlist" +paths = [ + '''gitleaks.toml''', + '''test/''', + '''node_modules/''', + '''coverage/''', + '''dist/''', + '''*.log''', + '''package-lock.json''', + '''yarn.lock''', + '''pnpm-lock.yaml''', + '''*.md''', + '''*.spec.ts''', + '''*.test.ts''', + '''**/test/**''', + '''**/tests/**''', + '''**/__tests__/**''', + '''cypress/''', + '''.github/''' +] + +# Additional regex-based allowlisting for common false positives +regexes = [ + '''test@example\.com''', + '''localhost''', + '''127\.0\.0\.1''', + '''0\.0\.0\.0''', + '''192\.168\.\d+\.\d+''', + '''10\.\d+\.\d+\.\d+''', + '''172\.(1[6-9]|2[0-9]|3[01])\.\d+\.\d+''' +] + +# Regex patterns for secrets +[[rules]] +description = "GitHub Personal Access Token" +id = "github-pat" +regex = '''ghp_[a-zA-Z0-9]{36}''' +keywords = ["ghp_"] + +[[rules]] +description = "GitHub OAuth Token" +id = "github-oauth" +regex = '''gho_[a-zA-Z0-9]{36}''' +keywords = ["gho_"] + +[[rules]] +description = "GitHub App Token" +id = "github-app" +regex = '''ghu_[a-zA-Z0-9]{36}''' +keywords = ["ghu_"] + +[[rules]] +description = "GitHub Server Token" +id = "github-server" +regex = '''ghs_[a-zA-Z0-9]{36}''' +keywords = ["ghs_"] + +[[rules]] +description = "GitHub Refresh Token" +id = "github-refresh" +regex = '''ghr_[a-zA-Z0-9]{36}''' +keywords = ["ghr_"] + +[[rules]] +description = "Stripe API Key" +id = "stripe-api-key" +regex = '''sk_live_[a-zA-Z0-9]{24}''' +keywords = ["sk_live_", "stripe"] + +[[rules]] +description = "Stripe Test API Key" +id = "stripe-test-key" +regex = '''sk_test_[a-zA-Z0-9]{24}''' +keywords = ["sk_test_", "stripe"] + +[[rules]] +description = "AWS Access Key ID" +id = "aws-access-key" +regex = '''AKIA[0-9A-Z]{16}''' +keywords = ["AKIA"] + +[[rules]] +description = "AWS Secret Access Key" +id = "aws-secret-key" +regex = '''[0-9a-zA-Z/+]{40}''' +keywords = ["aws"] + +[[rules]] +description = "Generic API Key" +id = "generic-api-key" +regex = '''(?i)(api[_-]?key|apikey|api[_-]?secret|secret[_-]?key)[\s=:'"`]+[0-9a-zA-Z]{16,}''' +keywords = ["api", "key", "secret"] + +[[rules]] +description = "JWT Token" +id = "jwt-token" +regex = '''eyJ[a-zA-Z0-9_-]*\.eyJ[a-zA-Z0-9_-]*\.[a-zA-Z0-9_-]*''' +keywords = ["eyJ"] + +[[rules]] +description = "Private Key" +id = "private-key" +regex = '''-----BEGIN (RSA |OPENSSH |DSA |EC |PGP )?PRIVATE KEY-----''' +keywords = ["BEGIN PRIVATE KEY", "BEGIN RSA PRIVATE KEY"] + +[[rules]] +description = "Database Connection String" +id = "database-connection" +regex = '''(?i)(mongodb|mysql|postgres|postgresql)://[^\s,;'"`]{20,}''' +keywords = ["mongodb://", "mysql://", "postgres://", "postgresql://"] + +[[rules]] +description = "Slack Token" +id = "slack-token" +regex = '''xox[baprs]-[0-9]{12}-[0-9]{12}-[0-9]{12}-[a-zA-Z0-9]{32}''' +keywords = ["xoxb-", "xoxp-", "xoxa-", "xoxr-", "xoxs-"] + +[[rules]] +description = "SendGrid API Key" +id = "sendgrid-key" +regex = '''SG\.[a-zA-Z0-9_-]{22}\.[a-zA-Z0-9_-]{43}''' +keywords = ["SG."] + +[[rules]] +description = "Twilio API Key" +id = "twilio-key" +regex = '''SK[0-9a-fA-F]{32}''' +keywords = ["SK"] + +[[rules]] +description = "NPM Token" +id = "npm-token" +regex = '''npm_[a-zA-Z0-9_-]{36}''' +keywords = ["npm_"] + +[[rules]] +description = "Docker Hub Token" +id = "docker-hub-token" +regex = '''dckr_pat_[a-zA-Z0-9_-]{32}''' +keywords = ["dckr_pat_"] + +[[rules]] +description = "Google API Key" +id = "google-api-key" +regex = '''AIza[0-9A-Za-z_-]{35}''' +keywords = ["AIza"] + +[[rules]] +description = "Heroku API Key" +id = "heroku-key" +regex = '''[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}''' +keywords = ["heroku"] + +[[rules]] +description = "Mailgun API Key" +id = "mailgun-key" +regex = '''key-[0-9a-zA-Z]{32}''' +keywords = ["key-", "mailgun"] + +[[rules]] +description = "Slack Webhook" +id = "slack-webhook" +regex = '''https://hooks\.slack\.com/services/T[0-9A-Z]{8}/B[0-9A-Z]{8}/[a-zA-Z0-9]{24}''' +keywords = ["hooks.slack.com"] + +# Disabled - causes too many false positives in test data and documentation +# [[rules]] +# description = "Email Address" +# id = "email-address" +# regex = '''[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}''' +# keywords = ["@", ".com", ".org", ".net", ".edu"] + +# Disabled - causes too many false positives +# [[rules]] +# description = "IP Address" +# id = "ip-address" +# regex = '''\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b''' +# keywords = ["192.168.", "10.", "127."] + +[[rules]] +description = "URL with credentials" +id = "url-with-credentials" +regex = '''https?://[^\s/$.?#].[^\s]*:[^\s@]+@[^\s/$.?#].[^\s]*''' +keywords = ["http://", "https://", "@"] diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..3abaa3553 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["codeium.codeium"] +} diff --git a/.zap/rules.tsv b/.zap/rules.tsv new file mode 100644 index 000000000..cc2f8387d --- /dev/null +++ b/.zap/rules.tsv @@ -0,0 +1,58 @@ +# OWASP ZAP Rules Configuration +# Format: rule_id description enabled + +# Disable rules that may cause false positives in testing environment +10021 X-Content-Type-Options header missing false +10055 Cookie without SameSite Attribute false +10056 CSP Scanner Rule Report false +10017 X-Debug-Token Information Leak false +10020 X-AspNet-Version Response Header false +10054 Cookie without Secure Attribute false + +# Enable important security rules +10003 Cross Domain Script Inclusion true +10004 Content-Type Header Missing true +10005 X-Frame-Options Header true +10006 Information Disclosure - Debug Error Messages true +10009 Information Disclosure - Sensitive Information in URL true +10010 Information Disclosure - Sensitive Information in HTTP Referrer Header true +10011 Information Disclosure - Sensitive Information in HTTP Response Header true +10012 Information Disclosure - Sensitive Information in JavaScript Files true +10013 Information Disclosure - Sensitive Information in HTML Comments true +10014 Information Disclosure - Sensitive Information in CSS Files true +10015 Information Disclosure - Sensitive Information in HTML Forms true +10016 Information Disclosure - Sensitive Information in HTML Meta Tags true +10018 Information Disclosure - Sensitive Information in JavaScript Variables true +10019 Information Disclosure - Sensitive Information in HTML Attributes true +10022 Directory Browsing true +10023 Heartbleed OpenSSL Vulnerability true +10024 SSL Certificate - Expired true +10025 SSL Certificate - Self-Signed true +10026 SSL Certificate - Invalid Hostname true +10027 SSL Certificate - Weak Signature Algorithm true +10028 SSL Certificate - Weak Key true +10029 SSL Certificate - Incomplete Chain true +10030 SSL Certificate - Revoked true +10031 SSL Certificate - Mismatched true +10032 SSL Certificate - Weak Cipher Suites true +10033 SSL Certificate - Weak Protocol true +10034 SSL Certificate - Weak DH Parameters true +10035 SSL Certificate - Weak Ephemeral Key true +10036 SSL Certificate - Weak MAC Algorithm true +10037 SSL Certificate - Weak Hash Algorithm true +10038 SSL Certificate - Weak Compression true +10039 SSL Certificate - Weak Random Number Generator true +10040 SSL Certificate - Weak Session Resumption true +10041 SSL Certificate - Weak Session Ticket true +10042 SSL Certificate - Weak Session ID true +10043 SSL Certificate - Weak Session Cache true +10044 SSL Certificate - Weak Session Timeout true +10045 SSL Certificate - Weak Session Size true +10046 SSL Certificate - Weak Session Lifetime true +10047 SSL Certificate - Weak Session Reuse true +10048 SSL Certificate - Weak Session Forward Secrecy true +10049 SSL Certificate - Weak Session Compression true +10050 SSL Certificate - Weak Session Encryption true +10051 SSL Certificate - Weak Session Authentication true +10052 SSL Certificate - Weak Session Integrity true +10053 SSL Certificate - Weak Session Perfect Forward Secrecy true diff --git a/ACCESSIBILITY_AND_I18N_GUIDE.md b/ACCESSIBILITY_AND_I18N_GUIDE.md deleted file mode 100644 index 38fa257af..000000000 --- a/ACCESSIBILITY_AND_I18N_GUIDE.md +++ /dev/null @@ -1,625 +0,0 @@ -# Accessibility & Internationalization Implementation Guide - -## Overview - -This document describes the comprehensive accessibility and internationalization (i18n) system implemented in the StrellerMinds Backend. The system ensures WCAG 2.1 AA compliance and supports 15+ languages including RTL language support. - -## Table of Contents - -1. [Internationalization (i18n)](#internationalization) -2. [Accessibility Features](#accessibility-features) -3. [RTL Language Support](#rtl-language-support) -4. [Testing & Compliance](#testing--compliance) -5. [API Endpoints](#api-endpoints) -6. [Implementation Examples](#implementation-examples) - ---- - -## Internationalization - -### Supported Languages (15+) - -The system supports the following languages: - -- **English** (en) - English, US -- **Spanish** (es) - Español, ES -- **French** (fr) - Français, FR -- **German** (de) - Deutsch, DE -- **Italian** (it) - Italiano, IT -- **Portuguese** (pt) - Português, PT -- **Russian** (ru) - Русский, RU -- **Japanese** (ja) - 日本語, JP -- **Chinese** (zh) - 中文, CN -- **Korean** (ko) - 한국어, KR -- **Arabic** (ar) - العربية, SA - **RTL** -- **Hindi** (hi) - हिन्दी, IN -- **Thai** (th) - ไทย, TH -- **Vietnamese** (vi) - Tiếng Việt, VN -- **Turkish** (tr) - Türkçe, TR - -### I18n Service - -The `I18nService` manages all translation functionality: - -```typescript -// Inject the service -constructor(private readonly i18nService: I18nService) {} - -// Translate a single key -const text = this.i18nService.translate('common.welcome', 'en'); - -// Translate with parameters -const text = this.i18nService.translate('errors.passwordTooShort', 'es', {}); - -// Get multiple translations -const translations = this.i18nService.translateMultiple( - ['common.welcome', 'common.goodbye'], - 'fr' -); - -// Detect language from header -const language = this.i18nService.detectLanguageFromHeader('en-US,en;q=0.9,fr;q=0.8'); - -// Check if language is RTL -const isRTL = this.i18nService.isRTL('ar'); // true - -// Get language metadata -const metadata = this.i18nService.getLanguageMetadata('ja'); -``` - -### Language Detection - -The `LanguageDetectionMiddleware` automatically detects and sets the user's language based on: - -1. **URL query parameter**: `?lang=en` -2. **Language cookie**: Previously set language preference -3. **Accept-Language header**: Browser/client preference -4. **Default**: Falls back to English - -The detected language is available on the request object: - -```typescript -@Get('profile') -getProfile(@Req() req) { - const language = req['language']; // Detected language - const isRTL = req['isRTL']; // RTL flag - const locale = req['locale']; // Full locale string (e.g., 'ar-SA') -} -``` - -### Translation File Structure - -Each language has a JSON translation file at `src/i18n/translations/{lang}.json`: - -```json -{ - "common": { - "welcome": "Welcome", - "goodbye": "Goodbye" - }, - "auth": { - "login": "Login", - "password": "Password" - }, - "errors": { - "required": "This field is required" - } -} -``` - -### I18n API Endpoints - -- **GET** `/i18n/languages` - Get all supported languages -- **GET** `/i18n/translations?lang=en&keys=common.welcome,auth.login` - Get translations -- **GET** `/i18n/detect` - Detect language from Accept-Language header -- **GET** `/i18n/translate?key=common.welcome&lang=es` - Translate a single key - ---- - -## Accessibility Features - -### WCAG 2.1 AA Compliance - -The system implements comprehensive WCAG 2.1 AA compliance including: - -#### 1. **Perceivable** -- Non-text content has text alternatives (alt text) -- Color contrast ratio of at least 4.5:1 for normal text -- Flexible text sizing - -#### 2. **Operable** -- All functionality available from keyboard -- No keyboard traps -- Logical focus order -- Skip links for keyboard navigation - -#### 3. **Understandable** -- Language of page is identified -- Clear error identification -- Error prevention for critical transactions - -#### 4. **Robust** -- Name, role, and value of components are available -- Status messages conveyed to assistive technologies - -### Accessibility Service - -The `AccessibilityService` provides utilities for building accessible components: - -```typescript -// Build ARIA attributes -const ariaAttrs = this.accessibilityService.buildAriaAttributes({ - role: AriaRole.BUTTON, - label: 'Close dialog', - ariaPressed: false, - ariaExpanded: false, -}); - -// Create keyboard navigation handler -const handler = this.accessibilityService.createKeyboardNavigationHandler({ - onEscape: () => this.closeDialog(), - onEnter: () => this.submitForm(), - onArrowUp: () => this.selectPrevious(), - onArrowDown: () => this.selectNext(), -}); - -// Check color contrast -const contrast = this.accessibilityService.checkContrastRatio('#000000', '#FFFFFF'); -console.log(contrast.meetsAA); // true - -// Get skip navigation links -const skipLinks = this.accessibilityService.getSkipNavigationLinks(); - -// Get WCAG checklist -const checklist = this.accessibilityService.getWCAGComplianceChecklist(); -``` - -### Accessibility Decorators - -Use decorators to mark accessible components: - -```typescript -import { - AccessibleName, - ScreenReaderOptimized, - KeyboardNavigable, - WCAGCompliance, -} from './accessibility/decorators/accessibility.decorators'; - -@Controller('users') -@ScreenReaderOptimized() -@WCAGCompliance('AA') -export class UserController { - @Get('profile') - @KeyboardNavigable('#user-profile') - @AccessibleName('User Profile') - getProfile() { - // Implementation - } -} -``` - -### Screen Reader Support - -The system is fully optimized for screen readers: - -- **ARIA Landmarks**: All major sections use semantic HTML or ARIA landmarks -- **Live Regions**: Dynamic content uses `aria-live` regions -- **Form Labels**: All inputs have associated labels -- **Error Messages**: Errors are properly announced -- **Skip Links**: Users can skip navigation - -Example ARIA implementation: - -```typescript -buildAriaAttributes({ - role: AriaRole.ALERT, - ariaLive: AriaPoliteness.ASSERTIVE, - ariaAtomic: true, - label: 'Error message: Password too short', -}) -``` - -### Keyboard Navigation - -Full keyboard navigation support with proper focus management: - -```typescript -const keyboardHandler = this.accessibilityService.createKeyboardNavigationHandler({ - onEscape: () => this.closeModal(), - onEnter: () => this.submitForm(), - onTab: () => this.moveFocus(), - onArrowUp: () => this.selectPreviousItem(), - onArrowDown: () => this.selectNextItem(), - onHome: () => this.selectFirstItem(), - onEnd: () => this.selectLastItem(), -}); - -document.addEventListener('keydown', keyboardHandler); -``` - ---- - -## RTL Language Support - -### RTL Service - -The `RTLService` handles all right-to-left language requirements: - -```typescript -// Check if language is RTL -if (this.rtlService.isRTL('ar')) { - // Apply RTL styles -} - -// Get text direction -const direction = this.rtlService.getDirection('ar'); // 'rtl' - -// Get HTML attributes -const attrs = this.rtlService.getHtmlAttributes('ar'); -// { lang: 'ar', dir: 'rtl' } - -// Flip spacing based on direction -const spacing = this.rtlService.flipSpacing('ar', { - left: 16, - right: 8, - top: 4, - bottom: 4, -}); -// { left: 8, right: 16, top: 4, bottom: 4 } - -// Format numbers in language locale -const formatted = this.rtlService.formatNumber('ar', 1234.56); // '١٬٢٣٤٫٥٦' - -// Format dates -const date = this.rtlService.formatDate('ar', new Date()); - -// Format currency -const price = this.rtlService.formatCurrency('ar', 99.99, 'SAR'); - -// Format lists -const list = this.rtlService.formatList('ar', ['item1', 'item2', 'item3']); -``` - -### Supported RTL Languages - -- **Arabic** (ar) -- **Hebrew** (he) -- **Persian/Farsi** (fa) -- **Urdu** (ur) - -### RTL Implementation Guidelines - -1. **Use logical CSS properties** when possible: - - `margin-inline-start` instead of `margin-left` - - `padding-inline-end` instead of `padding-right` - - `text-align: start` instead of `text-align: left` - -2. **Use the RTL service for positioning**: - ```typescript - const position = this.rtlService.getPosition('ar', { - left: '16px', - right: undefined, - }); - ``` - -3. **Set HTML dir attribute**: - ```typescript - const attrs = this.rtlService.getHtmlAttributes('ar'); - // Returns: { lang: 'ar', dir: 'rtl', ... } - ``` - ---- - -## Testing & Compliance - -### Accessibility Testing Service - -The `AccessibilityTestingService` provides comprehensive testing utilities: - -```typescript -// Run comprehensive audit -const audit = this.testingService.runComprehensiveAudit(html); - -// Validate WCAG compliance -const results = this.testingService.validateWCAGCompliance(html); - -// Validate keyboard navigation -const keyboardResults = this.testingService.validateKeyboardNavigation(html); - -// Validate screen reader compatibility -const screenReaderResults = this.testingService.validateScreenReaderCompat(html); - -// Check WCAG 2.1 AA compliance -const isCompliant = this.testingService.meetsWCAG21AA(results); - -// Generate report -const report = this.testingService.generateReport(results); -``` - -### Test Utils - -The `AccessibilityTestUtils` provides detailed testing: - -```typescript -// Test keyboard navigation -const keyboardTest = this.testUtils.testKeyboardNavigation(html); - -// Test screen reader compatibility -const srTest = this.testUtils.testScreenReaderCompat(html); - -// Test color contrast -const contrast = this.testUtils.testColorContrast('#000000', '#FFFFFF'); - -// Validate form accessibility -const formTest = this.testUtils.validateFormAccessibility(html); - -// Test image accessibility -const imageTest = this.testUtils.testImageAccessibility(html); - -// Test heading structure -const headingTest = this.testUtils.testHeadingStructure(html); - -// Generate comprehensive report -const report = this.testUtils.generateReport(html, css); -``` - -### Audit Endpoints - -- **POST** `/accessibility/audit` - Run accessibility audit on HTML -- **GET** `/accessibility/wcag-checklist` - Get WCAG 2.1 AA checklist -- **POST** `/accessibility/screen-reader-text` - Generate screen reader text -- **GET** `/accessibility/contrast?foreground=#000&background=#FFF` - Check contrast ratio - ---- - -## API Endpoints - -### I18n Endpoints - -``` -GET /i18n/languages -GET /i18n/translations?lang=en&keys=common.welcome -GET /i18n/detect -GET /i18n/translate?key=common.welcome&lang=es -``` - -### Accessibility Endpoints - -``` -GET /accessibility/wcag-checklist -POST /accessibility/build-aria -POST /accessibility/screen-reader-text -GET /accessibility/contrast?foreground=#000&background=#FFF -GET /accessibility/skip-links -POST /accessibility/audit -GET /accessibility/overview -``` - ---- - -## Implementation Examples - -### Example 1: Multilingual Form Validation - -```typescript -@Post('register') -async register(@Body() dto: RegisterDto, @Req() req) { - const language = req['language']; - const isRTL = req['isRTL']; - - // Validate with localized error messages - if (!dto.email) { - const errorMsg = this.i18nService.translate( - 'errors.required', - language, - ); - throw new BadRequestException(errorMsg); - } - - // Build ARIA attributes for form - const ariaAttrs = this.accessibilityService.buildAriaAttributes({ - role: AriaRole.FORM, - labelledBy: 'form-title', - }); - - return { - success: true, - direction: this.rtlService.getDirection(language), - ariaAttributes: ariaAttrs, - }; -} -``` - -### Example 2: Accessible Button Component - -```typescript -buildAccessibleButton(label: string, language: string) { - const ariaAttrs = this.accessibilityService.buildAriaAttributes({ - role: AriaRole.BUTTON, - label: this.i18nService.translate(label, language), - ariaDisabled: false, - }); - - const textAlign = this.rtlService.getTextAlign(language); - - return { - ariaAttributes: ariaAttrs, - textAlign, - direction: this.rtlService.getDirection(language), - }; -} -``` - -### Example 3: Accessibility Audit - -```typescript -@Post('audit-content') -async auditContent(@Body() body: { html: string }) { - const report = this.testingService.generateReport( - this.testingService.runComprehensiveAudit(body.html), - ); - - return { - compliant: report.wcagCompliance.meets, - issues: report.summary, - recommendations: report.recommendations, - }; -} -``` - -### Example 4: Language Switching - -```typescript -@Post('set-language') -setLanguage(@Body() body: { language: string }, @Res() res: Response) { - const normalizedLang = this.i18nService.normalizeLanguageCode(body.language); - - // Set language cookie - res.cookie('language', normalizedLang, { - httpOnly: true, - maxAge: 365 * 24 * 60 * 60 * 1000, // 1 year - }); - - // Get metadata - const metadata = this.i18nService.getLanguageMetadata(normalizedLang); - const isRTL = this.rtlService.isRTL(normalizedLang); - - return { - language: normalizedLang, - metadata, - isRTL, - }; -} -``` - ---- - -## Best Practices - -### For Frontend Integration - -1. **Always include lang and dir attributes**: - ```html - - ``` - -2. **Use semantic HTML elements**: - ```html -