# Deploying moneywise.co.ke to Shared Hosting (PHP)

The app is standard Laravel 12 and runs comfortably on shared hosting (e.g. Kinsta is the current host;
cPanel-style hosts work too). Requirements: **PHP ≥ 8.2** with `pdo_mysql, mbstring, openssl, fileinfo,
zip, intl, gd, curl`, and one MySQL database. **No Node.js is needed on the server** — assets are built
locally and uploaded.

## 1. Build locally

```powershell
npm run build                 # compiles Tailwind/JS into public/build (commit or upload this)
```

## 2. Prepare the production .env

Copy `.env.example` → `.env` on the server and set:

```env
APP_NAME="Money-Wise"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://moneywise.co.ke

DB_DATABASE=<cpanel_db>
DB_USERNAME=<cpanel_user>
DB_PASSWORD=<secret>

SESSION_DRIVER=database
QUEUE_CONNECTION=database
CACHE_STORE=database

MAIL_MAILER=smtp
MAIL_HOST=<host smtp>
MAIL_FROM_ADDRESS="sema@moneywise.co.ke"
```

Generate a fresh key once on the server: `php artisan key:generate`.

## 3. Upload layout (two options)

**Option A — docroot points at `public/` (preferred).** If the host lets you set the document root
(Kinsta, Ploi, RunCloud, many cPanel hosts): upload the whole project to e.g. `~/moneywise` and point
the docroot to `~/moneywise/public`. Done.

**Option B — classic cPanel `public_html`.** Upload the project to `~/moneywise` (outside web root),
then copy the *contents* of `public/` into `~/public_html` and edit `public_html/index.php`:

```php
require __DIR__.'/../moneywise/vendor/autoload.php';
$app = require_once __DIR__.'/../moneywise/bootstrap/app.php';
```

(Each `../vendor` and `../bootstrap` becomes `../moneywise/vendor` etc.)

## 4. First-run commands (SSH or cPanel Terminal)

```bash
cd ~/moneywise
composer install --no-dev --optimize-autoloader
php artisan migrate --force
php artisan db:seed --force          # seeds roles, admin, the Health Check, course, content
php artisan storage:link
php artisan optimize                 # caches config, routes, views
```

No SSH? Run `composer install --no-dev` locally on PHP 8.2, upload `vendor/` along with the app,
and trigger migrations once via a temporary route or phpMyAdmin import.

## 5. Cron (one entry — replaces queue workers)

Shared hosting can't run a persistent queue worker. Add one cron job:

```
* * * * * php /home/USER/moneywise/artisan schedule:run >> /dev/null 2>&1
```

Then in `routes/console.php` schedule the queue to drain itself:

```php
Schedule::command('queue:work --stop-when-empty --max-time=50')->everyMinute()->withoutOverlapping();
```

(Currently nothing queues — mail is synchronous — so this is future-proofing.)

## 6. Security checklist

- [ ] **Change the admin password immediately**: seeded login is `admin@moneywise.co.ke` / `ChangeMe!2026`
      → `/admin` → profile, or `php artisan tinker` to reset.
- [ ] `APP_DEBUG=false` — confirm, or stack traces leak.
- [ ] HTTPS forced (host-level or in `bootstrap/app.php` middleware `\Illuminate\Http\Middleware\TrustProxies` + redirect).
- [ ] `storage/` and `bootstrap/cache/` writable (755 + owner).
- [ ] Keep the domain's existing SEO: import the 301 redirect map (docs/STRUCTURE-PLAN.md §Old → New URL map)
      into `public/.htaccess` or the host's redirect manager.

## 7. After every deploy

```bash
php artisan migrate --force && php artisan optimize:clear && php artisan optimize
```

## Deploy — moneywise.pa-desk.com (cPanel: pinchafrica.com / user padesk)

**Status: LIVE at https://moneywise.pa-desk.com (11 Jun 2026).**

