Multi-site distribution readiness: settings-driven site config, security closeout, release engineering, v0.5.0
Make the app distributable to other GE Aerospace sites (one self-hosted
instance per site, ADR-004). GE values remain the shipped defaults; every
site-specific behavior is now a Setting an admin can change in the UI.
Settings-driven site config:
- Branding: site/QR/badge logos, favicon, primary color (upload endpoints
mirror the map-blueprint pattern; new Settings > Branding section).
- ServiceNow: search/incident/change URL templates ({ticket}), ticket
prefixes, enable toggle. Defaults point at the current
geaerospaceqa.service-now.com global search. Disabled = plain-text tickets.
- Employee-id regex (employeeid_pattern), printer hostname template,
QR label targets (qr_target_printer / qr_target_usb, blank = asset page,
else URL template with placeholders), usb_label_style (barcode|qr).
- West Jefferson floor-plan PNGs removed from the tree; generic placeholder
ships as the map default and sites upload their own blueprint.
Security closeout:
- dashboarddefaults writes now require admin.
- Collector: generic error messages (no str(exc) leak); API key accepted
via X-API-Key header only (BREAKING: querystring api_key removed).
- IP-based login rate limiting (AUTH_RATELIMIT_* knobs) atop account lockout.
- Setting.set() creation race fixed (IntegrityError retry).
Release engineering and docs:
- __version__ 0.5.0 (distinct from __contract_version__, ADR-007),
CHANGELOG.md, Gitea Actions CI config, frontend version aligned.
- One wizard-first install story across README/DEPLOY; new CONFIG.md,
UPGRADE.md, BACKUP-RESTORE.md; CLAUDE.md and ROADMAP de-staled.
- Dockerfile multi-stage build now bundles the frontend; compose binds
MySQL to 127.0.0.1; stale database/schema.sql and one-off SQL removed.
Debt and fixes:
- .query.get() -> db.session.get() sweep; datetime.utcnow() removed
(naive-UTC via timezone-aware now); users.py on authz decorators.
- Fixed 4 stale tests (slides feed shape, shopfloor splitperemployee,
plugin contract purity) and the USB label page field mapping (both usb
modes emit the cmmc shape: device_id/device_desc).
- Health endpoint reports the real version.
248 tests pass; naming/style check green; frontend builds; fresh-DB
flask db upgrade + seeds verified; QR targets verified by decoding
rendered codes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -43,6 +43,13 @@ docker compose build
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
The Docker image builds the Vue frontend in a first stage and copies the
|
||||
compiled SPA into the API image, so `docker compose build` produces a
|
||||
self-contained image with the UI already built. No separate Node step is
|
||||
needed for a container deploy. (For a bare-metal/venv install instead, build
|
||||
the frontend by hand: `cd frontend && npm ci && npm run build`, which writes
|
||||
`frontend/dist/` for Flask to serve.)
|
||||
|
||||
The MySQL container initializes its volume on first run. The API container waits for `db` to be healthy via `healthcheck`. Check logs:
|
||||
|
||||
```bash
|
||||
@@ -67,17 +74,30 @@ CREATE DATABASE shopdb_flask CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
|
||||
`DATABASE_URL` must keep `?charset=utf8mb4` so the connection matches. On MySQL older than 5.7 also enable `innodb_large_prefix=ON` + `innodb_file_format=Barracuda`, or the utf8mb4 indexes exceed the 767-byte prefix limit (error 1071). MySQL 5.7+ and 8.0 need no extra config.
|
||||
|
||||
## Step 4: Seed reference data
|
||||
## Step 4: Seed permissions, settings, and reference data
|
||||
|
||||
Run all three seeders. They are idempotent, so re-running them on an existing
|
||||
database is safe (it adds anything missing and leaves existing rows alone).
|
||||
|
||||
```bash
|
||||
docker compose exec api flask seed permissions
|
||||
docker compose exec api flask seed settings
|
||||
docker compose exec api flask seed reference-data
|
||||
```
|
||||
|
||||
Creates: default `Vendor`, `Location`, `BusinessUnit`, `OperatingSystem`, `AssetStatus`, `RelationshipType` rows seeded with the platform contract values (`partof`, `controls`, `connectedto`).
|
||||
- `seed permissions` - creates the RBAC permission rows and the default roles
|
||||
the app checks with `@require_permission`. Run this before anyone logs in or
|
||||
permission checks have nothing to match.
|
||||
- `seed settings` - writes the default Setting rows (branding, ServiceNow
|
||||
integration, floor-map placeholders, search toggles, site identity). A site
|
||||
overrides these later in Settings or the setup wizard.
|
||||
- `seed reference-data` - creates default `Vendor`, `Location`, `BusinessUnit`,
|
||||
`OperatingSystem`, `AssetStatus`, `RelationshipType` rows seeded with the
|
||||
platform contract values (`partof`, `controls`, `connectedto`).
|
||||
|
||||
## Step 5: Pick plugins to enable
|
||||
|
||||
The image bundles all six plugins (computers, equipment, network, notifications, printers, usb). Only enabled plugins are loaded.
|
||||
The image bundles ten plugins (computers, employees, equipment, knowledgebase, network, notifications, printers, slides, usb, warranty). Only enabled plugins are loaded.
|
||||
|
||||
```bash
|
||||
docker compose exec api flask plugin list
|
||||
@@ -88,7 +108,16 @@ docker compose exec api flask plugin install equipment
|
||||
|
||||
To install a sister-site or third-party plugin (per ADR-003), drop its directory into `<repo>/plugins/<name>/` (the docker-compose mounts this read-only into the container) and run `flask plugin install <name>`.
|
||||
|
||||
## Step 6: Create the admin user
|
||||
## Step 6: Create the first admin (setup wizard)
|
||||
|
||||
The primary path is the first-run setup wizard. Once the stack is up and the DB
|
||||
is seeded, browse to the site (through the reverse proxy configured in Step 7,
|
||||
or directly at the API port during bring-up) and go to `/setup`. The wizard
|
||||
creates the first admin account and captures site identity (facility name,
|
||||
optional logo). It runs only while `setup_complete` is false; after it finishes
|
||||
the route redirects to the app.
|
||||
|
||||
Headless alternative (no browser, e.g. automated provisioning):
|
||||
|
||||
```bash
|
||||
docker compose exec api flask seed admin --username admin --email admin@facility.example.com
|
||||
@@ -129,7 +158,10 @@ Per-site MySQL backups are the site's responsibility. Recommended: nightly `mysq
|
||||
docker compose exec -T db mysqldump -u root -p"${MYSQL_ROOT_PASSWORD}" shopdb_flask | gzip > backup-$(date +%F).sql.gz
|
||||
```
|
||||
|
||||
Verify a restore quarterly.
|
||||
Verify a restore quarterly. Back up the `instance/` directory alongside the DB;
|
||||
it holds uploaded floor plans, branding, `plugins.json`, and tokens that are not
|
||||
in MySQL. See [docs/BACKUP-RESTORE.md](BACKUP-RESTORE.md) for the full backup and
|
||||
restore procedure.
|
||||
|
||||
## Step 9: Updates
|
||||
|
||||
@@ -140,7 +172,7 @@ docker compose up -d api
|
||||
docker compose exec api flask db upgrade
|
||||
```
|
||||
|
||||
The framework's `__contract_version__` may have moved. Check `docs/adr/` for any new ADRs since the last update. If an ADR introduces a breaking change, the upgrade may require coordinated work; the ADR's "Consequences" section documents it.
|
||||
The framework's `__contract_version__` may have moved. Check `docs/adr/` for any new ADRs since the last update. If an ADR introduces a breaking change, the upgrade may require coordinated work; the ADR's "Consequences" section documents it. See [docs/UPGRADE.md](UPGRADE.md) for the full upgrade procedure, including re-seeding and the v0.5+ floor-plan note.
|
||||
|
||||
## Common issues
|
||||
|
||||
@@ -169,4 +201,7 @@ If this returns a 500 or no JSON, the container is unhealthy. Check `docker comp
|
||||
- [docs/adr/ADR-003-plugin-distribution.md](adr/ADR-003-plugin-distribution.md) - bundled vs external plugins
|
||||
- [docs/adr/ADR-006-collector-contract.md](adr/ADR-006-collector-contract.md) - per-plugin collector endpoints
|
||||
- [docs/PLUGIN-QUICKSTART.md](PLUGIN-QUICKSTART.md) - building a custom plugin for your site
|
||||
- [docs/CONFIG.md](CONFIG.md) - every environment variable and every Setting key
|
||||
- [docs/UPGRADE.md](UPGRADE.md) - upgrade procedure for an existing site
|
||||
- [docs/BACKUP-RESTORE.md](BACKUP-RESTORE.md) - backup and restore procedure
|
||||
- [shopdb/config.py](../shopdb/config.py) - all the env-vars in one place
|
||||
|
||||
Reference in New Issue
Block a user