# Where to copy this, and what not to break

> **Already deployed?** You are past this document. Your app is at
> `public_html/aiseo` and the next step is `START-HERE.md` — extract over the top,
> open `migrate-web.php`, create the tables. This file is the reference for a
> fresh install or a second environment.

## The short answer

**Extract the whole package, as-is, into its own folder.** Nothing gets
rearranged; every folder lands exactly as it is in the zip:

```
public_html/aiseo/
```

If that folder already holds a different app, extract into `public_html/aiseo-new/`
instead and switch over when the new one works — two apps cannot share one
`index.php`, and overwriting it is what takes a site down. There is deliberately
no `RewriteBase` in the `.htaccess`, so **renaming the folder later needs no edit
to it**; only `APP_URL` in `.env` has to follow.

---

## Option A — new folder (recommended)

### 1. Upload

Extract the zip and upload the **whole tree** to `public_html/aiseo/`. When
you are done it looks like this — identical to the zip:

```
public_html/aiseo/
├── .htaccess              <- from deploy/htaccess-A-separate-folder.txt
├── .env                   <- you create this from .env.example
├── bootstrap.php
├── preflight.php
├── public/
│   ├── index.php
│   ├── styleguide.html
│   └── assets/css/, assets/js/
├── config/
├── src/
├── templates/
├── db/migrations/
├── bin/
├── tests/
├── render-service/
└── var/
```

### 2. The .htaccess

**Nothing to do — the `.htaccess` in the zip is already the right one** for this
layout, and needs no editing. There is deliberately no `RewriteBase`, so you can
rename the folder later without touching it.

(`deploy/htaccess-A-separate-folder.txt` is the same file, kept as a reference
copy in case yours gets overwritten.)

### 3. Create .env

```bash
cd ~/public_html/aiseo
cp .env.example .env
chmod 600 .env
```

Then set at minimum:

```
APP_URL=https://dghanalytics.com/aiseo
APP_KEY=<run: php -r "echo bin2hex(random_bytes(32));">
DB_NAME=dolrad_...
DB_USER=dolrad_...
DB_PASS="your password in double quotes"
```

Leave every `AISEO_*_DIR` line blank. In this layout the defaults are correct and
auto-discovery finds everything.

### 4. Permissions

```bash
chmod 755 var var/logs var/cache
chmod 600 .env
```

**`0600` is only correct if `.env` is owned by the user PHP runs as** — normal on
cPanel, but not with plain `mod_php`. If it is not, PHP silently cannot read the
file and every page 404s. The app now detects this and prints the fix; see the
permissions table in `ENV-SETUP.md`. Using `setup-env.php` avoids the problem
entirely, because the file is then written by the PHP user itself.

### 5. Check before doing anything else

```
https://dghanalytics.com/aiseo/preflight.php
```

Then create the tables. **Without SSH**, open:

```
https://dghanalytics.com/aiseo/migrate-web.php
```

With SSH:

```bash
php bin/doctor.php
php bin/migrate.php
php bin/audit.php --list
```

### 6. Delete the setup tools

`setup-env.php`, `migrate-web.php` and `preflight.php` all expose paths and PHP
configuration. Remove all three once the app runs — `migrate-web.php` has a button
that deletes itself.

### 7. Switching over, later

When the new app works and you want it at `/aiseo/`:

```bash
cd ~/public_html
mv aiseo aiseo-old-$(date +%Y%m%d)      # keep the old one, do not delete it
mv aiseo-new aiseo
# then in aiseo/.env:  APP_URL=https://dghanalytics.com/aiseo
```

**One line to edit — `APP_URL` — and nothing in `.htaccess`.** There is no
`RewriteBase` to change: inside a `.htaccess` the rewrite target is already
relative to its own directory. Even `APP_URL` is not load-bearing for routing: the
app derives its base path from the filesystem and shows a banner telling you the
two disagree. It matters because the Google OAuth redirect URI is built from it and
cannot self-correct. The old app is still on disk if you need it back.

---

## Option B — into the existing `/aiseo/` folder

Only if you have decided to replace your current app now. **Back it up first:**

```bash
cd ~/public_html
cp -a aiseo aiseo-backup-$(date +%Y%m%d)
```

