feat(server): add PATCH peer and permanent delete endpoints for federation admin
This commit is contained in:
@@ -494,6 +494,88 @@ export async function federationRoutes(app: FastifyInstance): Promise<void> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// ─── PATCH /api/federation/peers/:id ────────────────────────────────────────
|
||||||
|
// Admin-only: update peer settings (e.g. auto-rotation interval).
|
||||||
|
app.patch<{ Params: { id: string }; Body: { autoRotateIntervalDays?: number } }>(
|
||||||
|
'/api/federation/peers/:id',
|
||||||
|
{ 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 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateData: Record<string, number> = {};
|
||||||
|
|
||||||
|
if (request.body.autoRotateIntervalDays !== undefined) {
|
||||||
|
const interval = Number(request.body.autoRotateIntervalDays);
|
||||||
|
if (isNaN(interval) || !Number.isInteger(interval) || interval < 1 || interval > 365) {
|
||||||
|
return reply.code(400).send({ error: 'autoRotateIntervalDays must be an integer between 1 and 365', statusCode: 400 });
|
||||||
|
}
|
||||||
|
updateData.autoRotateIntervalDays = interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(updateData).length === 0) {
|
||||||
|
return reply.code(400).send({ error: 'No valid fields to update', statusCode: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
db.update(schema.federationPeers)
|
||||||
|
.set(updateData)
|
||||||
|
.where(eq(schema.federationPeers.id, id))
|
||||||
|
.run();
|
||||||
|
|
||||||
|
const updated = db
|
||||||
|
.select()
|
||||||
|
.from(schema.federationPeers)
|
||||||
|
.where(eq(schema.federationPeers.id, id))
|
||||||
|
.get();
|
||||||
|
|
||||||
|
return reply.code(200).send({ peer: sanitizePeer(updated!) });
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// ─── DELETE /api/federation/peers/:id/permanent ─────────────────────────────
|
||||||
|
// Admin-only: permanently delete a revoked peer record.
|
||||||
|
app.delete<{ Params: { id: string } }>(
|
||||||
|
'/api/federation/peers/:id/permanent',
|
||||||
|
{ 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 !== 'revoked') {
|
||||||
|
return reply.code(400).send({
|
||||||
|
error: 'Only revoked peers can be permanently deleted. Revoke the peer first.',
|
||||||
|
statusCode: 400,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
db.delete(schema.federationPeers)
|
||||||
|
.where(eq(schema.federationPeers.id, id))
|
||||||
|
.run();
|
||||||
|
|
||||||
|
return reply.code(200).send({ success: true });
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
// ─── POST /api/federation/peers/:id/rotate ──────────────────────────────────
|
// ─── POST /api/federation/peers/:id/rotate ──────────────────────────────────
|
||||||
// Admin-only: trigger immediate secret rotation for a peer.
|
// Admin-only: trigger immediate secret rotation for a peer.
|
||||||
app.post<{ Params: { id: string } }>(
|
app.post<{ Params: { id: string } }>(
|
||||||
|
|||||||
Reference in New Issue
Block a user