# ShopDB - Windows + IIS install runbook A step-by-step, **tested** install for a new site on Windows Server / Windows 11 with IIS in front of the Flask app (HttpPlatformHandler -> waitress), backed by MySQL. This runbook was validated end to end on a win11 + IIS + MySQL 5.6 box. `APP_ROOT` below = the deploy folder, e.g. `C:\shopdb-flask` (where `wsgi.py` lives). Run PowerShell as Administrator. --- ## 0. Prerequisites | Need | Notes | | --- | --- | | **Python 3.12** (64-bit) | `python --version` | | **IIS** with **HttpPlatformHandler** | https://www.iis.net/downloads/microsoft/httpplatformhandler (direct MSI: `download.microsoft.com/download/8/1/3/813AC4E6-9203-4F7A-8DD5-F3D54D10C5CD/httpPlatformHandler_amd64.msi`) | | **MySQL 5.7+/8.0** (or 5.6 with the flags in step 1) | reachable from the app host | | URL Rewrite (optional) | only for the real-client-IP rule; skip it and the app still runs | The app itself pulls in `waitress` and `tzdata` from `requirements.txt` (step 4). --- ## 1. MySQL: flags (5.6 only) + database + user On **MySQL 5.6 only**, add to `my.ini`/`my.cnf` under `[mysqld]` and restart MySQL (5.7+/8.0 need none of this): ``` innodb_file_per_table = 1 innodb_file_format = Barracuda innodb_large_prefix = 1 ``` Without them, `flask db upgrade` fails with **error 1071** ("key too long") - the migrations use `ROW_FORMAT=DYNAMIC`, which needs the 3072-byte prefix these unlock. Then create the database (utf8mb4) and an app user: ```sql CREATE DATABASE shopdb_flask CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'shopdb'@'%' IDENTIFIED BY 'CHANGE_ME'; GRANT ALL PRIVILEGES ON shopdb_flask.* TO 'shopdb'@'%'; FLUSH PRIVILEGES; ``` --- ## 2. Deploy the app files Copy the release (the repo minus `venv/`, `.git/`, `node_modules/`, `frontend/src/`) to `APP_ROOT`. It must contain `wsgi.py`, `shopdb/`, `plugins/`, `migrations/`, `requirements.txt`, and the pre-built `frontend/dist/`. --- ## 3. Virtual env + dependencies ```powershell cd APP_ROOT python -m venv venv venv\Scripts\python -m pip install -r requirements.txt ``` This installs Flask, SQLAlchemy, PyMySQL, **waitress** (the WSGI server IIS launches) and **tzdata** (Windows has no IANA tz database; without it the notifications plugin fails with "No time zone found with key America/New_York"). --- ## 4. Secrets + connection (.env) Create `APP_ROOT\.env` (read by `wsgi.py` via `load_dotenv()`). Lock its ACLs to the app-pool identity + admins. ``` FLASK_ENV=production SECRET_KEY=<64+ random chars> JWT_SECRET_KEY= DATABASE_URL=mysql+pymysql://shopdb:CHANGE_ME@:3306/shopdb_flask?charset=utf8mb4 CORS_ORIGINS=http:// ``` Generate a key: `venv\Scripts\python -c "import secrets;print(secrets.token_urlsafe(64))"`. Production **refuses to boot** if any of `SECRET_KEY`, `JWT_SECRET_KEY`, `DATABASE_URL`, `CORS_ORIGINS` is missing or a dev default. --- ## 5. Preflight (catch problems before installing) ```powershell $env:FLASK_APP="shopdb" venv\Scripts\flask db-utils preflight ``` Checks Python, required env, DB connectivity, and the MySQL 5.6 index flags, and prints exactly what to fix. Fix any **FAIL** before continuing. --- ## 6. Schema + data + plugins + admin ```powershell $env:FLASK_APP="shopdb" venv\Scripts\flask db upgrade # creates every table (to head) venv\Scripts\flask seed reference-data # statuses, machine/location/rel types venv\Scripts\flask seed permissions venv\Scripts\flask seed settings # enable the plugins this site tracks (registry is empty on a fresh box). # usb + employees install DISABLED by default - enable them later in the wizard # if the site wants those (they create extra tables). foreach ($p in "computers","equipment","network","notifications","printers","knowledgebase","slides","warranty") { venv\Scripts\flask plugin install $p } # first admin (password generated + printed once - store it): venv\Scripts\flask seed admin --username admin --email admin@yourfacility.example.com ``` > Prefer no CLI? Skip `seed admin` (and even the seed steps): start the site, and > the login page offers to **create the first admin** on a fresh instance, then > the setup wizard can seed reference data. Either path works. --- ## 7. IIS site 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**: ```powershell Import-Module WebAdministration New-WebAppPool -Name shopdbflask Set-ItemProperty IIS:\AppPools\shopdbflask -Name managedRuntimeVersion -Value "" ``` 3. Grant the app-pool identity access: ```powershell icacls APP_ROOT /grant "IIS AppPool\shopdbflask:(OI)(CI)RX" /T icacls APP_ROOT\logs /grant "IIS AppPool\shopdbflask:(OI)(CI)M" /T ``` 4. **Unlock the handler sections** (locked server-wide by default; without this IIS returns **HTTP 500.19**): ```powershell %windir%\system32\inetsrv\appcmd unlock config /section:system.webServer/handlers %windir%\system32\inetsrv\appcmd unlock config /section:system.webServer/httpPlatform ``` 5. Create the site (own port; the classic ASP site can keep 8080): ```powershell New-Website -Name shopdb-flask -Port 8090 -PhysicalPath APP_ROOT -ApplicationPool shopdbflask New-NetFirewallRule -DisplayName "shopdb-flask 8090" -Direction Inbound -Protocol TCP -LocalPort 8090 -Action Allow Start-Website shopdb-flask ``` 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). > 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 > `` block, to record real client IPs in audit logs. --- ## 8. Smoke test + first run ```powershell (Invoke-WebRequest http://localhost:8090/ -UseBasicParsing).StatusCode # 200 (SPA) Invoke-WebRequest http://localhost:8090/api/auth/login -Method POST ` -Body '{"username":"admin","password":""}' ` -ContentType application/json -UseBasicParsing # 200 + token ``` Browse to `http://:8090`, sign in as the admin, and the **setup wizard** walks through site name, features (per-plugin: create tables here vs connect a DB), floor-map upload, and starter data. Multiple Flask apps can share one IIS box - each gets its own site, app pool, port, and venv. --- ## Troubleshooting | Symptom | Cause / fix | | --- | --- | | `flask db upgrade` -> error **1071** | MySQL 5.6 without the step-1 flags (or server not restarted). | | IIS **500.19** | handler sections not unlocked (step 7.4), or the `` block active without URL Rewrite. | | **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. | | ConfigError on boot | a required `.env` var missing or left at a dev default. |