Domain-Check: fuehrt die Domain wirklich hierher?
Eine Domain zu registrieren ist der kleinste Teil der Arbeit — DNS, Reverse Proxy und Zertifikat liegen ausserhalb dieser Anwendung. Fehlt dort etwas, merkt man es am gedruckten Code, der nicht aufgeht. Der Knopf "Pruefen" geht den Weg der Scanner nach: Namen aufloesen, Zertifikat ansehen, Adresse aufrufen — und sich beweisen lassen, dass am anderen Ende diese Installation steht. Der Beweis ist ein HMAC ueber eine frische Zufallszahl; das Geheimnis dafuer entsteht beim ersten Check und verlaesst den Server nie. Geprueft wird nur ein Hostname aus der Datenbank, nie einer aus der Anfrage — sonst waere die Route ein Werkzeug zum Abklopfen fremder Adressen. Scheitert schon DNS, fallen die drei Folgeschritte aus, statt dreimal dasselbe zu melden. Die Grenze der Aussage steht im Bericht selbst: Der Aufruf startet dort, wo der Container laeuft. Refs #7
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { DomainBericht } from "@/components/domain-bericht";
|
||||
import { Icon } from "@/components/icons";
|
||||
import { Modal } from "@/components/modal";
|
||||
import {
|
||||
@@ -14,6 +15,7 @@ import {
|
||||
TextInput,
|
||||
} from "@/components/ui";
|
||||
import { ApiError, apiJson } from "@/lib/api-client";
|
||||
import type { Pruefbericht } from "@/lib/domain-check";
|
||||
|
||||
type Grant = { groupId: string; groupName: string; isDefault: boolean };
|
||||
|
||||
@@ -44,6 +46,10 @@ export function DomainAdmin({ baseUrlHostname }: { baseUrlHostname: string }) {
|
||||
|
||||
const [deleting, setDeleting] = useState<AdminDomain | null>(null);
|
||||
|
||||
/** Prüfberichte je Domain — bleiben stehen, bis erneut geprüft wird. */
|
||||
const [reports, setReports] = useState<Record<string, Pruefbericht>>({});
|
||||
const [checking, setChecking] = useState<string | null>(null);
|
||||
|
||||
const load = useCallback(async () => {
|
||||
try {
|
||||
const result = await apiJson<{
|
||||
@@ -122,6 +128,26 @@ export function DomainAdmin({ baseUrlHostname }: { baseUrlHostname: string }) {
|
||||
}
|
||||
};
|
||||
|
||||
const check = async (domain: AdminDomain) => {
|
||||
setChecking(domain.id);
|
||||
setError(null);
|
||||
try {
|
||||
const { report } = await apiJson<{ report: Pruefbericht }>(
|
||||
`/api/admin/domains/${domain.id}/check`,
|
||||
{ method: "POST" },
|
||||
);
|
||||
setReports((bisher) => ({ ...bisher, [domain.id]: report }));
|
||||
} catch (caught) {
|
||||
setError(
|
||||
caught instanceof ApiError
|
||||
? caught.message
|
||||
: "Die Prüfung ließ sich nicht ausführen.",
|
||||
);
|
||||
} finally {
|
||||
setChecking(null);
|
||||
}
|
||||
};
|
||||
|
||||
if (domains === null) {
|
||||
return <p className="text-sm text-slate-500">Domains werden geladen …</p>;
|
||||
}
|
||||
@@ -192,6 +218,16 @@ export function DomainAdmin({ baseUrlHostname }: { baseUrlHostname: string }) {
|
||||
<Icon name="pencil" className="size-3.5" />
|
||||
Gruppen zuteilen
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
disabled={checking !== null}
|
||||
onClick={() => void check(domain)}
|
||||
>
|
||||
<Icon name="scan" className="size-3.5" />
|
||||
{checking === domain.id ? "Prüft …" : "Prüfen"}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
@@ -213,6 +249,10 @@ export function DomainAdmin({ baseUrlHostname }: { baseUrlHostname: string }) {
|
||||
Löschen
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{reports[domain.id] ? (
|
||||
<DomainBericht bericht={reports[domain.id]} />
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user