# Ease Ecom — Shared Hosting Deployment Guide

This guide covers deploying the **Ease Ecom** Laravel + React (Inertia.js) application to a shared
hosting environment using the included **`deploy.sh`** (macOS/Linux) or **`deploy.ps1`** (Windows)
packaging scripts.

The workflow is **build → zip → upload → extract → configure**. Frontend assets and PHP
dependencies are bundled into the zip on your local machine, so the server needs **no Node.js**
and (optionally) **no Composer**.

---

## Table of Contents

1. [Server Requirements](#1-server-requirements)
2. [Shared Hosting Directory Structure](#2-shared-hosting-directory-structure)
3. [Build & Package with `deploy.sh`](#3-build--package-with-deploysh)
4. [First-Time Deployment](#4-first-time-deployment)
5. [Subsequent Deployments](#5-subsequent-deployments)
6. [Post-Deployment Checklist](#6-post-deployment-checklist)
7. [Queue Workers & Scheduler (Cron)](#7-queue-workers--scheduler-cron)
8. [Troubleshooting](#8-troubleshooting)

---

## 1. Server Requirements

Verify these with your hosting provider before proceeding:

| Requirement     | Minimum                                                                                                     |
|-----------------|-------------------------------------------------------------------------------------------------------------|
| PHP             | **8.2** (8.3 recommended)                                                                                    |
| PHP Extensions  | BCMath, Ctype, cURL, DOM, Fileinfo, JSON, Mbstring, OpenSSL, PCRE, PDO, MySQL PDO (or SQLite3), Tokenizer, XML, GD/Imagick (for product image handling) |
| MySQL / MariaDB | 8.0+ / 10.4+ (or SQLite 3.x)                                                                                 |
| mod_rewrite     | Enabled                                                                                                      |
| SSH / Terminal  | Recommended (cPanel Terminal or SSH)                                                                         |
| Composer        | Only needed on the server if you choose **not** to bundle `vendor/` (see §3)                                 |
| Node / npm      | **Not required on the server** — assets are built locally                                                    |

> ⚠️ **PHP version**: Many shared hosts default to PHP 7.x. In cPanel → **MultiPHP Manager**, set the
> domain to PHP 8.2+. If you can't, add to `public_html/.htaccess`:
> ```apacheconf
> AddHandler application/x-httpd-php82 .php
> ```

---

## 2. Shared Hosting Directory Structure

Shared hosts expose only `public_html/` to the web, but Laravel's web root is `public/`. Place the
application **outside** `public_html` and point the web root **into** `public/`.

### Recommended Layout

```
/home/<username>/
├── ease-ecom/           ← Laravel root (NOT web-accessible)
│   ├── app/
│   ├── bootstrap/
│   ├── config/
│   ├── database/
│   ├── public/          ← contains build/, index.php, .htaccess
│   ├── resources/
│   ├── routes/
│   ├── storage/
│   ├── vendor/          ← bundled in the zip (see §3)
│   ├── .env             ← created manually on the server
│   └── ...
└── public_html/         ← Web root (served by Apache)
    └── (see Option A or B below)
```

### Option A — Symlink `public_html` (preferred, requires SSH)

```bash
# SSH into your server
rm -rf ~/public_html
ln -s ~/ease-ecom/public ~/public_html
```

### Option B — Modify `index.php` (no SSH needed)

Copy `ease-ecom/public/index.php` into `public_html/` and update the two `require` paths:

```php
// public_html/index.php
<?php

use Illuminate\Http\Request;

define('LARAVEL_START', microtime(true));

// Maintenance mode check
if (file_exists($maintenance = __DIR__.'/../ease-ecom/storage/framework/maintenance.php')) {
    require $maintenance;
}

// Register the Composer autoloader
require __DIR__.'/../ease-ecom/vendor/autoload.php';

// Bootstrap Laravel and handle the request
(require_once __DIR__.'/../ease-ecom/bootstrap/app.php')
    ->handleRequest(Request::capture());
```

Then copy `ease-ecom/public/.htaccess` to `public_html/.htaccess` **unchanged**, and copy the
`ease-ecom/public/build/` folder (and any other static files such as `favicon.ico`, `robots.txt`)
into `public_html/`.

> Option A keeps `public/` as the single source of truth (assets update automatically). Option B
> requires re-copying `public/build/` into `public_html/` after every deploy — prefer Option A when
> SSH is available.

---

## 3. Build & Package with `deploy.sh`

The repo includes packaging scripts at the project root:

- **macOS / Linux:** `deploy.sh`
- **Windows (PowerShell):** `deploy.ps1`

### What the script does

1. Runs `npm run build` (compiles Vite assets into `public/build/`).
2. Zips the **entire project folder**, excluding `.env`, `.gitignore`, `.git/`, and `node_modules/`.
3. Produces a timestamped archive: **`ease_com_DDMMYYYYHHMMSS.zip`** (e.g. `ease_com_28062026142530.zip`)
   in the project root. The zip contains a single top-level folder matching your project directory name.

### Important: bundle production dependencies first

`deploy.sh` does **not** run Composer, but it **does** include `vendor/` if it's present. To ship a
clean, production-optimized `vendor/` (and avoid needing Composer on the server), run this **before**
packaging:

```bash
composer install --no-dev --optimize-autoloader
```

Then build the package:

```bash
# macOS / Linux
./deploy.sh

# Windows (PowerShell)
.\deploy.ps1
```

> 🧹 After deploying, switch back to your dev dependencies locally with `composer install` so your
> local environment keeps its dev tooling (PHPUnit, Pint, etc.).

> 💡 If you'd rather run Composer **on the server**, you can skip bundling `vendor/` — delete it
> before zipping (or add `-x "$FOLDER_NAME/vendor/*"` to `deploy.sh`) and run
> `composer install --no-dev --optimize-autoloader` on the server after extracting.

---

## 4. First-Time Deployment

### Step 1 — Package locally

```bash
composer install --no-dev --optimize-autoloader
./deploy.sh
```

### Step 2 — Upload the zip

Upload `ease_com_<timestamp>.zip` to `/home/<username>/` using cPanel **File Manager**, FTP
(FileZilla), or `scp`:

```bash
scp ease_com_*.zip user@yourhost.com:~/
```

### Step 3 — Extract

In cPanel File Manager, right-click the zip → **Extract**, or via SSH:

```bash
cd ~
unzip ease_com_*.zip
# The archive extracts to a folder named after your project directory; rename it for clarity:
mv ease-ecom-01 ease-ecom    # adjust to match the extracted folder name
```

### Step 4 — Create `.env` on the server

Create `~/ease-ecom/.env` (via SSH or File Manager). Start from `.env.example` and set production
values:

```dotenv
APP_NAME="Ease Ecom"
APP_ENV=production
APP_KEY=                       # generated in Step 5
APP_DEBUG=false
APP_URL=https://yourdomain.com

APP_LOCALE=en
APP_FALLBACK_LOCALE=en

APP_MAINTENANCE_DRIVER=file
BCRYPT_ROUNDS=12

LOG_CHANNEL=stack
LOG_STACK=single
LOG_LEVEL=error

# --- Database (MySQL recommended for production) ---
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=your_db_name
DB_USERNAME=your_db_user
DB_PASSWORD=your_db_password

# --- Or use SQLite (simpler; create the file with: touch database/database.sqlite) ---
# DB_CONNECTION=sqlite
# DB_DATABASE=/home/<username>/ease-ecom/database/database.sqlite

SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=true

QUEUE_CONNECTION=database
CACHE_STORE=database
FILESYSTEM_DISK=public          # product images are served from storage/app/public

MAIL_MAILER=smtp
MAIL_HOST=mail.yourdomain.com
MAIL_PORT=465
MAIL_USERNAME=no-reply@yourdomain.com
MAIL_PASSWORD=your_mail_password
MAIL_ENCRYPTION=ssl
MAIL_FROM_ADDRESS="no-reply@yourdomain.com"
MAIL_FROM_NAME="${APP_NAME}"

VITE_APP_NAME="${APP_NAME}"
```

> 📷 **`FILESYSTEM_DISK=public`** matters for this app — uploaded product images live in
> `storage/app/public/products/` and are served through the `public/storage` symlink (Step 6).

### Step 5 — Generate the app key & prepare the database

Via SSH or cPanel Terminal:

```bash
cd ~/ease-ecom

php artisan key:generate            # writes APP_KEY into .env

# If you skipped bundling vendor/, install it now:
# composer install --no-dev --optimize-autoloader --no-interaction

php artisan migrate --force
php artisan db:seed --force         # optional: seeds initial catalog/admin data
```

### Step 6 — Link storage (required)

The `public/storage` symlink is created with an **absolute local path** when built on your machine,
so it is **broken on the server**. Recreate it:

```bash
cd ~/ease-ecom
rm -f public/storage               # remove the broken symlink if present
php artisan storage:link
```

> If your host disables `symlink()`, create a folder instead and copy assets, or ask support to
> enable symlinks. As a fallback you can point `public/storage` at `../storage/app/public` manually.

### Step 7 — Cache & permissions

```bash
cd ~/ease-ecom

php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache

chmod -R 775 storage bootstrap/cache
```

### Step 8 — Set up the web root

Follow [Option A or B](#2-shared-hosting-directory-structure) to point `public_html` into `public/`.

Visit `https://yourdomain.com` — the storefront should load.

---

## 5. Subsequent Deployments

```bash
# 1. On your local machine — package a fresh build with production deps
composer install --no-dev --optimize-autoloader
./deploy.sh

# 2. Upload the new ease_com_<timestamp>.zip and extract it over the existing folder
#    (back up .env and the database first; .env is NOT in the zip, so it is preserved
#    only if you extract into the SAME folder without deleting it)
```

On the server (SSH or cPanel Terminal):

```bash
cd ~/ease-ecom

php artisan down --render="errors::503"     # maintenance mode

# (extract the new files here, keeping your existing .env)

php artisan migrate --force
php artisan storage:link                     # re-link if the symlink was overwritten
php artisan optimize:clear                    # clear stale caches
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache

php artisan up                                # back online
```

> ⚠️ **Protect your `.env` and database** when extracting over an existing install. The zip does not
> contain `.env`, but extraction can overwrite other files. Safest pattern: extract into a fresh
> folder, copy your existing `.env` into it, run migrations, then switch the `public_html` symlink to
> the new folder (zero-downtime swap).
>
> If you use **Option B**, also re-copy `public/build/` into `public_html/` after each deploy.

---

## 6. Post-Deployment Checklist

- [ ] `APP_ENV=production` and `APP_DEBUG=false` in `.env`
- [ ] `APP_KEY` is set and **unchanged** across deployments (changing it invalidates sessions/encrypted data)
- [ ] `SESSION_ENCRYPT=true`
- [ ] Migrations applied — `php artisan migrate:status`
- [ ] `php artisan storage:link` ran and `public/storage` resolves (product images load)
- [ ] `FILESYSTEM_DISK=public` in `.env`
- [ ] `public/build/` is present and contains `manifest.json`
- [ ] `public_html/` correctly points into `public/` (Option A or B)
- [ ] `.env` lives outside `public_html/` and is not web-accessible
- [ ] `storage/` and `bootstrap/cache/` are writable (`chmod -R 775`)
- [ ] HTTPS / SSL active and `APP_URL` uses `https://`
- [ ] Cron entries added for queue + scheduler (see §7)

---

## 7. Queue Workers & Scheduler (Cron)

This app uses the **database** queue and cache drivers, so background jobs (e.g. order emails) need a
worker. Shared hosts rarely allow persistent processes, so use **cron**.

In cPanel → **Cron Jobs**, add:

```bash
# Process queued jobs every minute (exits cleanly after ~55s)
* * * * * /usr/local/bin/php /home/<username>/ease-ecom/artisan queue:work --stop-when-empty --max-time=55 --quiet

# Run Laravel's scheduler every minute
* * * * * /usr/local/bin/php /home/<username>/ease-ecom/artisan schedule:run >> /dev/null 2>&1
```

> Adjust the PHP binary path (`/usr/local/bin/php` or `php`) to match your host. If Supervisor is
> available, prefer it for queues — see the [Laravel Queue docs](https://laravel.com/docs/queues#supervisor-configuration).

---

## 8. Troubleshooting

| Problem | Solution |
|---------|----------|
| **500 error on first visit** | Check `storage/logs/laravel.log`. Usually a missing `APP_KEY` or wrong DB credentials. |
| **White screen / blank page** | Temporarily set `APP_DEBUG=true`, reload, read the error, then set back to `false`. |
| **Assets 404 (`/build/...`)** | Confirm `public/build/` (with `manifest.json`) was uploaded. With Option B, re-copy `build/` into `public_html/`. |
| **Product images not showing** | Run `php artisan storage:link`; verify the `public/storage` symlink resolves and `FILESYSTEM_DISK=public`. |
| **`storage:link` says "already exists"** | `rm -f public/storage` first (it's a broken absolute symlink from your local build), then re-run. |
| **Migrations fail** | Verify DB credentials; ensure the DB user has `CREATE`/`ALTER` privileges. |
| **Permission denied** | `chmod -R 775 storage bootstrap/cache` on the server. |
| **Changes not taking effect** | Run `php artisan optimize:clear`, then re-cache config/routes/views. |
| **`mod_rewrite` not working** | Ensure `.htaccess` is in `public_html/`; enable `AllowOverride All` if you control Apache. |
| **Composer memory limit (server)** | `php -d memory_limit=-1 $(which composer) install --no-dev --optimize-autoloader` |
| **PHP version mismatch** | Set PHP 8.2+ in cPanel MultiPHP Manager, or use the `.htaccess` `AddHandler` directive. |
| **Queued emails never send** | Confirm the `queue:work` cron is active and `QUEUE_CONNECTION=database`. |
