generated from Dicken/dickendock
round27: Ping-Regression rueckgaengig gemacht (Echo/Samsung-Geraete ignorieren ICMP), Aktualisiert-Text entfernt, Suchmaschinen-Hinweis+README, API-Scanner stark erweitert mit eigenem Reiter
This commit is contained in:
78
apps/frontend/src/routes/admin/ApisPage.tsx
Normal file
78
apps/frontend/src/routes/admin/ApisPage.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import { useServices } from "../../hooks/useServices.js";
|
||||
import { useDetectedApis, type DetectedApiEntry } from "../../hooks/useDetectedApis.js";
|
||||
import { AdminPageHeader } from "./AdminPageHeader.js";
|
||||
|
||||
function formatDate(iso: string): string {
|
||||
try {
|
||||
return new Date(iso).toLocaleString("de-DE", { dateStyle: "medium", timeStyle: "short" });
|
||||
} catch {
|
||||
return iso;
|
||||
}
|
||||
}
|
||||
|
||||
export function ApisPage() {
|
||||
const { data: services } = useServices();
|
||||
const { data: apis, isLoading, isError } = useDetectedApis();
|
||||
|
||||
const serviceById = new Map((services ?? []).map((s) => [s.id, s]));
|
||||
const groups = new Map<string, DetectedApiEntry[]>();
|
||||
for (const entry of apis ?? []) {
|
||||
const list = groups.get(entry.serviceId) ?? [];
|
||||
list.push(entry);
|
||||
groups.set(entry.serviceId, list);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AdminPageHeader
|
||||
title="APIs"
|
||||
description='Von einem eigenen, separaten Scan gefundene API-Endpunkte bekannter Dienste (siehe Admin -> Scanner -> "APIs jetzt scannen"). Läuft nie automatisch mit.'
|
||||
/>
|
||||
|
||||
{isLoading ? (
|
||||
<p className="text-sm text-black/40 dark:text-white/40">Lade …</p>
|
||||
) : isError ? (
|
||||
<p className="text-sm text-red-500">Konnte nicht geladen werden.</p>
|
||||
) : groups.size === 0 ? (
|
||||
<p className="text-sm text-black/40 dark:text-white/40">
|
||||
Noch keine APIs gefunden. Auf der Scanner-Seite einmal „APIs jetzt scannen" ausführen.
|
||||
</p>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{Array.from(groups.entries()).map(([serviceId, entries]) => {
|
||||
const service = serviceById.get(serviceId);
|
||||
return (
|
||||
<div key={serviceId} className="rounded-2xl border border-black/10 p-4 dark:border-white/10">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<h3 className="font-medium text-black dark:text-white">
|
||||
{service?.displayName ?? "Unbekannter Dienst"}
|
||||
</h3>
|
||||
<span className="text-xs text-black/30 dark:text-white/30">
|
||||
zuletzt geprüft: {formatDate(entries[0].detectedAt)}
|
||||
</span>
|
||||
</div>
|
||||
<ul className="flex flex-wrap gap-2">
|
||||
{entries.map((e) => (
|
||||
<li key={e.id}>
|
||||
<a
|
||||
href={`${service?.url ?? ""}${e.path}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
title={`HTTP ${e.status}`}
|
||||
className="flex items-center gap-1.5 rounded-full bg-black/5 px-2.5 py-1 text-xs
|
||||
text-black/70 hover:bg-black/10 dark:bg-white/10 dark:text-white/70
|
||||
dark:hover:bg-white/20"
|
||||
>
|
||||
{e.type} · {e.path}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user