# Installation & Deployment

This guide covers a **local Windows development machine** (no Docker, no Redis, no Linux VM) and a
**production deployment checklist**.

---

## 1. Prerequisites

| Component | Version | Notes |
|---|---|---|
| PHP | 8.2 or newer | XAMPP 8.2 works. Required extensions: `pdo_mysql`, `mbstring`, `openssl`, `fileinfo`, `gd`, `tokenizer`, `xml`, `ctype`, `curl`. Argon2id support is built into PHP 8.2 on Windows. |
| Composer | 2.x | |
| Node.js / npm | Node 20+ (tested with Node 24, npm 11) | Only needed to build CSS/JS. |
| Database | **MySQL 8.0.16+** (production target) or **MariaDB 10.4+** (XAMPP) | CHECK constraints are enforced by both. |
| Web server | `php artisan serve` locally; Nginx/Apache/IIS in production | Document root must be `public/`. |

## 2. Local setup

```bash
# 1. Get the project (clone or copy the folder), then from the project root:
composer install

# 2. Environment file
copy .env.example .env            # PowerShell / CMD   (Git Bash: cp .env.example .env)
php artisan key:generate
```

Edit `.env` — the important values:

```dotenv
APP_URL=http://127.0.0.1:8000
DB_CONNECTION=mysql               # also correct for MariaDB
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=sub_reseller_portal
DB_USERNAME=root
DB_PASSWORD=
PORTAL_TIMEZONE=America/New_York  # default display timezone (changeable in Settings)
```

```sql
-- 3. Create the database (and the test database) once, e.g. in phpMyAdmin or the mysql client:
CREATE DATABASE sub_reseller_portal      CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE sub_reseller_portal_test CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```

```bash
# 4. Schema + reference data (+ demo data when APP_ENV=local)
php artisan migrate --seed

# 5. Front-end assets
npm install
npm run build          # production build, or keep "npm run dev" running for hot reload

# 6. Run
php artisan serve      # open http://127.0.0.1:8000 and sign in with a demo account (see README)
```

`composer run dev` starts the web server, queue listener and Vite together (uses `concurrently`).

### What the seeders create

| Seeder | Environments | Content |
|---|---|---|
| `RolesAndPermissionsSeeder` | all | Permissions (from `App\Enums\Permission`) and the six system roles |
| `ServiceSeeder` | all | Internet, TV / Streaming, Home Phone / Voice, Mobile (inactive) |
| `DemoDataSeeder` (+ catalog & operations) | **local / testing only** | 3 organizations, demo users, vendors, packages, sellers, customers and ~75 work orders with realistic history |

Re-seeding is safe: reference seeders are idempotent and keep administrator changes.
To start over locally: `php artisan migrate:fresh --seed`.

### E-mail (password reset links)

`MAIL_MAILER=log` writes e-mails — including password reset links — to `storage/logs/laravel.log`.
Configure SMTP in `.env` for real delivery.

### Scheduler (deadline alerts)

Due-soon and overdue alerts are produced by `php artisan work-orders:deadline-alerts`, scheduled hourly
between 06:00 and 20:00 business time.

* Development: run `php artisan schedule:work` in a separate terminal (or run the command manually).
* Production (Windows): Task Scheduler entry every minute → `php artisan schedule:run`.
* Production (Linux): cron `* * * * * cd /path && php artisan schedule:run >> /dev/null 2>&1`.

No queue worker is required: notifications are written synchronously to the database.

## 3. Running the tests

```bash
php artisan test        # 210 tests, ~1 minute; uses the sub_reseller_portal_test database
```

See [TESTING.md](TESTING.md).

## 4. Production deployment checklist

```dotenv
APP_ENV=production
APP_DEBUG=false                 # never expose stack traces, SQL or paths
APP_URL=https://portal.example.com
LOG_LEVEL=warning
SESSION_SECURE_COOKIE=true      # cookies only over HTTPS
SESSION_ENCRYPT=true
SECURITY_HSTS=true              # Strict-Transport-Security
TRUSTED_PROXIES=10.0.0.10       # only when behind a load balancer / reverse proxy
HASH_DRIVER=argon2id
MAIL_MAILER=smtp                # + MAIL_* credentials
```

1. Serve over **HTTPS only**; the web server document root is `public/`.
2. `composer install --no-dev --optimize-autoloader`
3. `npm ci && npm run build` (or build in CI and deploy `public/build`).
4. `php artisan migrate --force` then `php artisan db:seed --class=ReferenceDataSeeder --force`
   (**never** run the demo seeders — they refuse to run in production anyway).
5. Create the first administrator: `php artisan portal:create-admin` (prompts for name, e-mail, password).
6. `php artisan config:cache && php artisan route:cache && php artisan view:cache && php artisan event:cache`
7. Make `storage/` and `bootstrap/cache/` writable by the web server user only.
8. Configure the scheduler (see above).
9. In Administration → Settings: review session timeouts, password policy and enable
   *Require two-factor authentication for administrators*.
10. Run `composer audit` and `npm audit` before every release.

**Database user:** the application needs `SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX, DROP,
REFERENCES` (migrations) and `TRIGGER` (audit-log protection triggers, created by one migration). On MySQL with
binary logging enabled, creating triggers may require `log_bin_trust_function_creators=1`; if trigger creation
is not permitted, the migration logs a warning and the application-level protection still applies.

## 5. Backup & recovery

| What | Where | How |
|---|---|---|
| Database | MySQL/MariaDB schema `sub_reseller_portal` | `mysqldump --single-transaction --routines --triggers sub_reseller_portal > backup.sql` |
| Uploaded attachments | `storage/app/private/attachments/` | File-level backup (organized by organization ULID / year / month) |
| Logo | `storage/app/private/branding/` | File-level backup |
| Secrets | `.env` (especially `APP_KEY`) | Store in your secrets vault. **Without `APP_KEY` encrypted values (vendor account numbers, 2FA secrets) cannot be decrypted.** |

Restore: create the database, import the dump, restore the two storage folders and `.env`, then
`php artisan config:cache`. There is intentionally **no** "download database backup" feature in the portal.

## 6. Troubleshooting

| Symptom | Fix |
|---|---|
| `Vite manifest not found` | Run `npm run build` (or `npm run dev`). |
| `SQLSTATE[HY000] [2002]` | Database not running / wrong `DB_HOST`/`DB_PORT`. |
| Times look shifted | The DB connection is forced to UTC (`timezone => +00:00`); change the *display* timezone in Settings. |
| `429 Too many requests` on export | Exports are limited to 10 per minute per user (`config/portal.php`). |
| Signed out unexpectedly | Idle / absolute session timeouts (Settings → Security). |