Then the package folders map like this. Note that the **contents** of `public/`
move up one level, because your front controller is served at `/aiseo/` directly:

| From the zip | To |
|---|---|
| `bootstrap.php` | `aiseo/bootstrap.php` |
| `preflight.php` | `aiseo/preflight.php` |
| **contents of** `public/` | `aiseo/` — so `aiseo/index.php`, `aiseo/assets/`, `aiseo/styleguide.html` |
| `config/` | `aiseo/config/` |
| `src/` | `aiseo/3-source-code/src/` (merges with what you already have) |
| `templates/` | `aiseo/3-source-code/templates/` |
| `db/migrations/` | `aiseo/3-source-code/db/migrations/` |
| `bin/` | `aiseo/bin/` |
| `tests/` | `aiseo/tests/` |
| `render-service/` | `aiseo/render-service/` |
| `var/` | `aiseo/var/` |
| `deploy/htaccess-B-front-controller-at-root.txt` | `aiseo/.htaccess` |

Do **not** create `aiseo/public/` — the app would end up served at
`/aiseo/public/`.

Then set the explicit paths in `.env`, because in this layout the source is not
where the package defaults expect:

```
AISEO_SRC_DIR=/home/dolrad/public_html/aiseo/3-source-code/src
AISEO_TEMPLATES_DIR=/home/dolrad/public_html/aiseo/3-source-code/templates
AISEO_MIGRATIONS_DIR=/home/dolrad/public_html/aiseo/3-source-code/db/migrations
AISEO_PUBLIC_DIR=/home/dolrad/public_html/aiseo
AISEO_VAR_DIR=/home/dolrad/aiseo-var
APP_URL=https://dghanalytics.com/aiseo
```

Replace `/home/dolrad/public_html` with your real account path — run `pwd` in the
cPanel terminal to confirm it.

---

## If a previous app was at this path

If `/aiseo/` returns 500 while static files still serve, PHP is failing, not
Apache. To restore what was there:

1. Put your original `index.php` back from your backup or from git.
2. If you also replaced `.htaccess`, rename mine: `mv .htaccess .htaccess.mine`
3. Reload. Your old app should return.

Nothing of mine touches `3-source-code/src` in a destructive way — the package
adds files there, it does not delete yours. The only file of mine that replaces
one of yours is `index.php`, plus `.htaccess` if you copied it.

If you do not have a backup of `index.php`, say so — the app's own source is
still on disk in `3-source-code/`, so it is recoverable.

---

## The .htaccess in the zip

The root `.htaccess` is now the **Option A** version and needs **no editing** —
there is no `RewriteBase` to set, so the folder can be renamed freely.

Both variants were verified against real Apache 2.4 with `AllowOverride All`:

| Request | Expected |
|---|---|
| the app root, and any route | 200 |
| `assets/css/tokens.css` | 200 |
| `setup-env.php`, `preflight.php` | 200 — they must stay reachable to work |
| `.env`, `bootstrap.php`, `config/paths.php` | 403 |
| `src/…`, `templates/…`, `bin/…`, `db/…`, `tests/…`, `var/logs/app.log` | 403 |
| `3-source-code/…` and any other numbered folder | 403 |

An earlier version denied every `.php` except `index.php`, which also blocked
`setup-env.php` and `preflight.php` — the tools you need in order to diagnose
anything. It also carried `ErrorDocument 404 /index.php`, an absolute path that
resolves at the domain root and produces the second 403 in
"additionally, a 403 Forbidden error was encountered while trying to use an
ErrorDocument". Both are fixed. **Protection for the two tools is deleting them
after use, not a rule that also locks you out.**

## Upload notes that cause blank 500s

- **Use binary mode**, or upload the zip and extract it with cPanel File Manager.
  FTP in text mode mangles line endings and can truncate a `.php` file, which
  produces a parse error and an empty 500.
- **Extract server-side if you can.** Upload `aiseo-code.zip`, then File Manager
  → right-click → Extract. Far fewer failure modes than transferring 51 files.
- **Check file permissions after extracting:** files 644, folders 755,
  `.env` 600, `var/` and its subfolders 755.
- **Filenames are case-sensitive on Linux.** `SearchConsole.php` must not become
  `searchconsole.php`. Extracting the zip preserves case; a Windows drag-and-drop
  over FTP sometimes does not.
