# cPanel Production Deployment

This guide deploys FieldOps Monitor without changing application source code or hard-coding credentials.

## 1. Confirm the hosting runtime

In **cPanel → MultiPHP Manager**, select PHP **8.2, 8.3, 8.4, or 8.5** for the target domain. Laravel 12 requires PHP 8.2 or newer.

In **Select PHP Version / PHP Extensions**, enable:

`ctype`, `curl`, `dom`, `fileinfo`, `filter`, `hash`, `mbstring`, `openssl`, `pcre`, `pdo`, `pdo_mysql`, `session`, `tokenizer`, and `xml`.

Also confirm Composer 2 and Apache `mod_rewrite` are available. The application does not require Node.js on the server.

## 2. Create the MySQL database

1. Open **cPanel → MySQL Databases**.
2. Create a database, for example `account_fieldops`.
3. Create a dedicated database user with a strong password.
4. Add the user to the database and grant **ALL PRIVILEGES** for that database only.
5. Retain the full cPanel-prefixed database and user names for `.env`.

No credentials are stored in source control. Laravel reads all database settings from `.env`.

## 3. Upload and extract the package

1. Upload `fieldops-laravel-cpanel.zip` through **cPanel → File Manager**.
2. Extract it outside the public web root, preferably as:

   `/home/CPANEL_USER/fieldops`

3. Do not place `.env`, `app`, `config`, `database`, `storage`, or `vendor` directly in `public_html`.

## 4. Configure the environment

From cPanel Terminal:

```bash
cd /home/CPANEL_USER/fieldops
cp .env.example .env
```

Edit `.env` in File Manager and set at least:

```dotenv
APP_NAME="FieldOps Monitor"
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=https://fieldops.example.com
APP_TIMEZONE=Asia/Dhaka

DB_CONNECTION=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=account_fieldops
DB_USERNAME=account_fieldops_user
DB_PASSWORD=your-strong-database-password

FIELDOPS_ACCESS_USER=fieldops
FIELDOPS_ACCESS_PASSWORD=your-separate-strong-application-password
FIELDOPS_ALLOW_DATA_RESET=false
```

Use quotes around values that contain spaces, `#`, or other special characters. Keep `.env` permissions at `600` or `640` where supported.

## 5. Install production dependencies

```bash
cd /home/CPANEL_USER/fieldops
composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction
php artisan key:generate --force
```

If cPanel has no Terminal or Composer interface, run the same Composer command on a trusted computer using the same PHP major/minor version, then upload the resulting `vendor` directory with the application. Never upload a development `.env`.

## 6. Point the domain to Laravel public

### Recommended: custom document root

In **cPanel → Domains**, set the target domain or subdomain document root to:

`/home/CPANEL_USER/fieldops/public`

This is the safest layout and requires no source changes.

### Fallback: cPanel forces `public_html`

1. Keep the Laravel application at `/home/CPANEL_USER/fieldops`.
2. Copy the contents of `fieldops/public/` into the relevant `public_html/` directory, including `.htaccess`, `assets`, and `robots.txt`.
3. Copy `deploy/cpanel-public-html/index.php.example` to `public_html/index.php`.
4. In that one file, replace `__LARAVEL_ROOT__` with the absolute path `/home/CPANEL_USER/fieldops`.
5. Copy `deploy/cpanel-public-html/.htaccess` if cPanel did not preserve the original hidden file.

Do not copy the rest of the Laravel project into `public_html`.

## 7. Set writable directories and storage

Laravel must be able to write to `storage` and `bootstrap/cache`:

```bash
cd /home/CPANEL_USER/fieldops
chmod -R 775 storage bootstrap/cache
php artisan storage:link
```

Normal files should remain `644` and directories `755` unless the hosting provider requires group-write access. Do not use `777`.

If the host blocks symbolic links, set `FILESYSTEM_DISK=public_direct` in `.env`, create `public/uploads`, and make that directory writable (`775`). This fallback stores public uploads under the web root while keeping all credentials and private files outside it.

## 8. Create tables and import the supplied data

For the first deployment only:

```bash
cd /home/CPANEL_USER/fieldops
php artisan migrate --seed --force
```

This creates all MySQL tables, relationships, foreign keys, indexes, cache structures, and imports the provided project/employee engagement dataset.

If Terminal is unavailable, create a temporary one-time cron job in **cPanel → Cron Jobs** using the cPanel PHP binary, for example:

```text
/usr/local/bin/php /home/CPANEL_USER/fieldops/artisan migrate --seed --force
```

Run it once, confirm success, and immediately remove the cron job. Ask the host for the exact PHP binary path when it differs.

Do not run `db:seed` again on a live system unless you intentionally want to replace current operational data with the imported starter dataset.

## 9. Optimize and verify production

```bash
cd /home/CPANEL_USER/fieldops
php artisan optimize:clear
php artisan optimize
php deploy/preflight.php
```

Then verify:

1. `https://fieldops.example.com/up` returns HTTP 200 after authentication.
2. The dashboard opens over HTTPS with no debug output.
3. Projects show 142 records and Employees show 24 records.
4. September 2026 Monthly Employee Engagement Report displays the imported assignments.
5. Create a test booking and confirm overlapping employee/device allocations are blocked.
6. Confirm a normal save persists after page refresh.
7. Confirm Excel/PDF report downloads work.
8. If uploads are used, confirm the uploaded URL is reachable through `/storage/...` or `/uploads/...` for the fallback disk.

Application errors are written to `storage/logs/laravel-YYYY-MM-DD.log`. With `APP_DEBUG=false`, stack traces and secrets are not shown to visitors.

## 10. Updating the application later

Before every update, back up the database and the `storage/app` directory. Then:

```bash
cd /home/CPANEL_USER/fieldops
php artisan down --retry=60
composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction
php artisan migrate --force
php artisan optimize:clear
php artisan optimize
php artisan up
```

Do not overwrite the live `.env` or `storage/app` directory during an update.
