# Shopno Books House server: install, connect and check (Update 6)

This guide covers a new install and upgrades. It needs no command line: everything is done in cPanel and in the browser.

## 1. How the pieces fit together (checked in the code, not assumed)

```text
Visitor ──> shopnobookshouse.com            The shop + staff dashboard (/admin).
            (React / TanStack Start, hosted   Pages are rendered on the server so search
             by Lovable, not PHP)             engines get full HTML.
                 │  HTTPS JSON requests
                 ▼
            api.shopnobookshouse.com/api     THIS PACKAGE: PHP 7.4+ API, sitemaps, robots
            (your cPanel hosting)            data, IndexNow, emails, backups, cron jobs.
                 │  PDO (localhost)
                 ▼
            MySQL / MariaDB database         Books, orders, customers, settings, media list.
```

- The shop is **not** PHP, so this package is not copied into the shop. It is a separate API that the shop calls.
- The link between them is one setting in the shop: `VITE_API_BASE_URL` (default `https://api.shopnobookshouse.com/api`). The API only accepts browser requests from addresses in `ALLOWED_ORIGINS`.
- Staff sign in at `https://shopnobookshouse.com/admin/login`. Sessions are HttpOnly cookies plus a CSRF token; passwords are bcrypt hashed. Full endpoint list: `documentation/ADMIN_API.md` and `documentation/API_SPEC.md`.
- Sitemaps, `robots.txt` and `llms.txt` are served on the shop's own domain; the shop fetches their data from this API.

## 2. Before you start (5 minutes)

1. **Back up** (upgrades only): cPanel → phpMyAdmin → your database → Export. Also download your current `public_html/api/config.php`.
2. **Subdomain**: cPanel → Domains → create `api.shopnobookshouse.com` with document root `public_html/api` (skip if it exists).
3. **HTTPS**: cPanel → SSL/TLS Status → run AutoSSL for the api subdomain.
4. **Database** (new installs): cPanel → MySQL Databases → create a database, a user with a strong password, and *Add User To Database* with **ALL PRIVILEGES**. Note the full names (cPanel adds your account prefix, e.g. `youruser_shopno`).
5. **PHP**: cPanel → MultiPHP Manager → PHP 8.1 or newer (7.4 is the minimum). Extensions needed: pdo_mysql, mbstring, json, fileinfo, curl, openssl; gd is recommended (WebP images).

## 3. Upload the ZIP

1. cPanel → File Manager → open `public_html`. Click **Settings** (top right) and tick **Show Hidden Files** — the `.htaccess` files must be uploaded.
2. Upload `shopno-books-house-server.zip`, right-click → **Extract**. You get an `api` folder and a `documentation` folder.
3. New install: if `public_html/api` did not exist, the extracted `api` folder is it.
   Upgrade: move the *contents* of the new `api` folder over your existing `public_html/api` and choose to overwrite. **`config.php` is not in the ZIP, and `uploads/`, `cache/` and `backups/` contain only their protective `.htaccess` files, so your settings, images and backups are never overwritten.**
4. Delete the uploaded ZIP file. Keep `documentation` outside `public_html` if you like; it is not needed on the server.

## 4. Run the setup wizard

Open `https://api.shopnobookshouse.com/api/setup.php`.

1. **Access code**: the wizard writes a one-time code into `api/setup-code.php`. Open that file in File Manager (right-click → View) and type the code. On an upgrade you may instead type `INSTALL_KEY` from your existing `config.php`. Ten wrong tries lock the session.
2. **Hosting check**: PHP version, extensions, folder permissions, `.htaccess`, upload size. Fix anything marked *Error*, then *Check again*.
3. **Database**: enter host (`localhost`), database name, user and password, then *Test connection*. The wizard also checks that the user may create tables. On upgrades leave the password empty to keep the saved one.
4. **Website and API**: shop address, API address (pre-filled), allowed shop addresses, sign-in cookie mode (keep *Strict*). The wizard writes `config.php` with permissions 600, generates `APP_KEY`, `CRON_SECRET` and `INSTALL_KEY`, and **keeps existing keys on upgrades** (`APP_KEY` decrypts saved SMTP and backup passwords, so it must never change). A copy of the previous `config.php` is kept in `api/cache/` (blocked from the web).
5. **Install tables**: creates all tables on an empty database, otherwise runs only new updates (`migrations/005_update6.php` for this release). Existing books, orders, settings and web addresses are kept. If a step fails nothing is deleted; fix and reload.
6. **Owner account**: only on new installs (first staff sign-in).
7. **Health report**: the checks described in section 6. Then **Finish and lock setup**: it writes `install.lock`, deletes `setup-code.php` and (ticked by default) `install.php`. To run the wizard again later, delete `api/install.lock`.

Command-line alternative (SSH, optional): `php install.php YOUR_INSTALL_KEY owner@email 'password' 'Name'`.

## 5. Connect the shop and test without touching the live site

