Support subpath IIS deployment as a second install method
Some checks failed
CI / backend (push) Successful in 1m41s
CI / naming (push) Successful in 2s
CI / frontend (push) Successful in 8s
CI / migrations-mysql (push) Failing after 8s

The app can run as an IIS Application under an existing site
(e.g. https://host/ops/) instead of its own site + port:

- frontend: vite base via VITE_BASE_PATH; router history, axios
  baseURL, and root-absolute asset/route paths resolve through
  utils/basePath.js withBase()
- backend: MOUNT_PATH (env or .env) wraps the app in a WSGI
  middleware that shifts the prefix into SCRIPT_NAME, so one knob
  serves API + SPA under the mount
- docs: INSTALL-WINDOWS-IIS.md section 7b runbook + troubleshooting
  rows; DEPLOY-WINDOWS-IIS.md pointer; commented examples in
  deploy/windows/web.config and .env.example

Root deployment unchanged (MOUNT_PATH unset, base '/'). Also folds
two stray root-absolute callers into the shared plumbing
(MachineForm relationship-types fetch, reports CSV window.open).
This commit is contained in:
cproudlock
2026-07-13 16:11:12 -04:00
parent 69dd6d0abe
commit 6010f01de1
19 changed files with 143 additions and 25 deletions

View File

@@ -129,6 +129,16 @@ venv\Scripts\flask seed admin --username admin --email admin@yourfacility.exampl
## 7. IIS site
Two supported deployment methods:
- **Method A - own site (recommended, default):** the app gets its own IIS
site, port (or hostname), app pool, and venv. Steps 1-5 below.
- **Method B - subpath under an existing site:** the app runs as an IIS
**Application** (e.g. `/ops`) under a site you already have (such as the
classic ASP site or Default Web Site), so it shares that site's binding and
TLS cert: `https://<host>/ops/`. Do steps 1-4 below, then follow **7b**
instead of step 5.
1. Copy `deploy\windows\web.config` to `APP_ROOT\web.config`. If `APP_ROOT` is not
`C:\shopdb-flask`, fix the paths inside it. Create `APP_ROOT\logs`.
2. Create an app pool with **No Managed Code**:
@@ -159,6 +169,32 @@ IIS launches `waitress-serve --port=%HTTP_PLATFORM_PORT% wsgi:app` per the
web.config and reverse-proxies the site port to it. First request takes ~15s
(the app boots + connects to MySQL).
### 7b. Method B: subpath under an existing site
The mount path must match in **three places**: the IIS Application alias, the
`MOUNT_PATH` the backend sees, and the `VITE_BASE_PATH` the frontend was built
with. `/ops` is the example throughout; any alias works.
1. Rebuild the frontend for the subpath (on the dev box, then copy `dist`):
```bash
cd frontend && VITE_BASE_PATH=/ops/ npm run build # note the trailing slash
```
2. Create the Application under the existing site (instead of `New-Website`):
```powershell
New-WebApplication -Site "Default Web Site" -Name ops -PhysicalPath APP_ROOT -ApplicationPool shopdbflask
```
3. Tell the backend its mount path: in `APP_ROOT\web.config`, uncomment the
`MOUNT_PATH` environment variable (value `/ops`), or set `MOUNT_PATH=/ops`
in `APP_ROOT\.env`. `wsgi.py` then serves everything under the prefix
(requests outside it get a plain 404 naming the mount).
4. Recycle the app pool. The app is at `http(s)://<host>/ops/` and the API at
`/ops/api/...`.
The handler mappings in the app's web.config apply only inside the
Application, so the parent site's own handlers (classic ASP, static files)
are untouched. `CORS_ORIGINS` in `.env` is origin-only (scheme + host + port,
no path), so it is the same for both methods.
> The `X-Forwarded-For` URL Rewrite rule in web.config is **commented out by
> default**. It needs the URL Rewrite module; with it active but the module
> absent, IIS returns 500.19. Install URL Rewrite, then uncomment the
@@ -191,4 +227,6 @@ each gets its own site, app pool, port, and venv.
| **500** with an empty HttpPlatform log | app-pool identity can't read `APP_ROOT` / run the venv (step 7.3), or `.env` missing/invalid. |
| "No time zone found with key America/New_York" | `tzdata` not installed (`pip install tzdata`). |
| Nav missing Equipment/PCs/... | plugins not installed (step 6 `flask plugin install`), or site not recycled. |
| Method B: blank page / assets 404 under `/ops` | frontend `dist` built without `VITE_BASE_PATH=/ops/` (step 7b.1). |
| Method B: SPA loads but every API call 404s | `MOUNT_PATH` unset or not matching the Application alias (step 7b.3). |
| ConfigError on boot | a required `.env` var missing or left at a dev default. |