refactor(api): route baserow calls through internal proxy
This commit is contained in:
@@ -1,104 +1,122 @@
|
|||||||
const BASE = import.meta.env.VITE_BASEROW_URL;
|
|
||||||
const TOKEN = import.meta.env.VITE_BASEROW_TOKEN;
|
|
||||||
const SERVERS_TABLE = import.meta.env.VITE_SERVERS_TABLE_ID;
|
|
||||||
const PROCESSES_TABLE = import.meta.env.VITE_PROCESSES_TABLE_ID;
|
|
||||||
|
|
||||||
const headers = {
|
|
||||||
Authorization: `Token ${TOKEN}`,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
};
|
|
||||||
|
|
||||||
/* ── Types ─────────────────────────────────────────── */
|
/* ── Types ─────────────────────────────────────────── */
|
||||||
|
|
||||||
export interface Server {
|
export interface Server {
|
||||||
id: number;
|
id: number;
|
||||||
Name: string;
|
Name: string;
|
||||||
Beschreibung: string;
|
Beschreibung: string;
|
||||||
Sortierung?: number;
|
Sortierung?: number;
|
||||||
Prozesse: { id: number; value: string }[];
|
Prozesse: { id: number; value: string }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Process {
|
export interface Process {
|
||||||
id: number;
|
id: number;
|
||||||
Name: string;
|
Name: string;
|
||||||
Server?: { id: number; value: string }[];
|
Server?: { id: number; value: string }[];
|
||||||
Status?: { id: number; value: string; color: string };
|
Status?: { id: number; value: string; color: string };
|
||||||
Wiederholung?: { id: number; value: string; color: string };
|
Wiederholung?: { id: number; value: string; color: string };
|
||||||
Farbe?: string;
|
Farbe?: string;
|
||||||
Start?: string;
|
Start?: string;
|
||||||
Ende?: string;
|
Ende?: string;
|
||||||
'Start Minute'?: number | null;
|
"Start Minute"?: number | null;
|
||||||
Dauer?: string;
|
Dauer?: string;
|
||||||
'Erste Ausführung'?: string;
|
"Erste Ausführung"?: string;
|
||||||
'Ausführung bis'?: string;
|
"Ausführung bis"?: string;
|
||||||
'Intervall zwischen'?: string;
|
"Intervall zwischen"?: string;
|
||||||
Wochentage?: string;
|
Wochentage?: string;
|
||||||
Sichtbarkeit?: { id: number; value: string; color: string };
|
Sichtbarkeit?: { id: number; value: string; color: string };
|
||||||
Sortierung?: number;
|
Sortierung?: number;
|
||||||
}
|
|
||||||
|
|
||||||
interface BaserowList<T> {
|
|
||||||
count: number;
|
|
||||||
next: string | null;
|
|
||||||
previous: string | null;
|
|
||||||
results: T[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Generic helpers ───────────────────────────────── */
|
/* ── Generic helpers ───────────────────────────────── */
|
||||||
|
|
||||||
async function listRows<T>(tableId: string): Promise<T[]> {
|
function assertPositiveInteger(id: number, label: string): void {
|
||||||
const res = await fetch(
|
if (!Number.isInteger(id) || id <= 0) {
|
||||||
`${BASE}/api/database/rows/table/${tableId}/?user_field_names=true&size=200`,
|
throw new Error(`${label} must be a positive integer.`);
|
||||||
{ headers }
|
}
|
||||||
);
|
|
||||||
if (!res.ok) throw new Error(`Baserow GET failed: ${res.status}`);
|
|
||||||
const data: BaserowList<T> = await res.json();
|
|
||||||
return data.results;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createRow(tableId: string, fields: Record<string, unknown>) {
|
function removeUndefinedValues(
|
||||||
const res = await fetch(
|
fields: Record<string, unknown>,
|
||||||
`${BASE}/api/database/rows/table/${tableId}/?user_field_names=true`,
|
): Record<string, unknown> {
|
||||||
{ method: 'POST', headers, body: JSON.stringify(fields) }
|
return Object.fromEntries(
|
||||||
);
|
Object.entries(fields).filter(([, value]) => value !== undefined),
|
||||||
if (!res.ok) throw new Error(`Baserow POST failed: ${res.status}`);
|
);
|
||||||
return res.json();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateRow(tableId: string, rowId: number, fields: Record<string, unknown>) {
|
async function readErrorMessage(response: Response): Promise<string> {
|
||||||
const res = await fetch(
|
const message = await response.text();
|
||||||
`${BASE}/api/database/rows/table/${tableId}/${rowId}/?user_field_names=true`,
|
return message || response.statusText || "Unknown error";
|
||||||
{ method: 'PATCH', headers, body: JSON.stringify(fields) }
|
|
||||||
);
|
|
||||||
if (!res.ok) throw new Error(`Baserow PATCH failed: ${res.status}`);
|
|
||||||
return res.json();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteRow(tableId: string, rowId: number) {
|
async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
|
||||||
const res = await fetch(
|
const method = init.method ?? "GET";
|
||||||
`${BASE}/api/database/rows/table/${tableId}/${rowId}/`,
|
const headers = new Headers(init.headers);
|
||||||
{ method: 'DELETE', headers }
|
headers.set("Accept", "application/json");
|
||||||
);
|
|
||||||
if (!res.ok) throw new Error(`Baserow DELETE failed: ${res.status}`);
|
if (init.body !== undefined && init.body !== null) {
|
||||||
|
headers.set("Content-Type", "application/json");
|
||||||
|
}
|
||||||
|
|
||||||
|
let response: Response;
|
||||||
|
try {
|
||||||
|
response = await fetch(`/api${path}`, {
|
||||||
|
...init,
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
const message =
|
||||||
|
error instanceof Error ? error.message : "Unknown network error";
|
||||||
|
throw new Error(
|
||||||
|
`Request ${method} ${path} failed (network error): ${message}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const message = await readErrorMessage(response);
|
||||||
|
throw new Error(
|
||||||
|
`Request ${method} ${path} failed (${response.status}): ${message}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status === 204) {
|
||||||
|
return undefined as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (await response.json()) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Public API ────────────────────────────────────── */
|
/* ── Public API ────────────────────────────────────── */
|
||||||
|
|
||||||
export const api = {
|
export const api = {
|
||||||
// Servers
|
getServers: () => request<Server[]>("/servers"),
|
||||||
getServers: () => listRows<Server>(SERVERS_TABLE),
|
createServer: (name: string, description: string) =>
|
||||||
createServer: (name: string, description: string) =>
|
request<Server>("/servers", {
|
||||||
createRow(SERVERS_TABLE, { Name: name, Beschreibung: description }),
|
method: "POST",
|
||||||
deleteServer: (id: number) => deleteRow(SERVERS_TABLE, id),
|
body: JSON.stringify({
|
||||||
|
Name: name.trim(),
|
||||||
|
Beschreibung: description.trim(),
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
deleteServer: (id: number) => {
|
||||||
|
assertPositiveInteger(id, "Server ID");
|
||||||
|
return request<void>(`/servers/${id}`, { method: "DELETE" });
|
||||||
|
},
|
||||||
|
|
||||||
// Processes
|
getProcesses: () => request<Process[]>("/processes"),
|
||||||
getProcesses: () => listRows<Process>(PROCESSES_TABLE),
|
createProcess: (fields: Record<string, unknown>) =>
|
||||||
createProcess: (fields: Record<string, unknown>) =>
|
request<Process>("/processes", {
|
||||||
createRow(PROCESSES_TABLE, fields),
|
method: "POST",
|
||||||
updateProcess: (id: number, fields: Record<string, unknown>) => {
|
body: JSON.stringify(fields),
|
||||||
// If fields.Sichtbarkeit is undefined, it won't be sent, which protects against the field missing.
|
}),
|
||||||
const cleanFields = Object.fromEntries(Object.entries(fields).filter(([_, v]) => v !== undefined));
|
updateProcess: (id: number, fields: Record<string, unknown>) => {
|
||||||
return updateRow(PROCESSES_TABLE, id, cleanFields);
|
assertPositiveInteger(id, "Process ID");
|
||||||
},
|
return request<Process>(`/processes/${id}`, {
|
||||||
deleteProcess: (id: number) => deleteRow(PROCESSES_TABLE, id),
|
method: "PATCH",
|
||||||
|
body: JSON.stringify(removeUndefinedValues(fields)),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deleteProcess: (id: number) => {
|
||||||
|
assertPositiveInteger(id, "Process ID");
|
||||||
|
return request<void>(`/processes/${id}`, { method: "DELETE" });
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user