1. In the shop project settings set `VITE_API_BASE_URL=https://api.shopnobookshouse.com/api` (and `VITE_USE_DEMO_DATA=false` once real books exist), then publish.
2. Test the link before you announce anything:
   - `https://api.shopnobookshouse.com/api/settings` shows JSON starting with `{"data":`.
   - `https://api.shopnobookshouse.com/api/config.php` shows **Forbidden** (private files are protected).
   - `https://shopnobookshouse.com/sitemap.xml` lists sitemap files — this proves the shop reads from the API.
   - Sign in at `/admin/login` → **System** → *Check again*: every row should be Working/Connected.
   - Create a draft book in the dashboard and open its preview; place a test order with your own phone and track it at `/track-order`, then cancel it.
3. Nothing in the API writes to the shop, and draft or noindex items never appear in sitemaps, so testing does not affect search results.

## 6. System health dashboard

Dashboard → **System** runs the same report as the wizard. Each row is *Connected*, *Disconnected*, *Working*, *Warning*, *Error* or *Not configured*, with the reason and how to fix it.

| Group | Checks |
| --- | --- |
| Server | PHP running and version, required extensions, image optimisation |
| Database | Connection and speed, database structure up to date |
| Files and security | uploads/cache/backups writable, `config.php` permissions, secret keys not left at example values (values never shown), debug mode off, HTTPS, installer locked, private files (config, logs, schema, code, backups) not downloadable |
| Connections | API answers, website answers, website reads data from this server, allowed origins, email sending, IndexNow, Search Console / Bing (marked *Not configured*: they need your Google/Bing account, the server cannot read them on its own) |
| Search engines | sitemap.xml, robots.txt (flags a site-wide block), structured data valid on the homepage and newest book page, canonical tag, llms.txt, local SEO issues |
| Scheduled tasks | Last run of the minute and daily cron jobs |
| Errors | PHP errors in the last 24 hours (from the private `api/error_log`), failed API requests (server errors are counted automatically) |

## 7. Cron jobs (cPanel → Cron Jobs)

```text
* * * * *    php /home/USER/public_html/api/cron.php minute    # emails
15 3 * * *   php /home/USER/public_html/api/cron.php daily     # cleanup, analytics, recurring costs
30 3 * * *   php /home/USER/public_html/api/cron.php backup    # weekly automatic backup
*/5 * * * *  php /home/USER/public_html/api/cron.php seo       # scheduled publishing + IndexNow
```
Replace `USER` with your cPanel user name (the wizard's final page shows the exact paths). Each run is recorded, so System shows when it last ran.

## 8. Security checklist

- `config.php` is written with permissions 600 and blocked by `.htaccess`; `error_log`, `.sql`, `.md`, `lib/`, `routes/`, `migrations/`, `cache/`, `backups/`, `install.lock` and `setup-code.php` are blocked too. Uploaded files can never run as scripts.
- `DEBUG_ERRORS` is always written as `false`. Errors go to `api/error_log`, which is private.
- The wizard needs the one-time code, uses CSRF-protected forms, never shows saved passwords, and locks itself when finished.
- Customers cannot upload files anywhere (Update 6). Only signed-in staff can upload in the Media library.
- Keep PHP updated in cPanel, keep regular backups (Dashboard → Backup and restore), and remove staff who leave (Dashboard → Staff).

## 9. Updating later

1. Back up (Dashboard → Backup and restore → *Back up now*, plus a phpMyAdmin export).
2. Upload and extract the new ZIP over `public_html/api` (your config, uploads and backups are not in it).
3. Delete `api/install.lock`, open `setup.php`, enter `INSTALL_KEY` or the new code, keep your settings and click through: only new updates are applied. Finish and lock again.

## 10. Troubleshooting

| What you see | What to do |
| --- | --- |
| `setup.php` says the folder is not writable | File Manager → `api` → Permissions 755. |
| "The user name or password was refused" | Re-check the full prefixed user name and password; reset the password in MySQL Databases. |
| "That database name does not exist" | Use the full prefixed name, e.g. `youruser_shopno`. |
| "Connected, but the user may not create tables" | MySQL Databases → *Add User To Database* → ALL PRIVILEGES. |
| API answers 404 or 500 on every address | `.htaccess` missing: turn on *Show Hidden Files* and upload it again. Check `api/error_log`. |
| Private files show as downloadable | Same as above; the hosting must allow `.htaccess` (Apache/LiteSpeed). |
| Shop shows sample data / "Website reads data from this server" warns | Set `VITE_API_BASE_URL` in the shop and publish again; add the shop address to `ALLOWED_ORIGINS` (wizard step 4). |
| Staff sign-in works but saving says "session expired" | Use HTTPS on both addresses and keep the cookie mode *Strict* (same domain). |
| robots.txt shows Error | Expected on preview/test addresses. On the real domain check `SITE_URL` matches the published shop and maintenance mode is off. |
| Cron shows *Has never run* | Add the jobs in section 7 and wait a minute. |
| Images are not resized | Enable the `gd` extension (cPanel → Select PHP Version). |

## Verified before release

Checked on PHP 8.4 + MariaDB 11.4 (not on your cPanel host): the wizard end to end on an empty database (wrong code refused, wrong DB password explained, config written with 600, tables installed, short owner password refused, health report, lock → 403), the health report with a connected shop copy, and the API test suites in `tests/` (admin, Update 4, Update 5 and Update 6: bulk edit, no customer uploads, real order tracking). MySQL 5.7 and your host's Apache rules are not yet tested.
