# Deployment Guide — Majed Portfolio (Laravel 12 + Filament 5)

This guide covers deploying to shared hosting (cPanel), a VPS, or a cloud platform.

## 0. Pre-flight checklist (read first)

| Item | Required value |
|------|----------------|
| PHP | `>= 8.2` (8.4 recommended) with `mysqli`, `pdo_mysql`, `mbstring`, `openssl`, `curl`, `zip`, `gd`/`imagick` |
| Composer | v2 (PHP 8.4 compatible) |
| Node.js | `>= 20` for building frontend assets |
| Database | MySQL 8+ (or MariaDB 10.6+) |
| Web server | Nginx or Apache |
| HTTPS | **Mandatory** — Lets Encrypt / Cloudflare. The app forces secure cookies when `SESSION_SECURE_COOKIE=true`. |

---

## 1. cPanel / shared hosting

### 1.1 Upload
1. Build in Git and push the repo; your host pulls it, **or** upload a zip of the project root to `public_html`.
2. Move Laravel's `public/` contents into the docroot and adjust `public/index.php` paths, **or** (simplest and safest) keep the project in a folder above `public_html` and set the **document root to `/path/to/project/public`** — most cPanel hosts allow this via *"Manage document root"*.

### 1.2 Install dependencies (dedicated server / SSH available)
```bash
composer install --no-dev --optimize-autoloader
npm ci && npm run build
```

### 1.3 Environment
```bash
cp .env.example .env
php artisan key:generate
```
Edit `.env` for production:
```php
APP_ENV=production
APP_DEBUG=false
APP_URL=https://yoursite.com
SESSION_DRIVER=database
SESSION_SECURE_COOKIE=true
CACHE_STORE=file
QUEUE_CONNECTION=database
DB_CONNECTION=mysql
# ... your real DB credentials ...
ADMIN_USERNAME=admin
ADMIN_PASSWORD="a-Very-Strong-Passphrase!"
```

### 1.4 Database & storage
```bash
php artisan migrate --force
php artisan db:seed --force
php artisan storage:link          # public/storage -> storage/app/public
```
> `storage:link` is essential for uploaded media to be served. On cPanel where symlinks are restricted, upload a copy of the `storage/app/public` folder or ask your host to allow symlinks.

### 1.5 Queue worker
Send notifications and run scheduled tasks with a worker. On shared hosting without a daemon, set `QUEUE_CONNECTION=sync` **only if** low volume is acceptable (notifications then send inline). Better: create a cron entry:

```
* * * * * /path/to/php /path/to/project/artisan queue:work --tries=1 --timeout=60 --stop-when-empty
* * * * * /path/to/php /path/to/project/artisan schedule:run
```

### 1.6 Cron for cache / schedule
```bash
* * * * * /usr/bin/php /path/to/project/artisan schedule:run >> /dev/null 2>&1
```

### 1.7 Production cache
```bash
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
```
> Run these **after every deploy**. Without them every request re-parses the whole
> config tree and recompiles routes/views. Also enable **OPcache** in your host's
> PHP settings if available (it caches compiled PHP files in memory — the single
> biggest TTFB win on shared hosting).
>
> The public site also uses a small auto-invalidating data cache (`app/Support/SiteCache.php`)
> for settings/navigation/footer/social/home-section rows, so admin edits appear
> immediately without any manual `cache:clear`.

---

## 2. VPS / single server (Nginx)

### 2.1 Nginx site config
```nginx
server {
    listen 443 ssl http2;
    server_name yoursite.com;

    root /var/www/majed-portfolio/public;
    index index.php;

    # HSTS (only after HTTPS + domain verified)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }

    location ~ \.env$ { deny all; }
}
# HTTP -> HTTPS redirect server block here.
```

### 2.2 Supervisord for the queue worker
```ini
[program:majed-queue]
command=php /var/www/majed-portfolio/artisan queue:work --sleep=3 --tries=1 --timeout=60
directory=/var/www/majed-portfolio
autostart=true
autorestart=true
user=www-data
```

### 2.3 Permissions
```bash
sudo chown -R www-data:www-data storage bootstrap/cache
sudo chmod -R 775 storage bootstrap/cache
```

---

## 3. Cloud platforms (Forge / Ploi / Railway / Heroku)

- **Forge/Ploi:** handled by the panel; point your repo, set env vars in the UI, run the deploy script.
- **Railway/Heroku:** add a `Procfile` with `web` and `worker` processes; configure the build command (`composer install --no-dev --optimize-autoloader && npm ci && npm run build`) and run migrations via `php artisan migrate --force` on deploy.

---

## 4. Post-deploy hardening checklist

1. **`APP_DEBUG=false`** — verify no error stack traces are shown.
2. **HTTPS everywhere** — set `SESSION_SECURE_COOKIE=true`, `APP_URL=https://...`.
3. **Strong admin password** — change via the Filament profile page; never leave the seeded default.
4. **Set `ADMIN_PASSWORD`** to a strong secret in production (the seeder uses it).
5. **Enable the CSP** once verified on staging: set `SECURITY_CSP_ENABLED=true` in `.env`.
6. **Rotate `APP_KEY`** — a leaked key decrypts sessions, remember tokens, and the stored encrypted SMTP password.
7. **Restrict DB user** to least privilege (no `GRANT` beyond app needs).
8. **Backup** `storage/` (uploads) and the database regularly; test restores.
9. **Monitor** `storage/logs/laravel.log` and the `activity_log` table for failed logins and admin changes.
10. **Keep dependencies updated:** `composer update` + `npm audit fix` on a schedule.

---

## 5. Rollback

Keep the previous release tagged. To roll back:
1. Deploy the previous code (git checkout / upload).
2. `php artisan migrate:rollback --step=1` only if the DB was the issue AND you understand the impact.
3. Re-run `php artisan config:cache && php artisan route:cache && php artisan view:cache`.
4. Restart the queue worker (`php artisan queue:restart`).
