Skip to content

Latest commit

 

History

History
565 lines (410 loc) · 14.3 KB

File metadata and controls

565 lines (410 loc) · 14.3 KB

Deployment Guide - KNII Ticketing System

Version: 2.2.1 Last Updated: January 2026 Target Project: KNII Ticketing System (Node.js 20 + Express 5 + PostgreSQL 16)

This guide covers deploying the ticketing system in a production environment on your local server.

Quick Start (Docker)

# 1. Setup environment
cp .env.example .env
# Edit .env with your production settings

# 2. Start containers
docker-compose build
docker-compose up -d

# 3. Create floors, departments, and admin user
# Option A: Use JSON configuration (recommended for customization)
docker-compose exec web npm run seed:hospital
# This reads from config/seed-data/floors.json and config/seed-data/departments.json

# Option B: Interactive admin setup (minimal setup)
docker-compose exec web npm run seed-admin
# Follow prompts to create a super admin user

# Option C: Seed with sample data (for testing)
docker-compose exec web npm run seed:sample
# Creates sample tickets and comments for demonstration

# 4. Access the application
# Public: http://localhost:3000
# Admin: http://localhost:3000/auth/login

Seeding Options Explained:

  • seed:hospital - Recommended. Creates floors and departments from JSON config files, fully customizable
  • seed-admin - Quick setup with just a super admin user
  • seed:sample - Adds realistic sample tickets and comments for testing/demo purposes

Prerequisites

  • Docker and Docker Compose installed
  • Node.js 18+ (if running without Docker)
  • PostgreSQL 16 (if running without Docker)
  • Sufficient disk space for database and logs

Production Deployment Options

Option 1: Docker Deployment (Recommended)

1. Prepare Environment

Copy the example environment file and configure for production:

cp .env.example .env

Edit .env with production values:

# IMPORTANT: Update these values for production!
NODE_ENV=production
PORT=3000

# Docker Configuration
DOCKER_COMMAND=npm start
RESTART_POLICY=unless-stopped

# Database - CHANGE THESE CREDENTIALS
POSTGRES_USER=ticketing_user
POSTGRES_PASSWORD=YOUR_STRONG_PASSWORD_HERE
POSTGRES_DB=ticketing_db
DB_PORT=5432
DATABASE_URL=postgres://ticketing_user:YOUR_STRONG_PASSWORD_HERE@db:5432/ticketing_db

# Session Secret - Generate with: openssl rand -base64 32
SESSION_SECRET=YOUR_GENERATED_SECRET_HERE

2. Generate Secure Credentials

# Generate a strong session secret
openssl rand -base64 32

# Generate a strong database password
openssl rand -base64 24

3. Build and Start Services

# Build the containers
docker-compose build

# Start in production mode
docker-compose up -d

# View logs to see initialization
docker-compose logs -f web

The database will be automatically initialized on first startup!

PostgreSQL automatically runs the SQL migration files from the ./migrations folder on first startup, creating all necessary tables (users, tickets, comments, session).

4. Initial Admin Setup

The system automatically creates a default super admin user on first startup:

Default Super Admin Credentials:

  • Username: admin
  • Password: admin123
  • Email: admin@example.com
  • Role: super_admin (can manage other users)

⚠️ CRITICAL SECURITY REQUIREMENT:

  1. Login immediately after deployment
  2. Navigate to User Management and change the admin password
  3. Use a strong password meeting these requirements:
    • Minimum 8 characters
    • At least one uppercase letter
    • At least one lowercase letter
    • At least one number
    • At least one special character

5. User Management Setup

After logging in as super admin:

  1. Access User Management: Click "User Management" in the header
  2. Change Admin Password:
    • Go to Edit → Reset Password
    • Enter a strong password
  3. Create Additional Users (if needed):
    • Click "Create New User"
    • Set username, email, password, and role
    • Choose admin for regular admins or super_admin for user managers

User Roles:

  • admin: Can manage tickets and comments only
  • super_admin: All admin permissions + user management capabilities

6. Verify Deployment

# Check service status
docker-compose ps

# Check application logs
docker-compose logs web

# Check database logs
docker-compose logs db

# Test the application
curl http://localhost:3000

7. Access the Application

  • Public Ticket Submission: http://localhost:3000
  • Admin Login: http://localhost:3000/auth/login
  • Admin Dashboard: http://localhost:3000/admin/dashboard (after login)
  • User Management: http://localhost:3000/admin/users (super_admin only)

