REQ-D: restore waitress and tzdata to requirements.in. They existed ONLY in the
generated requirements.txt (hand-added in bf9e60e), so the next
`uv pip compile` would have silently removed the WSGI server and the IANA
timezone database from every Windows install.
REQ-E: split production and development requirements. requirements.txt was
installing pytest, pytest-cov, pytest-flask, coverage, iniconfig and pluggy onto
production servers. Verified on a real Windows Server box before this change.
CI, scripts/test-external-plugin.sh and the dev docs now use requirements-dev.txt.
REQ-F: standardise on Python 3.14. The repo declared four different versions
(Dockerfile 3.12, DEPLOY-WINDOWS-IIS 3.12, INSTALL-WINDOWS-IIS 3.13, CI 3.13,
plus README, web.config and PLUGIN-EXTERNAL-REPO). 3.14 is in active bugfix
support until ~Apr 2027 and supported to Oct 2030; 3.13 entered security-only in
Apr 2026. All four compiled dependencies publish win_amd64 wheels for 3.14
(cryptography via an abi3 wheel), verified by building an offline wheelhouse and
installing it on Windows Server 2025.
REQ-G: state MySQL 8.0 as the standard for new installs; 5.7+/5.6 remain
supported on an existing server.
Lockfiles regenerated with uv pip compile. Production deps 44 -> 38.
115 lines
5.0 KiB
XML
115 lines
5.0 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!--
|
|
IIS site config for shopdb-flask via HttpPlatformHandler.
|
|
|
|
IIS launches waitress (a Windows-friendly WSGI server; gunicorn does NOT run
|
|
on Windows) and forwards requests to it on a private loopback port that IIS
|
|
assigns via %HTTP_PLATFORM_PORT%. One process serves both /api and the built
|
|
Vue SPA (frontend/dist), so no separate static site is needed.
|
|
|
|
Prerequisites on the box:
|
|
- HttpPlatformHandler IIS module installed
|
|
(https://www.iis.net/downloads/microsoft/httpplatformhandler)
|
|
- URL Rewrite module installed (only for the optional X-Forwarded-For rule)
|
|
- Python 3.14 + a venv at APP_ROOT\venv with requirements.txt + waitress
|
|
- Secrets live in APP_ROOT\.env (wsgi.py load_dotenv() reads it). Keep them
|
|
OUT of this file. Lock .env ACLs to the app pool identity + admins.
|
|
|
|
Replace APP_ROOT (C:\shopdb-flask below) with the real deploy path. The IIS
|
|
site's physical path MUST be APP_ROOT (where wsgi.py lives).
|
|
-->
|
|
<configuration>
|
|
<system.webServer>
|
|
|
|
<handlers>
|
|
<add name="httpplatformhandler" path="*" verb="*"
|
|
modules="httpPlatformHandler" resourceType="Unspecified" />
|
|
</handlers>
|
|
|
|
<httpPlatform
|
|
processPath="C:\shopdb-flask\venv\Scripts\waitress-serve.exe"
|
|
arguments="--port=%HTTP_PLATFORM_PORT% --host=127.0.0.1 --threads=8 --trusted-proxy=127.0.0.1 --trusted-proxy-headers=x-forwarded-for wsgi:app"
|
|
stdoutLogEnabled="true"
|
|
stdoutLogFile="C:\shopdb-flask\logs\httpplatform"
|
|
startupTimeLimit="120"
|
|
startupRetryCount="3">
|
|
<environmentVariables>
|
|
<!-- FLASK_ENV MUST be production here or wsgi.py defaults to the dev
|
|
config (SQL echo, debug, wrong DB URL). Real secrets go in .env. -->
|
|
<environmentVariable name="FLASK_ENV" value="production" />
|
|
<environmentVariable name="PYTHONPATH" value="C:\shopdb-flask" />
|
|
<!-- Subpath method only: when this web.config sits in an IIS
|
|
Application (e.g. /ops) under an existing site instead of its own
|
|
site, tell the app its mount path. Must match the alias the
|
|
Application was created with AND the VITE_BASE_PATH the frontend
|
|
was built with ('/ops/'). Omit for the own-site method.
|
|
<environmentVariable name="MOUNT_PATH" value="/ops" />
|
|
-->
|
|
</environmentVariables>
|
|
</httpPlatform>
|
|
|
|
<!--
|
|
OPTIONAL: forward the real client IP so audit logs and the kiosk
|
|
visitor-location feature (IP -> business unit) see the caller, not the
|
|
loopback that HttpPlatformHandler connects from.
|
|
|
|
This block is COMMENTED OUT by default because it needs the URL Rewrite
|
|
module; with it uncommented but URL Rewrite not installed, IIS returns
|
|
HTTP 500.19 ("configuration section not well-formed / cannot be read").
|
|
Install URL Rewrite (https://www.iis.net/downloads/microsoft/url-rewrite)
|
|
and then uncomment the <rewrite> block below to enable it.
|
|
|
|
<rewrite>
|
|
<allowedServerVariables>
|
|
<add name="HTTP_X_FORWARDED_FOR" />
|
|
</allowedServerVariables>
|
|
<rules>
|
|
<rule name="Set X-Forwarded-For" stopProcessing="false">
|
|
<match url=".*" />
|
|
<serverVariables>
|
|
<set name="HTTP_X_FORWARDED_FOR" value="{REMOTE_ADDR}" />
|
|
</serverVariables>
|
|
<action type="None" />
|
|
</rule>
|
|
</rules>
|
|
</rewrite>
|
|
-->
|
|
|
|
</system.webServer>
|
|
|
|
<!--
|
|
Installer downloads: serve /installers/* as IIS static files instead of
|
|
forwarding them to Flask. The handler above is path="*", so without this a
|
|
request for /installers/Foo.exe goes to waitress, which has no such route
|
|
(SPA fallback), and large binaries would stream through a Python thread.
|
|
|
|
This <location> clears the httpPlatformHandler for that one subpath and puts
|
|
the static file handler back, so IIS serves the bytes directly (kernel-mode,
|
|
range/resume, no Python thread held).
|
|
|
|
Requires a physical folder at APP_ROOT\installers (the site's physical path
|
|
is APP_ROOT). Drop the installer binaries there, e.g. robocopy them from the
|
|
classic wwwroot\installers. The stored installpath 'installers/Foo.exe' then
|
|
resolves to <mount>/installers/Foo.exe (e.g. /shopdb/installers/Foo.exe).
|
|
|
|
.exe/.msi are given an explicit MIME map; if the parent site has a Request
|
|
Filtering rule that denies executable extensions, also allow them there.
|
|
-->
|
|
<location path="installers">
|
|
<system.webServer>
|
|
<handlers>
|
|
<clear />
|
|
<add name="StaticFile" path="*" verb="*"
|
|
modules="StaticFileModule" resourceType="File"
|
|
requireAccess="Read" />
|
|
</handlers>
|
|
<staticContent>
|
|
<remove fileExtension=".exe" />
|
|
<mimeMap fileExtension=".exe" mimeType="application/octet-stream" />
|
|
<remove fileExtension=".msi" />
|
|
<mimeMap fileExtension=".msi" mimeType="application/octet-stream" />
|
|
</staticContent>
|
|
</system.webServer>
|
|
</location>
|
|
</configuration>
|