Files
pi-car-companion/web/src/api.ts
T

111 lines
3.2 KiB
TypeScript

export type User = { id: number; username: string; role: "admin" };
export type AuthState = {
setupRequired: boolean;
user: User | null;
csrfToken: string;
};
export type Availability<T> =
| { available: true; value: T }
| { available: false; reason: string };
export type HeadUnitStatus =
| {
available: true;
value: {
state: "connected";
identity: {
manufacturer: Availability<string>;
model: Availability<string>;
androidVersion: Availability<string>;
apiLevel: Availability<number>;
buildId: Availability<string>;
abi: Availability<string>;
};
display: {
size: Availability<{ widthPixels: number; heightPixels: number }>;
densityDpi: Availability<number>;
};
};
}
| {
available: false;
state: "disabled" | "unavailable" | "offline" | "unauthorized" | "ambiguous";
reason: string;
};
export type SystemStatus = {
collectedAt: string;
hostname: string;
uptimeSeconds: number;
operatingSystem: string;
cpu: {
model: string;
loadAverage: number[];
temperatureCelsius: Availability<number>;
};
memory: { totalBytes: number; usedBytes: number; availableBytes: number };
disk: Availability<{ totalBytes: number; usedBytes: number; availableBytes: number; mount: string }>;
network: {
interfaces: Array<{ name: string; address: string; family: string }>;
interfaceReason: string | null;
wifi: Availability<string>;
};
service: { state: "healthy"; processUptimeSeconds: number };
headUnit: HeadUnitStatus;
};
export type UpdateStatus = {
state: "idle" | "checking" | "building" | "current" | "success" | "failed";
message: string;
fromRevision: string | null;
toRevision: string | null;
updatedAt: string;
installedRevision: string | null;
supported: boolean;
};
type ErrorResponse = { error?: { code?: string; message?: string } };
export class ApiError extends Error {
constructor(
message: string,
public readonly status: number,
public readonly code: string
) {
super(message);
}
}
async function parseResponse<T>(response: Response): Promise<T> {
const data = (await response.json().catch(() => ({}))) as T & ErrorResponse;
if (!response.ok) {
throw new ApiError(data.error?.message ?? "The request failed", response.status, data.error?.code ?? "REQUEST_FAILED");
}
return data;
}
export async function getAuthState(): Promise<AuthState> {
return parseResponse<AuthState>(await fetch("/api/auth/state", { credentials: "same-origin" }));
}
export async function post<T>(path: string, body: unknown, csrfToken: string): Promise<T> {
return parseResponse<T>(
await fetch(path, {
method: "POST",
credentials: "same-origin",
headers: { "Content-Type": "application/json", "X-CSRF-Token": csrfToken },
body: JSON.stringify(body)
})
);
}
export async function getStatus(): Promise<SystemStatus> {
return parseResponse<SystemStatus>(await fetch("/api/status", { credentials: "same-origin" }));
}
export async function getUpdateStatus(): Promise<UpdateStatus> {
return parseResponse<UpdateStatus>(await fetch("/api/system/update", { credentials: "same-origin" }));
}