8. Database Backups

# Create a backup directory (already mounted in docker-compose.yml)
mkdir -p backups

# Backup database
docker-compose exec db pg_dump -U ticketing_user ticketing_db > backups/backup-$(date +%Y%m%d-%H%M%S).sql

# Restore from backup
docker-compose exec -T db psql -U ticketing_user ticketing_db < backups/backup-YYYYMMDD-HHMMSS.sql

Option 2: PM2 Deployment (Without Docker)

1. Install Dependencies

npm install --production

2. Configure Environment

cp .env.example .env
# Edit .env with production values (see Docker section above)

3. Setup PostgreSQL

Install and configure PostgreSQL locally, then run migrations:

# Run migrations
npm run init-db

# Option A: Create floors, departments, and users from JSON config
npm run seed:hospital

# Option B: Interactive admin user setup
npm run seed-admin

# Option C: Add sample data for testing
npm run seed:sample

Seeding Options:

  • seed:hospital - Creates floors and departments from config/seed-data/ JSON files (recommended)
  • seed-admin - Interactive prompt to create a super admin user
  • seed:sample - Adds sample tickets and comments for demonstration

4. Start with PM2

# Install PM2 globally (if not already installed)
npm install -g pm2

# Start application in cluster mode
npm run prod

# Check status
pm2 status

# View logs
npm run prod:logs

# Monitor in real-time
npm run prod:monitor

5. PM2 Process Management

# Restart application
npm run prod:restart

# Stop application
npm run prod:stop

# Start on system boot
pm2 startup
pm2 save

Post-Deployment Tasks

1. Security Checklist

  • Changed default database password
  • Generated secure SESSION_SECRET (min 32 characters)
  • Set NODE_ENV=production
  • Enabled HTTPS (if using reverse proxy)
  • Configured firewall to restrict database port access
  • Reviewed Helmet security headers in index.js
  • Disabled development tools (nodemon, source maps)
  • Changed default admin password (admin/admin123)
  • Created at least one backup super_admin account
  • Tested account locking after failed login attempts
  • Verified audit logging is working
  • Reviewed user roles and permissions
  • Disabled or removed any test/demo user accounts
  • Verified rate limiting is active on login and ticket submission
  • Confirmed input length limits are enforced
  • Tested session invalidation when user is deactivated
  • Reviewed Winston logs for proper output

2. Monitoring

Docker Monitoring

# View resource usage
docker stats

# View logs
docker-compose logs -f --tail=100

# Restart services
docker-compose restart web

PM2 Monitoring

# Real-time monitoring
pm2 monit

# View logs
pm2 logs ticketing-system

# Application metrics
pm2 show ticketing-system

3. Log Management

Winston Application Logs:

  • Location: logs/error.log (errors only), logs/combined.log (all logs)
  • Rotation: Automatic at 5MB, keeps 5 files
  • Log Level: Configure via LOG_LEVEL environment variable (error, warn, info, debug)
  • Viewing logs:
    # View all logs
    tail -f logs/combined.log
    
    # View errors only
    tail -f logs/error.log
    
    # Docker deployment
    docker-compose exec web tail -f logs/combined.log

Container Logs:

  • Docker: docker-compose logs
  • PM2: ./logs/pm2-*.log
  • Application: Morgan HTTP logs to stdout/stderr

PM2 Log Rotation:

# For PM2 logs
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7

Maintenance Tasks

Database Maintenance

# Vacuum database (Docker)
docker-compose exec db psql -U ticketing_user -d ticketing_db -c "VACUUM ANALYZE;"

# Check database size
docker-compose exec db psql -U ticketing_user -d ticketing_db -c "SELECT pg_size_pretty(pg_database_size('ticketing_db'));"

Update Application

# Pull latest changes
git pull origin main

# Docker deployment
docker-compose down
docker-compose build
docker-compose up -d

# PM2 deployment
npm install --production
npm run prod:restart

Troubleshooting

Issue: Application won't start

# Check logs
docker-compose logs web
# or
pm2 logs ticketing-system

# Verify environment variables
docker-compose exec web env | grep -E 'NODE_ENV|DATABASE_URL|SESSION_SECRET'
# or
cat .env

Issue: Database connection fails

# Check database is running
docker-compose ps db

# Test database connection
docker-compose exec db psql -U ticketing_user -d ticketing_db -c "SELECT 1;"

