feat(federation): add POST /api/federation/peers/:id/reset
Admin-only endpoint for recovering from needs_attention. Deletes the local peer row; FK cascade removes queued outbox entries. Gated to peers in needs_attention to prevent accidental resets of healthy peerings (use /peers/:id for revoke on active peers). Also extends the Task 8.5 test mock of '../db/index.js' to re-export `schema`. federation.ts imports `schema` from the re-export alongside `getDb`; the previous mock only exposed `getDb`, causing the route handler to blow up with 500s before reaching any assertion. This is a scaffolding fix — no test assertions were changed.
This commit is contained in:
@@ -16,8 +16,10 @@ let currentUserId = 'admin-user';
|
||||
let currentUserIsAdmin = true;
|
||||
|
||||
// Mock getDb BEFORE importing the route module so the module reads our test DB.
|
||||
// Must also re-export `schema` because federation.ts imports it from '../db/index.js'.
|
||||
vi.mock('../db/index.js', () => ({
|
||||
getDb: () => testDb,
|
||||
schema,
|
||||
}));
|
||||
|
||||
// Mock the auth middleware to honour test-controlled currentUserId / currentUserIsAdmin.
|
||||
|
||||
@@ -845,6 +845,45 @@ export async function federationRoutes(app: FastifyInstance): Promise<void> {
|
||||
},
|
||||
);
|
||||
|
||||
// ─── POST /api/federation/peers/:id/reset ──────────────────────────────────
|
||||
// Admin-only: reset a peer that has transitioned to needs_attention.
|
||||
// Deletes the local peer row (cascade-deletes outbox entries via FK).
|
||||
// Admin must re-initiate peering out of band after reset.
|
||||
app.post<{ Params: { id: string } }>(
|
||||
'/api/federation/peers/:id/reset',
|
||||
{ preHandler: [authenticate, requireAdmin] },
|
||||
async (request, reply) => {
|
||||
const { id } = request.params;
|
||||
const db = getDb();
|
||||
|
||||
const peer = db
|
||||
.select()
|
||||
.from(schema.federationPeers)
|
||||
.where(eq(schema.federationPeers.id, id))
|
||||
.get();
|
||||
|
||||
if (!peer) {
|
||||
return reply.code(404).send({ error: 'Peer not found', statusCode: 404 });
|
||||
}
|
||||
|
||||
if (peer.status !== 'needs_attention') {
|
||||
return reply.code(400).send({
|
||||
error: 'Reset is only available for peers in the needs_attention state. Use revoke for active peers.',
|
||||
statusCode: 400,
|
||||
});
|
||||
}
|
||||
|
||||
// Cascade-delete handles federation_outbox entries (FK onDelete: 'cascade').
|
||||
db.delete(schema.federationPeers)
|
||||
.where(eq(schema.federationPeers.id, id))
|
||||
.run();
|
||||
|
||||
connectionManager.sendToAdmins({ type: 'federation_peers_changed' as const });
|
||||
|
||||
return reply.code(200).send({ success: true });
|
||||
},
|
||||
);
|
||||
|
||||
// ─── PATCH /api/federation/peers/:id ────────────────────────────────────────
|
||||
// Admin-only: update peer settings (e.g. auto-rotation interval).
|
||||
app.patch<{ Params: { id: string }; Body: { autoRotateIntervalDays?: number } }>(
|
||||
|
||||
Reference in New Issue
Block a user