# ICTAZ Smart Register — Production Deployment Guide

Ubuntu 22.04 · Nginx · Gunicorn · PM2 · MySQL · `sr.ictaz.org.zm`

**Architecture overview:**
```
Internet → Nginx (SSL termination)
             ├── /api/*  /admin/*  /static/*  /media/*  → Gunicorn (Django, unix socket)
             └── everything else                         → PM2 (Next.js, port 3000)
```

## 1. Server Prerequisites

```bash
sudo apt update && sudo apt upgrade -y
sudo apt install -y python3-pip python3-dev python3-venv \
    default-libmysqlclient-dev build-essential nginx git curl
# Node.js 20 LTS
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
# PM2
sudo npm install -g pm2
```

---

## 2. MySQL Database

```bash
sudo mysql -u root -p
```

```sql
CREATE DATABASE db_sr_ictaz CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'sr_ictaz'@'localhost' IDENTIFIED BY 'your-secure-db-password';
GRANT ALL PRIVILEGES ON db_sr_ictaz.* TO 'sr_ictaz'@'localhost';
FLUSH PRIVILEGES;
EXIT;
```

---

## 3. Application Directory

```bash
sudo mkdir -p /var/www/html/smartRegister
sudo chown -R $USER:$USER /var/www/html/smartRegister
cd /var/www/html/smartRegister
git clone <repo-url> .
```

---

## 4. Backend Setup (Django + Gunicorn)

```bash
cd /var/www/html/smartRegister/backend
python3 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt
```

### Backend `.env`

```bash
cp .env.example .env
nano .env
```

Paste and adjust:

```dotenv
# Django
DEBUG=False
SECRET_KEY='your-very-long-random-secret-key-here'
ALLOWED_HOSTS='sr.ictaz.org.zm,*.ictaz.org.zm,127.0.0.1,localhost'
CORS_ALLOWED_ORIGINS=https://sr.ictaz.org.zm

# Database (MySQL)
DB_ENGINE=django.db.backends.mysql
DB_NAME=db_sr_ictaz
DB_USER=sr_ictaz
DB_PASSWORD=your-secure-db-password
DB_HOST=localhost
DB_PORT=3306

# Email
EMAIL_HOST=smtp.office365.com
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_HOST_USER=ess@ictaz.org.zm
EMAIL_HOST_PASSWORD=your-email-password
DEFAULT_FROM_EMAIL=ess@ictaz.org.zm
SERVER_EMAIL=ess@ictaz.org.zm

# JWT
JWT_ACCESS_TOKEN_LIFETIME_HOURS=8
JWT_REFRESH_TOKEN_LIFETIME_DAYS=7

# External EMS API
EMS_API_BASE_URL=https://ems-uat.ictaz.org.zm/api/v1
EMS_API_USERNAME=your-ems-username
EMS_API_PASSWORD=your-ems-password
```

### Initialise database and static files

```bash
DJANGO_SETTINGS_MODULE=config.settings.production python manage.py migrate
DJANGO_SETTINGS_MODULE=config.settings.production python manage.py collectstatic --noinput
DJANGO_SETTINGS_MODULE=config.settings.production python manage.py createsuperuser
```

### Gunicorn systemd service

```bash
sudo nano /etc/systemd/system/gunicorn_ictaz.service
```

```ini
[Unit]
Description=Gunicorn — ICTAZ Smart Register (Django)
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/html/smartRegister/backend
EnvironmentFile=/var/www/html/smartRegister/backend/.env
Environment="DJANGO_SETTINGS_MODULE=config.settings.production"
ExecStart=/var/www/html/smartRegister/backend/venv/bin/gunicorn \
          --access-logfile - \
          --workers 3 \
          --bind unix:/var/www/html/smartRegister/ictaz_smart_register.sock \
          config.wsgi:application

[Install]
WantedBy=multi-user.target
```

```bash
sudo chown -R www-data:www-data /var/www/html/smartRegister/backend
sudo chmod -R 755 /var/www/html/smartRegister/backend
sudo systemctl daemon-reload
sudo systemctl enable --now gunicorn_ictaz
sudo systemctl status gunicorn_ictaz
```

---

## 5. Frontend Setup (Next.js + PM2)

```bash
cd /var/www/html/smartRegister/frontend
npm install
```

### Frontend `.env.local`

```bash
nano .env.local
```

```dotenv
NEXT_PUBLIC_API_URL=https://sr.ictaz.org.zm
```

### Build (standalone output)

```bash
npm run build
# Copy public assets into standalone bundle
cp -r public .next/standalone/public
cp -r .next/static .next/standalone/.next/static
```

### PM2 ecosystem file

```bash
nano /var/www/html/smartRegister/frontend/ecosystem.config.js
```

```js
module.exports = {
  apps: [
    {
      name: 'smartregister-frontend',
      script: '.next/standalone/server.js',
      cwd: '/var/www/html/smartRegister/frontend',
      instances: 1,
      autorestart: true,
      watch: false,
      env: {
        NODE_ENV: 'production',
        PORT: 3000,
        HOSTNAME: '127.0.0.1',
        NEXT_PUBLIC_API_URL: 'https://sr.ictaz.org.zm',
      },
    },
  ],
};
```

### Start and persist PM2

