+16
-24
@@ -1,5 +1,4 @@
|
||||
import { appConfig } from "@/config/env";
|
||||
import { useSessionStore } from "@/stores/session-store";
|
||||
import { ApiError } from "@/lib/errors";
|
||||
import type { z } from "zod";
|
||||
import {
|
||||
@@ -28,17 +27,20 @@ import type {
|
||||
SignInRequest,
|
||||
} from "./types";
|
||||
|
||||
interface ApiClientConfig {
|
||||
baseUrl: string;
|
||||
getToken: () => string | null;
|
||||
onUnauthorized: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP transport for Orion. Holds the bearer token as private state — the auth
|
||||
* store pushes it in via {@link setToken} on sign-in / restore and clears it
|
||||
* on sign-out. The client itself has no opinion about what a 401 means; it
|
||||
* just throws, and the query-client onError handler is the single place that
|
||||
* turns a 401 into a session invalidation.
|
||||
*/
|
||||
class ApiClient {
|
||||
private config: ApiClientConfig;
|
||||
private token: string | null = null;
|
||||
|
||||
constructor(config: ApiClientConfig) {
|
||||
this.config = config;
|
||||
constructor(private readonly baseUrl: string) {}
|
||||
|
||||
setToken(token: string | null): void {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
private async fetch(
|
||||
@@ -52,19 +54,17 @@ class ApiClient {
|
||||
headers["Content-Type"] = "application/json";
|
||||
}
|
||||
|
||||
const token = this.config.getToken();
|
||||
if (token) {
|
||||
headers["Authorization"] = `Bearer ${token}`;
|
||||
if (this.token) {
|
||||
headers["Authorization"] = `Bearer ${this.token}`;
|
||||
}
|
||||
|
||||
const response = await fetch(`${this.config.baseUrl}${path}`, {
|
||||
const response = await fetch(`${this.baseUrl}${path}`, {
|
||||
method,
|
||||
headers,
|
||||
body: body ? JSON.stringify(body) : undefined,
|
||||
});
|
||||
|
||||
if (response.status === 401) {
|
||||
this.config.onUnauthorized();
|
||||
throw new ApiError(401, "Unauthorized");
|
||||
}
|
||||
|
||||
@@ -273,12 +273,4 @@ class ApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
export const apiClient = new ApiClient({
|
||||
baseUrl: appConfig.orionUrl,
|
||||
getToken: () => useSessionStore.getState().token,
|
||||
// SecureStore writes are async; we fire-and-forget so the throwing
|
||||
// request doesn't have to wait for persistence to finish.
|
||||
onUnauthorized: () => {
|
||||
void useSessionStore.getState().clearToken();
|
||||
},
|
||||
});
|
||||
export const apiClient = new ApiClient(appConfig.orionUrl);
|
||||
|
||||
Reference in New Issue
Block a user