# Deployment & Maintenance Guide

This guide covers production hardening steps after a fresh install, and ongoing maintenance tasks.

---

## Immediate post-install checklist

- [ ] Delete `public_html/setup/install.php` — leaving it accessible is a critical security risk
- [ ] Delete `public_html/setup/generate-vapid-keys.php` (if you ran it)
- [ ] Set `APP_DEBUG` to `false` in `config.php` — never leave debug mode on in production
- [ ] Confirm `FORCE_HTTPS` is `true`
- [ ] Verify the site loads over HTTPS (padlock in browser)
- [ ] Confirm upload directories are writable but not publicly listable (the `uploads/.htaccess` denies PHP execution; directory listing is off in the root `.htaccess`)
- [ ] Submit `sitemap.xml` to Google Search Console and Bing Webmaster Tools

---

## Email configuration

PHP's built-in `mail()` function is blocked or unreliable on most shared hosting providers for outbound delivery. For reliable email (verification links, password resets):

1. Set `MAIL_DRIVER` to `'smtp'` in `config.php`
2. Configure the SMTP constants:

```php
define('MAIL_DRIVER',    'smtp');
define('SMTP_HOST',      'smtp.your-provider.example');
define('SMTP_PORT',      587);
define('SMTP_USER',      'noreply@your-domain.example');
define('SMTP_PASS',      'your_smtp_password');
define('SMTP_ENCRYPTION','tls');  // 'tls' for port 587, 'ssl' for port 465
```

Good free options for campus use: Mailgun (3,000 emails/month free), Brevo (300/day free), or your institution's SMTP relay if it allows application-level access.

---

## Cron jobs (cPanel Cron Jobs manager)

| Job | Command | Schedule | Purpose |
|---|---|---|---|
| Sitemap | `php /home/USER/public_html/cron/generate-sitemap.php` | `0 2 * * *` (daily 2 AM) | Regenerates sitemap.xml |

Replace `USER` with your cPanel username. To find it: `echo $HOME` in cPanel Terminal, or check the path shown in File Manager.

**Testing a cron job manually:** run the command in cPanel Terminal (or SSH) and confirm it outputs the expected result before scheduling it.

---

## Backing up

cPanel's built-in backup tool (Backup Wizard) handles both files and databases. Schedule weekly full backups. For the database specifically:

```bash
mysqldump -u DB_USER -p DB_NAME > backup_$(date +%Y%m%d).sql
```

Critical directories to include in file backups:
- `public_html/uploads/` — all user-uploaded media
- `public_html/config/config.php` — your live configuration (never commit this to git)

---

## Updating the application

Each phase is a complete cumulative package — it includes all files from all previous phases. To update:

1. Download the new phase ZIP and extract it locally
2. Upload the contents of `public_html/` to your web root via FTP (overwrite all files)
3. If the phase has a `database/phaseN-delta.sql`, import it in phpMyAdmin before or immediately after uploading
4. Clear any opcode cache if your host uses OPcache (cPanel → PHP OPcache → Reset)

The `config/config.php` file is NEVER included in the ZIP (only `config.sample.php` is) — your live config is safe to overwrite with the new `public_html/` tree.

---

## PHP configuration recommendations

Check these in cPanel → MultiPHP INI Editor (choose your PHP version):

| Setting | Recommended value | Why |
|---|---|---|
| `upload_max_filesize` | `30M` | Audio reports can be up to 25MB |
| `post_max_size` | `35M` | Must be larger than upload_max_filesize |
| `max_execution_time` | `60` | AI tool calls can take up to 45 seconds |
| `memory_limit` | `256M` | GD image processing and large queries |
| `session.cookie_secure` | `1` | HTTPS-only session cookies |
| `session.cookie_httponly` | `1` | Prevent JavaScript access to session cookie |
| `session.cookie_samesite` | `Lax` | CSRF mitigation at the cookie level |

---

## Performance notes

**CSS/JS caching:** The header uses `?v=10` query strings on all local CSS and JS files. When you make changes to these files in future, increment the version number in `includes/header.php` to bust the browser cache.

**Image optimisation:** The `Upload::image()` helper re-encodes uploaded images via GD to strip metadata and normalise format. For further optimisation, consider running a cron job that compresses images in `uploads/` using an image optimisation tool available on your host.

**Service worker caching:** The service worker caches static assets (CSS, JS, icons) with a cache-first strategy and HTML pages with a network-first/stale-while-revalidate strategy. When you bump the SW cache version (`STATIC_CACHE = 'jms-static-vN'` in `service-worker.js`), the old cache is automatically deleted on the next SW activation.

**Database indexes:** All foreign keys are indexed. The engagement tables use compound indexes on `(content_type, content_id)` for the polymorphic pattern. The `ai_tool_usage` table has a unique index on `(user_id, tool_name, usage_date)` used by the atomic upsert. No additional indexes should be needed at campus scale; if response times degrade, add an index on `content.published_at` for the homepage queries.

---

## Security maintenance

**Rotate `APP_SECRET` periodically:** Changing this value invalidates all active sessions (everyone is logged out). Do it during a low-traffic period and announce it in advance.

**Monitor the audit log:** `editor/audit-log.php` gives a full record of all editorial actions. Review it weekly for unexpected activity (e.g. a student account that was somehow granted editor access).

**Keep PHP updated:** cPanel's MultiPHP manager lets you switch PHP versions per domain without affecting other sites. Run PHP 8.1+ for best security and performance.

**API keys:** Rotate AI API keys and VAPID keys if you suspect they've been exposed. After rotating, update `config.php` and — for VAPID keys — also delete all existing push subscriptions (they're tied to the old public key and will all return 410 Gone on the next send, which will clean them up automatically, but it's cleaner to truncate `push_subscriptions` after a key rotation).

---

## Troubleshooting common production issues

**"Headers already sent" error:** A PHP file has whitespace or a BOM before `<?php`. Check with: `grep -rn $'\xEF\xBB\xBF' public_html/` and remove the BOMs.

**Session not persisting:** Confirm `session.cookie_secure = 1` only when HTTPS is actually active. On HTTP, secure cookies are never sent. Check your SSL certificate is valid and not expired.

**Service worker not updating:** The SW is served with `no-cache` headers in `.htaccess`. If visitors report seeing stale content, ask them to visit the site and press Shift+Reload, or navigate to `chrome://serviceworker-internals` and unregister the SW manually.

**Push notifications stopped working after a server move:** If your domain changed, all existing push subscriptions are invalid (they contain the old origin). Truncate `push_subscriptions` and ask users to re-enable notifications.

**Large audio upload fails:** Check `upload_max_filesize` and `post_max_size` in PHP settings (both must be ≥ 25MB). Also check if your cPanel account has a disk quota that's nearly full.
