fix(server): seed-admin self-heals the admin role + signs a ledger event
Field failure on park-buzi: reset-db --users wipes the roles table and points to seed-admin — which inserted the user with roleId "admin" without recreating the role row (migration 0007 never re-runs), dying on the role_id FOREIGN KEY. The script now upserts the built-in admin role first (the row alone suffices — admin permissions resolve in code). It also appends a SIGNED config_change (admin.passwordReset / admin.seeded, operator console:seed-admin) via the server's compiled EventLog + signer: a console seed/reset by the Linux admin can't be gated by the app, but it stays attributable in the chain. Best-effort — no build/signing key warns loudly and proceeds (locking an admin out to protect an audit line would invert the priority). Both paths verified against a scratch DB reproducing the post-reset state. Runbook: appliance-provisioning §7e — lost app-admin password reset via FORCE=1 (interactive preferred; sessions not revoked → rotate JWT_SECRET if theft suspected); §7d notes the FK failure + self-heal. Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
+45
@@ -2414,3 +2414,48 @@ printer routing is role + failoverRank (printer-routing.ts). Wizard now skips th
|
||||
hides the "Cilën barrierë shërben kjo pajisje?" panel, and stops persisting the binding for
|
||||
printers (a stale pre-fix binding drops off on next edit); the device list shows the printer's
|
||||
ROLE instead of a bogus amber "unbound". Server never required it (no validation change).
|
||||
|
||||
## [2026-07-06] update | Tariff Lab: fee breakdown — "how is this sum produced"
|
||||
|
||||
Operator: a lab outcome of "ALL 740 / 3h 2m" gave no derivation. Added explainFee to
|
||||
@parking/shared: the SAME computeFee walk with an optional trace collector (zero fee change —
|
||||
golden V1 regression still green), so Σ line items ≡ the amount by construction. Items: banded
|
||||
same-price increment runs (time window · N × unit · card name), window-package occurrences,
|
||||
stepped day totals (top-tier repeat flagged), daily-cap clamps as NEGATIVE adjustments, entry
|
||||
grace. /api/tariff/simulate returns `breakdown` (null when settled); the lab's Outcome panel
|
||||
renders it as a lined table with the rounding note (raw min → billed min at the increment) and a
|
||||
total row. 4 new engine tests pin the sum invariant + item shapes. This also largely delivers the
|
||||
wiki's open "composer price preview" item — see [[tariff]].
|
||||
|
||||
## [2026-07-06] update | Composer: increment-unit price labels + ≠60 warning (the 60→10 trap)
|
||||
|
||||
Operator walked into the sharp edge the wiki flat-rate warning had already named: ladder/flat
|
||||
prices are PER BILLING INCREMENT, so changing "Intervali i faturimit" 60→10 silently multiplies
|
||||
every price ×6, while the price header just said "Çmimi / interval". Composer now: price labels
|
||||
are DYNAMIC ("Çmimi / orë" at 60, "Çmimi / {{N}} min" otherwise — same for the flat-mode radio),
|
||||
and an amber warning appears whenever the increment ≠ 60 ("çdo çmim faturohet për çdo N minuta,
|
||||
JO për orë"). Band DURATIONS stay in hours — they're real wall time, increment-independent (the
|
||||
operator asked if "orë" there was wrong; it isn't). See [[tariff]] (§increment).
|
||||
|
||||
## [2026-07-06] update | UI-wide date standard ("25 Qer") + currency-scaled composer examples
|
||||
|
||||
Two operator UX complaints. (1) Dates were a mix of browser-locale "7/6/2026" (raw
|
||||
toLocaleString) and catalog "25 Qershor" — unified: formatDate/formatDateTime/formatClock in
|
||||
lib/format.ts using new common.monthsShort ("25 Qer 14:30", year only when ≠ current, 24h clock);
|
||||
formatRelativeDateTime switched to short months; ALL ~20 raw toLocale* date call sites swept
|
||||
(shifts, subs, plans, drawer, snapshots, device footer, event detail, tariff composer + lab incl.
|
||||
the fee-breakdown row times). Number toLocaleString (thousand separators on money) untouched.
|
||||
(2) Composer example defaults were euro-scaled ("2.00"/hour ≈ 2 lekë) — now currency-aware
|
||||
(ALL: 200/100 ladder, 200/500 steps, 2000 lost ticket; EUR/USD keep 2/1/2/5/20), threaded through
|
||||
emptyForm/emptyLadder/emptyTier/pricingFromCard so a mode switch on an ALL card also shows lek-
|
||||
plausible templates. Blank-form currency stays ALL.
|
||||
|
||||
## [2026-07-06] update | seed-admin signs a ledger event; lost-app-admin-password runbook (§7e)
|
||||
|
||||
Follow-through on the FK fix: seed-admin.mjs now appends a signed config_change
|
||||
(admin.passwordReset / admin.seeded, operator console:seed-admin) via the server's compiled
|
||||
EventLog + signer from dist/ — a console reset by the Linux admin can't gate on the app, but it
|
||||
stays attributable in the chain. Best-effort: no build/key → loud warning, seed still proceeds
|
||||
(verified both paths on a scratch DB). [[appliance-provisioning]] gained §7e: FORCE=1 reset
|
||||
commands (interactive preferred — keeps the password out of shell history), sessions-not-revoked
|
||||
caveat + JWT_SECRET rotation for suspected theft, role-row self-heal note added to §7d.
|
||||
|
||||
Reference in New Issue
Block a user