61 lines
2.1 KiB
TypeScript
61 lines
2.1 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import { z } from 'zod';
|
|
import { requireAdmin } from '@/lib/auth/admin';
|
|
import { upsertHandleEntries } from '@/lib/federation/handles';
|
|
|
|
const gossipSchema = z.object({
|
|
nodes: z.array(z.string().min(1)).min(1),
|
|
since: z.string().optional(),
|
|
});
|
|
|
|
export async function POST(request: Request) {
|
|
try {
|
|
await requireAdmin();
|
|
|
|
const body = await request.json();
|
|
const data = gossipSchema.parse(body);
|
|
|
|
const results = [];
|
|
|
|
for (const node of data.nodes) {
|
|
const baseUrl = node.startsWith('http') ? node : `https://${node}`;
|
|
const url = new URL('/.well-known/synapsis-handles', baseUrl);
|
|
if (data.since) {
|
|
url.searchParams.set('since', data.since);
|
|
}
|
|
|
|
try {
|
|
const res = await fetch(url.toString(), { method: 'GET' });
|
|
if (!res.ok) {
|
|
results.push({ node, success: false, error: `HTTP ${res.status}` });
|
|
continue;
|
|
}
|
|
|
|
const payload = await res.json();
|
|
const handles = Array.isArray(payload?.handles) ? payload.handles : [];
|
|
const merged = await upsertHandleEntries(handles);
|
|
|
|
results.push({
|
|
node,
|
|
success: true,
|
|
added: merged.added,
|
|
updated: merged.updated,
|
|
});
|
|
} catch (error) {
|
|
results.push({ node, success: false, error: 'Fetch failed' });
|
|
}
|
|
}
|
|
|
|
return NextResponse.json({ results });
|
|
} catch (error) {
|
|
if (error instanceof z.ZodError) {
|
|
return NextResponse.json({ error: 'Invalid payload', details: error.issues }, { status: 400 });
|
|
}
|
|
if (error instanceof Error && error.message === 'Admin required') {
|
|
return NextResponse.json({ error: 'Admin required' }, { status: 403 });
|
|
}
|
|
console.error('Gossip error:', error);
|
|
return NextResponse.json({ error: 'Failed to gossip handles' }, { status: 500 });
|
|
}
|
|
}
|