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:
2025-09-21 03:27:57 +02:00
parent 5b7c3ba4b2
commit 86211923db
306 changed files with 52956 additions and 0 deletions
+17
View File
@@ -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
+222
View File
@@ -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
+55
View File
@@ -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
+71
View File
@@ -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
+267
View File
@@ -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
+60
View File
@@ -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"]
+29
View File
@@ -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"]
+36
View File
@@ -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
+184
View File
@@ -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
+161
View File
@@ -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
+217
View File
@@ -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
+57
View File
@@ -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%
```
+60
View File
@@ -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;
}
}
+24
View File
@@ -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
-- ============================================================================
+236
View File
@@ -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;