100 lines
3.1 KiB
TypeScript
100 lines
3.1 KiB
TypeScript
import { apiRequest, ApiError } from './api-client';
|
||
import { moduleSchema, type Module } from './schemas';
|
||
|
||
/** Typsichere API-Funktionen für die Modul-Verwaltung. */
|
||
|
||
export async function fetchModules(): Promise<Module[]> {
|
||
const response = await apiRequest<{ modules: unknown[] }>('/api/v1/modules');
|
||
return response.modules.map((module) => moduleSchema.parse(module));
|
||
}
|
||
|
||
export async function installModule(file: File): Promise<Module> {
|
||
const formData = new FormData();
|
||
formData.append('package', file);
|
||
|
||
const headers: Record<string, string> = {};
|
||
const csrfToken = readCsrfToken();
|
||
if (csrfToken) {
|
||
headers['X-CSRF-Token'] = csrfToken;
|
||
}
|
||
|
||
const response = await fetch('/api/v1/modules/install', {
|
||
method: 'POST',
|
||
headers,
|
||
credentials: 'same-origin',
|
||
body: formData,
|
||
});
|
||
|
||
if (!response.ok) {
|
||
let message = 'Installation fehlgeschlagen';
|
||
let details: Record<string, string | string[]> | undefined;
|
||
try {
|
||
const errorBody = (await response.json()) as {
|
||
message?: string | string[];
|
||
details?: Record<string, string | string[]>;
|
||
};
|
||
if (typeof errorBody.message === 'string') {
|
||
message = errorBody.message;
|
||
} else if (Array.isArray(errorBody.message)) {
|
||
message = errorBody.message.join(', ');
|
||
}
|
||
details = errorBody.details;
|
||
} catch {
|
||
// Kein JSON in der Antwort – Standardmeldung verwenden.
|
||
}
|
||
throw new ApiError(response.status, message, details);
|
||
}
|
||
|
||
const response_ = (await response.json()) as { module: unknown };
|
||
return moduleSchema.parse(response_.module);
|
||
}
|
||
|
||
export async function startModule(id: string): Promise<Module> {
|
||
const response = await apiRequest<{ module: unknown }>(`/api/v1/modules/${id}/start`, {
|
||
method: 'POST',
|
||
});
|
||
return moduleSchema.parse(response.module);
|
||
}
|
||
|
||
export async function stopModule(id: string): Promise<Module> {
|
||
const response = await apiRequest<{ module: unknown }>(`/api/v1/modules/${id}/stop`, {
|
||
method: 'POST',
|
||
});
|
||
return moduleSchema.parse(response.module);
|
||
}
|
||
|
||
export async function restartModule(id: string): Promise<Module> {
|
||
const response = await apiRequest<{ module: unknown }>(`/api/v1/modules/${id}/restart`, {
|
||
method: 'POST',
|
||
});
|
||
return moduleSchema.parse(response.module);
|
||
}
|
||
|
||
export async function setModuleEnabled(id: string, enabled: boolean): Promise<Module> {
|
||
const response = await apiRequest<{ module: unknown }>(`/api/v1/modules/${id}/enabled`, {
|
||
method: 'PATCH',
|
||
body: { enabled },
|
||
});
|
||
return moduleSchema.parse(response.module);
|
||
}
|
||
|
||
export async function removeModule(id: string): Promise<void> {
|
||
await apiRequest(`/api/v1/modules/${id}`, { method: 'DELETE' });
|
||
}
|
||
|
||
export async function checkModuleHealth(
|
||
id: string,
|
||
): Promise<{ healthy: boolean; latencyMs: number; detail: string }> {
|
||
return apiRequest(`/api/v1/modules/${id}/health`);
|
||
}
|
||
|
||
/** Liest das CSRF-Token aus dem nicht-HttpOnly-Cookie. */
|
||
function readCsrfToken(): string | null {
|
||
for (const part of document.cookie.split(';')) {
|
||
const [name, ...value] = part.trim().split('=');
|
||
if (name === 'mpm_csrf') {
|
||
return decodeURIComponent(value.join('='));
|
||
}
|
||
}
|
||
return null;
|
||
} |