diff --git a/api/fofos-client.ts b/api/fofos-client.ts index 71fb9f2..4d30a72 100644 --- a/api/fofos-client.ts +++ b/api/fofos-client.ts @@ -48,6 +48,14 @@ type RequestOptions = { /** Already-serialised. Forwarded as given so what fofos receives is what the caller sent. */ body?: string | FormData | null; contentType?: string | null; + /** + * Who is asking, as an email — sent as `X-Actor`, and written into fofos's own change history. + * + * fofos has no users, so it cannot work this out and cannot check it: the header is as trustworthy + * as the token that carried it. That is enough for what it is for, which is telling two colleagues + * apart rather than proving anything. + */ + actor?: string | null; }; export type FofosResponse = { status: number; body: string; contentType: string | null }; @@ -70,6 +78,7 @@ export async function fofosRequest( // Left unset for FormData: fetch has to write its own, including the multipart boundary, and a // hand-set Content-Type here produces a body the other end cannot parse. if (options.contentType) headers['Content-Type'] = options.contentType; + if (options.actor) headers['X-Actor'] = options.actor; let response: Response; try { diff --git a/api/router.ts b/api/router.ts index f911546..dabe833 100644 --- a/api/router.ts +++ b/api/router.ts @@ -762,6 +762,9 @@ router.all('/*', async (ctx) => { search: url.search, body, contentType: body ? 'application/json' : null, + // Officer's session is the only place the acting user is known. fofos writes it into its own + // history — see its record.ts, and the note on `actor` in fofos-client. + actor: ctx.get('user')?.email ?? null, }); return passThrough(response); } catch (error) {