# Production Deployment Guide

A concise operational guide for deploying and maintaining **arb.admedia.com**.

---

## 1. Key Architectural Facts

* **PHP 8.5 & Laravel 13:** CLI commands and PHP-FPM must run on PHP 8.5 (`php8.5`).
* **`vendor/` is Git-Tracked:** Do **not** run `composer install` in production. New dependencies are committed to Git in dev.
* **No Frontend Build (`npm run build`):** UI uses Bootstrap 5.3 CDN and Livewire/Volt. There is no Vite or Webpack bundler.
* **Puppeteer Runtime:** Node and Chrome binaries are required for backend scraper scripts (Meta Ad Library and Brand Signal extraction).
* **Permissions:** Web server (`www-data` or `nginx`) needs write access to `storage/` and `bootstrap/cache/`. In production, set web server group ownership with `775` (or POSIX ACLs). On shared dev environments without sudo access, `./pf.sh` / `777` is provided as a local fallback.

---

## 2. Server Prerequisites

Ask the sysadmin to confirm these are in place before deploying:

* **PHP 8.5:** `php8.5-fpm`, `php8.5-cli`, extensions: `pdo_mysql`, `gd`, `curl`, `mbstring`, `xml`, `pcntl`, `posix`, `bcmath`, `zip`.
* **Node.js & Chrome Dependencies:** Node 22+ LTS and system libraries for headless Chrome: `libnss3`, `libatk1.0-0`, `libx11-xcb1`, `libgbm1`, `libasound2`.
* **Nginx & MySQL:** Web server pointing to `<project-dir>/public`; MySQL database pre-created (`arb` or as configured in `.env`).
* **Supervisor:** For long-running background queue workers.
* **Cron:** Standard crontab entry for Laravel's scheduler.

---

## 3. First-Time Server Setup

> **Note:** The `.env` file is provided separately by the developer. Place it at the project root before proceeding.

### Step 1: Clone and enter the repository

```bash
git clone <repo-url> /var/www/arb.admedia.com
cd /var/www/arb.admedia.com
```

### Step 2: Place the `.env` file

Copy the provided `.env` file to the project root and lock it down:

```bash
chmod 600 .env
```

### Step 3: Generate `APP_KEY` (one-time only)

```bash
php8.5 artisan key:generate --no-interaction
```

### Step 4: Run the deploy script

The deploy script handles everything: Puppeteer install, migrations, first-time seeding (auto-detected), caches, permissions, and OPcache reload.

```bash
chmod +x deploy.sh
./deploy.sh
```

**What the script does automatically on first run:**
- Installs Node modules and Puppeteer Chrome browser.
- Runs all database migrations.
- **Detects first-time setup** (checks if the `avatars` table is empty) and runs `ProductionSeeder` and `PlatformConnectionSeeder` automatically — seeding avatars, voices, ad accounts, Facebook pages, and platform connections.
- Warms all Laravel caches.
- Creates the `public/storage` symlink.
- Signals queue workers to restart.
- Re-applies file permissions.
- Reloads PHP-FPM.

On subsequent deployments, the seeder is skipped automatically.

### Step 5: Install Cron & Supervisor (see Section 4)

---

### A. Scheduler Cron (Mandatory — install once)

Required for daily OAuth token refresh (`platforms:refresh-tokens`), campaign performance sync (`campaigns:sync-performance`), database queue pruning, and scheduled tasks. 

#### Method 1: `admediacrons` Centralized Runner (AdMedia Company Standard)
If cron tasks are maintained in the centralized `admediacrons` repository:
1. Deploy `html/arb_cron.php` into the `admediacrons.com` repository.
2. In the `admediacrons` server's master crontab:
```cron
# /var/admediacrons.com/html/arb_cron.php triggers https://arb.admedia.com/api/cron/run
* * * * * cd /var/admediacrons.com/html && /usr/bin/php arb_cron.php schedule:run >> /tmp/arb_cron.log 2>&1
```
*(The runner uses AdMedia Redis locking, sends NOC alerts on failure, and triggers Laravel tasks on the application server under PHP 8.5).*

#### Method 2: Drop-in File via `/etc/cron.d/` (On ARB Server)
Create `/etc/cron.d/arb`:
```cron
# /etc/cron.d/arb - Laravel task scheduler
* * * * * www-data cd /var/www/arb.admedia.com && /usr/bin/php8.5 artisan schedule:run >> /dev/null 2>&1
```
Ensure permissions:
```bash
sudo chmod 0644 /etc/cron.d/arb
```

#### Method 3: Systemd Timer (For systemd-only environments without cron)
1. Create `/etc/systemd/system/arb-scheduler.service`:
```ini
[Unit]
Description=ARB Laravel Scheduler
After=network.target

[Service]
Type=oneshot
User=www-data
Group=www-data
WorkingDirectory=/var/www/arb.admedia.com
ExecStart=/usr/bin/php8.5 /var/www/arb.admedia.com/artisan schedule:run
```

2. Create `/etc/systemd/system/arb-scheduler.timer`:
```ini
[Unit]
Description=Run ARB Laravel Scheduler every minute

[Timer]
OnCalendar=*:0/1
AccuracySec=1s
Persistent=true

[Install]
WantedBy=timers.target
```

