feat(nextcloud-status): Clouds verwalten, Logos, stündliche Prüfung und Sortierung

- Ändern, Entfernen, Logo-Upload und -Abruf, Sammelprüfung nur mit Verwalten
- Stündlicher Auftrag beim Start registriert, Prüfung je Cloud an ihren Mandanten gebunden
- Sortierung nach Kundenname, Status, Version und Support-Ende, je Benutzer gemerkt
- Formular zum Anlegen und Bearbeiten, Zugriffsinventar angepasst

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-02 14:54:58 +02:00
parent 5ef7b0c6cc
commit 6698ef1a92
23 changed files with 2061 additions and 30 deletions
+90
View File
@@ -84,3 +84,93 @@ export function logoSrc(
}
return instance.logoUrl ?? null;
}
/** Liest die NestJS-Fehlermeldung aus dem Antwortkoerper, faellt sonst auf einen Standardtext zurueck. */
async function readErrorMessage(res: Response, fallback: string): Promise<string> {
try {
const body = await res.json();
if (typeof body?.message === 'string') return body.message;
if (Array.isArray(body?.message) && body.message.length > 0) return String(body.message[0]);
} catch {
/* Antwort war kein JSON — Standardtext bleibt */
}
return fallback;
}
export interface NextcloudInstancePayload {
customerName: string;
baseUrl: string;
/** Leere Zeichenkette = Logo-Adresse entfernen (nur beim Aendern). */
logoUrl?: string;
}
const BASE = `${API_URL}/modules/nextcloud-status/instances`;
/** POST /instances — Cloud anlegen und sofort pruefen (nur Verwalten). */
export async function createInstance(
payload: NextcloudInstancePayload,
): Promise<NextcloudInstance> {
const res = await fetch(BASE, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error(await readErrorMessage(res, 'Failed to create nextcloud instance'));
return res.json();
}
/** PUT /instances/:id — Cloud aendern (nur Verwalten). */
export async function updateInstance(
id: string,
payload: Partial<NextcloudInstancePayload>,
): Promise<NextcloudInstance> {
const res = await fetch(`${BASE}/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify(payload),
});
if (!res.ok) throw new Error(await readErrorMessage(res, 'Failed to update nextcloud instance'));
return res.json();
}
/** DELETE /instances/:id — Cloud entfernen (nur Verwalten). */
export async function deleteInstance(id: string): Promise<void> {
const res = await fetch(`${BASE}/${id}`, { method: 'DELETE', credentials: 'include' });
if (!res.ok) throw new Error(await readErrorMessage(res, 'Failed to delete nextcloud instance'));
}
/** POST /instances/check — "Jetzt pruefen" fuer die ganze Liste, liefert die frische Liste. */
export async function checkAll(): Promise<NextcloudList> {
const res = await fetch(`${BASE}/check`, { method: 'POST', credentials: 'include' });
if (!res.ok) throw new Error(await readErrorMessage(res, 'Failed to check nextcloud instances'));
return res.json();
}
/** POST /instances/:id/check — eine Cloud sofort pruefen, liefert die aktualisierte Kachel. */
export async function checkOne(id: string): Promise<NextcloudInstance> {
const res = await fetch(`${BASE}/${id}/check`, { method: 'POST', credentials: 'include' });
if (!res.ok) throw new Error(await readErrorMessage(res, 'Failed to check nextcloud instance'));
return res.json();
}
/** POST /instances/:id/logo — Logo hochladen (Formularfeld `logo`, nur Verwalten). */
export async function uploadLogo(id: string, file: File): Promise<NextcloudInstance> {
const form = new FormData();
form.append('logo', file);
const res = await fetch(`${BASE}/${id}/logo`, {
method: 'POST',
credentials: 'include',
body: form,
});
if (!res.ok) throw new Error(await readErrorMessage(res, 'Failed to upload logo'));
return res.json();
}
/** DELETE /instances/:id/logo — hochgeladenes Logo entfernen (nur Verwalten). */
export async function removeLogo(id: string): Promise<NextcloudInstance> {
const res = await fetch(`${BASE}/${id}/logo`, { method: 'DELETE', credentials: 'include' });
if (!res.ok) throw new Error(await readErrorMessage(res, 'Failed to remove logo'));
return res.json();
}