Findings F2, F4, F5, F6, F7, F8, F9, F10, F11 and F13 from wj-architectural-review.md. F1/F1a (the non-sargable LOWER pattern) and F3 (the warranty sync holding a pooled connection) are being handled separately and are untouched here. - A collector check-in that raced another for the same PC lost everything: the unique index on the application fingerprint raised, nothing caught it, and the whole check-in rolled back, discarding state, events and hardware, answering the agent with form copy meant for a person. The insert now runs in a savepoint, reconciles what the other transaction already wrote, one row per savepoint so a second collision skips a row rather than the batch, and the check-in commits with a warning. This is the shape the delete side of the same function already had. - Enrollment is deliberately unauthenticated, so it now carries a per-address rate limit and a cap on pending requests, and the cap is a setting an admin can see and change. A flood can no longer bury the approval queue or grow the table without bound. - The fleet alert poll no longer formats debug strings on every request. - BusinessUnit is on the contract surface (0.34.0), so the endpoints plugin stops reaching into a core table with raw SQL. - A test walks every asset type descriptor and proves the endpoint it names really refuses a caller without the declared permission. - The plugin settings routes now need a permission; they were telling anyone which modules a site runs and where its components live. - The connection pool is sized explicitly with a short timeout, and the arithmetic against the server's connection ceiling is written next to the worker count instead of being left to defaults nobody chose. - Fleet alerts carry both the correct lowercase keys and the old camelCase pair. Trays older than 1.7.0 match keys case-sensitively and would have gone quiet on every PC not yet upgraded; the old pair goes when they are. - The dev server proxy points at localhost again, with environment variables for the remote-box workflow, and one developer's address is out of the server certificate page.
138 lines
5.9 KiB
Plaintext
138 lines
5.9 KiB
Plaintext
# 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.
|
|
#
|
|
# See the DEPLOY wiki page for the full per-site deployment 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://<user>:<password>@<host>:<port>/<database>?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
|
|
|
|
# ---- Database connection pool (optional, defaults shown) ----
|
|
# Per WORKER process, so the real ceiling is workers x (size + overflow). See
|
|
# the arithmetic beside --workers in docker-compose.yml before raising either:
|
|
# at the shipped 4 workers this is 4 x (10 + 10) = 80 against the mysql:8.4
|
|
# default max_connections of 151. More workers needs a smaller pool here or a
|
|
# higher --max-connections on the db service.
|
|
# DB_POOL_TIMEOUT is the wait for a free connection. 5s fails fast and
|
|
# legibly; the SQLAlchemy default of 30s turns pool exhaustion into what looks
|
|
# like a dead database.
|
|
DB_POOL_SIZE=10
|
|
DB_MAX_OVERFLOW=10
|
|
DB_POOL_TIMEOUT=5
|
|
|
|
# ---- 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 IIS
|
|
# or nginx serves it under a subpath such as /shopdb. 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 IIS.
|
|
#
|
|
# 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 docker-compose.yml; 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).
|
|
IMAGE_TAG=0.7.0
|
|
|
|
# ---- 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/<pluginname>. The framework checks
|
|
# COLLECTOR_API_KEY_<PLUGINNAME> 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
|
|
#
|
|
# Dev-only: directories whose UNSIGNED plugins are trusted, honored ONLY under
|
|
# DEBUG/TESTING (external-repo/symlink dev). Production ignores this.
|
|
# PLUGIN_DEV_TRUST_DIRS=/home/dev/my-plugin-repo
|
|
#
|
|
# 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 consumed by the notifications plugin and the
|
|
# public shopfloor kiosks. Leave unset if the feature is not used; there is no
|
|
# safe default for the password, so an unset password fails loud.
|
|
# EMPLOYEE_DB_HOST=
|
|
# EMPLOYEE_DB_USER=
|
|
# EMPLOYEE_DB_PASSWORD=
|
|
# EMPLOYEE_DB_NAME=wjf_employees
|
|
|
|
# ---- cmmc_usb database (USB check-in/out) ----
|
|
# Separate read-write MySQL DB used by the USB plugin to track device
|
|
# check-in/out, lockers, and the check-in/out log. Leave unset if the feature
|
|
# is not used; there is no safe default for the password, so an unset password
|
|
# fails loud.
|
|
# CMMC_USB_DB_HOST=
|
|
# CMMC_USB_DB_USER=
|
|
# CMMC_USB_DB_PASSWORD=
|
|
# CMMC_USB_DB_NAME=cmmc_usb
|
|
|
|
# ---- Subpath deployment (optional) ----
|
|
# Serve the app under a URL prefix instead of the server root, e.g. as an IIS
|
|
# Application at /ops under an existing site. The frontend must be rebuilt with
|
|
# the matching base: VITE_BASE_PATH=/ops/ npm run build. Leave unset when the
|
|
# app owns its own site/port (the default). See the INSTALL-WINDOWS-IIS wiki page.
|
|
# MOUNT_PATH=/ops
|