<?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 --trusted-proxy-count=1 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>

    <!--
      Forward the real client IP, so the audit log, the kiosk visitor-location
      feature (IP -> business unit), the GE-Enforce IP allowlist and per-host
      login rate limiting all see the caller rather than the loopback address
      HttpPlatformHandler connects from.

      IIS does not set X-Forwarded-For on its own. Without the rule below there
      is no such header at all, and every client looks like 127.0.0.1 - so the
      allowlist and the visitor-location lookup silently stop working.

      ONLY CORRECT WHEN IIS IS DIRECTLY EXPOSED. It overwrites the header with
      REMOTE_ADDR, which is what stops a client spoofing its own X-Forwarded-For.
      Behind a reverse proxy (ARR, a load balancer) REMOTE_ADDR is the PROXY, so
      this rule would destroy the real client IP - there, leave it disabled and
      let the proxy set the header.

      It ships DISABLED because it needs the URL Rewrite module; enabled without
      it, IIS returns HTTP 500.19 ("configuration section not well-formed").

      There is deliberately NO <allowedServerVariables> block below. Setting a
      server variable requires that variable to be allowed, but the section
      system.webServer/rewrite/allowedServerVariables ships with
      overrideModeDefault="Deny", so declaring it in an application's own
      web.config is refused outright: IIS answered 500.52 with error 0x80070021,
      "this configuration section cannot be used at this path", BEFORE it ever
      reached httpPlatformHandler - so python was never launched and the stdout
      log stayed empty, which looks like an application fault and is not one.

      The installer instead allows the single variable at server level, which
      grants exactly HTTP_X_FORWARDED_FOR rather than unlocking the section and
      letting every site on the machine declare arbitrary server variables.

      The installer handles both: -ClientIpSource direct installs URL Rewrite
      from the bundle, allows the variable, and enables this; -ClientIpSource
      proxy leaves it alone. By hand: install URL Rewrite, run
        appcmd set config /section:system.webServer/rewrite/allowedServerVariables ^
          /+"[name='HTTP_X_FORWARDED_FOR']" /commit:apphost
      then delete the two marker lines below.
    -->
    <!-- SHOPDB-CLIENTIP-BEGIN
    <rewrite>
      <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>
    SHOPDB-CLIENTIP-END -->

  </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>
