diff --git a/src/admin/resource-types.ts b/src/admin/resource-types.ts index 236e06e..6173015 100644 --- a/src/admin/resource-types.ts +++ b/src/admin/resource-types.ts @@ -221,6 +221,9 @@ export interface FormConfig { // ── Op context — passed to CRUD ops and actions ───────────────────────────── export interface OpContext { user: { id: number; role: string }; + /** Request origin (e.g. "https://bifrost.fenja.ai") — used to build absolute + * URLs in ActionResults like invite links. Always set by the route handler. */ + origin: string; /** * Raw POST FormData — opt-in escape hatch for resources whose form has * embedded sub-forms (e.g. the pulse fieldset inside dispatches). Most diff --git a/src/admin/resources/invitations.ts b/src/admin/resources/invitations.ts index c0d0a2a..14b495f 100644 --- a/src/admin/resources/invitations.ts +++ b/src/admin/resources/invitations.ts @@ -166,11 +166,11 @@ export const invitationsResource: Resource = { // Surface the one-shot magic link via the result mechanism — the route // handler propagates it as ?invite_url= and the panel renders a copy - // block on the next page load. - const origin = process.env.PUBLIC_ORIGIN ?? ''; + // block on the next page load. ctx.origin comes from Astro.url.origin + // so the link is always absolute and clickable. ctx.result = { kind: 'invite-link', - url: `${origin}/invite?t=${token}`, + url: `${ctx.origin}/invite?t=${token}`, }; return id; }, diff --git a/src/pages/admin/[resource].astro b/src/pages/admin/[resource].astro index 84753e9..4b37e9e 100644 --- a/src/pages/admin/[resource].astro +++ b/src/pages/admin/[resource].astro @@ -38,7 +38,10 @@ if (!resource) { } const resourceBase = `/admin/${resource.key}`; -const opCtx: OpContext = { user: { id: user.id, role: user.role } }; +const opCtx: OpContext = { + user: { id: user.id, role: user.role }, + origin: Astro.url.origin, +}; // ── Form-data → typed record (driven by the field configs) ──────────────── function parseFormData(