import * as AtomHttpApi from "effect/unstable/reactivity/AtomHttpApi"; import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http"; import { AdminUsersHttpApi } from "@executor-js/api/client"; import * as Effect from "effect/Effect"; import { reportApiClientInfrastructureCause } from "./client"; import { EXECUTOR_ORG_HEADER, getActiveOrgSlug, getExecutorApiBaseUrl, getExecutorServerAuthorizationHeader, } from "./server-connection"; // --------------------------------------------------------------------------- // Shared admin client — the tenant-wide `/admin/users*` operator surface. // // A third AtomHttpApi service alongside `ExecutorApiClient` (the product plane: // "what can I reach") and `AccountApiClient` (identity + org membership). This // one answers the OWNER's question: who are my users and what have they // connected. Its paths are `/admin/*` with no `/api` — `transformClient` // prepends the API base, exactly like the account client. // // Both hosts that serve it (cloud: an org API key or an admin-role session; // self-host: a Better Auth owner/admin) implement identical routes, so one // client works for both. Requests carry the same session cookie the browser // sends anyway; the org header scopes cloud to the org the console URL is on. // // `reportApiClientInfrastructureCause` (not `handleApiClientCause`) on purpose: // a 401 here means "not an admin of this tenant", which must render a denied // state on the page — it must NOT pop the local-auth gate the core client // raises when the whole product plane is unreachable. // --------------------------------------------------------------------------- const AdminApiClient = AtomHttpApi.Service<"AdminApiClient">()("AdminApiClient", { api: AdminUsersHttpApi, httpClient: FetchHttpClient.layer, transformClient: HttpClient.mapRequest((request) => { let next = HttpClientRequest.prependUrl(request, getExecutorApiBaseUrl()); const authorization = getExecutorServerAuthorizationHeader(); if (authorization) { next = HttpClientRequest.setHeader(next, "authorization", authorization); } // Scope to the org the console URL is on (see server-connection). const orgSlug = getActiveOrgSlug(); if (orgSlug) { next = HttpClientRequest.setHeader(next, EXECUTOR_ORG_HEADER, orgSlug); } return next; }), transformResponse: (effect) => Effect.tapCause(effect, reportApiClientInfrastructureCause), }); export { AdminApiClient };