docs: wiki update for API docs, printer installer, geenforce cutover, timezone
Execute WIKI-UPDATE-PLAN.md (14 items): - NEW docs/PRINTER-INSTALLER.md: install-list / pc-default / install-batch contract + public installer map page. - NEW-shape docs/API-REFERENCE.md: index + pointer to the live generated docs (/api/docs Redoc, openapi.json, llms.txt, MCP), replacing a stale full dump. - geenforce cutover + GE-ENFORCE-DISPLAY/CLIENT/DEPLOY: server-first display dispatcher (display-role by FQDN, display-type.txt fallback), dashboarddefaults FQDN keying, legacy kiosk autostart self-heal (Wow6432Node), per-PC-type cutover status. - PLUGINS: printers/slides rows + plugin-permissions note (slides.manage). - IMPORT-API: dashboarddefaults FQDN-first keying. - CONFIG: word-wise search, site_timezone setting. - PILOT-DEPLOY: servers-to-network reclassify step. IMPORT-ADOPTION: fixup note. - CLAUDE.md: test count 1077->1159, HTTPS-cutover state. CHANGELOG: timezone + kiosk-autostart fixes, site_timezone setting.
This commit is contained in:
17
CHANGELOG.md
17
CHANGELOG.md
@@ -10,6 +10,14 @@ ADR-007 and ADR-002.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Configurable site timezone: a `site_timezone` site setting (default
|
||||
`America/New_York`, public-readable), editable in Settings > Site >
|
||||
Localization. Notification start/end times are entered and displayed in this
|
||||
zone, and daily-reset notification expiry is computed in it. A shared
|
||||
`frontend/src/utils/datetime.js` (Intl-based, DST-safe) does the conversion.
|
||||
|
||||
### Changed
|
||||
|
||||
- Asset detail pages (machines, PCs, printers, network devices, measuring
|
||||
@@ -24,6 +32,15 @@ ADR-007 and ADR-002.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Notification start/end times were off by the timezone offset (a 2:34 PM entry
|
||||
displayed as 6:34 PM). Times are now stored UTC and shown/entered in the site
|
||||
timezone; the calendar keys all-day events off the site-local day.
|
||||
- Kiosk displays showed a white screen on login: a legacy 32-bit kiosk
|
||||
installer's autostart kept relaunching Edge at a now-dead URL. The install's
|
||||
HKLM Run value was WOW64-redirected into `SOFTWARE\Wow6432Node\...\Run` and
|
||||
survived earlier cleanup. The `gea-shopfloor-display` dispatcher now purges
|
||||
the legacy autostarts every enforce cycle across both registry views, all
|
||||
user hives, Run/RunOnce/policy-Run, and every Startup folder.
|
||||
- List pages keep the current page (and search term) in the URL query, so
|
||||
paging to page 9, opening an item, and hitting browser Back returns to page 9
|
||||
instead of resetting to page 1. Applies to all 18 list views via a shared
|
||||
|
||||
@@ -44,7 +44,8 @@ Refactor phases 0-5 landed; phase 6 (multi-site distribution readiness) largely
|
||||
|
||||
### Active state
|
||||
|
||||
- 1077 tests, naming/style check green, Gitea Actions CI (backend + naming + frontend build + a lean-build job + a migrations-mysql job that runs the real fresh upgrade on utf8mb4 MySQL 8)
|
||||
- 1159 tests, naming/style check green, Gitea Actions CI (backend + naming + frontend build + a lean-build job + a migrations-mysql job that runs the real fresh upgrade on utf8mb4 MySQL 8)
|
||||
- GE-Enforce HTTPS cutover: the displays/kiosks cohort now fetches manifest + inline payloads entirely over HTTPS (share-less); the `gea-shopfloor-display` scope is authored in code (`plugins/geenforce/seed_display_scope.py`) and published via `seed_display_scope(publish=True)`. Other fleet PC types still enforce from the SMB share and only report. See `docs/geenforce-api-cutover.md`.
|
||||
- `__contract_version__` at 0.15.0 (0.12.0 mailer, 0.13.0 User/Role, 0.14.0 send_webhook, 0.15.0 authorized_service_token) (product `__version__` 0.7.0, tags v0.5.0/v0.6.0/v0.7.0 - distinct series, ADR-007)
|
||||
- 13 bundled plugins all satisfy contract: computers, employees, geenforce, knowledgebase, machines, measuringtools, network, notifications, printedparts, printers, slides, usb, warranty
|
||||
- Core Alembic chain: baseline `68b3947ae14f` -> head `7d26_settings_description_text` (33 core migrations). Each plugin owns its own chain (ADR-008); deploy runs `flask db upgrade` then `flask plugin upgrade-all`. Reproducible + idempotent from empty (env.py relaxes session sql_mode so the chain runs on strict MySQL 8).
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -145,6 +145,14 @@ read back through the API.
|
||||
| `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
|
||||
|
||||
@@ -319,6 +327,11 @@ 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
|
||||
|
||||
@@ -106,6 +106,12 @@ retrofit it. `-NoTask` provisions identity + kit without registering the task.
|
||||
|
||||
Re-running it updates identity/config and re-registers the task in place.
|
||||
|
||||
For PC types that have cut over to HTTPS manifest delivery (currently
|
||||
displays/kiosks), the full server-side setup, auth model (IP allowlist vs
|
||||
ApiToken), and per-PC-type cutover playbook live in `geenforce-api-cutover.md`.
|
||||
This doc covers what gets laid on the PC; that doc covers where the manifest
|
||||
comes from.
|
||||
|
||||
---
|
||||
|
||||
## 5. The engine boundary
|
||||
|
||||
@@ -65,7 +65,17 @@ creates a new version).
|
||||
re-asserts each cycle, but the script is idempotent (it skips relaunch if a
|
||||
kiosk process is already serving the target URL).
|
||||
|
||||
### display-type -> target map
|
||||
### Role resolution: server first, display-type.txt fallback
|
||||
|
||||
The dispatcher first asks the server: `GET
|
||||
/api/dashboarddefaults/display-role?fqdn=<fqdn>` (public, unauthenticated). A row
|
||||
in `dashboarddefaults` keyed by the display's FQDN (IP fallback) wins and returns
|
||||
the role and frontend path directly. Only when the server is unreachable or has
|
||||
no mapping does the dispatcher fall back to the local `display-type.txt` map
|
||||
below. To repurpose a display, edit its `dashboarddefaults` row; the change takes
|
||||
effect on the next enforce cycle.
|
||||
|
||||
Fallback map (local file):
|
||||
|
||||
| display-type.txt | kiosk route | notes |
|
||||
| --- | --- | --- |
|
||||
@@ -73,6 +83,39 @@ creates a new version).
|
||||
| `Lobby` | `/tv` | slides plugin TV dashboard (surface `lobby`) |
|
||||
| `3DPrintRoom` | `/parts-kiosk` | **PLACEHOLDER, TODO-confirm** printedparts parts kiosk route; confirm the real 3D-print-room target with the floor team before publishing to production displays |
|
||||
|
||||
### Dashboard-defaults FQDN keying
|
||||
|
||||
`dashboarddefaults` rows were historically keyed by IP. Migration
|
||||
`7d31_dashboarddefault_fqdn` added an `fqdn` column; resolution is now FQDN-first
|
||||
with IP as fallback (`_resolve_default` in
|
||||
`shopdb/core/api/dashboarddefaults.py`). FQDNs are stored lowercase. This
|
||||
survives DHCP churn on kiosk subnets. `POST /api/dashboarddefaults` accepts
|
||||
`fqdn`, `ipaddress`, `displayrole` (`dashboard`|`lobby`|`partskiosk`),
|
||||
`businessunitid`, and `description`; `displaypath` is not stored but derived from
|
||||
the role (`DISPLAY_ROLE_PATHS`). Two public read endpoints consume it:
|
||||
`/api/dashboarddefaults/display-role` (dispatcher) and
|
||||
`/api/dashboarddefaults/visitor-location` (lobby business-unit lookup). The
|
||||
server derives a display's FQDN from its reported BIOS serial as
|
||||
`F<serial>.<domain>` (`derive_display_fqdn`, domain from the `display_fqdn_domain`
|
||||
setting); the dispatcher in `plugins/geenforce/seed_display_scope.py` builds the
|
||||
same FQDN client-side for its lookup.
|
||||
|
||||
### Legacy autostart self-heal
|
||||
|
||||
The dispatcher also cleans up after the old GE Aerospace Dashboard / Lobby
|
||||
Display Inno installers, which planted autostarts (a Public-Desktop `.lnk`, an
|
||||
all-users Startup `.lnk`, and an `HKLM ...\CurrentVersion\Run` value) that
|
||||
relaunch Edge at now-dead URLs (`/shopfloor-dashboard/`, `/tv-dashboard/`) and
|
||||
white-screen. The 32-bit installer's Run value was WOW64-redirected into
|
||||
`Wow6432Node`, which is why it survived earlier cleanup. Every enforce cycle the
|
||||
dispatcher sweeps both registry views, all loaded user hives, Run/RunOnce/policy
|
||||
Run keys, and every per-user and common Startup folder, matching by legacy name
|
||||
and by the old URLs, then kills any old-URL Edge. The kiosk shortcut it writes is
|
||||
a direct Edge shortcut (no launcher or VBS). The fix ships by re-publishing this
|
||||
code-authored scope (`seed_display_scope(publish=True)`), not an import-share.
|
||||
`pxe-images/github/find-legacy-kiosk-autostart.ps1` is a read-only locator for
|
||||
stragglers.
|
||||
|
||||
## Self-sufficient: displays do NOT inherit common
|
||||
|
||||
The `gea-shopfloor-display` scope carries everything a display enforces. It does
|
||||
|
||||
@@ -288,8 +288,15 @@ The engine sources the manifest and reports results using the reference kit in
|
||||
(BaseUrl + a `geenforce.fetch`/`geenforce.report` service token). See
|
||||
`docs/GE-ENFORCE-CLIENT.md` for the fetch/report contract, the last-known-good
|
||||
cache, shadow mode, and the staged cutover from share-sourced to shopdb-sourced
|
||||
manifests. Until that cutover, the client only REPORTS; the manifest still comes
|
||||
from the share via Export to Share (4.2).
|
||||
manifests.
|
||||
|
||||
The cutover from share-sourced to shopdb-sourced manifests is per PC type. The
|
||||
**displays/kiosks cohort has cut over**: share-less display PCs fetch their
|
||||
manifest and payloads entirely over HTTPS (see `docs/geenforce-api-cutover.md`
|
||||
and `docs/GE-ENFORCE-DISPLAY.md`). All other fleet PC types (cmm, collections,
|
||||
keyence, genspect, heattreat, partmarker, nocollections, common) still enforce
|
||||
from the SFLD SMB share via Export to Share (4.2) and only REPORT to shopdb. The
|
||||
playbook for moving the next PC type is `geenforce-api-cutover.md` section 11.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -25,6 +25,11 @@ implementation #1. Read it alongside this guide.
|
||||
- `run.py` - ordered `stage_*` functions. Each reads a slice of the source,
|
||||
POSTs it, and records the crosswalk later stages resolve foreign keys against.
|
||||
|
||||
Post-import fixups that re-point existing assets (example:
|
||||
`scripts/reclassify_servers_to_network.py`, servers imported as PCs moved to
|
||||
network devices in place) belong in the site loader's verify stage, not in the
|
||||
stable API layer.
|
||||
|
||||
### Stage order matters
|
||||
|
||||
Reference/lookup tables first (so foreign keys resolve), then the entity hub,
|
||||
|
||||
@@ -234,10 +234,13 @@ upload them after import via `POST /api/models/<modelid>/image` (multipart
|
||||
| `pctype` | `POST /api/computers/types` | `typename` -> `computertype`, `description` | `computertype` |
|
||||
| `subnettypes` | (see subnets) | used as `subnettype` string on subnets | - |
|
||||
| `subnets` | `POST /api/network/subnets` | `cidr`, `description` -> `name`/`description`, `vlan` -> create VLAN first (`POST /api/network/vlans`) then `vlanid`, `subnettypeid` -> `subnettype` name | `cidr` |
|
||||
| `dashboarddefaults` | `POST /api/dashboarddefaults` | `ipaddress` -> `ipaddress`, `businessunitid` (remapped), `description` | `ipaddress` |
|
||||
| `dashboarddefaults` | `POST /api/dashboarddefaults` | `fqdn` (preferred key, stored lowercase), `ipaddress` (fallback key), `displayrole` (`dashboard`/`lobby`/`partskiosk`), `businessunitid` (remapped; only the `dashboard` role uses it), `description` | `fqdn`, else `ipaddress` |
|
||||
| `controllertypes` | remap into `vendors` + `models` | e.g. "Fanuc" -> a Vendor; the controller model -> a Model; then set `controllervendorid`/`controllermodelid` on the machine | - |
|
||||
| `comstypes` | `communicationtypes` (seeded, no API) | ensure `flask seed reference-data` created IP/Serial/USB/... before importing comms | - |
|
||||
|
||||
Resolution at runtime is FQDN-first with IP fallback (migration
|
||||
`7d31_dashboarddefault_fqdn`); import both when the legacy source has them.
|
||||
|
||||
Note on communication types: the classic `comstypes.typename` values
|
||||
(IP, Serial, Network_Interface, USB, Parallel, VNC, FTP, DNC) correspond to the
|
||||
seeded `communicationtypes.comtype`. They are created by the reference-data seed,
|
||||
|
||||
@@ -80,12 +80,28 @@ import API. It is site glue, not product code.
|
||||
printers -> dependents -> relationships -> subnets -> usb -> verify). It is
|
||||
idempotent - a crashed run resumes from `idmap.json`.
|
||||
|
||||
3. Reclassify servers into network devices. The classic DB stored servers as
|
||||
PCs, so the import lands them as `computer` assets. Re-point them in place:
|
||||
|
||||
```
|
||||
DATABASE_URL=... venv/bin/python scripts/reclassify_servers_to_network.py # dry run, prints matches
|
||||
DATABASE_URL=... venv/bin/python scripts/reclassify_servers_to_network.py --commit # apply
|
||||
# match on an exact computer type instead of the SVR- name prefix:
|
||||
... --type "Server" --commit
|
||||
```
|
||||
|
||||
The assetid does not change: communications, relationships, map position, and
|
||||
audit history carry over. Only the extension row is swapped (computers ->
|
||||
networkdevices) and the asset type flipped; reclassified devices get the
|
||||
`Server` networkdevicetype. Run the dry run, eyeball the list, then commit.
|
||||
Re-running is safe (already-moved assets no longer match).
|
||||
|
||||
Expected magnitude (from the WJ dumps used in development - your fresh dumps will
|
||||
differ slightly):
|
||||
|
||||
| entity | count |
|
||||
|---|---|
|
||||
| assets | ~983 (computer ~663, machine ~76, network ~58, measuring-tool ~136, printer ~50) |
|
||||
| assets [*] | ~983 (computer ~663, machine ~76, network ~58, measuring-tool ~136, printer ~50) |
|
||||
| locations | ~24 |
|
||||
| employees | ~415 |
|
||||
| installs | ~850 |
|
||||
@@ -97,6 +113,16 @@ differ slightly):
|
||||
| subnets | ~37 |
|
||||
| USB devices / events | ~18 / ~232 |
|
||||
|
||||
[*] Counts taken AFTER `scripts/reclassify_servers_to_network.py --commit`.
|
||||
Servers imported as computers are re-pointed to network devices, so the computer
|
||||
count drops and network rises by the same amount versus a raw import.
|
||||
|
||||
> PLACEHOLDER - re-measure before publishing. The computer/network split shown
|
||||
> in the assets row above still reflects a RAW import (pre-reclassify). Re-run
|
||||
> the counts on the current prodscratch AFTER the reclassify step above and drop
|
||||
> in the actual numbers; do not carry these development figures forward as if
|
||||
> they already account for the reclassify.
|
||||
|
||||
The `verify` stage prints a source-vs-target row-count audit; the gaps are the
|
||||
documented skips (inactive rows, duplicate machinenumbers, LocationOnly, the
|
||||
9999 placeholder).
|
||||
|
||||
@@ -10,7 +10,7 @@ These plugins are in `plugins/` in this repo. Enable per site with `flask plugin
|
||||
|--------|--------|-------|
|
||||
| `machines` | Manufacturing machinery: 5-axis mills, lathes, broachers, heat treatment ovens | Manually entered. See [ADR-005](adr/ADR-005-equipment-vs-measuringtools.md). Subtype tables for FOCAS / CLM / MTConnect controller protocols (planned). |
|
||||
| `computers` | Shop-floor PCs and engineering workstations | Fed by the PXE pipeline collector per [ADR-006](adr/ADR-006-collector-contract.md). |
|
||||
| `printers` | Network and shop-floor printers | Optional Zabbix integration for supply tracking. Legacy `PrinterData` retiring per ADR-001. |
|
||||
| `printers` | Network and shop-floor printers | Public installer map page + fleet install contract (`/api/printers/install-list`, `/pc-default`, `/install-batch`; see [PRINTER-INSTALLER.md](PRINTER-INSTALLER.md)). Optional Zabbix integration for supply tracking. Legacy `PrinterData` retiring per ADR-001. |
|
||||
| `network` | Switches, routers, access points, IDFs as locations | Asset-only; cleanest of the bundled set. |
|
||||
| `usb` | USB devices issued to shop-floor users | Lightweight checkout / check-in. |
|
||||
| `notifications` | Shop-floor notifications, recognitions, kiosk feed | Used by `ShopfloorDashboard.vue`. |
|
||||
@@ -19,9 +19,16 @@ These plugins are in `plugins/` in this repo. Enable per site with `flask plugin
|
||||
| `geenforce` | GE-Enforce manifest store: imaging PC-type scopes and their install manifests (apps, scripts, files, registry, version gates) | Per [ADR-012](adr/ADR-012-geenforce-manifest-ownership.md). Served to the GE-Enforce client as JSON. Requires GE-Enforce lib >= 2.6 on target PCs. Ships `default_enabled: false`. |
|
||||
| `knowledgebase` | Knowledge Base articles linking to external resources | Lightweight article store. |
|
||||
| `printedparts` | 3D-printed parts inventory | Kiosk checkout / check-in. Ships `default_enabled: false`. |
|
||||
| `slides` | Slides for the lobby display and shop-floor screensaver | Upload / reorder / delete per surface. |
|
||||
| `slides` | Slides for the lobby display and shop-floor screensaver | Upload / reorder / delete per surface. Management gated on the `slides.manage` permission, grantable to non-admin curators; display routes are public. |
|
||||
| `warranty` | Asset warranty tracking | Manual entry now, Dell / Lenovo / HP provider lookups later. Derived coverage status with report buckets. |
|
||||
|
||||
## Plugin permissions
|
||||
|
||||
Plugins may register their own permissions (for example `slides.manage`). Admins
|
||||
implicitly hold them; grant them to specific roles or users to delegate curation
|
||||
without admin. Each plugin's registered permissions appear in its `plugin.py`
|
||||
`get_permissions()`.
|
||||
|
||||
## Building your own
|
||||
|
||||
Guides:
|
||||
|
||||
104
docs/PRINTER-INSTALLER.md
Normal file
104
docs/PRINTER-INSTALLER.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Printer installer map and install endpoints
|
||||
|
||||
How the shop-floor fleet installs network printers from shopdb-flask, replacing
|
||||
the classic ASP `apiprinters.asp` / `apipcdefaultprinter.asp` / `installprinter.asp`
|
||||
contract. Shopfloor 2.0 PCs cannot run unsigned `.bat` maps, so a signed
|
||||
installer EXE (and the public web map page) drives installs from three endpoints
|
||||
in the printers plugin.
|
||||
|
||||
- Server code: `plugins/printers/api/asset_routes.py`
|
||||
(`printer_install_list`, `pc_default_printer`, `printer_install_batch`)
|
||||
- Consumed as a fleet manifest entry: the `common` scope's `printer map`
|
||||
entry (see `GE-ENFORCE-DISPLAY.md`).
|
||||
|
||||
All three endpoints are `@jwt_required(optional=True)`: an anonymous fleet
|
||||
client works, and a logged-in browser (the public map page) works too.
|
||||
|
||||
---
|
||||
|
||||
## 1. The public map page
|
||||
|
||||
`PrinterInstallerMap` is a public (no-login) frontend page: the floor map with
|
||||
printer hotspots positioned at each printer's `mapx` / `mapy`. The user clicks
|
||||
the printers they want, and the page requests an install batch. The PC's default
|
||||
printer is preselected via `pc-default`.
|
||||
|
||||
---
|
||||
|
||||
## 2. `GET /api/printers/install-list`
|
||||
|
||||
Flat, unpaginated list of active NETWORK printers. A printer counts as network
|
||||
only if it has a hostname or a non-USB IP; USB-only printers are excluded.
|
||||
|
||||
Fields per row:
|
||||
|
||||
| Field | Notes |
|
||||
|---|---|
|
||||
| `printerid` | Printer id (the token `install-batch` takes). |
|
||||
| `name` | Asset name, else asset number. |
|
||||
| `machinenumber` | The asset number. |
|
||||
| `windowsname` | Standardized Windows printer name. |
|
||||
| `sharename` | Share / CSF name. |
|
||||
| `hostname` | Print-queue host. |
|
||||
| `ipaddress` | Primary IP (falls back to any communication row). |
|
||||
| `vendorname` | Direct vendor, else the model's vendor. |
|
||||
| `modelnumber` | Model name. |
|
||||
| `installpath` | Installer path for this printer (see install-batch). |
|
||||
| `iscsf` | CSF flag. |
|
||||
| `locationname` | Location name, if the asset has one. |
|
||||
| `mapx` / `mapy` | Floor-map hotspot position. |
|
||||
|
||||
`?format=text` returns a pipe-delimited line per printer, one printer per line,
|
||||
with a fixed field order so the Inno / Pascal installer does a `split()` instead
|
||||
of parsing JSON:
|
||||
|
||||
```
|
||||
printerid|windowsname|vendorname|modelnumber|hostname|ipaddress|mapx|mapy
|
||||
```
|
||||
|
||||
Any pipe or newline inside a value is neutralized to a space so the field count
|
||||
stays fixed. The web map uses the default JSON.
|
||||
|
||||
---
|
||||
|
||||
## 3. `GET /api/printers/pc-default?machine=NNNN`
|
||||
|
||||
The PC's default printer, by machine (asset) number persisted at PXE enrollment.
|
||||
Parity with classic `apipcdefaultprinter.asp`: the installer preselects a PC's
|
||||
default-printer hotspot on the site-map wizard. The link is resolved through the
|
||||
`defaultprinter` asset relationship (PC asset -> printer asset), so it stays
|
||||
inside the contract surface (no cross-plugin model import).
|
||||
|
||||
Returns `{printerid, windowsname}`, or `{}` when the machine is unknown or has no
|
||||
active default printer set.
|
||||
|
||||
`?format=text` returns one pipe-delimited line (`printerid|windowsname`), or an
|
||||
EMPTY body when there is no default (so the installer's split yields nothing).
|
||||
|
||||
---
|
||||
|
||||
## 4. `GET /api/printers/install-batch?printerids=1,2,3`
|
||||
|
||||
Returns a self-deleting Windows `.bat` that installs the selected printers,
|
||||
grouped the same way classic `installprinter.asp` grouped them:
|
||||
|
||||
- HP / Xerox: one universal `PrinterInstaller.exe /PRINTER="a,b,c"` call.
|
||||
- Printers with a `.exe` `installpath`: a PowerShell `Invoke-WebRequest` download
|
||||
(using the caller's Windows credentials, against the site base URL + the
|
||||
IIS-served `/installers` folder) followed by running it `/SILENT`.
|
||||
- No `installpath`, or a non-`.exe` payload (e.g. a `.zip`): listed as a manual
|
||||
install rather than run blindly.
|
||||
|
||||
`printerids` is required, comma-separated; non-numeric tokens are ignored. An
|
||||
empty / missing list is a validation error.
|
||||
|
||||
The install name preference is `windowsname`, else `sharename`, else the asset
|
||||
name / number.
|
||||
|
||||
---
|
||||
|
||||
## 5. Fleet wiring
|
||||
|
||||
The `common` scope's `printer map` manifest entry (see `GE-ENFORCE-DISPLAY.md`)
|
||||
lays down the signed installer that consumes these endpoints. The web map page
|
||||
covers the same install flow for a human at a browser.
|
||||
@@ -183,6 +183,40 @@ No full doc for [E]; this note plus a CHANGELOG line covers it.
|
||||
|
||||
---
|
||||
|
||||
## 12. docs/CONFIG.md - UPDATE (site timezone) + note in notifications coverage
|
||||
|
||||
**Action: update.** New `site_timezone` site setting (default `America/New_York`, public), added in `shopdb/core/api/settings.py` (`build_default_settings`, category `site`) and surfaced in `frontend/src/views/settings/SiteSettings.vue` (Localization group, common-zone dropdown). Add to the site-settings section of CONFIG.md:
|
||||
|
||||
> `site_timezone` (site, default `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. Stored/served UTC; the frontend converts via `frontend/src/utils/datetime.js` (Intl-based, DST-safe).
|
||||
|
||||
Also add a CHANGELOG line: notification times are now timezone-correct (stored UTC, shown in `site_timezone`); fixes the prior offset bug where a 2:34 PM entry displayed as 6:34 PM.
|
||||
|
||||
---
|
||||
|
||||
## 13. docs/geenforce-api-cutover.md + GE-ENFORCE-DISPLAY.md - UPDATE (legacy kiosk autostart purge)
|
||||
|
||||
**Action: update both.** The display dispatcher now self-heals the legacy kiosk autostarts the OLD Dashboard/Lobby Inno installers left behind. Add a subsection to the dispatcher coverage in `geenforce-api-cutover.md` (near the dispatcher pattern) and a short note in `GE-ENFORCE-DISPLAY.md`:
|
||||
|
||||
> ### Legacy autostart self-heal
|
||||
>
|
||||
> The old GE Aerospace Dashboard / Lobby Display Inno installers planted three autostarts each: a Public-Desktop `.lnk`, an all-users Startup `.lnk`, and an `HKLM ...\CurrentVersion\Run` value, all launching Edge at now-dead URLs (`/shopfloor-dashboard/`, `/tv-dashboard/`) which 404 to a white screen. Because those installers were 32-bit, the Run value was WOW64-redirected into `HKLM\SOFTWARE\Wow6432Node\...\Run`, invisible to 64-bit tooling - the reason it survived earlier cleanup. The dispatcher (`build_dispatcher_script` in `seed_display_scope.py`) now sweeps, every enforce cycle: both the native and Wow6432Node registry views, every loaded user hive (HKU), Run + RunOnce + Policies\Explorer\Run, matching by the legacy value names AND by any value pointing at the old URLs; plus every per-user and common Startup folder; then kills any running old-URL Edge. A read-only locator, `pxe-images/github/find-legacy-kiosk-autostart.ps1`, hunts all these locations (and Edge startup-URL policy, scheduled tasks, Assigned Access) when a straggler persists.
|
||||
|
||||
Note that the kiosk shortcut is a direct Edge shortcut (no launcher/VBS) and that the fix ships by re-publishing the code-authored `gea-shopfloor-display` scope (`seed_display_scope(publish=True)`), not an import-share.
|
||||
|
||||
---
|
||||
|
||||
## 14. docs/API-REFERENCE.md - CREATE (revise item 11 scope: link the LIVE docs)
|
||||
|
||||
**Action: supersedes item 11's "hand-written index only" framing.** The repo now ships generated, hosted API docs, so API-REFERENCE.md is primarily a pointer to those plus the audience/auth index:
|
||||
|
||||
- **Live interactive spec:** `GET /api/docs` (self-hosted Redoc), raw spec at `GET /api/docs/openapi.json` (OpenAPI 3.1, ~238 paths / ~362 ops, generated by `scripts/gen_openapi.py` from `docs/api-inventory.json`).
|
||||
- **LLM/agent entry point:** `GET /api/docs/llms.txt` (llms.txt convention) and the read-only MCP server (`mcp/shopdb_mcp.py`, `FastMCP.from_openapi`; set up on a work PC via `pxe-images/github/setup-mcp.cmd`).
|
||||
- Keep item 11's audience tables (fleet/client contracts, import API, core UI API) as the human index, each row linking the owning contract doc. State at top: interactive/full shapes live at `/api/docs`; contract docs hold the deep semantics; this page answers "what exists, who calls it, what auth".
|
||||
|
||||
Cross-link from PLUGINS.md header, DEPLOY.md security section, and `docs/llms.txt` provenance. The `docs/` blueprint lives in `shopdb/core/api/docs.py` (vendored Redoc under `staticdocs/`); mention it is a core blueprint always mounted.
|
||||
|
||||
---
|
||||
|
||||
## Out of scope for the wiki but flagged to the maintainer
|
||||
|
||||
- CLAUDE.md drift (test count 1077 vs 1159, phantom "lean-build job", 2026-07-13 "Current state" missing the entire HTTPS-cutover arc) is repo-doc, not wiki, but should ride the same commit.
|
||||
|
||||
@@ -335,18 +335,51 @@ entry that got reinstalled). `Always`/no-detection entries install every cycle
|
||||
by design and are not flagged, so the server-side status derivation
|
||||
(`service.record_enforcement_report`: failed > selfhealed > ok) stays honest.
|
||||
|
||||
### The display-type.txt dispatcher pattern
|
||||
### The display dispatcher: server-resolved role, file fallback
|
||||
|
||||
For kiosks, per-subtype behavior does not fork the scope: ONE scope
|
||||
(`gea-shopfloor-display`), one inline dispatcher entry that reads
|
||||
`C:\Enrollment\display-type.txt` at enforce time and acts on it. Subtype
|
||||
changes are a one-file edit plus the next 15-min cycle, and retargeting a
|
||||
subtype is a table edit in `seed_display_scope.py` + republish. The dispatcher
|
||||
writes an all-users Startup shortcut (never Start-Process - see gotchas), and
|
||||
sweeps stale kiosk launchers first (its own `ShopDB Kiosk*.lnk`, the imaging
|
||||
installers' `GE Aerospace Dashboard*`/`GE Aerospace Lobby*` shortcuts, any
|
||||
browser .lnk with `-kiosk`-ish args or shopdb URLs, and .url files pointing at
|
||||
the kiosk routes).
|
||||
(`gea-shopfloor-display`), one inline dispatcher entry (built by
|
||||
`plugins/geenforce/seed_display_scope.py`) that resolves what the display should
|
||||
show at enforce time, in two steps:
|
||||
|
||||
1. **Server (authoritative):** `GET
|
||||
$KioskBaseUrl/api/dashboarddefaults/display-role?fqdn=<own-fqdn>`. This is a
|
||||
PUBLIC endpoint (no token). The server matches the FQDN against the
|
||||
`dashboarddefaults` table (IP fallback) and returns `{role, path,
|
||||
businessunitid, businessunit}`. Roles: `dashboard`, `lobby`, `partskiosk`.
|
||||
Changing a display's job is now a server-side edit; no touch on the PC.
|
||||
2. **Fallback (offline, or unmapped):** the local
|
||||
`C:\Enrollment\display-type.txt` value against the `DISPLAY_TYPE_TARGETS` map
|
||||
baked into the script. If neither resolves, the dispatcher logs and
|
||||
configures nothing.
|
||||
|
||||
The FQDN is built as `F<BIOS serial>.<domain>` (GE device naming); the domain
|
||||
comes from HKLM `DisplayFqdnDomain` or the built-in default
|
||||
(`device.geaerospace.net`). `DetectionMethod = Always`, but the script is
|
||||
idempotent: it rewrites the all-users Startup shortcut (never Start-Process -
|
||||
see gotchas) only when the resolved target changed.
|
||||
|
||||
### Legacy autostart self-heal
|
||||
|
||||
The old GE Aerospace Dashboard / Lobby Display Inno installers planted three
|
||||
autostarts each: a Public-Desktop `.lnk`, an all-users Startup `.lnk`, and an
|
||||
`HKLM ...\CurrentVersion\Run` value, all launching Edge at now-dead URLs
|
||||
(`/shopfloor-dashboard/`, `/tv-dashboard/`) which 404 to a white screen. Because
|
||||
those installers were 32-bit, the Run value was WOW64-redirected into
|
||||
`HKLM\SOFTWARE\Wow6432Node\...\Run`, invisible to 64-bit tooling - the reason it
|
||||
survived earlier cleanup. The dispatcher (`build_dispatcher_script` in
|
||||
`seed_display_scope.py`) now sweeps, every enforce cycle: both the native and
|
||||
Wow6432Node registry views, every loaded user hive (HKU), Run + RunOnce +
|
||||
Policies\Explorer\Run, matching by the legacy value names AND by any value
|
||||
pointing at the old URLs; plus every per-user and common Startup folder; then
|
||||
kills any running old-URL Edge. A read-only locator,
|
||||
`pxe-images/github/find-legacy-kiosk-autostart.ps1`, hunts all these locations
|
||||
(and Edge startup-URL policy, scheduled tasks, Assigned Access) when a straggler
|
||||
persists.
|
||||
|
||||
The kiosk shortcut is a direct Edge shortcut (no launcher/VBS); the fix ships by
|
||||
re-publishing the code-authored `gea-shopfloor-display` scope
|
||||
(`seed_display_scope(publish=True)`), not an import-share.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user