feat(api): Agent-API mit Enrollment, Report und Ping (#004)
Gehashte Tokens mit Praefix, Rate-Limit je IP und Token, zod-Validierung mit durchgaengig optionalen Feldern, idempotente Reports ueber reportId, Key- und Server-ID-Historie sowie automatischer Laufzeit-Vorschlag. 32 Tests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import {
|
||||
AGENT_VERSION,
|
||||
bearerToken,
|
||||
enrollRateLimit,
|
||||
fehlerAntwort,
|
||||
fehlerBehandeln,
|
||||
jsonEingabe,
|
||||
rateLimitPruefen,
|
||||
} from "@/server/agent/http";
|
||||
import { enrollSchema } from "@/server/agent/schema";
|
||||
import { registriereAgent } from "@/server/agent/service";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
/** Registrierung eines Agents mit einem Enrollment-Token. */
|
||||
export async function POST(request: Request) {
|
||||
const token = bearerToken(request);
|
||||
const gebremst = rateLimitPruefen(enrollRateLimit, request.headers, token);
|
||||
if (gebremst) return gebremst;
|
||||
|
||||
if (!token) {
|
||||
return fehlerAntwort(401, "token_fehlt", "Enrollment-Token fehlt (Authorization: Bearer …).");
|
||||
}
|
||||
|
||||
const eingabe = await jsonEingabe(request, enrollSchema);
|
||||
if ("antwort" in eingabe) return eingabe.antwort;
|
||||
|
||||
try {
|
||||
const ergebnis = await registriereAgent(eingabe.daten, token);
|
||||
return Response.json(
|
||||
{
|
||||
ok: true,
|
||||
hostId: ergebnis.hostId,
|
||||
token: ergebnis.token,
|
||||
bestehenderHost: ergebnis.bestehenderHost,
|
||||
serverIdBereitsBekannt: ergebnis.serverIdBereitsBekannt,
|
||||
agentVersion: AGENT_VERSION,
|
||||
serverZeit: new Date().toISOString(),
|
||||
},
|
||||
{ status: ergebnis.bestehenderHost ? 200 : 201 },
|
||||
);
|
||||
} catch (fehler) {
|
||||
return fehlerBehandeln(fehler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
AGENT_VERSION,
|
||||
agentRateLimit,
|
||||
bearerToken,
|
||||
fehlerAntwort,
|
||||
fehlerBehandeln,
|
||||
rateLimitPruefen,
|
||||
} from "@/server/agent/http";
|
||||
import { authentifiziereHost } from "@/server/agent/service";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
/** Verbindungstest für `proxledger-agent --test`. */
|
||||
export async function GET(request: Request) {
|
||||
const token = bearerToken(request);
|
||||
const gebremst = rateLimitPruefen(agentRateLimit, request.headers, token);
|
||||
if (gebremst) return gebremst;
|
||||
|
||||
if (!token) {
|
||||
return fehlerAntwort(401, "token_fehlt", "Host-Token fehlt (Authorization: Bearer …).");
|
||||
}
|
||||
|
||||
try {
|
||||
const host = await authentifiziereHost(token);
|
||||
return Response.json({
|
||||
ok: true,
|
||||
hostId: host.id,
|
||||
hostname: host.hostname,
|
||||
agentVersion: AGENT_VERSION,
|
||||
serverZeit: new Date().toISOString(),
|
||||
});
|
||||
} catch (fehler) {
|
||||
return fehlerBehandeln(fehler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { clientIp } from "@/lib/ratelimit";
|
||||
import {
|
||||
AGENT_VERSION,
|
||||
agentRateLimit,
|
||||
bearerToken,
|
||||
fehlerAntwort,
|
||||
fehlerBehandeln,
|
||||
jsonEingabe,
|
||||
rateLimitPruefen,
|
||||
} from "@/server/agent/http";
|
||||
import { reportSchema } from "@/server/agent/schema";
|
||||
import { authentifiziereHost, verarbeiteReport } from "@/server/agent/service";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
/** Täglicher Statusbericht eines Hosts. */
|
||||
export async function POST(request: Request) {
|
||||
const token = bearerToken(request);
|
||||
const gebremst = rateLimitPruefen(agentRateLimit, request.headers, token);
|
||||
if (gebremst) return gebremst;
|
||||
|
||||
if (!token) {
|
||||
return fehlerAntwort(401, "token_fehlt", "Host-Token fehlt (Authorization: Bearer …).");
|
||||
}
|
||||
|
||||
const eingabe = await jsonEingabe(request, reportSchema);
|
||||
if ("antwort" in eingabe) return eingabe.antwort;
|
||||
|
||||
try {
|
||||
const host = await authentifiziereHost(token);
|
||||
const ergebnis = await verarbeiteReport(host, eingabe.daten, clientIp(request.headers));
|
||||
|
||||
return Response.json({
|
||||
ok: true,
|
||||
hostId: host.id,
|
||||
doppelt: ergebnis.doppelt,
|
||||
laufzeitVorschlag: ergebnis.laufzeitVorschlagAngelegt,
|
||||
agentVersion: AGENT_VERSION,
|
||||
serverZeit: new Date().toISOString(),
|
||||
});
|
||||
} catch (fehler) {
|
||||
return fehlerBehandeln(fehler);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user