```bash
cd /var/www/html/smartRegister/frontend
pm2 start ecosystem.config.js
pm2 save
pm2 startup   # follow the printed command to register PM2 on boot
pm2 status
```

---

## 6. Nginx Configuration

Update `/etc/nginx/sites-available/sr.ictaz.org.zm`:

```bash
sudo nano /etc/nginx/sites-available/sr.ictaz.org.zm
```

```nginx
# Redirect HTTP → HTTPS
server {
    listen 80;
    server_name sr.ictaz.org.zm;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name sr.ictaz.org.zm;

    ssl_certificate     /etc/ssl/certs/ictaz.org.zm.fullchain.crt;
    ssl_certificate_key /etc/ssl/private/ictaz.org.zm.key;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;

    # ── Django static & media ────────────────────────────────────────────────
    location /static/ {
        alias /var/www/html/smartRegister/backend/staticfiles/;
        expires 30d;
        add_header Cache-Control "public, max-age=2592000";
    }

    location /media/ {
        alias /var/www/html/smartRegister/backend/media/;
        expires 30d;
        add_header Cache-Control "public, max-age=2592000";
    }

    location = /favicon.ico {
        alias /var/www/html/smartRegister/backend/staticfiles/favicon.ico;
        access_log off;
        log_not_found off;
    }

    # ── Next.js immutable static chunks ─────────────────────────────────────
    location /_next/static/ {
        alias /var/www/html/smartRegister/frontend/.next/static/;
        expires 1y;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }

    # ── Django API + Admin → Gunicorn ────────────────────────────────────────
    location ~ ^/(api|admin)/ {
        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_pass http://unix:/var/www/html/smartRegister/ictaz_smart_register.sock;
    }

    # ── Everything else → Next.js (PM2 on port 3000) ────────────────────────
    location / {
        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;
        proxy_pass http://127.0.0.1:3000;
    }

    error_log  /var/log/nginx/sr_ictaz_error.log;
    access_log /var/log/nginx/sr_ictaz_access.log;
}
```

```bash
# If not already symlinked:
sudo ln -sf /etc/nginx/sites-available/sr.ictaz.org.zm /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```

---

## 7. Permissions

```bash
# Backend dirs must be readable by www-data (gunicorn user)
sudo chown -R www-data:www-data /var/www/html/smartRegister/backend
sudo chmod -R 755 /var/www/html/smartRegister/backend

# Frontend dirs: owned by deploy user, readable by all
sudo chmod -R 755 /var/www/html/smartRegister/frontend/.next
```

---

## 8. Updating the Application

```bash
cd /var/www/html/smartRegister
git pull

# --- Backend ---
cd backend
source venv/bin/activate
pip install -r requirements.txt
DJANGO_SETTINGS_MODULE=config.settings.production python manage.py migrate
DJANGO_SETTINGS_MODULE=config.settings.production python manage.py collectstatic --noinput
sudo systemctl restart gunicorn_ictaz

# --- Frontend ---
cd ../frontend
npm install
npm run build
cp -r public .next/standalone/public
cp -r .next/static .next/standalone/.next/static
pm2 restart smartregister-frontend
```

---

## 9. Monitoring & Logs

```bash
# Gunicorn
sudo journalctl -u gunicorn_ictaz -f

# PM2 (Next.js)
pm2 logs smartregister-frontend
pm2 monit

# Nginx
sudo tail -f /var/log/nginx/sr_ictaz_error.log
sudo tail -f /var/log/nginx/sr_ictaz_access.log
```

---

## 10. Database Backup

```bash
mysqldump -u sr_ictaz -p db_sr_ictaz > ictaz_backup_$(date +%Y%m%d_%H%M).sql
```

Automate with cron (`crontab -e`):

```cron
0 2 * * * mysqldump -u sr_ictaz -pYOUR_PASSWORD db_sr_ictaz > /backups/ictaz_$(date +\%Y\%m\%d).sql 2>/dev/null
```

---

## 11. Troubleshooting

| Symptom | Check |
|---------|-------|
| 502 Bad Gateway | `sudo systemctl status gunicorn_ictaz` — socket missing or gunicorn crashed |
| Frontend blank / 502 | `pm2 status` — is `smartregister-frontend` online? `pm2 logs` for errors |
| API calls failing | `CORS_ALLOWED_ORIGINS` must be `https://sr.ictaz.org.zm` (no trailing slash) |
| Static files 404 | `collectstatic` not run, or `alias` path in nginx is wrong |
| DB connection error | Check `DB_*` vars in `.env` and MySQL user grants |
| EMS import fails | Verify `EMS_API_USERNAME` / `EMS_API_PASSWORD` and that `EMS_API_BASE_URL` is reachable from server |

```bash
# Quick restart all services
sudo systemctl restart gunicorn_ictaz
pm2 restart smartregister-frontend
sudo systemctl reload nginx
```

---

## 12. Security Checklist

- `DEBUG=False` in backend `.env`
- `SECRET_KEY` is a long random string (not the dev default)
- Database password is strong and user has minimal privileges
- UFW firewall: only ports 22, 80, 443 open (`sudo ufw allow 'Nginx Full' && sudo ufw enable`)
- `.env` file is not committed to git (`.gitignore` must include `backend/.env` and `frontend/.env.local`)
- Regular database backups scheduled
