Add personal API tokens; wire measuring tools into remaining surfaces
Some checks failed
CI / naming (push) Has been cancelled
CI / frontend (push) Has been cancelled
CI / backend (push) Has been cancelled

API tokens: any user mints named, optionally-expiring tokens
(shopdb_pat_..., sha256-stored, secret shown once) at Settings > API
Tokens; a before-request shim swaps a valid PAT for a request-scoped
JWT of its owner, so the entire existing auth/authz/import-mode stack
works unchanged and revoked/expired tokens 401 cleanly. Built for
long-running scripts - the legacy import no longer dies when a login
JWT expires. Migration 7d21_apitokens; create/revoke audit-logged.

Audited integration gaps fixed: Asset.to_dict serializes measuring
tools (typedata + pluginid - relationship links to tools resolve); map
subtype filter/colors and MapEditor include them; dashboard totals
count them; warranty links use a new by-asset route; the measuringtools
ADR-010 hooks are real (corrected presentation token, implemented
map-overlay endpoint); the login avatar resolves through the
employee-photo helper.

737 tests pass; naming green; frontend builds; both features verified
live end-to-end.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-07-12 08:33:02 -04:00
parent 64a5abdb08
commit da86b3ae0c
31 changed files with 1197 additions and 38 deletions

View File

@@ -15,9 +15,10 @@ export const useAuthStore = defineStore('auth', {
isAdmin: (state) => state.user?.roles?.includes('admin') || false,
// Full name from the employee directory (falls back to username/SSO).
displayName: (state) => state.user?.directoryname || state.user?.username || '',
// Employee photo URL if the directory has one for this SSO.
avatarUrl: (state) => state.user?.directorypicture
? `/static/employees/${state.user.directorypicture}` : null
// Employee photo URL if the directory has one for this SSO. The directory
// resolver already returns a usable URL (self-hosted upload or external HR
// path), so it is used as-is.
avatarUrl: (state) => state.user?.directoryphotourl || null
},
actions: {
@@ -77,7 +78,7 @@ export const useAuthStore = defineStore('auth', {
const emp = response.data?.data
if (emp) {
this.user.directoryname = `${emp.First_Name || ''} ${emp.Last_Name || ''}`.trim() || null
this.user.directorypicture = emp.Picture || null
this.user.directoryphotourl = emp.photourl || null
}
} catch (err) { /* directory unavailable - fall back to username */ }
}