# shopdb-flask environment template. # # Copy to .env and fill in the values. .env is gitignored. ProductionConfig # refuses to boot when SECRET_KEY, JWT_SECRET_KEY, DATABASE_URL, or # CORS_ORIGINS are missing or use the dev defaults. # # This template covers what a container deploy sets. Docker is the shipping # path for a receiving site: docker-compose.yml builds from source, # docker-compose.pull.yml runs a published image, and # docker-compose.airgap.yml runs an image loaded from a tarball. See the # DEPLOY wiki page for the full per-site runbook. # ---- Flask ---- FLASK_APP=wsgi.py # Set to 'production' for live sites. Other valid values: 'development', # 'testing'. Production triggers ProductionConfig.validate() at boot. FLASK_ENV=production # ---- Required secrets (production refuses to boot without these) ---- # Generate strong random values, e.g.: # python -c "import secrets; print(secrets.token_urlsafe(64))" SECRET_KEY=change-this-to-a-secure-random-string JWT_SECRET_KEY=change-this-to-another-secure-random-string # ---- Database (required) ---- # Format: mysql+pymysql://:@:/?charset=utf8mb4 # In docker-compose, host is `db` (the service name). # The ?charset=utf8mb4 keeps the connection on utf8mb4; create the database as # utf8mb4 too (see the DEPLOY wiki page). Both must be utf8mb4 to match the schema. DATABASE_URL=mysql+pymysql://shopdb:CHANGE_ME@db:3306/shopdb_flask?charset=utf8mb4 # ---- CORS (required, no wildcards in production) ---- # Comma-separated list of explicit origins permitted to call the API. # Example for a single-host facility deploy: # CORS_ORIGINS=https://shopdb.facility-a.example.com # Wildcard '*' is rejected by ProductionConfig.validate(). CORS_ORIGINS=http://localhost:5173 # ---- Reverse proxy (optional, default 0 = no proxy) ---- # Set to the number of proxies actually in front of this instance - 1 when # nginx or another front end terminates TLS for the container. Without it the # app sees the proxy's connection instead of the browser's (scheme http, the # proxy's host and IP), so any absolute URL it builds is wrong at the browser. # That is what made a redirect on the endpoints list unfollowable behind a # proxy. # # Leave it at 0 when nothing fronts the app. It makes X-Forwarded-* trusted, # and a client can send those headers itself - the login rate limit and the # account lockout are keyed on the client address. TRUSTED_PROXY_COUNT=0 # ---- JWT lifecycle (optional, defaults shown) ---- JWT_ACCESS_TOKEN_EXPIRES=3600 JWT_REFRESH_TOKEN_EXPIRES=2592000 # ---- Logging (optional) ---- LOG_LEVEL=INFO # ---- docker-compose only ---- # These are read by the compose files; not used by the Flask app directly. MYSQL_ROOT_PASSWORD=CHANGE_ME_ROOT_PASSWORD MYSQL_PASSWORD=CHANGE_ME_APP_PASSWORD MYSQL_PORT=3306 API_PORT=5001 # Air-gapped deploy only (docker-compose.airgap.yml): the loaded image tag, # which MUST match what build-offline-bundle.ps1 -Version produced. Ignored by # the connected build template (docker-compose.yml builds from source) and by # the pull template (which uses SHOPDB_IMAGE instead). IMAGE_TAG=0.7.0 # Pull deploy only (docker-compose.pull.yml): the full image reference to pull. # SHOPDB_IMAGE=ghcr.io//shopdb-flask:stable # # Non-empty lights a channel badge in the sidebar. Leave unset on a real site; # the dev stacks set it to DEV. # SHOPDB_ENV_LABEL= # ---- Zabbix integration (optional, for printer supply monitoring) ---- ZABBIX_URL= ZABBIX_TOKEN= # ---- Per-plugin collector API keys (optional) ---- # Per ADR-006, each plugin can accept external collector input at # /api/collector/. The framework checks # COLLECTOR_API_KEY_ first, then COLLECTOR_API_KEY as fallback. # COLLECTOR_API_KEY= # COLLECTOR_API_KEY_COMPUTERS= # COLLECTOR_API_KEY_ENDPOINTS= # ---- Trusted plugin publisher keys (ADR-013, optional) ---- # Public-key PEM paths (OS path separator: ':' on Linux, ';' on Windows) used # to verify signed plugin artifacts. Delivered with this config, NEVER from the # plugin shelf. Empty on a site that does not adopt marketplace plugins. # PLUGIN_TRUSTED_KEYS=/etc/shopdb/keys/curator.pub # # Enforce signatures: a plugin only loads/migrates if its tree matches a # trusted signature. Default off. Turn on only after stamping plugins # (flask plugin stamp-bundled) and pinning keys above. # PLUGIN_REQUIRE_SIGNED=false # # Read-only folder the app pulls plugin artifacts from (a SharePoint-synced or # copied shelf). The app reads this folder; it never speaks any network. # PLUGIN_SHELF_DIR=/srv/shopdb/plugin-shelf # ---- Employee directory database (optional, read-only) ---- # Separate HR/employee lookup DB. Core reads it in two places: the employee # domain of global search (shopdb/core/api/search.py) and audit-log name # resolution (shopdb/core/api/auditlogs.py). Leave unset if the site has no # such database; there is no safe default for the password, so an unset # password fails loud rather than connecting as someone else. # EMPLOYEE_DB_HOST= # EMPLOYEE_DB_USER= # EMPLOYEE_DB_PASSWORD= # EMPLOYEE_DB_NAME=employees # ---- Subpath deployment (optional) ---- # Serve the app under a URL prefix instead of the server root, e.g. behind an # nginx location at /ops. wsgi.py reads this to mount the WSGI app under the # prefix. The frontend must be rebuilt with the matching base: # VITE_BASE_PATH=/ops/ npm run build. Leave unset when the app owns its own # host/port, which is the container default. # MOUNT_PATH=/ops