3. Enable and start:
```bash
sudo systemctl daemon-reload
sudo systemctl enable --now arb-scheduler.timer
```

#### Method 4: User Crontab (`crontab -e`)
Add to `crontab -e` for user `www-data`:
```cron
* * * * * cd /var/www/arb.admedia.com && php8.5 artisan schedule:run >> /dev/null 2>&1
```


### B. Supervisor Queue Worker (Recommended — install once)

AI video generation and scraping jobs can take several minutes. Long-running workers must be managed by Supervisor.

Create `/etc/supervisor/conf.d/arb-worker.conf`:

```ini
[program:arb-worker]
process_name=%(program_name)s_%(process_num)02d
directory=/var/www/arb.admedia.com
command=php8.5 artisan queue:work database --sleep=3 --tries=1 --timeout=2000 --max-time=3600
user=www-data
numprocs=2
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stopwaitsecs=2160
redirect_stderr=true
stdout_logfile=/var/www/arb.admedia.com/storage/logs/queue-worker.log
```

Apply:
```bash
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start arb-worker:*
```

> **Fallback (no Supervisor yet):** The scheduler cron automatically kicks off a short-lived queue worker every minute (`queue:work --stop-when-empty`). Jobs will process but may queue behind slow video renders. Set up Supervisor when possible.

---

## 5. Routine Deployments (After Every Code Push)

Using the automated script:
```bash
./deploy.sh
# or for a specific branch:
./deploy.sh production
```

The script handles everything. No manual steps required after the initial setup.

---

## 6. Manual Instructions for Sysadmins (If unable to run `deploy.sh`)

If company policy or environment restrictions prevent executing shell automation scripts, sysadmins can perform all actions manually:

### A. One-Time Setup (Run Once on New Server)

Execute these commands in order from `/var/www/arb.admedia.com`:

```bash
cd /var/www/arb.admedia.com

# 1. Environment configuration
cp .env.example .env
chmod 600 .env
# Edit .env with database credentials and AI API keys
php8.5 artisan key:generate --no-interaction

# 2. Scraper dependencies (Puppeteer Chrome)
npm install
npx puppeteer browsers install chrome

# 3. Database migrations
php8.5 artisan migrate --force --no-interaction

# 4. Initial database seeding (One-time only)
# Seeds avatars, voices, ad accounts, Facebook pages, and platform credentials
php8.5 artisan db:seed --class=ProductionSeeder --force --no-interaction
php8.5 artisan db:seed --class=PlatformConnectionSeeder --force --no-interaction

# 5. Public storage symlink
php8.5 artisan storage:link --no-interaction

# 6. Optimize and warm caches
php8.5 artisan optimize:clear --no-interaction
php8.5 artisan optimize --no-interaction

# 7. File permissions (Production standard: 775 with web server group ownership)
sudo chown -R www-data:www-data storage bootstrap/cache
sudo chmod -R 775 storage bootstrap/cache
# If deploying as a separate user (e.g. deploy), add user to group and enable SGID:
# sudo usermod -a -G www-data $USER && sudo chmod -R g+s storage bootstrap/cache
# (Fallback for unprivileged environments without sudo: ./pf.sh)

# 8. Setup Scheduler & Queue Worker (One-time)
# Scheduler: create /etc/cron.d/arb (or systemd timer) — see Section 4A
# Worker: configure Supervisor (or systemd service) — see Section 4B
```

---

### B. Routine Release Steps (Run on Every Deployment / Code Release)

Whenever a new code release or branch update is deployed:

```bash
cd /var/www/arb.admedia.com

# 1. Pull latest code
git fetch origin production
git pull origin production

# 2. Update Node dependencies (only if package.json was modified)
npm install
npx puppeteer browsers install chrome

# 3. Run pending database migrations
php8.5 artisan migrate --force --no-interaction

# 4. Flush and re-warm Laravel caches
php8.5 artisan optimize:clear --no-interaction
php8.5 artisan optimize --no-interaction

# 5. Signal queue workers to restart (gracefully reloads Supervisor workers)
php8.5 artisan queue:restart --no-interaction

# 6. Verify file permissions
sudo chmod -R 775 storage bootstrap/cache 2>/dev/null || ./pf.sh

# 7. Reload PHP-FPM to flush OPcache
sudo systemctl reload php8.5-fpm
```

*(Note: `composer install` is **never** run because `vendor/` is tracked in git. `npm run build` is **never** run because Bootstrap 5.3 CDN + Livewire is used).*

---

## 7. Nginx VirtualHost Reference

```nginx
server {
    listen 443 ssl http2;
    server_name arb.admedia.com;
    root /var/www/arb.admedia.com/public;
    index index.php;

    charset utf-8;
    client_max_body_size 50m;

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

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

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

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

---

## 8. Verifying the Deployment

```bash
# App health check (should return 200)
curl -s -o /dev/null -w "%{http_code}" https://arb.admedia.com/up

# Check migration status
php8.5 artisan migrate:status --no-interaction

# Check queue workers are running
sudo supervisorctl status arb-worker:*

# Check for failed jobs
php8.5 artisan queue:failed --no-interaction
```