Two non-obvious gotchas on this cPanel box (bit us once — don't repeat):

1. **Subdomain docroot prepends `public_html/`.** `cpapi2 SubDomain addsubdomain dir=moneywise/public`
   does NOT yield docroot `~/moneywise/public` — it prepends `~/public_html/`. To point a subdomain at
   an app dir outside `public_html`, correct the docroot after create across **6 userdata files** under
   `/var/cpanel/userdata/<user>/` (the vhost `yaml` + `.cache` + `_SSL` + `_SSL.cache` + `cache` + `cache.json`),
   then `rebuildhttpdconf` + `ea-nginx config <user>`.
2. **`DB_HOST=127.0.0.1` fails — use `DB_HOST=localhost`.** cPanel grants the new DB user `@localhost` only,
   not `@127.0.0.1`; MySQL treats them as different hosts → "Access denied".
3. **License corrupted → every `uapi`/`cpapi2` exits 255** with an HTML "License corrupted" page (server-wide,
   also breaks the cPanel UI). Fix as root: `/usr/local/cpanel/cpkeyclt` re-fetches & validates the license.

### Original staging steps (for reference / re-deploy)

**Was: app fully staged on the server; BLOCKED on a corrupted cPanel license** (now resolved per above).

Done (all under `/home/padesk/moneywise`):

- Code uploaded + extracted (assets pre-built in `public/build`).
- `composer install --no-dev --optimize-autoloader` ran clean (6,828 classes).
- `storage/framework/*` + `bootstrap/cache` recreated, `chmod 775`.
- Production `.env` written (`chmod 600`) with a fresh `APP_KEY`, `APP_URL=https://moneywise.pa-desk.com`,
  and DB creds **`padesk_moneywise` / `padesk_mwise`** pre-filled. `php artisan storage:link` done.
- `~/moneywise/finalize-deploy.sh` staged — runs migrate + seed + cache in one shot.
- App boots: `php artisan --version` OK, 76 routes compile.

Blocker: every `uapi`/`cpapi2` call dies with `exited with status 255`. The underlying cPanel binary
returns an HTML page titled **"cPanel License Activation — License corrupted, please fetch a new license file."**
This is server-wide and also breaks the cPanel UI; it cannot be fixed from the `padesk` jailshell account.
Subdomain creation (Apache vhost + DNS) and MySQL database/user creation both route through this broken API,
so neither can proceed until the license is restored.

**To unblock (needs root / hosting provider):**

```bash
# as root over SSH or in WHM "Manage License":
/usr/local/cpanel/cpkeyclt        # re-fetches & validates the license
```

If that doesn't clear it, the host must refresh the cPanel license for pinchafrica.com (cpanel.net /
provider ticket). Verify with `uapi Variables get_user_information` returning JSON, not the HTML license page.

**Then finish (as `padesk`, once the license works):**

```bash
# 1. create the subdomain (docroot -> ~/moneywise/public)
uapi SubDomain addsubdomain domain=moneywise rootdomain=pa-desk.com dir=moneywise/public
# 2. create the DB + user + grant (match the .env that is already on the server)
uapi Mysql create_database name=padesk_moneywise
uapi Mysql create_user name=padesk_mwise password='MwSky58!PaDesk2026'
uapi Mysql set_privileges_on_database user=padesk_mwise database=padesk_moneywise privileges=ALL%20PRIVILEGES
# 3. migrate + seed + cache
bash ~/moneywise/finalize-deploy.sh
# 4. issue SSL (AutoSSL usually auto-runs; or:)  uapi SSL start_autossl
```

(Or do steps 1–2 in the cPanel UI: **Subdomains** → moneywise / pa-desk.com / dir `moneywise/public`;
**MySQL Databases** → create `padesk_moneywise`, user `padesk_mwise`, add user to DB with ALL privileges.
Then run `bash ~/moneywise/finalize-deploy.sh`.)

Security: change the seeded admin password (`admin@moneywise.co.ke` / `ChangeMe!2026`) right after first login.

## Local dev quick reference

- Serve: `C:\xampp\php\php.exe artisan serve --port=8080` (port 8000 is occupied by another project)
- Assets: `npm run dev` (hot reload) or `npm run build`
- Tests: `php artisan test` (35 passing)
- Admin: http://127.0.0.1:8080/admin — `admin@moneywise.co.ke` / `ChangeMe!2026`
