# Deployment

## Environment Variables

Key environment variables required (see `.env` and `.env.example`):

### Application
| Variable | Example | Description |
|----------|---------|-------------|
| `APP_NAME` | `Laravel` | Application name |
| `APP_ENV` | `local` / `production` | Environment |
| `APP_DEBUG` | `true` / `false` | Debug mode |
| `APP_URL` | `http://localhost:8000` | Application URL |
| `APP_KEY` | base64 key | Laravel app key |
| `APP_TIMEZONE` | `Asia/Kolkata` | Timezone |

### Database
| Variable | Example | Description |
|----------|---------|-------------|
| `DB_CONNECTION` | `mysql` | Database driver |
| `DB_HOST` | `127.0.0.1` | Database host |
| `DB_PORT` | `3306` | Database port |
| `DB_DATABASE` | `crm_app_db` | Database name |
| `DB_USERNAME` | `root` | Database username |
| `DB_PASSWORD` | - | Database password |

### Queue & Cache
| Variable | Example | Description |
|----------|---------|-------------|
| `QUEUE_CONNECTION` | `database` | Queue driver |
| `CACHE_STORE` | `database` | Cache driver |
| `SESSION_DRIVER` | `database` | Session driver |

### Cron (HTTP triggers)
| Variable | Example | Description |
|----------|---------|-------------|
| `CRON_SECRET` | 64-char hex | Shared secret for `/api/cron/schedule` and `/api/cron/queue`. Generate with `php -r "echo bin2hex(random_bytes(32));"`. See [HTTP-Triggered Cron](#http-triggered-cron-cpanel--shared-hosting). |

### SMS (External API)
| Variable | Example | Description |
|----------|---------|-------------|
| `SMS_API_URL` | `https://sms.bluwaves.in/sendsms/bulk.php` | SMS provider endpoint |
| `SMS_API_USERNAME` | `bluewavesm` | SMS API username |
| `SMS_API_PASSWORD` | secret | SMS API password |
| `SMS_API_SENDER` | `BLUWAV` | SMS sender ID |
| `SMS_API_TYPE` | `TEXT` | SMS type |
| `SMS_API_ENTITY_ID` | `1301159074611947351` | DLT entity ID |
| `SMS_API_LOGIN_TEMPLATE_ID` | `1707164345457901569` | Login OTP template |
| `SMS_API_MEETING_TEMPLATE_ID` | `1707173278785814832` | Meeting OTP template |

### IVR (External API)
| Variable | Example | Description |
|----------|---------|-------------|
| `CTC_API_URL` | `https://portal.bluewavesmedia.in/api/v1/Ctc/CtcCall/MakeCtcCall` | IVR API endpoint |

### Mail
| Variable | Example | Description |
|----------|---------|-------------|
| `MAIL_MAILER` | `log` / `smtp` | Mail driver |
| `MAIL_HOST` | `127.0.0.1` | SMTP host |
| `MAIL_PORT` | `2525` | SMTP port |
| `MAIL_FROM_ADDRESS` | `hello@example.com` | From address |

### Push Notifications (Firebase Cloud Messaging)

Mobile (Android/iOS) and browser web push use **separate Firebase projects** (different Google
accounts). Each project has its own service-account JSON. The `project_id` is read from inside each
JSON; the env vars below are optional overrides.

| Variable | Example | Description |
|----------|---------|-------------|
| `FCM_SERVICE_ACCOUNT_PATH` | `storage/app/private/firebase-service-account.json` | Mobile project service-account JSON path |
| `FCM_PROJECT_ID` | - | Mobile project id override (else read from JSON) |
| `FCM_WEB_SERVICE_ACCOUNT_PATH` | `storage/app/private/firebase-service-account-web.json` | Web project service-account JSON path |
| `FCM_WEB_PROJECT_ID` | - | Web project id override (else read from JSON) |
| `FCM_WEB_VAPID_PUBLIC_KEY` | - | Web project's VAPID public key, served to the frontend via `GET /api/config/push/vapid-key` (not a secret) |
| `FCM_WEB_NOTIFICATION_ICON` | `https://app.example.com/icon.png` | Fallback icon for browser notifications. The recipient's **account logo** is used automatically when set; this is only used when the account has no logo |
| `FCM_WEB_DEFAULT_URL` | `https://app.example.com` | URL opened on notification click when no `click_action` is provided |

Both service-account JSON files must be present under `storage/app/private/` on the server (they are
git-ignored). The public VAPID key can be committed via env; the JSON files must not be committed.

## Server Requirements

- **PHP**: ^8.2 with extensions: curl, openssl, gd (for Intervention), mbstring, pdo_mysql, xml, bcmath, json
- **Database**: MySQL 8.0+
- **Web Server**: Apache/Nginx with URL rewriting
- **Composer**: 2.x
- **Node.js**: 22+ (for build only)
- **Queue Worker**: Required for background jobs (notifications, calendar sync, attendance)

## Queue Configuration

- **Driver**: Database (no Redis required in production)
- **Worker command**: `php artisan queue:work --stop-when-empty --tries=3 --timeout=90`
- **Supervisor config**: Should run the queue worker as a daemon

## Scheduler

Tasks are defined in `routes/console.php`:

| Task | Schedule | Command |
|------|----------|---------|
| Dispatch notifications | Every minute | `php artisan schedule:run` (triggered by cron) |
| Generate recurring tasks | Daily 1:00 AM | Same scheduler |
| Process attendance | Daily 2:00 AM | Same scheduler |
| Queue worker | Every minute | Runs within scheduler |

If CLI cron is unavailable on the host, trigger the scheduler over HTTP instead — see
[HTTP-Triggered Cron](#http-triggered-cron-cpanel--shared-hosting).

**Cron entry** (run every minute):
```
* * * * * cd /path-to-project && php artisan schedule:run >> /dev/null 2>&1
```

## HTTP-Triggered Cron (cPanel / shared hosting)

On hosts where CLI cron cannot reliably run `php artisan` (wrong PHP binary path,
open_basedir, or filesystem permission issues), the scheduler and queue worker can
be triggered over HTTP instead. These endpoints run under the web-server user, which
sidesteps CLI path/permission problems.

**Endpoints** (defined in `routes/api.php`, handled by `CronController`):

| Endpoint | Method | Purpose | Equivalent CLI |
|----------|--------|---------|----------------|
| `/api/cron/schedule` | GET | Runs the task scheduler | `php artisan schedule:run` |
| `/api/cron/queue` | GET | Drains pending queue jobs and exits | `php artisan queue:work --stop-when-empty` |

**Authentication**: Both endpoints are guarded by the `cron` middleware
(`VerifyCronToken`), which requires a shared secret. Set `CRON_SECRET` in `.env` to a
strong random value:

```
php -r "echo bin2hex(random_bytes(32));"
```

Pass the secret via the `token` query string or the `X-Cron-Token` header. Requests
with a missing/invalid token (or when `CRON_SECRET` is unset) receive HTTP 403.

**cPanel cron entries** (use HTTPS in production, keep the secret out of shared logs):

```
# Run the scheduler every minute
* * * * * wget -q -O /dev/null "https://your-domain.com/api/cron/schedule?token=YOUR_CRON_SECRET"

# Drain the queue every minute (the request returns as soon as the queue is empty)
* * * * * wget -q -O /dev/null "https://your-domain.com/api/cron/queue?token=YOUR_CRON_SECRET"
```

Once these HTTP triggers are verified in production, the CLI `schedule:run` and
`queue:work` cron entries can be removed.

> **Note:** The scheduler remains the single source of truth for timing — all task
> frequencies stay in `routes/console.php`. The `/api/cron/schedule` endpoint only
> asks the scheduler to evaluate what is due, exactly like the CLI cron would.

## Build Process

1. Copy `.env.example` to `.env` and configure
2. `composer install --no-dev --optimize-autoloader`
3. `php artisan key:generate`
4. `php artisan migrate --force`
5. `npm ci && npm run build` (build frontend assets)
6. `php artisan storage:link` (create public symlink)

## Production Checklist

- [ ] `APP_ENV=production`
- [ ] `APP_DEBUG=false`
- [ ] Generate app key: `php artisan key:generate`
- [ ] Run migrations: `php artisan migrate --force`
- [ ] Seed permissions: `php artisan db:seed --class=PermissionSeeder`
- [ ] Cache config: `php artisan config:cache`
- [ ] Cache routes: `php artisan route:cache`
- [ ] Optimize: `php artisan optimize`
- [ ] Create storage link: `php artisan storage:link`
- [ ] Set up queue worker (Supervisor/systemd) — or the `/api/cron/queue` HTTP trigger on shared hosting
- [ ] Set up cron for scheduler — CLI `schedule:run` or the `/api/cron/schedule` HTTP trigger
- [ ] Set a strong `CRON_SECRET` if using the HTTP cron triggers
- [ ] Set proper file permissions (storage, bootstrap/cache)
- [ ] Configure MySQL with proper indexes
- [ ] Set up monitoring (queue health, failed jobs)

## Storage Permissions

Directories that need write access:
- `storage/` (full tree)
- `bootstrap/cache/`
- `public/storage/` (symlink target)

## One-Click Setup

Defined in `composer.json`:
```bash
composer setup
```
This runs: `composer install`, copies `.env`, generates key, runs migrations, installs npm, and builds assets.

## Development Server

```bash
composer dev
```
Runs concurrently: `php artisan serve`, `php artisan queue:listen`, and `npm run dev`.

## Testing

```bash
composer test
# or
php artisan test
```

Uses SQLite in-memory database. Tests are written in Pest PHP.

## CI/CD

No CI/CD configuration found in the project. This needs to be set up. Suggested tools: GitHub Actions, Laravel Forge, or Envoyer.
