# Deploying Webnedrix on cPanel shared hosting

Written for a Truehost cPanel plan. Check each limit against your own plan page before you rely on it: PHP version, MySQL access, SSH or Terminal, cron frequency, and file and inode limits all differ between plans.

## Before you start (on your own computer)

1. Install PHP 8.2 or newer, Composer and Node 20 or newer locally.
2. In the project folder run:
   ```
   composer install --no-dev --optimize-autoloader
   npm ci
   npm run build
   ```
   `npm run build` writes the compiled CSS, JavaScript and the 3D scene to `public/build`. The server never needs Node. Upload `public/build` with everything else.
3. Do not upload `node_modules`, `tests`, `.git` or `.env`.

## 1. Create the database

cPanel, MySQL Databases: create a database, a user, and add the user to the database with all privileges. Note the three names. cPanel adds your account prefix to both.

## 2. Choose PHP

cPanel, Select PHP Version (or MultiPHP Manager): pick 8.2 or newer for the domain. Enable these extensions: `pdo_mysql`, `mbstring`, `openssl`, `tokenizer`, `xml`, `ctype`, `json`, `fileinfo`, `curl`, `zip`, `intl`, `gd`, `bcmath`. Raise `memory_limit` to at least 256M and `upload_max_filesize` and `post_max_size` to at least 20M.

## 3. Upload the app outside the public folder

Laravel's `public` folder is the only part the web should reach.

```
/home/ACCOUNT/webnedrix/          <- the whole project (app, config, vendor, storage...)
/home/ACCOUNT/public_html/        <- contents of the project's public/ folder only
```

1. Upload the project as a zip to `/home/ACCOUNT/` and extract it into `webnedrix`.
2. Copy everything inside `webnedrix/public` into `public_html` (including `build`, `brand`, `index.php`, `.htaccess`).
3. Edit `public_html/index.php` and point the two paths at the project folder:
   ```php
   require __DIR__.'/../webnedrix/vendor/autoload.php';
   $app = require_once __DIR__.'/../webnedrix/bootstrap/app.php';
   ```
   and add `$app->usePublicPath(__DIR__);` right after the second line.

## 4. Configure `.env`

Create `/home/ACCOUNT/webnedrix/.env` from `.env.example` and set at least:

```
APP_NAME=Webnedrix
APP_ENV=production
APP_DEBUG=false
APP_URL=https://yourdomain.com
APP_KEY=            # generate with: php artisan key:generate --show   (run locally, paste here)

DB_CONNECTION=mysql
DB_HOST=localhost
DB_DATABASE=ACCOUNT_dbname
DB_USERNAME=ACCOUNT_dbuser
DB_PASSWORD=...

QUEUE_CONNECTION=database
CACHE_STORE=file
SESSION_DRIVER=file
FILESYSTEM_DISK=public

MAIL_MAILER=smtp
MAIL_HOST=...           # from cPanel, Email Accounts, Connect Devices
MAIL_PORT=465
MAIL_USERNAME=...
MAIL_PASSWORD=...
MAIL_ENCRYPTION=ssl
MAIL_FROM_ADDRESS=hello@yourdomain.com
MAIL_FROM_NAME="Webnedrix"

OWNER_NAME="Your name"
OWNER_EMAIL=you@yourdomain.com
OWNER_PASSWORD=choose-a-long-one
```

Set file permissions: `storage` and `bootstrap/cache` writable (755 on folders, 644 on files is normally enough on cPanel).

## 5. Create the tables and the first account

With Terminal (cPanel, Terminal) or SSH:

```
cd ~/webnedrix
php artisan migrate --force
php artisan db:seed --force
php artisan storage:link
php artisan config:cache && php artisan route:cache && php artisan view:cache
```

If your plan has no terminal: run `php artisan migrate --force --pretend` locally to get the SQL, or temporarily point your local `.env` at the remote database if your host allows remote MySQL, and run the commands from your computer.

`storage:link` needs symlinks. If the host blocks them, create the link in cPanel File Manager, or set the `public` disk root to a folder inside `public_html` in `config/filesystems.php`.

After the owner account is created, delete `OWNER_PASSWORD` from `.env`.

## 6. Scheduler and queue (one cron line)

cPanel, Cron Jobs, every minute:

```
* * * * * /usr/local/bin/php /home/ACCOUNT/webnedrix/artisan schedule:run >> /dev/null 2>&1
```

Use the PHP path your host shows for the right version. The schedule runs the queue worker for up to 50 seconds each minute, so emails, notifications and automations are processed without a permanent worker. If your plan allows cron only every few minutes, mail and notifications arrive that much later; lead assignment still happens instantly because it runs inside the request.

## 7. HTTPS

cPanel, SSL/TLS Status: run AutoSSL for the domain. Then force HTTPS by adding this to the top of `public_html/.htaccess`:

```
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
```

## 8. First sign-in and launch checks

1. Open `https://yourdomain.com/admin` and sign in with the owner email.
2. Homepage and Global settings: add your real email, phone and social links.
3. Add the Privacy and Terms pages (Content, Pages), have them reviewed by a lawyer, set status to Published. The footer links appear on their own.
4. Submit the Start a Project form once and watch the lead appear under CRM, Leads.
5. Visit `/sitemap.xml` and `/robots.txt`.

## Storage for client files

The portals store deliverables and client documents on a private disk named `r2` (Cloudflare R2). It is wired in the portal phase. Until then nothing uses it. When ready: `composer require league/flysystem-aws-s3-v3` and fill the R2 keys in `.env`. Files are served only through short-lived signed links, never as public URLs.

## Updating the site

1. Locally: pull changes, `composer install --no-dev --optimize-autoloader`, `npm ci`, `npm run build`.
2. Upload changed files (or the whole zip) over the project folder, and `public/build` into `public_html/build`.
3. On the server: `php artisan migrate --force && php artisan optimize:clear && php artisan config:cache && php artisan route:cache && php artisan view:cache`.

## Backups

cPanel, Backup: download a full backup before every release. Also schedule a database dump with a cron line such as:

```
0 2 * * * mysqldump -u ACCOUNT_dbuser -p'PASSWORD' ACCOUNT_dbname | gzip > /home/ACCOUNT/backups/db-$(date +\%F).sql.gz
```

Copy backups off the server regularly.

## If something breaks

- Blank page or 500: set `APP_DEBUG=true` for one minute, reload, read the message, set it back to false. Or read `storage/logs/laravel.log`.
- Styles missing: `public_html/build` is not uploaded, or `APP_URL` is wrong.
- 3D scene shows the flat picture: that is the fallback. It means the browser has no WebGL, or `public_html/build` is out of date. Rebuild and upload.
- Emails not arriving: check the SMTP values and that the cron line runs.
- Changes to settings not showing: `php artisan cache:clear`.