# Check DATABASE_URL format
# Should be: postgres://username:password@host:port/database

Issue: Can't login to admin

# Verify admin user exists and check status
docker-compose exec db psql -U ticketing_user -d ticketing_db -c "SELECT username, email, role, status, login_attempts FROM users;"

# Check if account is locked (login_attempts >= 5)
# If locked, reset login attempts:
docker-compose exec db psql -U ticketing_user -d ticketing_db -c "UPDATE users SET login_attempts = 0 WHERE username = 'admin';"

# Check if account status is 'active'
# If not, activate the account:
docker-compose exec db psql -U ticketing_user -d ticketing_db -c "UPDATE users SET status = 'active' WHERE username = 'admin';"

Issue: User account locked after failed login attempts

When a user fails to login 5 times, their account is automatically locked for security.

# Check locked accounts
docker-compose exec db psql -U ticketing_user -d ticketing_db -c "SELECT username, email, login_attempts FROM users WHERE login_attempts >= 5;"

# Unlock specific user account
docker-compose exec db psql -U ticketing_user -d ticketing_db -c "UPDATE users SET login_attempts = 0 WHERE username = 'USERNAME';"

# Unlock all accounts (use with caution)
docker-compose exec db psql -U ticketing_user -d ticketing_db -c "UPDATE users SET login_attempts = 0;"

Issue: Can't access User Management page

Only users with super_admin role can access the User Management page.

# Check user role
docker-compose exec db psql -U ticketing_user -d ticketing_db -c "SELECT username, role FROM users WHERE username = 'admin';"

# Upgrade user to super_admin
docker-compose exec db psql -U ticketing_user -d ticketing_db -c "UPDATE users SET role = 'super_admin' WHERE username = 'admin';"

Issue: Need to view audit logs

# View recent user management actions
docker-compose exec db psql -U ticketing_user -d ticketing_db -c "SELECT al.created_at, u.username as actor, al.action, al.target_type, al.details FROM audit_logs al JOIN users u ON al.actor_id = u.id ORDER BY al.created_at DESC LIMIT 20;"

# View logs for specific user
docker-compose exec db psql -U ticketing_user -d ticketing_db -c "SELECT * FROM audit_logs WHERE actor_id = (SELECT id FROM users WHERE username = 'admin') ORDER BY created_at DESC;"

Issue: Out of disk space

# Check disk usage
df -h

# Clean Docker resources
docker system prune -a --volumes

# Clean old logs
rm -f logs/*.log
pm2 flush

Issue: Rate limit preventing legitimate access

Rate limits are stored in-memory and reset on application restart.

# Docker deployment
docker-compose restart web

# PM2 deployment
pm2 restart ticketing-system

Note: Rate limits are per-IP address:

  • Login: 10 attempts per 15 minutes
  • Ticket submission: 5 attempts per hour

If you need to adjust these limits, modify middleware/rateLimiter.js.


Scaling Considerations

Horizontal Scaling (PM2 Cluster Mode)

PM2 automatically runs in cluster mode with instances: 'max' in ecosystem.config.js, utilizing all CPU cores.

Database Connection Pool

Connection pool is configured in config/database.js:

  • Max connections: 20
  • Idle timeout: 30s
  • Connection timeout: 2s

Adjust based on load:

// config/database.js
const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  max: 50, // Increase for higher load
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
});

Load Balancing

For multiple server instances, use nginx as a reverse proxy:

upstream ticketing_backend {
  server localhost:3000;
  server localhost:3001;
  server localhost:3002;
}

server {
  listen 80;
  server_name tickets.yourdomain.com;

  location / {
    proxy_pass http://ticketing_backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    proxy_set_header Host $host;
    proxy_cache_bypass $http_upgrade;
  }
}

Environment Variables Reference

Variable Required Default Description
NODE_ENV Yes development Set to production for production
PORT No 3000 Application port
DATABASE_URL Yes - PostgreSQL connection string
SESSION_SECRET Yes - Secret for session encryption (min 32 chars)
LOG_LEVEL No info Winston log level: error, warn, info, debug
POSTGRES_USER Docker only ticketing_user Database username
POSTGRES_PASSWORD Docker only - Database password
POSTGRES_DB Docker only ticketing_db Database name
DB_PORT Docker only 5432 Database port
DOCKER_COMMAND Docker only npm run dev Command to run in container
RESTART_POLICY Docker only no Docker restart policy

Additional Resources