The 0.11.0 release changed what a map position means and six documents still described the model it replaced. Each of these could have caused a real mistake rather than being merely out of date: - IMPORT-API mapped legacy mapleft/maptop to mapx/mapy with no mention of the level, so a scripted import - including the classic-ASP one still to run against production - would have produced markers the map shows as "level unknown". It now maps levelid too and says how to resolve the default level. - API-REFERENCE enumerates the unauthenticated surface in full, because that is what a deploy reviewer reads, and the three public /api/maplevels reads were missing from it. Also records why the write split is asymmetric: repositioning needs assets.edit, creating a level needs admin, since a level's dimensions are the coordinate space every marker on it is expressed in. - CONFIG still presented the four map_* settings as live, telling the reader to re-upload a blueprint in a settings page that no longer drives the map. They are marked superseded and kept for downgrade. - UPGRADE gained a 0.11.0 section: nothing moves on screen, and replacing a blueprint with one of different dimensions moves every marker on that level, so recalibrate from landmarks rather than editing width and height. - PLUGIN-HOOKS now states that a map overlay keys on assetid and must not return coordinates or a level - a second copy of a position is one that can disagree. Adds FLOOR-MAP.md, the operator's page: loading a plan, placing markers, and what to do when the plan changes, with the reasoning left in ADR-017. START-HERE routes to it from the new-site path, and specifically as the page to read BEFORE a floor plan changes.
362 lines
22 KiB
Markdown
362 lines
22 KiB
Markdown
# Configuration Reference
|
|
|
|
shopdb-flask reads configuration from two places, and the split is deliberate:
|
|
|
|
- **Environment variables** (`.env` / container env) hold **secrets and
|
|
deploy-time wiring**: database credentials, signing keys, CORS origins, ports,
|
|
API keys. These are read once at boot by `shopdb/config.py`. Never put a
|
|
secret in the Settings table.
|
|
- **The Settings table** (seeded by `flask seed settings`, edited in the UI
|
|
under Settings or the setup wizard) holds **site preferences**: branding,
|
|
ServiceNow links, floor-map images, search toggles, facility identity. These
|
|
can change at runtime without a restart and are per-instance.
|
|
|
|
Rule of thumb: if leaking it would be a security incident, it is an environment
|
|
variable. If it is a site preference an admin should be able to change in the
|
|
UI, it is a Setting.
|
|
|
|
---
|
|
|
|
## Part 1: Environment variables
|
|
|
|
Defined in `shopdb/config.py`. Copy `.env.example` to `.env` and fill in
|
|
values. In `production` (`FLASK_ENV=production`), `ProductionConfig.validate()`
|
|
refuses to boot if `SECRET_KEY`, `JWT_SECRET_KEY`, `DATABASE_URL`, or
|
|
`CORS_ORIGINS` are missing or set to the dev defaults.
|
|
|
|
### Flask core
|
|
|
|
| Variable | Required | Default | Notes |
|
|
|----------|----------|---------|-------|
|
|
| `FLASK_APP` | No | `wsgi.py` | Entry point for the `flask` CLI. |
|
|
| `FLASK_ENV` | Yes | `development` | `production` for live sites (triggers `validate()`). Other values: `development`, `testing`. |
|
|
| `SECRET_KEY` | Yes (prod) | dev default | Flask session/signing key. Generate: `python -c "import secrets; print(secrets.token_urlsafe(64))"`. |
|
|
| `JWT_SECRET_KEY` | Yes (prod) | dev default | JWT signing key. Different value from `SECRET_KEY`. |
|
|
| `JWT_ACCESS_TOKEN_EXPIRES` | No | `3600` | Access-token TTL in seconds. |
|
|
| `JWT_REFRESH_TOKEN_EXPIRES` | No | `2592000` | Refresh-token TTL in seconds (30 days). |
|
|
| `CORS_ORIGINS` | Yes (prod) | `http://localhost:5173` | Comma-separated explicit origins. Wildcard `*` is rejected in production. |
|
|
| `LOG_LEVEL` | No | `INFO` | Logging verbosity. |
|
|
|
|
### Database
|
|
|
|
| Variable | Required | Default | Notes |
|
|
|----------|----------|---------|-------|
|
|
| `DATABASE_URL` | Yes (prod) | dev localhost URL | `mysql+pymysql://<user>:<pass>@<host>:<port>/<db>?charset=utf8mb4`. Keep `?charset=utf8mb4`. |
|
|
|
|
### Authentication rate limiting
|
|
|
|
IP-based fixed-window limit on the login endpoint, defense-in-depth atop the
|
|
per-account lockout. Uses the existing cache extension (per-process, so the
|
|
limit is approximate across multiple gunicorn workers).
|
|
|
|
| Variable | Required | Default | Notes |
|
|
|----------|----------|---------|-------|
|
|
| `AUTH_RATELIMIT_ENABLED` | No | `True` | Set `False` to disable (TestingConfig disables it). |
|
|
| `AUTH_RATELIMIT_MAX` | No | `30` | Max login attempts per source IP per window before 429. |
|
|
| `AUTH_RATELIMIT_WINDOW_SECONDS` | No | `300` | Window length in seconds. |
|
|
|
|
### Collector ingest (ADR-006)
|
|
|
|
| Variable | Required | Default | Notes |
|
|
|----------|----------|---------|-------|
|
|
| `COLLECTOR_API_KEY` | No | (empty) | Shared key for `/api/collector/*`. Endpoint fails closed (denies) when unset and no managed token is presented. Sent as the `X-API-Key` header. |
|
|
| `COLLECTOR_API_KEY_<PLUGIN>` | No | (empty) | Per-plugin override, e.g. `COLLECTOR_API_KEY_COMPUTERS`. Checked before the shared key. |
|
|
|
|
The collector endpoints ALSO accept a managed API token (PAT) scoped to the
|
|
`collector.ingest` permission, sent in `X-API-Key` or as an
|
|
`Authorization: Bearer` token. Env keys stay supported as a bootstrap/legacy
|
|
fallback; a managed token is preferred because it is minted, rotated, and
|
|
revoked from Settings > API Tokens with `lastusedat` visibility. A
|
|
collector-scoped token is contained to the collector API and nothing else. See
|
|
`docs/COLLECTOR-INTEGRATION.md` (Managed collector tokens).
|
|
|
|
### Zabbix (printer supply monitoring)
|
|
|
|
| Variable | Required | Default | Notes |
|
|
|----------|----------|---------|-------|
|
|
| `ZABBIX_ENABLED` | No | `false` | Enable the Zabbix integration. |
|
|
| `ZABBIX_URL` | No | (empty) | Zabbix API URL. |
|
|
| `ZABBIX_TOKEN` | No | (empty) | Zabbix API bearer token. |
|
|
|
|
Note: Zabbix can also be configured via the Settings table (`zabbix_enabled`,
|
|
`zabbix_url`, `zabbix_token`). The environment values are the boot-time wiring;
|
|
prefer the Settings entries for runtime changes.
|
|
|
|
### Employee directory database (optional, read-only)
|
|
|
|
Separate HR/employee lookup DB consumed by the notifications plugin and the
|
|
public kiosks. There is no safe default for the password; an unset password
|
|
fails loud rather than trying a guessed credential.
|
|
|
|
| Variable | Required | Default | Notes |
|
|
|----------|----------|---------|-------|
|
|
| `EMPLOYEE_DB_HOST` | No | `localhost` | HR DB host. |
|
|
| `EMPLOYEE_DB_USER` | No | (empty) | HR DB user. |
|
|
| `EMPLOYEE_DB_PASSWORD` | No | (empty) | HR DB password. No safe default. |
|
|
| `EMPLOYEE_DB_NAME` | No | `wjf_employees` | HR DB name. |
|
|
|
|
Only used when `employee_directory_mode` (Setting) is `external`.
|
|
|
|
### CMMC USB database (optional, read-write)
|
|
|
|
Separate MySQL DB used by the USB plugin for check-in/out, lockers, and the log.
|
|
|
|
| Variable | Required | Default | Notes |
|
|
|----------|----------|---------|-------|
|
|
| `CMMC_USB_DB_HOST` | No | `localhost` | USB DB host. |
|
|
| `CMMC_USB_DB_USER` | No | (empty) | USB DB user. |
|
|
| `CMMC_USB_DB_PASSWORD` | No | (empty) | USB DB password. No safe default. |
|
|
| `CMMC_USB_DB_NAME` | No | `cmmc_usb` | USB DB name. |
|
|
|
|
Only used when `usb_directory_mode` (Setting) is `external`.
|
|
|
|
### docker-compose only
|
|
|
|
Read by `docker-compose.yml`, not by the Flask app directly.
|
|
|
|
| Variable | Required | Default | Notes |
|
|
|----------|----------|---------|-------|
|
|
| `MYSQL_ROOT_PASSWORD` | Yes | (none) | Root password for the bundled MySQL container. |
|
|
| `MYSQL_PASSWORD` | Yes | (none) | App-user password; must match the `DATABASE_URL` password. |
|
|
| `MYSQL_PORT` | No | `3306` | Host port for MySQL. Bound to `127.0.0.1` only. |
|
|
| `API_PORT` | No | `5001` | Host port for the API container. |
|
|
|
|
---
|
|
|
|
## Part 2: Settings table keys
|
|
|
|
Seeded by `flask seed settings` (idempotent; re-running adds anything missing).
|
|
Edited in the UI under Settings, or captured in the first-run setup wizard.
|
|
Values are stored as strings and typed by `valuetype`. Secrets in this table
|
|
(anything whose key contains `password`, `token`, or `secret`) are masked when
|
|
read back through the API.
|
|
|
|
### site
|
|
|
|
| Key | Default | Notes |
|
|
|-----|---------|-------|
|
|
| `setup_complete` | `false` | Set true once the first-run wizard finishes; gates the `/setup` route. |
|
|
| `employee_directory_mode` | `selfhosted` | `selfhosted` (tables in this app) or `external` (a separate HR database, see `EMPLOYEE_DB_*`). |
|
|
| `usb_directory_mode` | `selfhosted` | `selfhosted` or `external` (a separate `cmmc_usb` database, see `CMMC_USB_DB_*`). |
|
|
| `site_base_url` | (empty) | Public base URL (scheme + host) for QR codes and absolute links. Blank = use the browsing origin. |
|
|
| `facility_name` | (empty) | Facility name in the dashboard header. Blank = frontend falls back to `ShopDB`. |
|
|
| `pc_access_domain` | `device.geaerospace.net` | Domain appended to a PC hostname for remote-access links. Blank = hostname as-is. |
|
|
| `employeeid_pattern` | `^\d{9}$` | Regex a search term must match to be treated as an employee id. Invalid regex falls back to the default and never 500s. |
|
|
| `printer_hostname_template` | `Printer-{ip}.printer.geaerospace.net` | Printer hostname template. `{ip}` is the dash-separated IP address. |
|
|
| `contact_email_domain` | `geaerospace.com` | Email domain appended to a support contact's SSO to build email (`sso@domain`) and Teams-chat links. Blank hides the contact action buttons. |
|
|
| `dualpath_single_machine` | `true` | Treat a Dualpath pair (a dual-bay machine with one controller) as a single machine in the machines list, dashboard/report counts, and the floor map (the secondary bay is hidden). The data model always keeps both bay records; detail pages stay per-bay with a sibling banner. `false` lists and counts both bays separately. |
|
|
| `site_timezone` | `America/New_York` | IANA timezone for the site. Notification start/end times are entered and displayed in this zone (not the viewer's browser zone), and daily-reset notification expiry (`expirymode=dailytime`) is computed here. Editable in Settings > Site > Localization. Public-readable so kiosks/clients can resolve it. |
|
|
|
|
Notification times are stored and served in UTC; the frontend converts to
|
|
`site_timezone` via `frontend/src/utils/datetime.js` (Intl-based, DST-safe).
|
|
|
|
Change note: notification times are now timezone-correct (stored UTC, shown in
|
|
`site_timezone`); this fixes the prior offset bug where a 2:34 PM entry displayed
|
|
as 6:34 PM.
|
|
|
|
### branding
|
|
|
|
Blank values fall back to the shipped GE default asset so an un-reconfigured
|
|
install still renders. Upload replacements at Settings > Branding, which saves
|
|
them under `instance/branding/`.
|
|
|
|
| Key | Default | Notes |
|
|
|-----|---------|-------|
|
|
| `site_logo` | `/ge-aerospace-logo.svg` | Header and login-page logo. |
|
|
| `qr_logo` | `/ge-monogram.svg` | Logo composited in printer QR labels. Blank = no overlay. |
|
|
| `badge_logo` | `/ge-aerospace-logo.svg` | Logo on the machine badge print page. |
|
|
| `site_favicon` | (empty) | Browser tab favicon. Blank = shipped `/favicon.svg`. |
|
|
| `brand_primary_color` | (empty) | Primary brand color as a CSS color value (maps to `--primary`). Blank = built-in theme color. |
|
|
| `brand_primary_dark_color` | (empty) | Primary hover/active color (maps to `--primary-dark`). Blank = auto-derived by darkening the primary color ~15%. |
|
|
| `brand_accent_color` | (empty) | Accent color for secondary buttons and badges (maps to `--secondary`). Blank = built-in theme color. |
|
|
| `brand_sidebar_color` | (empty) | Sidebar background color (maps to `--sidebar-bg`). Blank = built-in theme color. |
|
|
|
|
### printing
|
|
|
|
| Key | Default | Notes |
|
|
|-----|---------|-------|
|
|
| `qr_target_printer` | (empty) | Custom URL template for printer QR labels. Blank = link to the printer page on this instance. Placeholders: `{printerid}`, `{assetid}`, `{assetnumber}`, `{serialnumber}`, `{ip}`, `{hostname}`. |
|
|
| `qr_target_usb` | (empty) | Custom URL template for USB label QR codes. Blank = link to the USB device page. Placeholders: `{id}`, `{serialnumber}`, `{alias}`. |
|
|
| `usb_label_style` | `barcode` | USB mini-label code style: `barcode` (CODE128 of the serial) or `qr` (QR code linking to the USB QR target). |
|
|
| `qr_target_machine` | (empty) | Custom URL template for machine labels. Blank = link to the machine page. Placeholders: `{assetid}`, `{assetnumber}`, `{serialnumber}`, `{name}`, `{pluginid}`. |
|
|
| `qr_target_computer` | (empty) | Custom URL template for computer labels. Blank = link to the computer page. Placeholders: `{assetid}`, `{assetnumber}`, `{serialnumber}`, `{name}`, `{pluginid}`. |
|
|
| `qr_target_network_device` | (empty) | Custom URL template for network-device labels. Blank = link to the device page. Placeholders: `{assetid}`, `{assetnumber}`, `{serialnumber}`, `{name}`, `{pluginid}`. |
|
|
| `qr_target_measuring_tool` | (empty) | Custom URL template for measuring-tool labels. Blank = link to the tool page. Placeholders: `{assetid}`, `{assetnumber}`, `{serialnumber}`, `{name}`, `{pluginid}`, `{locationcode}`, `{locationname}`. |
|
|
| `label_default_style` | `card` | Default asset-label layout used when a label first opens: `card` (badge with image and identity) or `plain` (just the code and a caption). |
|
|
| `label_default_codetype` | `qr` | Default asset-label code type used when a label first opens: `qr` (QR code) or `barcode` (CODE128). |
|
|
| `label_default_encodes_machine` | `assetnumber` | What a machine label encodes by default. |
|
|
| `label_default_encodes_computer` | `assetpage` | What a computer label encodes by default. |
|
|
| `label_default_encodes_printer` | `assetpage` | What a printer label encodes by default. |
|
|
| `label_default_encodes_network_device` | `assetpage` | What a network-device label encodes by default. |
|
|
| `label_default_encodes_measuring_tool` | `location` | What a measuring-tool label encodes by default. Values across these five: `assetpage`, `assetnumber`, `serialnumber`, `location` (measuring tools only), or `custom`. Overridable on the label page. |
|
|
|
|
The shared asset-label generator lives at `/print/asset-label/<assettype>/<id>` (public, like the other `/print/*` pages; `assettype` is one of `machine`, `computer`, `printer`, `network_device`, `measuring_tool`, and `id` is the asset's plugin id). It can encode the asset page link, the asset number, the serial number, a custom `qr_target_<type>` template, or - for measuring tools by default - the asset's inspection location code (the leading token of the location name, e.g. `0615`). A measuring tool with no location falls back to its asset page.
|
|
|
|
The batch generator at `/print/asset-label-batch/<assettype>` (reached from the "Print Labels" button on each asset list page) lays a multi-selection of one type onto ULINE label sheets: a 6-up 3 in x 3 in format or a dense 72-up mini-label format, with a start-cell offset for reusing partial sheets. It reuses the same code-type and `label_default_encodes_<type>` defaults as the single label.
|
|
|
|
### map
|
|
|
|
**SUPERSEDED as of 0.11.0.** A blueprint and its pixel dimensions are properties
|
|
of a LEVEL now, not of the site (ADR-017), because a site can have more than one
|
|
building and a building more than one floor. Manage them in
|
|
**Settings > Buildings and levels**, or through `/api/maplevels`.
|
|
|
|
These four keys are still present so that downgrading finds the blueprint it had,
|
|
and the levels migration copied their values onto the first level. Editing them
|
|
changes nothing on the map.
|
|
|
|
| Key | Default | Notes |
|
|
|-----|---------|-------|
|
|
| `map_blueprint_light` | `/static/images/floorplan-placeholder.svg` | Superseded by `maplevels.blueprintlight`. Retained for downgrade only. |
|
|
| `map_blueprint_dark` | `/static/images/floorplan-placeholder.svg` | Superseded by `maplevels.blueprintdark`. |
|
|
| `map_width` | `3300` | Superseded by `maplevels.mapwidth`. |
|
|
| `map_height` | `2550` | Superseded by `maplevels.mapheight`. |
|
|
|
|
### integrations
|
|
|
|
| Key | Default | Notes |
|
|
|-----|---------|-------|
|
|
| `servicenow_enabled` | `true` | Enable ServiceNow ticket recognition and links. Disabled = tickets render as plain text. |
|
|
| `servicenow_search_url` | geaerospaceqa.service-now.com global-search template | `{ticket}` is substituted. |
|
|
| `servicenow_ticket_prefixes` | `GEINC,GECHG,GERIT,GESCT` | Comma-separated prefixes recognized as ServiceNow tickets. |
|
|
| `servicenow_incident_url` | geaerospaceqa.service-now.com global-search template | `{ticket}` is substituted. Replace with a direct incident URL if your instance has one. |
|
|
| `servicenow_change_url` | geaerospaceqa.service-now.com global-search template | `{ticket}` is substituted. Replace with a direct change URL if your instance has one. |
|
|
| `zabbix_enabled` | `false` | Enable Zabbix for printer supply monitoring. |
|
|
| `zabbix_url` | (empty) | Zabbix API URL. |
|
|
| `zabbix_token` | (empty) | Zabbix API token (masked). |
|
|
| `warranty_dell_enabled` | `false` | Enable Dell warranty (service-tag) lookups. |
|
|
| `warranty_dell_clientid` | (empty) | Dell TechDirect API client id. |
|
|
| `warranty_dell_clientsecret` | (empty) | Dell TechDirect API client secret (masked). |
|
|
| `warranty_dell_tokenurl` | (empty) | Dell OAuth token URL. Blank = Dell default. |
|
|
| `warranty_dell_apiurl` | (empty) | Dell warranty API URL. Blank = Dell default. |
|
|
|
|
### email
|
|
|
|
| Key | Default | Notes |
|
|
|-----|---------|-------|
|
|
| `smtp_enabled` | `false` | Enable email notifications and alerts. |
|
|
| `smtp_host` | (empty) | SMTP server hostname. |
|
|
| `smtp_port` | `587` | SMTP port (587 TLS, 465 SSL, 25 plain). |
|
|
| `smtp_username` | (empty) | SMTP auth username. |
|
|
| `smtp_password` | (empty) | SMTP auth password (masked). |
|
|
| `smtp_use_tls` | `true` | Use TLS for the SMTP connection. |
|
|
| `smtp_from_address` | (empty) | From address for outgoing email. |
|
|
| `smtp_from_name` | `ShopDB` | From name for outgoing email. |
|
|
| `alert_recipients` | (empty) | Default alert/report recipients (comma-separated). |
|
|
|
|
#### Email flows and delivery model
|
|
|
|
The mail service (`shopdb/utils/mailer.py`, stdlib `smtplib`/`ssl`/`email`
|
|
only) reads the keys above settings-first via the cached settings map, with an
|
|
environment-variable fallback (`SMTP_HOST`, `SMTP_PORT`, `SMTP_USERNAME`,
|
|
`SMTP_PASSWORD`, `SMTP_USE_TLS`, `SMTP_FROM_ADDRESS`, `SMTP_FROM_NAME`,
|
|
`SMTP_ALERT_RECIPIENTS`, `SMTP_ENABLED`) applied only when any `SMTP_*` env var
|
|
is present. When `smtp_enabled` is false or `smtp_host` is blank, every send is
|
|
a graceful no-op that logs a warning and returns without error, so an
|
|
unconfigured site never crashes. The SMTP password is never logged.
|
|
|
|
Three flows use it:
|
|
|
|
- Welcome email. When an admin creates a user (POST `/api/users`), the account
|
|
is flagged `mustchangepassword` and a best-effort welcome email is sent with
|
|
the facility name (`facility_name`), the username, the temporary password,
|
|
and the sign-in link (`site_base_url` + `/login`). Mail is best-effort: the
|
|
user is created even if the send fails (the response carries a `warning`). On
|
|
first login the API returns `mustchangepassword: true`; the frontend forces
|
|
the user through `/change-password` (POST `/api/auth/change-password`) before
|
|
the app. Changing the password clears the flag and resets lockout counters.
|
|
Set `sendwelcome: false` or `mustchangepassword: false` in the create body to
|
|
opt out.
|
|
|
|
- Test email. POST `/api/settings/test-email` (settings.edit) sends a probe to
|
|
the supplied `to` (or `alert_recipients`). The Email / SMTP settings page
|
|
"Send Test Email" button calls it and shows the result; a real SMTP error is
|
|
surfaced with the password scrubbed out.
|
|
|
|
- Alerts and report delivery (on-demand). POST `/api/reports/email`
|
|
(reports.export) takes `{subject, columns, rows, intro?, to?}` and mails the
|
|
rows as an HTML table. Recipients default to `alert_recipients` when `to` is
|
|
omitted, so the same endpoint serves both report delivery and alerts. Report
|
|
pages (Warranty, Toner) carry an "Email report" button that posts the rows
|
|
they already loaded.
|
|
|
|
There is NO scheduler in this app: sending is on-demand. To automate a
|
|
recurring send (e.g. a nightly warranty digest), point an external cron job
|
|
at `/api/reports/email` using an API token (PAT) scoped to `reports.export`.
|
|
See `docs/IMPORT-API.md` for the token model.
|
|
|
|
### audit
|
|
|
|
| Key | Default | Notes |
|
|
|-----|---------|-------|
|
|
| `audit_retention_days` | `90` | Days to retain audit logs (0 = keep forever). |
|
|
|
|
### auth
|
|
|
|
| Key | Default | Notes |
|
|
|-----|---------|-------|
|
|
| `saml_enabled` | `false` | Enable SAML SSO. |
|
|
| `saml_idp_metadata_url` | (empty) | SAML IdP metadata URL. |
|
|
| `saml_entity_id` | (empty) | SAML SP entity id. |
|
|
| `saml_acs_url` | (empty) | SAML Assertion Consumer Service URL. |
|
|
| `saml_allow_local_login` | `true` | Allow local username/password login when SAML is on. |
|
|
| `saml_auto_create_users` | `true` | Auto-create users on first SAML login. |
|
|
| `saml_admin_group` | (empty) | SAML group name that grants the admin role. |
|
|
|
|
**Personal API tokens.** Besides login JWTs and SAML, a user may create
|
|
personal API tokens (PATs) for scripts and integrations, from Settings > API
|
|
Tokens (or `POST /api/apitokens`). A PAT is sent like a JWT
|
|
(`Authorization: Bearer shopdb_pat_...`), authenticates as its owning user
|
|
across the whole API, and does not carry the hourly `JWT_ACCESS_TOKEN_EXPIRES`
|
|
limit (it never expires unless an explicit expiry is set). Only the sha256 hash
|
|
is stored; the secret is shown once at creation. This is the recommended
|
|
credential for long-running imports (see `docs/IMPORT-API.md`). There is no env
|
|
var to configure; PATs are managed entirely through the API/UI.
|
|
|
|
Creating or managing a PAT requires the `apitokens.create` permission (admins
|
|
hold it by default; grant it to other roles from Settings > Users & Roles). By
|
|
default a PAT is unscoped and acts with the full authority of its owner. A PAT
|
|
may optionally carry a scopes list (a subset of the owner's permissions, capped
|
|
at what the owner actually holds): a scoped token grants ONLY those permissions,
|
|
intersected with the owner's live permissions at use time, and suspends the
|
|
admin bypass, so it is denied on role-gated (admin-only) endpoints and on import
|
|
mode. Use an unscoped token for admin-only work and imports.
|
|
|
|
### identifiers (dynamic)
|
|
|
|
One boolean key per asset identifier per asset type, keyed
|
|
`identifier_<name>_<assettype>_enabled` (default `true`). Admins choose which
|
|
optional identifiers show on which asset types. See ADR-001. The exact set is
|
|
generated from `IDENTIFIER_LABELS` x `IDENTIFIER_ASSETTYPES` in
|
|
`shopdb/core/api/settings.py`.
|
|
|
|
### search (dynamic)
|
|
|
|
One boolean key per search domain, keyed `search_<type>_enabled` (default
|
|
`true`). Toggles whether a domain appears in global search results. The set is
|
|
generated from `SEARCH_DOMAINS` in `shopdb/core/api/settings.py`.
|
|
|
|
Search terms are matched word-wise: a multi-word query returns rows containing
|
|
EVERY word, each word anywhere in the searched fields, in any order ("CSF Roles"
|
|
matches a row with "CSF" and "Roles" in different columns). Quoting does not
|
|
force a contiguous phrase.
|
|
|
|
## Custom fields
|
|
|
|
Site-defined extra attributes per asset type (Settings > Custom Fields, table
|
|
`customfields`). Each field has a `searchable` flag (default off). When on, the
|
|
field's stored values are matched by global search and a hit routes to the
|
|
owning asset's detail page. The asset's `search_<type>_enabled` domain toggle
|
|
still applies, so a custom-field hit on a computer only shows when the computer
|
|
search domain is enabled. Inactive or non-searchable fields are never matched.
|
|
|
|
---
|
|
|
|
## See also
|
|
|
|
- [DEPLOY.md](DEPLOY.md) - per-site deployment runbook
|
|
- [UPGRADE.md](UPGRADE.md) - upgrading an existing site
|
|
- [BACKUP-RESTORE.md](BACKUP-RESTORE.md) - backup and restore
|
|
- `shopdb/config.py` - authoritative env-var definitions
|
|
- `shopdb/core/api/settings.py` (`build_default_settings`) - authoritative Setting defaults
|