Backend Complete: Interface Refactoring & Service Container Enhancements
Repository Interface Optimization: - Created IBaseRepository.ts and IPaginatedRepository.ts - Refactored all 7 repository interfaces to extend base interfaces - Eliminated ~200 lines of redundant code (70% reduction) - Improved type safety and maintainability Dependency Injection Improvements: - Added EmailService and GameTokenService to DIContainer - Updated CreateUserCommandHandler constructor for DI - Updated RequestPasswordResetCommandHandler constructor for DI - Enhanced testability and service consistency Environment Configuration: - Created comprehensive .env.example with 40+ variables - Organized into 12 logical sections (Database, Security, Email, etc.) - Added security guidelines and best practices - Documented all backend environment requirements Documentation: - Added comprehensive codebase review - Created refactoring summary report - Added frontend implementation guide Impact: Improved code quality, reduced maintenance overhead, enhanced developer experience
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
# Development Environment Variables
|
||||
POSTGRES_PASSWORD=postgres
|
||||
JWT_SECRET=dev_jwt_secret_change_in_production_please_use_a_long_random_string
|
||||
JWT_EXPIRATION=24h
|
||||
JWT_REFRESH_EXPIRATION=7d
|
||||
MINIO_ACCESS_KEY=serpentrace
|
||||
MINIO_SECRET_KEY=serpentrace123!
|
||||
|
||||
# Optional: Email configuration for development
|
||||
EMAIL_HOST=
|
||||
EMAIL_PORT=
|
||||
EMAIL_USER=
|
||||
EMAIL_PASS=
|
||||
EMAIL_FROM=
|
||||
|
||||
# Optional: Other development settings
|
||||
NODE_ENV=development
|
||||
@@ -0,0 +1,222 @@
|
||||
# ==============================================
|
||||
# SerpentRace Backend Environment Configuration
|
||||
# ==============================================
|
||||
# Copy this file to .env and fill in your values
|
||||
# This file contains all environment variables used by the backend
|
||||
|
||||
# ==============================================
|
||||
# APPLICATION CONFIGURATION
|
||||
# ==============================================
|
||||
|
||||
# Node.js environment (development, production, test)
|
||||
NODE_ENV=development
|
||||
|
||||
# Server port number
|
||||
PORT=3000
|
||||
|
||||
# Base URL for the application (used for email links, etc.)
|
||||
APP_BASE_URL=http://localhost:3000
|
||||
|
||||
# ==============================================
|
||||
# DATABASE CONFIGURATION (PostgreSQL)
|
||||
# ==============================================
|
||||
|
||||
# Database connection details
|
||||
DB_HOST=localhost
|
||||
DB_PORT=5432
|
||||
DB_NAME=serpentrace
|
||||
DB_USERNAME=postgres
|
||||
DB_PASSWORD=your_db_password
|
||||
|
||||
# Database URL (alternative to individual settings)
|
||||
# DATABASE_URL=postgresql://username:password@localhost:5432/serpentrace
|
||||
|
||||
# ==============================================
|
||||
# REDIS CONFIGURATION
|
||||
# ==============================================
|
||||
|
||||
# Redis connection details (for caching and sessions)
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PORT=6379
|
||||
|
||||
# Redis URL (alternative to individual settings)
|
||||
REDIS_URL=redis://localhost:6379
|
||||
|
||||
# Redis password (if required)
|
||||
# REDIS_PASSWORD=your_redis_password
|
||||
|
||||
# ==============================================
|
||||
# JWT (JSON Web Token) CONFIGURATION
|
||||
# ==============================================
|
||||
|
||||
# Secret key for JWT signing (REQUIRED - use a strong, random key in production)
|
||||
JWT_SECRET=your_super_secret_jwt_key_change_in_production
|
||||
|
||||
# JWT token expiration time
|
||||
# Can be specified in seconds (e.g., 86400) or time format (e.g., 24h, 7d, 30m)
|
||||
JWT_EXPIRY=86400
|
||||
# Alternative format
|
||||
JWT_EXPIRATION=24h
|
||||
|
||||
# JWT refresh token expiration (for future use)
|
||||
JWT_REFRESH_EXPIRATION=7d
|
||||
|
||||
# Game token expiration (for game session tokens)
|
||||
GAME_TOKEN_EXPIRY=86400
|
||||
|
||||
# ==============================================
|
||||
# EMAIL SERVICE CONFIGURATION (SMTP)
|
||||
# ==============================================
|
||||
|
||||
# SMTP server configuration
|
||||
EMAIL_HOST=smtp.gmail.com
|
||||
EMAIL_PORT=587
|
||||
EMAIL_SECURE=false
|
||||
|
||||
# Email authentication
|
||||
EMAIL_USER=your_email@domain.com
|
||||
EMAIL_PASS=your_email_password
|
||||
|
||||
# From address for outgoing emails
|
||||
EMAIL_FROM=noreply@serpentrace.com
|
||||
|
||||
# ==============================================
|
||||
# CHAT SYSTEM CONFIGURATION
|
||||
# ==============================================
|
||||
|
||||
# Chat inactivity timeout (in minutes)
|
||||
CHAT_INACTIVITY_TIMEOUT_MINUTES=30
|
||||
|
||||
# Maximum messages per user per session
|
||||
CHAT_MAX_MESSAGES_PER_USER=100
|
||||
|
||||
# Cleanup old messages after X weeks
|
||||
CHAT_MESSAGE_CLEANUP_WEEKS=4
|
||||
|
||||
# ==============================================
|
||||
# GAME CONFIGURATION
|
||||
# ==============================================
|
||||
|
||||
# Board generation settings
|
||||
MAX_SPECIAL_FIELDS_PERCENTAGE=67
|
||||
MAX_GENERATION_TIME_SECONDS=20
|
||||
GENERATION_ERROR_TOLERANCE=15
|
||||
|
||||
# ==============================================
|
||||
# MINIO/S3 CONFIGURATION (File Storage)
|
||||
# ==============================================
|
||||
|
||||
# MinIO server configuration (for file uploads)
|
||||
MINIO_ENDPOINT=localhost
|
||||
MINIO_PORT=9000
|
||||
MINIO_ACCESS_KEY=serpentrace
|
||||
MINIO_SECRET_KEY=serpentrace123!
|
||||
MINIO_USE_SSL=false
|
||||
|
||||
# S3 bucket name (if using S3 instead of MinIO)
|
||||
# S3_BUCKET_NAME=serpentrace-files
|
||||
|
||||
# ==============================================
|
||||
# LOGGING CONFIGURATION
|
||||
# ==============================================
|
||||
|
||||
# Log level (error, warn, info, debug)
|
||||
LOG_LEVEL=info
|
||||
|
||||
# Log file retention (in days)
|
||||
LOG_RETENTION_DAYS=30
|
||||
|
||||
# ==============================================
|
||||
# SECURITY CONFIGURATION
|
||||
# ==============================================
|
||||
|
||||
# API rate limiting (requests per minute per IP)
|
||||
RATE_LIMIT_RPM=60
|
||||
|
||||
# Maximum file upload size (in MB)
|
||||
MAX_UPLOAD_SIZE_MB=10
|
||||
|
||||
# CORS allowed origins (comma-separated)
|
||||
CORS_ORIGINS=http://localhost:3000,http://localhost:3001,http://localhost:8080
|
||||
|
||||
# ==============================================
|
||||
# ADMIN CONFIGURATION
|
||||
# ==============================================
|
||||
|
||||
# Admin bypass settings
|
||||
ADMIN_BYPASS_ENABLED=true
|
||||
|
||||
# Default admin user (for development only)
|
||||
# ADMIN_DEFAULT_EMAIL=admin@serpentrace.com
|
||||
# ADMIN_DEFAULT_PASSWORD=change_this_password
|
||||
|
||||
# ==============================================
|
||||
# MONITORING & HEALTH CHECKS
|
||||
# ==============================================
|
||||
|
||||
# Health check endpoint timeout (in milliseconds)
|
||||
HEALTH_CHECK_TIMEOUT=5000
|
||||
|
||||
# Database connection pool settings
|
||||
DB_CONNECTION_POOL_MIN=2
|
||||
DB_CONNECTION_POOL_MAX=10
|
||||
|
||||
# ==============================================
|
||||
# DEVELOPMENT ONLY SETTINGS
|
||||
# ==============================================
|
||||
# These settings should only be used in development
|
||||
|
||||
# Enable detailed SQL logging
|
||||
DB_LOGGING=true
|
||||
|
||||
# Enable debug mode for various services
|
||||
DEBUG_MODE=false
|
||||
|
||||
# Disable email sending in development (logs emails instead)
|
||||
EMAIL_DEBUG_MODE=true
|
||||
|
||||
# ==============================================
|
||||
# PRODUCTION ONLY SETTINGS
|
||||
# ==============================================
|
||||
# These settings are typically used only in production
|
||||
|
||||
# Enable HTTPS (for production)
|
||||
# HTTPS_ENABLED=true
|
||||
# SSL_CERT_PATH=/path/to/cert.pem
|
||||
# SSL_KEY_PATH=/path/to/key.pem
|
||||
|
||||
# Sentry configuration (for error tracking)
|
||||
# SENTRY_DSN=https://your-sentry-dsn@sentry.io/project-id
|
||||
|
||||
# New Relic configuration (for performance monitoring)
|
||||
# NEW_RELIC_LICENSE_KEY=your_new_relic_license_key
|
||||
# NEW_RELIC_APP_NAME=SerpentRace Backend
|
||||
|
||||
# ==============================================
|
||||
# EXTERNAL API KEYS (Optional)
|
||||
# ==============================================
|
||||
|
||||
# Third-party service API keys (if used)
|
||||
# ANALYTICS_API_KEY=your_analytics_key
|
||||
# PAYMENT_API_KEY=your_payment_processor_key
|
||||
|
||||
# ==============================================
|
||||
# NOTES & SECURITY WARNINGS
|
||||
# ==============================================
|
||||
|
||||
# SECURITY NOTES:
|
||||
# - Never commit .env files to version control
|
||||
# - Use strong, unique passwords and keys
|
||||
# - Regularly rotate JWT secrets and API keys
|
||||
# - Use environment-specific values for each deployment
|
||||
|
||||
# REQUIRED VARIABLES:
|
||||
# The following variables are required for the application to start:
|
||||
# - NODE_ENV
|
||||
# - DB_HOST, DB_PORT, DB_NAME, DB_USERNAME, DB_PASSWORD
|
||||
# - REDIS_HOST, REDIS_PORT
|
||||
# - JWT_SECRET
|
||||
# - EMAIL_HOST, EMAIL_PORT, EMAIL_USER, EMAIL_PASS
|
||||
|
||||
# OPTIONAL VARIABLES:
|
||||
# All other variables have sensible defaults and are optional
|
||||
@@ -0,0 +1,55 @@
|
||||
# Production Environment Variables
|
||||
|
||||
# Production settings
|
||||
NODE_ENV=production
|
||||
|
||||
#Backend
|
||||
# Database
|
||||
DB_HOST=localhost
|
||||
DB_PORT=5432
|
||||
DB_NAME=serpentrace
|
||||
DB_USERNAME=postgres
|
||||
DB_PASSWORD=serpentrace_secure_password_2024!
|
||||
|
||||
# PostgreSQL Database (for docker-compose)
|
||||
POSTGRES_PASSWORD=serpentrace_secure_password_2024!
|
||||
|
||||
# Redis
|
||||
REDIS_URL=redis://localhost:6379
|
||||
REDIS_HOST=localhost
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=
|
||||
|
||||
# JWT - Use JWT_EXPIRY (seconds) or JWT_EXPIRATION (duration format like 24h, 7d)
|
||||
JWT_SECRET=serpentrace_super_secure_jwt_secret_key_2024_production!
|
||||
JWT_EXPIRY=86400
|
||||
JWT_EXPIRATION=24h
|
||||
JWT_REFRESH_EXPIRATION=7d
|
||||
|
||||
# Email
|
||||
EMAIL_HOST=smtp.example.com
|
||||
EMAIL_PORT=587
|
||||
EMAIL_SECURE=false
|
||||
EMAIL_USER=your_email@example.com
|
||||
EMAIL_PASS=your_email_password
|
||||
EMAIL_FROM="SerpentRace <noreply@serpentrace.com>"
|
||||
|
||||
# MinIO Object Storage
|
||||
MINIO_ENDPOINT=localhost
|
||||
MINIO_PORT=9000
|
||||
MINIO_USE_SSL=false
|
||||
MINIO_ACCESS_KEY=serpentrace_minio_admin
|
||||
MINIO_SECRET_KEY=serpentrace_minio_secret_key_2024!
|
||||
MINIO_BUCKET_NAME=serpentrace-logs
|
||||
|
||||
# Application
|
||||
APP_BASE_URL=http://localhost:3000
|
||||
PORT=3000
|
||||
|
||||
# Chat Limits
|
||||
CHAT_INACTIVITY_TIMEOUT_MINUTES=30
|
||||
CHAT_MAX_MESSAGES_PER_USER=100
|
||||
CHAT_MESSAGE_CLEANUP_WEEKS=4
|
||||
|
||||
# Logging
|
||||
MAX_LOGS_PER_FILE=10000
|
||||
@@ -0,0 +1,71 @@
|
||||
# SerpentRace Production Server Environment Variables
|
||||
# IMPORTANT: Change all placeholder values before deployment!
|
||||
|
||||
# Production settings
|
||||
NODE_ENV=production
|
||||
|
||||
# Database Configuration
|
||||
DB_HOST=postgres
|
||||
DB_PORT=5432
|
||||
DB_NAME=serpentrace
|
||||
DB_USERNAME=postgres
|
||||
# CHANGE THIS: Use a strong password
|
||||
POSTGRES_PASSWORD=CHANGE_THIS_STRONG_DATABASE_PASSWORD_123!
|
||||
|
||||
# Redis Configuration
|
||||
REDIS_URL=redis://redis:6379
|
||||
REDIS_HOST=redis
|
||||
REDIS_PORT=6379
|
||||
# CHANGE THIS: Set a Redis password for security
|
||||
REDIS_PASSWORD=CHANGE_THIS_REDIS_PASSWORD_123!
|
||||
|
||||
# JWT Configuration
|
||||
# CHANGE THIS: Use a strong secret key (minimum 32 characters)
|
||||
JWT_SECRET=CHANGE_THIS_JWT_SECRET_KEY_MINIMUM_32_CHARACTERS_FOR_PRODUCTION_SECURITY
|
||||
JWT_EXPIRY=86400
|
||||
JWT_EXPIRATION=24h
|
||||
JWT_REFRESH_EXPIRATION=7d
|
||||
|
||||
# Email Configuration (SMTP)
|
||||
# CHANGE THESE: Configure your email provider
|
||||
EMAIL_HOST=smtp.yourmailprovider.com
|
||||
EMAIL_PORT=587
|
||||
EMAIL_SECURE=false
|
||||
EMAIL_USER=your_email@yourdomain.com
|
||||
EMAIL_PASS=your_email_password
|
||||
EMAIL_FROM="SerpentRace <noreply@yourdomain.com>"
|
||||
|
||||
# MinIO Object Storage
|
||||
MINIO_ENDPOINT=minio
|
||||
MINIO_PORT=9000
|
||||
MINIO_USE_SSL=false
|
||||
# CHANGE THESE: Use strong credentials
|
||||
MINIO_ACCESS_KEY=serpentrace_admin
|
||||
MINIO_SECRET_KEY=CHANGE_THIS_MINIO_SECRET_KEY_123!
|
||||
MINIO_BUCKET_NAME=serpentrace-logs
|
||||
|
||||
# Application Settings
|
||||
APP_BASE_URL=http://your-domain.com
|
||||
PORT=3000
|
||||
|
||||
# Chat System Limits
|
||||
CHAT_INACTIVITY_TIMEOUT_MINUTES=30
|
||||
CHAT_MAX_MESSAGES_PER_USER=100
|
||||
CHAT_MESSAGE_CLEANUP_WEEKS=4
|
||||
|
||||
# Logging
|
||||
MAX_LOGS_PER_FILE=10000
|
||||
|
||||
# SSL/TLS Configuration (if using HTTPS)
|
||||
# Uncomment and configure if you have SSL certificates
|
||||
# SSL_CERT_PATH=/path/to/certificate.crt
|
||||
# SSL_KEY_PATH=/path/to/private.key
|
||||
# SSL_CA_PATH=/path/to/ca-bundle.crt
|
||||
|
||||
# Security Headers (already configured in nginx)
|
||||
# These are handled by the nginx configuration
|
||||
|
||||
# Backup Configuration (optional)
|
||||
# BACKUP_ENABLED=true
|
||||
# BACKUP_SCHEDULE=0 2 * * *
|
||||
# BACKUP_RETENTION_DAYS=30
|
||||
@@ -0,0 +1,267 @@
|
||||
# SerpentRace Docker Development Environment
|
||||
|
||||
This Docker setup provides a complete development environment for SerpentRace with hot reloading and all necessary services.
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Development Environment
|
||||
|
||||
1. **Start the development environment:**
|
||||
```bash
|
||||
# Windows
|
||||
docker-manage.bat dev:start
|
||||
|
||||
# Linux/Mac
|
||||
./docker-manage.sh dev:start
|
||||
```
|
||||
|
||||
2. **Access the applications:**
|
||||
- **Frontend:** http://localhost:5173
|
||||
- **Backend API:** http://localhost:3000
|
||||
- **Swagger API Docs:** http://localhost:3000/api-docs
|
||||
- **PostgreSQL:** localhost:5432 (user: postgres, password: postgres)
|
||||
- **Redis:** localhost:6379
|
||||
- **MinIO Console:** http://localhost:9001 (serpentrace / serpentrace123!)
|
||||
- **PgAdmin:** http://localhost:8080 (admin@serpentrace.dev / admin)
|
||||
- **Redis Commander:** http://localhost:8081
|
||||
|
||||
3. **Stop the environment:**
|
||||
```bash
|
||||
# Windows
|
||||
docker-manage.bat dev:stop
|
||||
|
||||
# Linux/Mac
|
||||
./docker-manage.sh dev:stop
|
||||
```
|
||||
|
||||
### Production Environment
|
||||
|
||||
1. **Configure production environment:**
|
||||
- Copy `.env.prod` and update all values with secure passwords
|
||||
- Update JWT secrets and database passwords
|
||||
|
||||
2. **Start production:**
|
||||
```bash
|
||||
# Windows
|
||||
docker-manage.bat prod:start
|
||||
|
||||
# Linux/Mac
|
||||
./docker-manage.sh prod:start
|
||||
```
|
||||
|
||||
## 📁 File Structure
|
||||
|
||||
```
|
||||
SzeSnake/
|
||||
├── docker-compose.dev.yml # Development environment
|
||||
├── docker-compose.prod.yml # Production environment
|
||||
├── docker-manage.sh # Linux/Mac management script
|
||||
├── docker-manage.bat # Windows management script
|
||||
├── .env.dev # Development environment variables
|
||||
├── .env.prod # Production environment variables
|
||||
├── SerpentRace_Backend/
|
||||
│ ├── Dockerfile # Production backend image
|
||||
│ ├── Dockerfile.dev # Development backend image
|
||||
│ └── .dockerignore
|
||||
└── SerpentRace_Frontend/
|
||||
├── Dockerfile # Production frontend image
|
||||
├── Dockerfile.dev # Development frontend image
|
||||
├── nginx.conf # Nginx configuration for production
|
||||
└── .dockerignore
|
||||
```
|
||||
|
||||
## 🛠 Development Features
|
||||
|
||||
### Hot Reloading
|
||||
- **Backend:** Uses `nodemon` and `ts-node` for automatic TypeScript compilation and server restart
|
||||
- **Frontend:** Uses Vite's built-in HMR (Hot Module Replacement)
|
||||
|
||||
### Volume Mapping
|
||||
- Source code is mounted as volumes for instant file changes
|
||||
- Node modules are preserved in named volumes for performance
|
||||
|
||||
### Development Tools
|
||||
- **PgAdmin:** Web-based PostgreSQL administration
|
||||
- **Redis Commander:** Web-based Redis management
|
||||
- **MinIO Console:** Object storage management
|
||||
|
||||
### Database Initialization
|
||||
- Automatic database setup with test data from `sql_dump_with_test_data.sql`
|
||||
|
||||
## 🐳 Docker Services
|
||||
|
||||
### Backend (`backend`)
|
||||
- **Image:** Node.js 20 Alpine
|
||||
- **Port:** 3000
|
||||
- **Features:** Hot reload, TypeScript support
|
||||
- **Dependencies:** PostgreSQL, Redis, MinIO
|
||||
|
||||
### Frontend (`frontend`)
|
||||
- **Image:** Node.js 20 Alpine (dev) / Nginx Alpine (prod)
|
||||
- **Port:** 5173 (dev) / 80 (prod)
|
||||
- **Features:** Vite HMR, React Fast Refresh
|
||||
|
||||
### PostgreSQL (`postgres`)
|
||||
- **Image:** PostgreSQL 15 Alpine
|
||||
- **Port:** 5432
|
||||
- **Database:** serpentrace
|
||||
- **Credentials:** postgres/postgres (dev)
|
||||
|
||||
### Redis (`redis`)
|
||||
- **Image:** Redis 7 Alpine
|
||||
- **Port:** 6379
|
||||
- **Features:** Persistence enabled
|
||||
|
||||
### MinIO (`minio`)
|
||||
- **Image:** MinIO latest
|
||||
- **Ports:** 9000 (API), 9001 (Console)
|
||||
- **Features:** S3-compatible object storage
|
||||
|
||||
## 🔧 Management Commands
|
||||
|
||||
### Using the Management Scripts
|
||||
|
||||
```bash
|
||||
# Start development environment
|
||||
./docker-manage.sh dev:start
|
||||
|
||||
# Stop development environment
|
||||
./docker-manage.sh dev:stop
|
||||
|
||||
# View logs for all services
|
||||
./docker-manage.sh logs
|
||||
|
||||
# View logs for specific service
|
||||
./docker-manage.sh logs backend
|
||||
./docker-manage.sh logs frontend
|
||||
|
||||
# Clean up all resources
|
||||
./docker-manage.sh cleanup
|
||||
|
||||
# Production commands
|
||||
./docker-manage.sh prod:start
|
||||
./docker-manage.sh prod:stop
|
||||
```
|
||||
|
||||
### Manual Docker Compose Commands
|
||||
|
||||
```bash
|
||||
# Development
|
||||
docker-compose -f docker-compose.dev.yml --env-file .env.dev up --build -d
|
||||
docker-compose -f docker-compose.dev.yml down
|
||||
|
||||
# Production
|
||||
docker-compose -f docker-compose.prod.yml --env-file .env.prod up --build -d
|
||||
docker-compose -f docker-compose.prod.yml down
|
||||
|
||||
# View logs
|
||||
docker-compose -f docker-compose.dev.yml logs -f [service_name]
|
||||
|
||||
# Rebuild specific service
|
||||
docker-compose -f docker-compose.dev.yml up --build backend
|
||||
|
||||
# Execute commands in running containers
|
||||
docker-compose -f docker-compose.dev.yml exec backend npm run test
|
||||
docker-compose -f docker-compose.dev.yml exec postgres psql -U postgres -d serpentrace
|
||||
```
|
||||
|
||||
## 🔒 Security Considerations
|
||||
|
||||
### Development
|
||||
- Default passwords are used for convenience
|
||||
- Services are exposed on localhost
|
||||
- Debug tools are included
|
||||
|
||||
### Production
|
||||
- **IMPORTANT:** Update all passwords in `.env.prod`
|
||||
- Use strong JWT secrets (256+ characters recommended)
|
||||
- Services are not directly exposed
|
||||
- No debug tools included
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Port conflicts:**
|
||||
- Check if ports 3000, 5173, 5432, 6379, 9000, 9001, 8080, 8081 are available
|
||||
- Modify port mappings in docker-compose files if needed
|
||||
|
||||
2. **File watching issues on Windows:**
|
||||
- WSL2 is recommended for better file system performance
|
||||
- Ensure Docker Desktop is configured to use WSL2
|
||||
|
||||
3. **Database connection issues:**
|
||||
- Wait for health checks to pass before the application starts
|
||||
- Check logs: `./docker-manage.sh logs postgres`
|
||||
|
||||
4. **Hot reload not working:**
|
||||
- Ensure volumes are properly mounted
|
||||
- Check file permissions on Linux/Mac systems
|
||||
|
||||
### Performance Tips
|
||||
|
||||
1. **Use WSL2 on Windows** for better file system performance
|
||||
2. **Increase Docker memory** allocation if needed
|
||||
3. **Use .dockerignore** to exclude unnecessary files
|
||||
4. **Prune unused Docker resources** regularly: `docker system prune`
|
||||
|
||||
## 📝 Environment Variables
|
||||
|
||||
### Development (.env.dev)
|
||||
```bash
|
||||
POSTGRES_PASSWORD=postgres
|
||||
JWT_SECRET=dev_jwt_secret_change_in_production
|
||||
MINIO_ACCESS_KEY=serpentrace
|
||||
MINIO_SECRET_KEY=serpentrace123!
|
||||
```
|
||||
|
||||
### Production (.env.prod)
|
||||
```bash
|
||||
POSTGRES_PASSWORD=your_secure_password
|
||||
JWT_SECRET=your_very_long_secure_jwt_secret
|
||||
MINIO_ACCESS_KEY=your_minio_access_key
|
||||
MINIO_SECRET_KEY=your_secure_minio_secret
|
||||
```
|
||||
|
||||
## 🔄 Health Checks
|
||||
|
||||
All services include health checks to ensure proper startup order:
|
||||
- **PostgreSQL:** `pg_isready`
|
||||
- **Redis:** `redis-cli ping`
|
||||
- **MinIO:** HTTP health endpoint
|
||||
- **Backend:** HTTP health endpoint
|
||||
- **Frontend:** HTTP health endpoint (production)
|
||||
|
||||
The application will only start after all dependencies are healthy.
|
||||
|
||||
## 📊 Monitoring
|
||||
|
||||
### Logs
|
||||
```bash
|
||||
# All services
|
||||
./docker-manage.sh logs
|
||||
|
||||
# Specific service
|
||||
./docker-manage.sh logs backend
|
||||
./docker-manage.sh logs frontend
|
||||
./docker-manage.sh logs postgres
|
||||
```
|
||||
|
||||
### Service Status
|
||||
```bash
|
||||
# Check running containers
|
||||
docker ps
|
||||
|
||||
# Check service health
|
||||
docker-compose -f docker-compose.dev.yml ps
|
||||
```
|
||||
|
||||
## 🚀 Deployment
|
||||
|
||||
For production deployment:
|
||||
|
||||
1. Update `.env.prod` with secure values
|
||||
2. Ensure proper firewall configuration
|
||||
3. Use reverse proxy (nginx/traefik) for SSL termination
|
||||
4. Consider using Docker Swarm or Kubernetes for orchestration
|
||||
5. Set up monitoring and backup solutions
|
||||
@@ -0,0 +1,60 @@
|
||||
# Production Dockerfile for SerpentRace Backend
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies needed for native modules
|
||||
RUN apk add --no-cache python3 make g++
|
||||
|
||||
# Copy package files
|
||||
COPY package.json package-lock.json* ./
|
||||
|
||||
# Install ALL dependencies for building (including devDependencies)
|
||||
RUN npm ci
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Build the application
|
||||
RUN npm run build || echo "No build script found"
|
||||
|
||||
# Production stage
|
||||
FROM node:20-alpine AS production
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies needed for native modules
|
||||
RUN apk add --no-cache python3 make g++
|
||||
|
||||
# Copy package files
|
||||
COPY package.json package-lock.json* ./
|
||||
|
||||
# Install only production dependencies
|
||||
RUN npm ci --only=production && npm cache clean --force
|
||||
|
||||
# Copy built application from builder stage
|
||||
COPY --from=builder /app/dist ./dist
|
||||
COPY --from=builder /app/package.json ./
|
||||
|
||||
# Create logs directory with proper permissions
|
||||
RUN mkdir -p logs && chmod 777 logs
|
||||
|
||||
# Create non-root user but don't switch to it for now
|
||||
RUN addgroup -g 1001 -S nodejs
|
||||
RUN adduser -S serpentrace -u 1001
|
||||
RUN chown -R serpentrace:nodejs /app
|
||||
|
||||
# Keep running as root to avoid permission issues with mounted volumes
|
||||
# USER serpentrace
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost:3000/health || exit 1
|
||||
|
||||
# Production command
|
||||
CMD ["npm", "start"]
|
||||
@@ -0,0 +1,29 @@
|
||||
# Development Dockerfile for SerpentRace Backend
|
||||
FROM node:20-alpine
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies needed for native modules
|
||||
RUN apk add --no-cache python3 make g++
|
||||
|
||||
# Copy package files
|
||||
COPY package.json package-lock.json* ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm install
|
||||
|
||||
# Install nodemon globally for development
|
||||
RUN npm install -g nodemon ts-node
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Create logs directory
|
||||
RUN mkdir -p logs
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
# Development command with hot reload
|
||||
CMD ["npm", "run", "dev"]
|
||||
@@ -0,0 +1,36 @@
|
||||
# Production Dockerfile for SerpentRace Frontend
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package.json package-lock.json* ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm ci --only=production
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Build the application
|
||||
RUN npm run build
|
||||
|
||||
# Production stage with nginx
|
||||
FROM nginx:alpine AS production
|
||||
|
||||
# Copy built application
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
|
||||
# Copy nginx configuration
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Expose port
|
||||
EXPOSE 80
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost || exit 1
|
||||
|
||||
# Start nginx
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -0,0 +1,20 @@
|
||||
# Development Dockerfile for SerpentRace Frontend
|
||||
FROM node:20-alpine
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package.json package-lock.json* ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm install
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Expose port
|
||||
EXPOSE 5173
|
||||
|
||||
# Development command with hot reload
|
||||
CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
|
||||
@@ -0,0 +1,147 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# Backend service using pre-built image
|
||||
backend:
|
||||
image: serpentrace-backend:latest
|
||||
container_name: serpentrace-backend
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- PORT=3000
|
||||
- DB_HOST=postgres
|
||||
- DB_PORT=5432
|
||||
- DB_NAME=serpentrace
|
||||
- DB_USERNAME=postgres
|
||||
- DB_PASSWORD=${POSTGRES_PASSWORD}
|
||||
- REDIS_URL=redis://redis:6379
|
||||
- REDIS_HOST=redis
|
||||
- REDIS_PORT=6379
|
||||
- JWT_SECRET=${JWT_SECRET}
|
||||
- JWT_EXPIRATION=${JWT_EXPIRATION:-24h}
|
||||
- JWT_REFRESH_EXPIRATION=${JWT_REFRESH_EXPIRATION:-7d}
|
||||
- MINIO_ENDPOINT=minio
|
||||
- MINIO_PORT=9000
|
||||
- MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}
|
||||
- MINIO_SECRET_KEY=${MINIO_SECRET_KEY}
|
||||
- MINIO_USE_SSL=false
|
||||
- EMAIL_HOST=${EMAIL_HOST}
|
||||
- EMAIL_PORT=${EMAIL_PORT}
|
||||
- EMAIL_SECURE=${EMAIL_SECURE}
|
||||
- EMAIL_USER=${EMAIL_USER}
|
||||
- EMAIL_PASS=${EMAIL_PASS}
|
||||
- EMAIL_FROM=${EMAIL_FROM}
|
||||
volumes:
|
||||
- backend_logs:/app/logs
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
minio:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
# Frontend service using pre-built image
|
||||
frontend:
|
||||
image: serpentrace-frontend:latest
|
||||
container_name: serpentrace-frontend
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
depends_on:
|
||||
- backend
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
# PostgreSQL Database
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: serpentrace-postgres
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_DB: serpentrace
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./sql_schema_only.sql:/docker-entrypoint-initdb.d/init.sql
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# Redis Cache
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: serpentrace-redis
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# MinIO Object Storage
|
||||
minio:
|
||||
image: minio/minio:latest
|
||||
container_name: serpentrace-minio
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}
|
||||
volumes:
|
||||
- minio_data:/data
|
||||
command: server /data --console-address ":9001"
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
driver: local
|
||||
redis_data:
|
||||
driver: local
|
||||
minio_data:
|
||||
driver: local
|
||||
backend_logs:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
serpentrace-network:
|
||||
driver: bridge
|
||||
@@ -0,0 +1,184 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# Backend service with hot reload
|
||||
backend:
|
||||
build:
|
||||
context: ../SerpentRace_Backend
|
||||
dockerfile: ../SerpentRace_Docker/Dockerfile_backend.dev
|
||||
container_name: serpentrace-backend-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- PORT=3000
|
||||
- DB_HOST=postgres
|
||||
- DB_PORT=5432
|
||||
- DB_NAME=serpentrace
|
||||
- DB_USERNAME=postgres
|
||||
- DB_PASSWORD=postgres
|
||||
- REDIS_URL=redis://redis:6379
|
||||
- REDIS_HOST=redis
|
||||
- REDIS_PORT=6379
|
||||
- JWT_SECRET=dev_jwt_secret_change_in_production
|
||||
- JWT_EXPIRATION=24h
|
||||
- JWT_REFRESH_EXPIRATION=7d
|
||||
- MINIO_ENDPOINT=minio
|
||||
- MINIO_PORT=9000
|
||||
- MINIO_ACCESS_KEY=serpentrace
|
||||
- MINIO_SECRET_KEY=serpentrace123!
|
||||
- MINIO_USE_SSL=false
|
||||
volumes:
|
||||
- ../SerpentRace_Backend:/app
|
||||
- /app/node_modules
|
||||
- ../SerpentRace_Backend/logs:/app/logs
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
minio:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
# Frontend service with hot reload
|
||||
frontend:
|
||||
build:
|
||||
context: ../SerpentRace_Frontend
|
||||
dockerfile: ../SerpentRace_Docker/Dockerfile_frontend.dev
|
||||
container_name: serpentrace-frontend-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5173:5173"
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- VITE_API_URL=http://localhost:3000
|
||||
volumes:
|
||||
- ../SerpentRace_Frontend:/app
|
||||
- /app/node_modules
|
||||
depends_on:
|
||||
- backend
|
||||
networks:
|
||||
- serpentrace-network
|
||||
|
||||
# PostgreSQL Database
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: serpentrace-postgres-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_DB: serpentrace
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
|
||||
volumes:
|
||||
- postgres_dev_data:/var/lib/postgresql/data
|
||||
- ./sql_dump_with_test_data.sql:/docker-entrypoint-initdb.d/init.sql:ro
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# Redis Cache
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: serpentrace-redis-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- redis_dev_data:/data
|
||||
command: redis-server --appendonly yes
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# MinIO Object Storage
|
||||
minio:
|
||||
image: minio/minio:latest
|
||||
container_name: serpentrace-minio-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
environment:
|
||||
MINIO_ROOT_USER: serpentrace
|
||||
MINIO_ROOT_PASSWORD: serpentrace123!
|
||||
volumes:
|
||||
- minio_dev_data:/data
|
||||
command: server /data --console-address ":9001"
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# Redis Commander for development debugging
|
||||
redis-commander:
|
||||
image: rediscommander/redis-commander:latest
|
||||
container_name: serpentrace-redis-commander-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8081:8081"
|
||||
environment:
|
||||
- REDIS_HOSTS=local:redis:6379
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- serpentrace-network
|
||||
|
||||
# Database administration tool
|
||||
pgadmin:
|
||||
image: dpage/pgadmin4:latest
|
||||
container_name: serpentrace-pgadmin-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:80"
|
||||
environment:
|
||||
PGADMIN_DEFAULT_EMAIL: admin@serpentrace.dev
|
||||
PGADMIN_DEFAULT_PASSWORD: admin
|
||||
PGADMIN_CONFIG_SERVER_MODE: 'False'
|
||||
PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: 'False'
|
||||
PGADMIN_CONFIG_WTF_CSRF_ENABLED: 'False'
|
||||
volumes:
|
||||
- pgadmin_dev_data:/var/lib/pgadmin
|
||||
- ./pgadmin_servers.json:/pgadmin4/servers.json:ro
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- serpentrace-network
|
||||
|
||||
volumes:
|
||||
postgres_dev_data:
|
||||
driver: local
|
||||
redis_dev_data:
|
||||
driver: local
|
||||
minio_dev_data:
|
||||
driver: local
|
||||
pgadmin_dev_data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
serpentrace-network:
|
||||
driver: bridge
|
||||
@@ -0,0 +1,161 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# Backend service
|
||||
backend:
|
||||
build:
|
||||
context: ../SerpentRace_Backend
|
||||
dockerfile: ../SerpentRace_Docker/Dockerfile_backend
|
||||
container_name: serpentrace-backend
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env.prod
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- PORT=3000
|
||||
- DB_HOST=postgres
|
||||
- DB_PORT=5432
|
||||
- DB_NAME=serpentrace
|
||||
- DB_USERNAME=postgres
|
||||
- DB_PASSWORD=${DB_PASSWORD}
|
||||
- REDIS_URL=redis://redis:6379
|
||||
- REDIS_HOST=redis
|
||||
- REDIS_PORT=6379
|
||||
- JWT_SECRET=${JWT_SECRET}
|
||||
- JWT_EXPIRATION=${JWT_EXPIRATION:-24h}
|
||||
- JWT_REFRESH_EXPIRATION=${JWT_REFRESH_EXPIRATION:-7d}
|
||||
- MINIO_ENDPOINT=minio
|
||||
- MINIO_PORT=9000
|
||||
- MINIO_ACCESS_KEY=${MINIO_ACCESS_KEY}
|
||||
- MINIO_SECRET_KEY=${MINIO_SECRET_KEY}
|
||||
- MINIO_USE_SSL=false
|
||||
- MINIO_BUCKET_NAME=${MINIO_BUCKET_NAME:-serpentrace-logs}
|
||||
- EMAIL_HOST=${EMAIL_HOST}
|
||||
- EMAIL_PORT=${EMAIL_PORT}
|
||||
- EMAIL_SECURE=${EMAIL_SECURE}
|
||||
- EMAIL_USER=${EMAIL_USER}
|
||||
- EMAIL_PASS=${EMAIL_PASS}
|
||||
- EMAIL_FROM=${EMAIL_FROM}
|
||||
- APP_BASE_URL=${APP_BASE_URL:-http://localhost:3000}
|
||||
- CHAT_INACTIVITY_TIMEOUT_MINUTES=${CHAT_INACTIVITY_TIMEOUT_MINUTES:-30}
|
||||
- CHAT_MAX_MESSAGES_PER_USER=${CHAT_MAX_MESSAGES_PER_USER:-100}
|
||||
- CHAT_MESSAGE_CLEANUP_WEEKS=${CHAT_MESSAGE_CLEANUP_WEEKS:-4}
|
||||
- MAX_LOGS_PER_FILE=${MAX_LOGS_PER_FILE:-10000}
|
||||
volumes:
|
||||
- logs-data:/app/logs
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
minio:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
# Frontend service with nginx
|
||||
frontend:
|
||||
build:
|
||||
context: ../SerpentRace_Frontend
|
||||
dockerfile: ../SerpentRace_Docker/Dockerfile_frontend
|
||||
container_name: serpentrace-frontend
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
depends_on:
|
||||
- backend
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
# PostgreSQL Database
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: serpentrace-postgres
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env.prod
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_DB: serpentrace
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# Redis Cache
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: serpentrace-redis
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
command: redis-server --appendonly yes
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# MinIO Object Storage
|
||||
minio:
|
||||
image: minio/minio:latest
|
||||
container_name: serpentrace-minio
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env.prod
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}
|
||||
volumes:
|
||||
- minio_data:/data
|
||||
command: server /data --console-address ":9001"
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
driver: local
|
||||
redis_data:
|
||||
driver: local
|
||||
minio_data:
|
||||
driver: local
|
||||
logs-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
serpentrace-network:
|
||||
driver: bridge
|
||||
@@ -0,0 +1,217 @@
|
||||
services:
|
||||
# Backend service with hot reload
|
||||
backend:
|
||||
build:
|
||||
context: ../SerpentRace_Backend
|
||||
dockerfile: ../SerpentRace_Docker/Dockerfile_backend.dev
|
||||
container_name: serpentrace-backend-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- PORT=3000
|
||||
- DB_HOST=postgres
|
||||
- DB_PORT=5432
|
||||
- DB_NAME=serpentrace
|
||||
- DB_USERNAME=postgres
|
||||
- DB_PASSWORD=postgres
|
||||
- REDIS_URL=redis://redis:6379
|
||||
- REDIS_HOST=redis
|
||||
- REDIS_PORT=6379
|
||||
- JWT_SECRET=dev_jwt_secret_change_in_production
|
||||
- JWT_EXPIRATION=24h
|
||||
- JWT_REFRESH_EXPIRATION=7d
|
||||
- MINIO_ENDPOINT=minio
|
||||
- MINIO_PORT=9000
|
||||
- MINIO_ACCESS_KEY=serpentrace
|
||||
- MINIO_SECRET_KEY=serpentrace123!
|
||||
- MINIO_USE_SSL=false
|
||||
volumes: [ ../SerpentRace_Backend/logs:/app/logs ]
|
||||
develop:
|
||||
watch:
|
||||
- action: sync
|
||||
path: ../SerpentRace_Backend/src
|
||||
target: /app/src
|
||||
ignore:
|
||||
- node_modules/
|
||||
- dist/
|
||||
- "*.log"
|
||||
- action: sync
|
||||
path: ../SerpentRace_Backend/package.json
|
||||
target: /app/package.json
|
||||
- action: rebuild
|
||||
path: ../SerpentRace_Backend/package-lock.json
|
||||
- action: rebuild
|
||||
path: ../SerpentRace_Backend/tsconfig.json
|
||||
- action: rebuild
|
||||
path: ./Dockerfile_backend.dev
|
||||
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
minio:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
# Frontend service with hot reload
|
||||
frontend:
|
||||
build:
|
||||
context: ../SerpentRace_Frontend
|
||||
dockerfile: ../SerpentRace_Docker/Dockerfile_frontend.dev
|
||||
container_name: serpentrace-frontend-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5173:5173"
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- VITE_API_URL=http://localhost:3000
|
||||
volumes: []
|
||||
develop:
|
||||
watch:
|
||||
- action: sync
|
||||
path: ../SerpentRace_Frontend/src
|
||||
target: /app/src
|
||||
ignore:
|
||||
- node_modules/
|
||||
- dist/
|
||||
- "*.log"
|
||||
- action: sync
|
||||
path: ../SerpentRace_Frontend/public
|
||||
target: /app/public
|
||||
- action: sync
|
||||
path: ../SerpentRace_Frontend/package.json
|
||||
target: /app/package.json
|
||||
- action: rebuild
|
||||
path: ../SerpentRace_Frontend/package-lock.json
|
||||
- action: rebuild
|
||||
path: ../SerpentRace_Frontend/vite.config.js
|
||||
- action: rebuild
|
||||
path: ./Dockerfile_frontend.dev
|
||||
depends_on:
|
||||
- backend
|
||||
networks:
|
||||
- serpentrace-network
|
||||
|
||||
# PostgreSQL Database
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: serpentrace-postgres-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
POSTGRES_DB: serpentrace
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
|
||||
volumes:
|
||||
- postgres_dev_data:/var/lib/postgresql/data
|
||||
- ./sql_dump_with_test_data.sql:/docker-entrypoint-initdb.d/init.sql:ro
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# Redis Cache
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: serpentrace-redis-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "6379:6379"
|
||||
volumes:
|
||||
- redis_dev_data:/data
|
||||
command: redis-server --appendonly yes
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# MinIO Object Storage
|
||||
minio:
|
||||
image: minio/minio:latest
|
||||
container_name: serpentrace-minio-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "9000:9000"
|
||||
- "9001:9001"
|
||||
environment:
|
||||
MINIO_ROOT_USER: serpentrace
|
||||
MINIO_ROOT_PASSWORD: serpentrace123!
|
||||
volumes:
|
||||
- minio_dev_data:/data
|
||||
command: server /data --console-address ":9001"
|
||||
networks:
|
||||
- serpentrace-network
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# Redis Commander for development debugging
|
||||
redis-commander:
|
||||
image: rediscommander/redis-commander:latest
|
||||
container_name: serpentrace-redis-commander-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8081:8081"
|
||||
environment:
|
||||
- REDIS_HOSTS=local:redis:6379
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- serpentrace-network
|
||||
|
||||
# Database administration tool
|
||||
pgadmin:
|
||||
image: dpage/pgadmin4:latest
|
||||
container_name: serpentrace-pgadmin-dev
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8080:80"
|
||||
environment:
|
||||
PGADMIN_DEFAULT_EMAIL: admin@serpentrace.dev
|
||||
PGADMIN_DEFAULT_PASSWORD: admin
|
||||
PGADMIN_CONFIG_SERVER_MODE: 'False'
|
||||
PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: 'False'
|
||||
PGADMIN_CONFIG_WTF_CSRF_ENABLED: 'False'
|
||||
volumes:
|
||||
- pgadmin_dev_data:/var/lib/pgadmin
|
||||
- ./pgadmin_servers.json:/pgadmin4/servers.json:ro
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- serpentrace-network
|
||||
|
||||
volumes:
|
||||
postgres_dev_data:
|
||||
driver: local
|
||||
redis_dev_data:
|
||||
driver: local
|
||||
minio_dev_data:
|
||||
driver: local
|
||||
pgadmin_dev_data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
serpentrace-network:
|
||||
driver: bridge
|
||||
@@ -0,0 +1,57 @@
|
||||
```bat
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
rem Define your services here
|
||||
set SERVICES=
|
||||
|
||||
rem Define the environment file
|
||||
set ENV_FILE=.env
|
||||
|
||||
rem Load the environment variables
|
||||
if exist "%ENV_FILE%" (
|
||||
for /f "usebackq tokens=*" %%i in ("%ENV_FILE%") do (
|
||||
set "%%i"
|
||||
)
|
||||
)
|
||||
|
||||
rem Define the default action
|
||||
set ACTION=up
|
||||
|
||||
rem Parse command line arguments
|
||||
:parse_args
|
||||
if "%~1"=="" goto :end_parse
|
||||
if "%~1"=="--build" (
|
||||
set ACTION=build
|
||||
) else if "%~1"=="--down" (
|
||||
set ACTION=down
|
||||
) else if "%~1"=="--help" (
|
||||
goto :help
|
||||
) else if "%~1"=="dev:watch" (
|
||||
goto :dev_watch
|
||||
)
|
||||
shift
|
||||
goto :parse_args
|
||||
|
||||
:end_parse
|
||||
|
||||
rem Display help
|
||||
:help
|
||||
echo Usage: docker-compose-wrapper [options]
|
||||
echo.
|
||||
echo Options:
|
||||
echo --build Build the services
|
||||
echo --down Stop and remove the containers
|
||||
echo --help Display this help message
|
||||
echo dev:watch Start development environment with file watchers
|
||||
goto :eof
|
||||
|
||||
rem Development watch mode
|
||||
:dev_watch
|
||||
echo Starting development environment with file watchers...
|
||||
docker-compose -f docker-compose.watch.yml up --build
|
||||
goto :eof
|
||||
|
||||
rem Execute the docker-compose command with the parsed action
|
||||
%DOCKER_COMPOSE% %ACTION% %SERVICES%
|
||||
```
|
||||
@@ -0,0 +1,60 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
|
||||
# Enable gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# Handle client routing
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# API proxy to backend
|
||||
location /api/ {
|
||||
proxy_pass http://backend:3000/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
|
||||
# WebSocket support
|
||||
location /socket.io/ {
|
||||
proxy_pass http://backend:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# Static assets caching
|
||||
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# Health check endpoint
|
||||
location /health {
|
||||
access_log off;
|
||||
return 200 "healthy\n";
|
||||
add_header Content-Type text/plain;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"Servers": {
|
||||
"1": {
|
||||
"Name": "SerpentRace PostgreSQL Dev",
|
||||
"Group": "Development",
|
||||
"Host": "postgres",
|
||||
"Port": 5432,
|
||||
"MaintenanceDB": "serpentrace",
|
||||
"Username": "postgres",
|
||||
"UseSSLMode": "prefer",
|
||||
"SSLMode": "prefer",
|
||||
"SSLCompression": 0,
|
||||
"Timeout": 10,
|
||||
"UseSSHTunnel": 0,
|
||||
"TunnelPort": "22",
|
||||
"TunnelAuthentication": 0,
|
||||
"KerberosAuthentication": false,
|
||||
"ConnectionParameters": {
|
||||
"sslmode": "prefer",
|
||||
"connect_timeout": "10"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
-- SerpentRace Backend Database Schema and Test Data
|
||||
-- Generated on: August 22, 2025
|
||||
-- PostgreSQL Database Dump
|
||||
|
||||
-- Enable UUID extension
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
-- ============================================================================
|
||||
-- DROP EXISTING TABLES (in reverse dependency order)
|
||||
-- ============================================================================
|
||||
DROP TABLE IF EXISTS "ChatArchives";
|
||||
DROP TABLE IF EXISTS "Chats";
|
||||
DROP TABLE IF EXISTS "Contacts";
|
||||
DROP TABLE IF EXISTS "Decks";
|
||||
DROP TABLE IF EXISTS "Users";
|
||||
DROP TABLE IF EXISTS "Organizations";
|
||||
DROP TABLE IF EXISTS "migrations";
|
||||
|
||||
-- ============================================================================
|
||||
-- CREATE TABLES
|
||||
-- ============================================================================
|
||||
|
||||
-- Organizations Table
|
||||
CREATE TABLE "Organizations" (
|
||||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"name" character varying(255) NOT NULL,
|
||||
"contactfname" character varying(100) NOT NULL,
|
||||
"contactlname" character varying(100) NOT NULL,
|
||||
"contactphone" character varying(20) NOT NULL,
|
||||
"contactemail" character varying(255) NOT NULL,
|
||||
"state" integer NOT NULL DEFAULT 0,
|
||||
"regdate" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
"updatedate" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
"url" character varying(500),
|
||||
"userinorg" integer NOT NULL DEFAULT 0,
|
||||
"maxOrganizationalDecks" integer,
|
||||
CONSTRAINT "PK_Organizations" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- Users Table
|
||||
CREATE TABLE "Users" (
|
||||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"orgid" uuid,
|
||||
"username" character varying(100) NOT NULL UNIQUE,
|
||||
"password" character varying(255) NOT NULL,
|
||||
"email" character varying(255) NOT NULL UNIQUE,
|
||||
"fname" character varying(100) NOT NULL,
|
||||
"lname" character varying(100) NOT NULL,
|
||||
"token" character varying(255),
|
||||
"TokenExpires" TIMESTAMP,
|
||||
"type" character varying(50) NOT NULL,
|
||||
"phone" character varying(20),
|
||||
"state" integer NOT NULL DEFAULT 0,
|
||||
"regdate" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
"updatedate" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
"Orglogindate" TIMESTAMP,
|
||||
CONSTRAINT "PK_Users" PRIMARY KEY ("id"),
|
||||
CONSTRAINT "FK_Users_Organizations" FOREIGN KEY ("orgid") REFERENCES "Organizations"("id")
|
||||
);
|
||||
|
||||
-- Decks Table
|
||||
CREATE TABLE "Decks" (
|
||||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"name" character varying(255) NOT NULL,
|
||||
"type" integer NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"creation_date" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
"cards" json NOT NULL,
|
||||
"played_number" integer NOT NULL DEFAULT 0,
|
||||
"ctype" integer NOT NULL DEFAULT 0,
|
||||
"update_date" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
"state" integer NOT NULL DEFAULT 0,
|
||||
"organization_id" uuid,
|
||||
CONSTRAINT "PK_Decks" PRIMARY KEY ("id"),
|
||||
CONSTRAINT "FK_Decks_Users" FOREIGN KEY ("user_id") REFERENCES "Users"("id"),
|
||||
CONSTRAINT "FK_Decks_Organizations" FOREIGN KEY ("organization_id") REFERENCES "Organizations"("id")
|
||||
);
|
||||
|
||||
-- Chats Table
|
||||
CREATE TABLE "Chats" (
|
||||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"users" uuid[] NOT NULL,
|
||||
"messages" json NOT NULL DEFAULT '[]',
|
||||
"updateDate" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
"state" integer NOT NULL DEFAULT 0,
|
||||
"type" character varying(50) NOT NULL DEFAULT 'direct',
|
||||
"name" character varying(255),
|
||||
"gameId" uuid,
|
||||
"createdBy" uuid,
|
||||
"lastActivity" TIMESTAMP,
|
||||
"createDate" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
"archiveDate" TIMESTAMP,
|
||||
CONSTRAINT "PK_Chats" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- Chat Archives Table
|
||||
CREATE TABLE "ChatArchives" (
|
||||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"chatId" uuid NOT NULL,
|
||||
"archivedMessages" json NOT NULL,
|
||||
"archivedAt" TIMESTAMP NOT NULL,
|
||||
"createDate" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
"chatType" character varying(50) NOT NULL,
|
||||
"chatName" character varying(255),
|
||||
"gameId" uuid,
|
||||
"participants" uuid[] NOT NULL,
|
||||
CONSTRAINT "PK_ChatArchives" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- Contacts Table
|
||||
CREATE TABLE "Contacts" (
|
||||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"name" character varying(255) NOT NULL,
|
||||
"email" character varying(255) NOT NULL,
|
||||
"userid" uuid,
|
||||
"type" integer NOT NULL,
|
||||
"txt" text NOT NULL,
|
||||
"state" integer NOT NULL DEFAULT 0,
|
||||
"createDate" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
"updateDate" TIMESTAMP NOT NULL DEFAULT now(),
|
||||
"adminResponse" text,
|
||||
"responseDate" TIMESTAMP,
|
||||
"respondedBy" uuid,
|
||||
CONSTRAINT "PK_Contacts" PRIMARY KEY ("id"),
|
||||
CONSTRAINT "FK_Contacts_Users" FOREIGN KEY ("userid") REFERENCES "Users"("id"),
|
||||
CONSTRAINT "FK_Contacts_Admins" FOREIGN KEY ("respondedBy") REFERENCES "Users"("id")
|
||||
);
|
||||
|
||||
-- Migrations table (for TypeORM)
|
||||
CREATE TABLE "migrations" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"timestamp" bigint NOT NULL,
|
||||
"name" character varying NOT NULL,
|
||||
CONSTRAINT "PK_migrations" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- CREATE INDEXES
|
||||
-- ============================================================================
|
||||
CREATE INDEX "IDX_DECK_USER_STATE_CTYPE" ON "Decks" ("user_id", "state", "ctype");
|
||||
CREATE INDEX "IDX_DECK_ORG_CTYPE_STATE" ON "Decks" ("organization_id", "ctype", "state");
|
||||
CREATE INDEX "IDX_USERS_EMAIL" ON "Users" ("email");
|
||||
CREATE INDEX "IDX_USERS_USERNAME" ON "Users" ("username");
|
||||
CREATE INDEX "IDX_USERS_ORGID" ON "Users" ("orgid");
|
||||
|
||||
-- ============================================================================
|
||||
-- INSERT TEST DATA
|
||||
-- ============================================================================
|
||||
|
||||
-- Organizations Test Data
|
||||
INSERT INTO "Organizations" ("id", "name", "contactfname", "contactlname", "contactphone", "contactemail", "state", "regdate", "updatedate", "url", "userinorg", "maxOrganizationalDecks") VALUES
|
||||
('11111111-1111-1111-1111-111111111111', 'Tech Solutions Inc', 'John', 'Smith', '+1-555-0001', 'john.smith@techsolutions.com', 1, '2024-01-15 10:00:00', '2024-01-15 10:00:00', 'https://techsolutions.com', 5, 20),
|
||||
('22222222-2222-2222-2222-222222222222', 'Educational Institute', 'Sarah', 'Johnson', '+1-555-0002', 'sarah.johnson@eduinst.edu', 1, '2024-02-01 09:30:00', '2024-02-01 09:30:00', 'https://eduinstitute.edu', 15, 50),
|
||||
('33333333-3333-3333-3333-333333333333', 'Healthcare Corp', 'Michael', 'Brown', '+1-555-0003', 'michael.brown@healthcorp.com', 0, '2024-03-10 14:20:00', '2024-03-10 14:20:00', NULL, 0, 10);
|
||||
|
||||
-- Users Test Data
|
||||
INSERT INTO "Users" ("id", "orgid", "username", "password", "email", "fname", "lname", "token", "TokenExpires", "type", "phone", "state", "regdate", "updatedate", "Orglogindate") VALUES
|
||||
-- Regular users
|
||||
('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', NULL, 'john_doe', '$2b$10$dPXxS9Byg7AbB.fngFtNWel1llS1nHJlQrTO4zQToy7vVitS9mr96', 'john.doe@email.com', 'John', 'Doe', NULL, NULL, 'personal', '+1-555-1001', 1, '2024-01-20 11:00:00', '2024-01-20 11:00:00', NULL),
|
||||
('bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', '11111111-1111-1111-1111-111111111111', 'jane_premium', '$2b$10$dPXxS9Byg7AbB.fngFtNWel1llS1nHJlQrTO4zQToy7vVitS9mr96', 'jane.smith@email.com', 'Jane', 'Smith', NULL, NULL, 'premium', '+1-555-1002', 2, '2024-01-25 12:30:00', '2024-01-25 12:30:00', '2024-01-25 12:30:00'),
|
||||
('cccccccc-cccc-cccc-cccc-cccccccccccc', '22222222-2222-2222-2222-222222222222', 'teacher_bob', '$2b$10$dPXxS9Byg7AbB.fngFtNWel1llS1nHJlQrTO4zQToy7vVitS9mr96', 'bob.teacher@eduinst.edu', 'Bob', 'Teacher', NULL, NULL, 'premium', '+1-555-1003', 2, '2024-02-05 09:15:00', '2024-02-05 09:15:00', '2024-02-05 09:15:00'),
|
||||
-- Admin user
|
||||
('dddddddd-dddd-dddd-dddd-dddddddddddd', NULL, 'admin_user', '$2b$10$dPXxS9Byg7AbB.fngFtNWel1llS1nHJlQrTO4zQToy7vVitS9mr96', 'admin@serpentrace.com', 'Admin', 'User', NULL, NULL, 'admin', '+1-555-9999', 5, '2024-01-01 08:00:00', '2024-01-01 08:00:00', NULL),
|
||||
-- Unverified user
|
||||
('eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee', NULL, 'new_user', '$2b$10$dPXxS9Byg7AbB.fngFtNWel1llS1nHJlQrTO4zQToy7vVitS9mr96', 'newuser@email.com', 'New', 'User', 'verification_token_12345', '2025-08-23 23:59:59', 'personal', NULL, 0, '2025-08-22 16:00:00', '2025-08-22 16:00:00', NULL);
|
||||
|
||||
-- Decks Test Data
|
||||
INSERT INTO "Decks" ("id", "name", "type", "user_id", "creation_date", "cards", "played_number", "ctype", "update_date", "state", "organization_id") VALUES
|
||||
-- Public decks
|
||||
('dddd1111-1111-1111-1111-111111111111', 'General Knowledge Quiz', 2, 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', '2024-02-01 10:00:00',
|
||||
'[
|
||||
{"id": "c1", "type": 0, "text": "What is the capital of France?", "answer": "Paris", "options": ["London", "Paris", "Berlin", "Madrid"]},
|
||||
{"id": "c2", "type": 0, "text": "Which planet is known as the Red Planet?", "answer": "Mars", "options": ["Venus", "Mars", "Jupiter", "Saturn"]},
|
||||
{"id": "c3", "type": 1, "text": "The Great Wall of China", "answer": "is visible from space", "options": ["is visible from space", "was built in one century"]},
|
||||
{"id": "c4", "type": 2, "text": "Describe the process of photosynthesis", "answer": null},
|
||||
{"id": "c5", "type": 3, "text": "The Earth is flat", "answer": false}
|
||||
]',
|
||||
25, 0, '2024-02-01 10:00:00', 0, NULL),
|
||||
|
||||
('dddd2222-2222-2222-2222-222222222222', 'Math Fundamentals', 2, 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', '2024-02-05 14:30:00',
|
||||
'[
|
||||
{"id": "m1", "type": 0, "text": "What is 2 + 2?", "answer": "4", "options": ["3", "4", "5", "6"]},
|
||||
{"id": "m2", "type": 0, "text": "What is the square root of 16?", "answer": "4", "options": ["2", "4", "8", "16"]},
|
||||
{"id": "m3", "type": 3, "text": "Pi is approximately 3.14", "answer": true},
|
||||
{"id": "m4", "type": 4, "text": "Complete the sequence: 2, 4, 6, ?", "answer": "8"}
|
||||
]',
|
||||
15, 0, '2024-02-05 14:30:00', 0, NULL),
|
||||
|
||||
-- Private decks
|
||||
('dddd3333-3333-3333-3333-333333333333', 'My Personal Study Notes', 2, 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', '2024-02-10 16:45:00',
|
||||
'[
|
||||
{"id": "p1", "type": 2, "text": "What did I learn about React hooks today?", "answer": null},
|
||||
{"id": "p2", "type": 2, "text": "Key points from the management meeting", "answer": null}
|
||||
]',
|
||||
3, 1, '2024-02-10 16:45:00', 0, NULL),
|
||||
|
||||
-- Organizational decks
|
||||
('dddd4444-4444-4444-4444-444444444444', 'Company Training Module', 2, 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', '2024-02-15 11:20:00',
|
||||
'[
|
||||
{"id": "o1", "type": 0, "text": "What is our company policy on remote work?", "answer": "Flexible hybrid model", "options": ["No remote work", "Full remote", "Flexible hybrid model", "Weekends only"]},
|
||||
{"id": "o2", "type": 3, "text": "All employees must attend the monthly all-hands meeting", "answer": true},
|
||||
{"id": "o3", "type": 2, "text": "Describe the steps for requesting vacation time", "answer": null}
|
||||
]',
|
||||
8, 2, '2024-02-15 11:20:00', 0, '11111111-1111-1111-1111-111111111111'),
|
||||
|
||||
('dddd5555-5555-5555-5555-555555555555', 'Educational Content for Students', 2, 'cccccccc-cccc-cccc-cccc-cccccccccccc', '2024-03-01 08:15:00',
|
||||
'[
|
||||
{"id": "e1", "type": 0, "text": "When did World War II end?", "answer": "1945", "options": ["1943", "1944", "1945", "1946"]},
|
||||
{"id": "e2", "type": 1, "text": "Shakespeare wrote", "answer": "Romeo and Juliet", "options": ["Romeo and Juliet", "The Great Gatsby"]},
|
||||
{"id": "e3", "type": 3, "text": "The American Revolution began in 1776", "answer": false},
|
||||
{"id": "e4", "type": 4, "text": "Name three primary colors", "answer": "Red, Blue, Yellow"}
|
||||
]',
|
||||
42, 2, '2024-03-01 08:15:00', 0, '22222222-2222-2222-2222-222222222222'),
|
||||
|
||||
-- Joker and Luck type decks
|
||||
('dddd6666-6666-6666-6666-666666666666', 'Lucky Challenges', 0, 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', '2024-03-05 13:40:00',
|
||||
'[
|
||||
{"id": "l1", "type": 4, "text": "Do 10 jumping jacks", "answer": null},
|
||||
{"id": "l2", "type": 4, "text": "Name your favorite childhood memory", "answer": null},
|
||||
{"id": "l3", "type": 4, "text": "Sing happy birthday", "answer": null}
|
||||
]',
|
||||
7, 0, '2024-03-05 13:40:00', 0, NULL),
|
||||
|
||||
('dddd7777-7777-7777-7777-777777777777', 'Wild Cards', 1, 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', '2024-03-08 19:25:00',
|
||||
'[
|
||||
{"id": "j1", "type": 4, "text": "Skip your next turn", "answer": null},
|
||||
{"id": "j2", "type": 4, "text": "Draw two extra cards", "answer": null},
|
||||
{"id": "j3", "type": 4, "text": "Trade places with another player", "answer": null},
|
||||
{"id": "j4", "type": 4, "text": "Double your next score", "answer": null}
|
||||
]',
|
||||
12, 0, '2024-03-08 19:25:00', 0, NULL);
|
||||
|
||||
-- Chats Test Data
|
||||
INSERT INTO "Chats" ("id", "users", "messages", "updateDate", "state", "type", "name", "gameId", "createdBy", "lastActivity", "createDate", "archiveDate") VALUES
|
||||
-- Direct message between two users
|
||||
('chat1111-1111-1111-1111-111111111111',
|
||||
'{"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}',
|
||||
'[
|
||||
{"id": "msg1", "date": "2024-03-20T10:30:00Z", "userid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "text": "Hey Jane! How are you doing?"},
|
||||
{"id": "msg2", "date": "2024-03-20T10:32:00Z", "userid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "text": "Hi John! I'\''m great, thanks for asking. How about you?"},
|
||||
{"id": "msg3", "date": "2024-03-20T10:35:00Z", "userid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "text": "Doing well! Want to play a quiz game later?"},
|
||||
{"id": "msg4", "date": "2024-03-20T10:37:00Z", "userid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "text": "Absolutely! I'\''ll prepare some questions."}
|
||||
]',
|
||||
'2024-03-20 10:37:00', 0, 'direct', NULL, NULL, NULL, '2024-03-20 10:37:00', '2024-03-20 10:30:00', NULL),
|
||||
|
||||
-- Group chat for organization
|
||||
('chat2222-2222-2222-2222-222222222222',
|
||||
'{"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "cccccccc-cccc-cccc-cccc-cccccccccccc", "dddddddd-dddd-dddd-dddd-dddddddddddd"}',
|
||||
'[
|
||||
{"id": "msg5", "date": "2024-03-21T14:15:00Z", "userid": "dddddddd-dddd-dddd-dddd-dddddddddddd", "text": "Welcome everyone to the study group!"},
|
||||
{"id": "msg6", "date": "2024-03-21T14:16:00Z", "userid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "text": "Thanks for organizing this!"},
|
||||
{"id": "msg7", "date": "2024-03-21T14:18:00Z", "userid": "cccccccc-cccc-cccc-cccc-cccccccccccc", "text": "I'\''ve prepared some educational content to share"},
|
||||
{"id": "msg8", "date": "2024-03-21T14:20:00Z", "userid": "dddddddd-dddd-dddd-dddd-dddddddddddd", "text": "Great! Let'\''s start with the basics"}
|
||||
]',
|
||||
'2024-03-21 14:20:00', 0, 'group', 'Study Group', NULL, 'dddddddd-dddd-dddd-dddd-dddddddddddd', '2024-03-21 14:20:00', '2024-03-21 14:15:00', NULL),
|
||||
|
||||
-- Game chat
|
||||
('chat3333-3333-3333-3333-333333333333',
|
||||
'{"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}',
|
||||
'[
|
||||
{"id": "msg9", "date": "2024-03-22T16:45:00Z", "userid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "text": "Ready to start the quiz game?"},
|
||||
{"id": "msg10", "date": "2024-03-22T16:46:00Z", "userid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "text": "Yes! Let'\''s do this!"},
|
||||
{"id": "msg11", "date": "2024-03-22T16:50:00Z", "userid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "text": "Great job on that last question!"},
|
||||
{"id": "msg12", "date": "2024-03-22T16:52:00Z", "userid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "text": "Thanks! This is fun!"}
|
||||
]',
|
||||
'2024-03-22 16:52:00', 0, 'game', 'Quiz Game Session', 'game1111-1111-1111-1111-111111111111', 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', '2024-03-22 16:52:00', '2024-03-22 16:45:00', NULL);
|
||||
|
||||
-- Chat Archives Test Data
|
||||
INSERT INTO "ChatArchives" ("id", "chatId", "archivedMessages", "archivedAt", "createDate", "chatType", "chatName", "gameId", "participants") VALUES
|
||||
('arch1111-1111-1111-1111-111111111111', 'chat0000-0000-0000-0000-000000000000',
|
||||
'[
|
||||
{"id": "oldmsg1", "date": "2024-01-15T09:00:00Z", "userid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "text": "This is an old conversation"},
|
||||
{"id": "oldmsg2", "date": "2024-01-15T09:05:00Z", "userid": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "text": "Yes, from last month"},
|
||||
{"id": "oldmsg3", "date": "2024-01-15T09:10:00Z", "userid": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "text": "Good times!"}
|
||||
]',
|
||||
'2024-02-15 00:00:00', '2024-02-15 00:00:00', 'direct', NULL, NULL, '{"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"}');
|
||||
|
||||
-- Contacts Test Data
|
||||
INSERT INTO "Contacts" ("id", "name", "email", "userid", "type", "txt", "state", "createDate", "updateDate", "adminResponse", "responseDate", "respondedBy") VALUES
|
||||
-- Bug report from registered user
|
||||
('cont1111-1111-1111-1111-111111111111', 'John Doe', 'john.doe@email.com', 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa', 0, 'I found a bug when creating a new deck. The cards are not saving properly when I add more than 10 cards.', 1, '2024-03-18 14:30:00', '2024-03-19 09:15:00', 'Thank you for reporting this issue. We have identified the problem and deployed a fix. Please try creating your deck again.', '2024-03-19 09:15:00', 'dddddddd-dddd-dddd-dddd-dddddddddddd'),
|
||||
|
||||
-- General question from anonymous user
|
||||
('cont2222-2222-2222-2222-222222222222', 'Sarah Wilson', 'sarah.wilson@email.com', NULL, 2, 'Hi, I'\''m interested in using SerpentRace for my classroom. Do you have any educational pricing or features specifically designed for teachers?', 0, '2024-03-19 11:20:00', '2024-03-19 11:20:00', NULL, NULL, NULL),
|
||||
|
||||
-- Problem report from premium user
|
||||
('cont3333-3333-3333-3333-333333333333', 'Jane Smith', 'jane.smith@email.com', 'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb', 1, 'I'\''m having trouble with the organization deck sharing feature. When I share a deck with my team, they can'\''t see the latest updates I made.', 0, '2024-03-20 16:45:00', '2024-03-20 16:45:00', NULL, NULL, NULL),
|
||||
|
||||
-- Sales inquiry
|
||||
('cont4444-4444-4444-4444-444444444444', 'Michael Chen', 'michael.chen@company.com', NULL, 3, 'Our company is interested in purchasing premium licenses for 50 employees. Could you provide pricing information and enterprise features?', 0, '2024-03-21 10:10:00', '2024-03-21 10:10:00', NULL, NULL, NULL),
|
||||
|
||||
-- Other type of contact
|
||||
('cont5555-5555-5555-5555-555555555555', 'Lisa Johnson', 'lisa.johnson@email.com', NULL, 4, 'I love using SerpentRace! Could you add support for audio questions in the quiz decks? This would be great for language learning.', 0, '2024-03-22 13:25:00', '2024-03-22 13:25:00', NULL, NULL, NULL);
|
||||
|
||||
-- Migration entries
|
||||
INSERT INTO "migrations" ("timestamp", "name") VALUES
|
||||
(1755691733404, 'test1755691733404'),
|
||||
(1755706019351, 'AddEmailVerificationFields1755706019351'),
|
||||
(1755817306222, 'AddChatMessagingSystem1755817306222'),
|
||||
(1755855028839, 'CreateContactTable1755855028839'),
|
||||
(1692712800000, 'AddMaxOrganizationalDecksToOrganization1692712800000');
|
||||
|
||||
-- ============================================================================
|
||||
-- UPDATE ORGANIZATION USER COUNTS
|
||||
-- ============================================================================
|
||||
UPDATE "Organizations" SET "userinorg" = (
|
||||
SELECT COUNT(*) FROM "Users" WHERE "Users"."orgid" = "Organizations"."id"
|
||||
);
|
||||
|
||||
-- ============================================================================
|
||||
-- HELPFUL QUERIES FOR TESTING
|
||||
-- ============================================================================
|
||||
|
||||
-- Query to see all users with their organizations
|
||||
-- SELECT u.username, u.email, u.state, o.name as organization_name
|
||||
-- FROM "Users" u
|
||||
-- LEFT JOIN "Organizations" o ON u.orgid = o.id;
|
||||
|
||||
-- Query to see deck distribution by type and visibility
|
||||
-- SELECT
|
||||
-- CASE ctype
|
||||
-- WHEN 0 THEN 'Public'
|
||||
-- WHEN 1 THEN 'Private'
|
||||
-- WHEN 2 THEN 'Organization'
|
||||
-- END as deck_type,
|
||||
-- CASE type
|
||||
-- WHEN 0 THEN 'Luck'
|
||||
-- WHEN 1 THEN 'Joker'
|
||||
-- WHEN 2 THEN 'Question'
|
||||
-- END as card_type,
|
||||
-- COUNT(*) as count
|
||||
-- FROM "Decks"
|
||||
-- WHERE state = 0
|
||||
-- GROUP BY ctype, type
|
||||
-- ORDER BY ctype, type;
|
||||
|
||||
-- Query to see active chats with participant count
|
||||
-- SELECT
|
||||
-- id,
|
||||
-- type,
|
||||
-- name,
|
||||
-- array_length(users, 1) as participant_count,
|
||||
-- json_array_length(messages) as message_count,
|
||||
-- lastActivity
|
||||
-- FROM "Chats"
|
||||
-- WHERE state = 0
|
||||
-- ORDER BY lastActivity DESC;
|
||||
|
||||
-- Query to see contact distribution by type and status
|
||||
-- SELECT
|
||||
-- CASE type
|
||||
-- WHEN 0 THEN 'Bug'
|
||||
-- WHEN 1 THEN 'Problem'
|
||||
-- WHEN 2 THEN 'Question'
|
||||
-- WHEN 3 THEN 'Sales'
|
||||
-- WHEN 4 THEN 'Other'
|
||||
-- END as contact_type,
|
||||
-- CASE state
|
||||
-- WHEN 0 THEN 'Active'
|
||||
-- WHEN 1 THEN 'Resolved'
|
||||
-- WHEN 2 THEN 'Deleted'
|
||||
-- END as status,
|
||||
-- COUNT(*) as count
|
||||
-- FROM "Contacts"
|
||||
-- GROUP BY type, state
|
||||
-- ORDER BY type, state;
|
||||
|
||||
-- ============================================================================
|
||||
-- END OF SQL DUMP
|
||||
-- ============================================================================
|
||||
@@ -0,0 +1,236 @@
|
||||
-- SerpentRace Database Schema
|
||||
-- Generated from TypeORM Entity Aggregates
|
||||
-- This file creates the complete database schema without initial data
|
||||
|
||||
-- Enable UUID extension
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
-- Create Users table
|
||||
CREATE TABLE "Users" (
|
||||
"id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
"orgid" UUID NULL,
|
||||
"username" VARCHAR(100) UNIQUE NOT NULL,
|
||||
"password" VARCHAR(255) NOT NULL,
|
||||
"email" VARCHAR(255) UNIQUE NOT NULL,
|
||||
"fname" VARCHAR(100) NOT NULL,
|
||||
"lname" VARCHAR(100) NOT NULL,
|
||||
"token" VARCHAR(255) NULL,
|
||||
"TokenExpires" TIMESTAMP NULL,
|
||||
"phone" VARCHAR(20) NULL,
|
||||
"state" INTEGER NOT NULL DEFAULT 0,
|
||||
"regdate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"Orglogindate" TIMESTAMP NULL
|
||||
);
|
||||
|
||||
-- Create Organizations table
|
||||
CREATE TABLE "Organizations" (
|
||||
"id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
"name" VARCHAR(255) NOT NULL,
|
||||
"contactfname" VARCHAR(100) NOT NULL,
|
||||
"contactlname" VARCHAR(100) NOT NULL,
|
||||
"contactphone" VARCHAR(20) NOT NULL,
|
||||
"contactemail" VARCHAR(255) NOT NULL,
|
||||
"state" INTEGER NOT NULL DEFAULT 0,
|
||||
"regdate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"url" VARCHAR(500) NULL,
|
||||
"userinorg" INTEGER NOT NULL DEFAULT 0,
|
||||
"maxOrganizationalDecks" INTEGER NULL
|
||||
);
|
||||
|
||||
-- Create Decks table
|
||||
CREATE TABLE "Decks" (
|
||||
"id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
"name" VARCHAR(255) NOT NULL,
|
||||
"type" INTEGER NOT NULL,
|
||||
"user_id" UUID NOT NULL,
|
||||
"creation_date" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"cards" JSONB NOT NULL DEFAULT '[]',
|
||||
"played_number" INTEGER NOT NULL DEFAULT 0,
|
||||
"ctype" INTEGER NOT NULL DEFAULT 0,
|
||||
"update_date" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"state" INTEGER NOT NULL DEFAULT 0,
|
||||
"organization_id" UUID NULL
|
||||
);
|
||||
|
||||
-- Create Chats table
|
||||
CREATE TABLE "Chats" (
|
||||
"id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
"type" VARCHAR(50) NOT NULL DEFAULT 'direct',
|
||||
"name" VARCHAR(255) NULL,
|
||||
"gameId" UUID NULL,
|
||||
"createdBy" UUID NULL,
|
||||
"users" UUID[] NOT NULL,
|
||||
"messages" JSONB NOT NULL DEFAULT '[]',
|
||||
"lastActivity" TIMESTAMP NULL,
|
||||
"createDate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updateDate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"state" INTEGER NOT NULL DEFAULT 0,
|
||||
"archiveDate" TIMESTAMP NULL
|
||||
);
|
||||
|
||||
-- Create Contacts table
|
||||
CREATE TABLE "Contacts" (
|
||||
"id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
"name" VARCHAR(255) NOT NULL,
|
||||
"email" VARCHAR(255) NOT NULL,
|
||||
"userid" UUID NULL,
|
||||
"type" INTEGER NOT NULL,
|
||||
"txt" TEXT NOT NULL,
|
||||
"state" INTEGER NOT NULL DEFAULT 0,
|
||||
"createDate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updateDate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"adminResponse" TEXT NULL,
|
||||
"responseDate" TIMESTAMP NULL,
|
||||
"respondedBy" UUID NULL
|
||||
);
|
||||
|
||||
-- Create Games table
|
||||
CREATE TABLE "Games" (
|
||||
"id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
"gamecode" VARCHAR(10) UNIQUE NOT NULL,
|
||||
"maxplayers" INTEGER NOT NULL,
|
||||
"logintype" INTEGER NOT NULL DEFAULT 0,
|
||||
"state" INTEGER NOT NULL DEFAULT 0,
|
||||
"playerids" UUID[] NOT NULL DEFAULT '{}',
|
||||
"decks" JSONB NOT NULL DEFAULT '[]',
|
||||
"boardsize" INTEGER NOT NULL DEFAULT 50,
|
||||
"createDate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updateDate" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"finishDate" TIMESTAMP NULL,
|
||||
"winnerid" UUID NULL,
|
||||
"createdBy" UUID NOT NULL,
|
||||
"organizationid" UUID NULL
|
||||
);
|
||||
|
||||
-- Add Foreign Key Constraints
|
||||
ALTER TABLE "Users"
|
||||
ADD CONSTRAINT "FK_Users_Organizations"
|
||||
FOREIGN KEY ("orgid") REFERENCES "Organizations"("id") ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE "Decks"
|
||||
ADD CONSTRAINT "FK_Decks_Users"
|
||||
FOREIGN KEY ("user_id") REFERENCES "Users"("id") ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE "Decks"
|
||||
ADD CONSTRAINT "FK_Decks_Organizations"
|
||||
FOREIGN KEY ("organization_id") REFERENCES "Organizations"("id") ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE "Contacts"
|
||||
ADD CONSTRAINT "FK_Contacts_Users"
|
||||
FOREIGN KEY ("userid") REFERENCES "Users"("id") ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE "Contacts"
|
||||
ADD CONSTRAINT "FK_Contacts_RespondedBy"
|
||||
FOREIGN KEY ("respondedBy") REFERENCES "Users"("id") ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE "Chats"
|
||||
ADD CONSTRAINT "FK_Chats_CreatedBy"
|
||||
FOREIGN KEY ("createdBy") REFERENCES "Users"("id") ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE "Chats"
|
||||
ADD CONSTRAINT "FK_Chats_Games"
|
||||
FOREIGN KEY ("gameId") REFERENCES "Games"("id") ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE "Games"
|
||||
ADD CONSTRAINT "FK_Games_CreatedBy"
|
||||
FOREIGN KEY ("createdBy") REFERENCES "Users"("id") ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE "Games"
|
||||
ADD CONSTRAINT "FK_Games_Organizations"
|
||||
FOREIGN KEY ("organizationid") REFERENCES "Organizations"("id") ON DELETE SET NULL;
|
||||
|
||||
ALTER TABLE "Games"
|
||||
ADD CONSTRAINT "FK_Games_Winner"
|
||||
FOREIGN KEY ("winnerid") REFERENCES "Users"("id") ON DELETE SET NULL;
|
||||
|
||||
-- Create Indexes for Performance
|
||||
CREATE INDEX "IDX_Users_Username" ON "Users" ("username");
|
||||
CREATE INDEX "IDX_Users_Email" ON "Users" ("email");
|
||||
CREATE INDEX "IDX_Users_OrgId" ON "Users" ("orgid");
|
||||
CREATE INDEX "IDX_Users_State" ON "Users" ("state");
|
||||
|
||||
CREATE INDEX "IDX_Organizations_Name" ON "Organizations" ("name");
|
||||
CREATE INDEX "IDX_Organizations_State" ON "Organizations" ("state");
|
||||
|
||||
CREATE INDEX "IDX_Decks_UserId" ON "Decks" ("user_id");
|
||||
CREATE INDEX "IDX_Decks_Type" ON "Decks" ("type");
|
||||
CREATE INDEX "IDX_Decks_CType" ON "Decks" ("ctype");
|
||||
CREATE INDEX "IDX_Decks_State" ON "Decks" ("state");
|
||||
CREATE INDEX "IDX_Decks_OrganizationId" ON "Decks" ("organization_id");
|
||||
|
||||
CREATE INDEX "IDX_Chats_Type" ON "Chats" ("type");
|
||||
CREATE INDEX "IDX_Chats_State" ON "Chats" ("state");
|
||||
CREATE INDEX "IDX_Chats_GameId" ON "Chats" ("gameId");
|
||||
CREATE INDEX "IDX_Chats_CreatedBy" ON "Chats" ("createdBy");
|
||||
|
||||
CREATE INDEX "IDX_Contacts_Type" ON "Contacts" ("type");
|
||||
CREATE INDEX "IDX_Contacts_State" ON "Contacts" ("state");
|
||||
CREATE INDEX "IDX_Contacts_UserId" ON "Contacts" ("userid");
|
||||
|
||||
CREATE INDEX "IDX_Games_GameCode" ON "Games" ("gamecode");
|
||||
CREATE INDEX "IDX_Games_State" ON "Games" ("state");
|
||||
CREATE INDEX "IDX_Games_CreatedBy" ON "Games" ("createdBy");
|
||||
CREATE INDEX "IDX_Games_OrganizationId" ON "Games" ("organizationid");
|
||||
|
||||
-- Create update trigger for updatedate columns
|
||||
CREATE OR REPLACE FUNCTION update_updatedate_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updatedate = CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- Apply update triggers
|
||||
CREATE TRIGGER update_users_updatedate
|
||||
BEFORE UPDATE ON "Users"
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updatedate_column();
|
||||
|
||||
CREATE TRIGGER update_organizations_updatedate
|
||||
BEFORE UPDATE ON "Organizations"
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updatedate_column();
|
||||
|
||||
CREATE TRIGGER update_decks_updatedate
|
||||
BEFORE UPDATE ON "Decks"
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updatedate_column();
|
||||
|
||||
CREATE TRIGGER update_chats_updatedate
|
||||
BEFORE UPDATE ON "Chats"
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updatedate_column();
|
||||
|
||||
CREATE TRIGGER update_contacts_updatedate
|
||||
BEFORE UPDATE ON "Contacts"
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updatedate_column();
|
||||
|
||||
CREATE TRIGGER update_games_updatedate
|
||||
BEFORE UPDATE ON "Games"
|
||||
FOR EACH ROW EXECUTE FUNCTION update_updatedate_column();
|
||||
|
||||
-- Comments for documentation
|
||||
COMMENT ON TABLE "Users" IS 'User accounts with authentication and profile information';
|
||||
COMMENT ON TABLE "Organizations" IS 'Organizations that can have multiple users and premium features';
|
||||
COMMENT ON TABLE "Decks" IS 'Card decks for the game, can be public, private, or organizational';
|
||||
COMMENT ON TABLE "Chats" IS 'Chat system supporting direct messages, groups, and game chats';
|
||||
COMMENT ON TABLE "Contacts" IS 'Contact form submissions and support tickets';
|
||||
COMMENT ON TABLE "Games" IS 'Game sessions with players, decks, and game state';
|
||||
|
||||
-- Enum value comments
|
||||
COMMENT ON COLUMN "Users"."state" IS '0=REGISTERED_NOT_VERIFIED, 1=VERIFIED_REGULAR, 2=VERIFIED_PREMIUM, 3=SOFT_DELETE, 4=DEACTIVATED, 5=ADMIN';
|
||||
COMMENT ON COLUMN "Organizations"."state" IS '0=REGISTERED, 1=ACTIVE, 2=SOFT_DELETE';
|
||||
COMMENT ON COLUMN "Decks"."type" IS '0=LUCK, 1=JOKER, 2=QUESTION';
|
||||
COMMENT ON COLUMN "Decks"."ctype" IS '0=PUBLIC, 1=PRIVATE, 2=ORGANIZATION';
|
||||
COMMENT ON COLUMN "Decks"."state" IS '0=ACTIVE, 1=SOFT_DELETE';
|
||||
COMMENT ON COLUMN "Chats"."type" IS 'direct, group, game';
|
||||
COMMENT ON COLUMN "Chats"."state" IS '0=ACTIVE, 1=ARCHIVE, 2=SOFT_DELETE';
|
||||
COMMENT ON COLUMN "Contacts"."type" IS '0=BUG, 1=PROBLEM, 2=QUESTION, 3=SALES, 4=OTHER';
|
||||
COMMENT ON COLUMN "Contacts"."state" IS '0=ACTIVE, 1=RESOLVED, 2=SOFT_DELETE';
|
||||
COMMENT ON COLUMN "Games"."state" IS '0=WAITING, 1=ACTIVE, 2=FINISHED, 3=CANCELLED';
|
||||
COMMENT ON COLUMN "Games"."logintype" IS '0=PUBLIC, 1=PRIVATE, 2=ORGANIZATION';
|
||||
|
||||
-- Grant permissions for application user
|
||||
-- Note: Replace 'serpentrace_app' with your actual application database user
|
||||
-- GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO serpentrace_app;
|
||||
-- GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO serpentrace_app;
|
||||
-- GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO serpentrace_app;
|
||||
Reference in New Issue
Block a user