# Encostay cPanel VPS Deployment

This repository contains two applications:

- `/` — Next.js 15 frontend, served by Phusion Passenger through cPanel's Node.js application manager.
- `/backend` — Laravel 12 stateless API, served by PHP 8.2+ with its document root set to `backend/public`.

The production examples below use `www.example.com` for the frontend and `api.example.com` for the API. Replace both everywhere.

## 1. Server requirements

- Node.js 20 or newer and npm 10 or newer.
- PHP 8.2 or newer with `ctype`, `curl`, `dom`, `fileinfo`, `filter`, `hash`, `mbstring`, `openssl`, `pdo_pgsql`, `session`, and `tokenizer`.
- Composer 2.
- A cPanel account able to create subdomains, Node.js applications, cron jobs, and AutoSSL certificates.
- Supabase PostgreSQL 16 with PostGIS enabled.

Redis is not required. Laravel uses the file cache, synchronous queue, and array sessions by default, avoiding the reconnect loop from the previous NestJS deployment.

## 2. DNS and directories

Create DNS records for the frontend and API, then deploy the repository outside `public_html` when possible. An example application directory is:

```text
/home/CPANEL_USER/encostay
```

Create the API subdomain with this document root:

```text
/home/CPANEL_USER/encostay/backend/public
```

The repository also contains `backend/.htaccess` as a fallback when cPanel only permits the document root to be `backend`, but pointing directly to `backend/public` is safer.

## 3. Configure Supabase

Enable PostGIS once in Supabase's Database Extensions screen. Use the direct connection or session pooler for migrations; transaction-pooler URLs may not support all migration operations.

Copy the backend environment template:

```bash
cd /home/CPANEL_USER/encostay/backend
cp .env.example .env
chmod 600 .env
```

Set these production values in `backend/.env`:

```dotenv
APP_ENV=production
APP_DEBUG=false
APP_URL=https://api.example.com
FRONTEND_URL=https://www.example.com
CORS_ALLOWED_ORIGINS=https://example.com
DB_CONNECTION=pgsql
DB_URL=postgresql://USER:PASSWORD@HOST:5432/postgres?sslmode=require
DB_SCHEMA=public,extensions
DB_SSLMODE=require
CACHE_STORE=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=array
```

URL-encode reserved characters in the database username or password. Never commit either `.env` file.

## 4. Install and initialize Laravel

Run these commands with the cPanel PHP 8.2+ binary. Some hosts expose it as `php`, while others use a path such as `/opt/cpanel/ea-php82/root/usr/bin/php`.

```bash
cd /home/CPANEL_USER/encostay/backend
composer install --no-dev --optimize-autoloader --no-interaction
php artisan key:generate
php artisan migrate --force
php artisan db:seed --force
php artisan storage:link
php artisan optimize
```

The seed command only installs the standard amenity catalog and is optional. Ensure the web server can write to `storage` and `bootstrap/cache`:

```bash
chmod -R ug+rwX storage bootstrap/cache
```

Do not use `chmod 777`. The API health check should now return HTTP 200:

```bash
curl --fail https://api.example.com/api/v1/health
```

## 5. Configure and build Next.js

Create the frontend environment before building because `NEXT_PUBLIC_*` values are embedded in browser bundles:

```bash
cd /home/CPANEL_USER/encostay
cp .env.example .env.production
chmod 600 .env.production
```

Set the public API URL:

```dotenv
NEXT_PUBLIC_API_URL=https://api.example.com/api/v1
```

Install exactly the locked dependencies and build:

```bash
npm ci
npm run build
```

The build script also copies `public` and `.next/static` into `.next/standalone`, as required by Next.js standalone deployments. Do not skip that post-build step.

## 6. Create the cPanel Node.js application

In **cPanel → Setup Node.js App**, create an application with:

- Node.js version: 20 or newer.
- Application mode: Production.
- Application root: `/home/CPANEL_USER/encostay`.
- Application URL: `https://www.example.com` (or the selected frontend subdomain).
- Startup file: `app.js`.

Add `NODE_ENV=production`. Passenger supplies its own port; do not hard-code a public port in cPanel. Save the application and select **Restart**.

From Terminal, Passenger can also be restarted with:

```bash
cd /home/CPANEL_USER/encostay
mkdir -p tmp
touch tmp/restart.txt
```

The startup file verifies Node.js 20+ and reports a clear error if `.next/standalone/server.js` is missing.

## 7. SSL, proxy, and CORS checks

Run AutoSSL for both domains, then force HTTPS in cPanel. Laravel trusts the cPanel reverse proxy, so generated URLs honor forwarded HTTPS headers.

The browser origin must exactly match `FRONTEND_URL` or one entry in `CORS_ALLOWED_ORIGINS`. Origins contain only scheme, hostname, and optional port—never `/api/v1` or another path.

Verify preflight handling:

```bash
curl -i -X OPTIONS https://api.example.com/api/v1/auth/login \
  -H 'Origin: https://www.example.com' \
  -H 'Access-Control-Request-Method: POST'
```

## 8. Repeatable updates

After deploying a new revision, run:

```bash
cd /home/CPANEL_USER/encostay/backend
composer install --no-dev --optimize-autoloader --no-interaction
php artisan migrate --force
php artisan optimize

cd /home/CPANEL_USER/encostay
npm ci
npm run build
mkdir -p tmp && touch tmp/restart.txt
```

Never force-push or rewrite published history; this repository is connected to Lovable.

## 9. Production verification

1. `GET https://api.example.com/api/v1/health` returns `status: ok`.
2. Registration returns a Sanctum Bearer token and creates both a user and wallet.
3. Login, `GET /api/v1/auth/me`, and logout work from the frontend domain without CORS errors.
4. `GET /api/v1/listings` returns published listings and the Next.js listing pages render them.
5. Browser requests for `/_next/static/*` return HTTP 200, not 404.
6. Both domains have valid SSL certificates and redirect HTTP to HTTPS.

## 10. Common failures

- **Passenger reports missing standalone server:** run `npm run build` from the repository root and restart the Node.js app.
- **JavaScript chunks return 404:** rerun the full build script so standalone static assets are copied.
- **CORS error:** make `FRONTEND_URL` match the exact browser origin, then run `php artisan config:clear && php artisan config:cache`.
- **Laravel 500 after deployment:** inspect `backend/storage/logs/laravel.log`, verify `APP_KEY`, PHP extensions, writable directories, and database connectivity.
- **PostGIS permission error:** enable PostGIS in Supabase before migrating and use a database role allowed to create the trigger and spatial index.
- **Authentication always returns 401:** confirm the `Authorization: Bearer ...` header reaches Apache; both included `.htaccess` files preserve it.