One person = one identity { label, primary_email (SSO login), alias_emails[],
tech_id, active, kind }. Every email + tech ID resolves to it.
Hub: lib/identity.js (NEW, SOURCE UNIQUE) — resolver + secured endpoints
(/identity/map|resolve read; upsert/alias/merge/delete admin-only). server.js
mounts /identity. auth.getDisplayNameByEmail consults the identity label first
(any alias → full name everywhere). Seed: Louis-Paul Bourdon, Louis Morneau.
SPA: composables/useIdentity + api/identity. IssueDetail dedupes assignment by
identity (m'ajouter + search = same person → no double chip), shows full names,
and hides departed people (active:false) from the picker. IdentityManager.vue
(Settings → Identités tab): edit label/aliases/tech link, merge duplicates,
block/reactivate (departed = non-destructive: hidden from pickers, history kept).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
18 lines
1009 B
JavaScript
18 lines
1009 B
JavaScript
// API identité unifiée — appelle le hub /identity/* (écritures réservées admin côté hub).
|
|
import { HUB_URL as HUB } from 'src/config/hub'
|
|
|
|
async function j (path, init) {
|
|
const r = await fetch(HUB + path, init)
|
|
const d = await r.json().catch(() => ({}))
|
|
if (!r.ok) throw new Error(d.error || ('Identity API ' + r.status))
|
|
return d
|
|
}
|
|
const post = (path, body) => j(path, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body || {}) })
|
|
|
|
export const getIdentityMap = () => j('/identity/map')
|
|
// upsert : { key?, label, primary_email, alias_emails?, tech_id?, employee? }
|
|
export const upsertIdentity = (body) => post('/identity', body)
|
|
export const setAlias = (key, email, remove = false) => post('/identity/' + encodeURIComponent(key) + '/alias', { email, remove })
|
|
export const mergeIdentity = (into, from) => post('/identity/merge', { into, from })
|
|
export const deleteIdentity = (key) => j('/identity/' + encodeURIComponent(key), { method: 'DELETE' })
|