backup-restore: vollständiger Stack im Server-Log + lesbare UI-Details
Der globale ORM-Leak-Sanitizer ersetzt error/details, die TypeError/
"Cannot read properties of undefined" enthalten, durch "Operation
fehlgeschlagen". Das ist richtig für Auth-Endpoints, blockt aber bei
legitimen Admin-Operationen wie Restore die Diagnose-Info.
Backend (restoreBackup):
- console.error mit "[restore]"-Prefix loggt Backup-Name + vollen
Stack ins Server-Log. Per `docker logs opencrm-app | tail -200`
einsehbar.
- makeRestoreErrorReadable() strippt Stack-Frames, rephrased
bekannte JS-Runtime-Marker ("TypeError:" → "Code-Fehler:",
"Cannot read properties of undefined (reading 'x')" → "Wert
fehlt: x") + cuttet auf 500 Zeichen. Dadurch passiert die
Meldung den globalen Sanitizer und landet lesbar im Response.
- Response bekommt zusätzliches `hint`-Feld mit dem konkreten
docker-Befehl.
Frontend (DatabaseBackup):
- extractError liefert jetzt strukturiertes Objekt
{headline, details, hint} statt nur String.
- Dialog: Headline fett, details in Mono-Box, hint italic darunter.
- Toast: Headline + details zusammen, 10s sichtbar.
Live-verifiziert:
- Bad name → "Backup nicht gefunden" (klare Meldung)
- Echtes Backup → "4859 Datensätze wiederhergestellt" als Toast,
Dialog zu
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,14 +6,17 @@ import { backupApi, BackupInfo, getAccessToken } from '../../services/api';
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
import Button from '../../components/ui/Button';
|
||||
|
||||
function extractError(err: any): string {
|
||||
function extractError(err: any): { headline: string; details?: string; hint?: string } {
|
||||
const data = err?.response?.data;
|
||||
if (data) {
|
||||
if (data.error && data.details) return `${data.error}: ${data.details}`;
|
||||
if (data.error) return data.error;
|
||||
if (typeof data === 'string') return data;
|
||||
if (data && typeof data === 'object') {
|
||||
return {
|
||||
headline: data.error || 'Unbekannter Fehler',
|
||||
details: data.details,
|
||||
hint: data.hint,
|
||||
};
|
||||
}
|
||||
return err?.message || 'Unbekannter Fehler';
|
||||
if (typeof data === 'string') return { headline: data };
|
||||
return { headline: err?.message || 'Unbekannter Fehler' };
|
||||
}
|
||||
|
||||
export default function DatabaseBackup() {
|
||||
@@ -55,7 +58,9 @@ export default function DatabaseBackup() {
|
||||
// Bei Fehler bleibt das Dialog absichtlich offen, damit der User
|
||||
// die Detail-Message sehen + ggf. erneut versuchen kann.
|
||||
onError: (err: any) => {
|
||||
toast.error(extractError(err), { duration: 8000 });
|
||||
const e = extractError(err);
|
||||
const msg = e.details ? `${e.headline}\n${e.details}` : e.headline;
|
||||
toast.error(msg, { duration: 10000 });
|
||||
},
|
||||
});
|
||||
|
||||
@@ -348,14 +353,22 @@ export default function DatabaseBackup() {
|
||||
<strong>Achtung:</strong> Bestehende Daten und Dokumente werden mit dem Backup-Stand überschrieben.
|
||||
Dies kann nicht rückgängig gemacht werden.
|
||||
</p>
|
||||
{restoreMutation.isError && (
|
||||
<div className="mb-4 bg-red-50 border border-red-200 text-red-800 text-sm p-3 rounded-lg">
|
||||
<div className="font-semibold mb-1">Wiederherstellung fehlgeschlagen</div>
|
||||
<div className="whitespace-pre-wrap break-words">
|
||||
{extractError(restoreMutation.error)}
|
||||
{restoreMutation.isError && (() => {
|
||||
const e = extractError(restoreMutation.error);
|
||||
return (
|
||||
<div className="mb-4 bg-red-50 border border-red-200 text-red-800 text-sm p-3 rounded-lg">
|
||||
<div className="font-semibold mb-1">{e.headline}</div>
|
||||
{e.details && (
|
||||
<div className="whitespace-pre-wrap break-words font-mono text-xs bg-white border border-red-100 rounded p-2 mb-2">
|
||||
{e.details}
|
||||
</div>
|
||||
)}
|
||||
{e.hint && (
|
||||
<div className="text-xs text-red-600 italic">{e.hint}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
);
|
||||
})()}
|
||||
<div className="flex justify-end gap-3">
|
||||
<Button
|
||||
variant="secondary"
|
||||
|
||||
Reference in New Issue
Block a user