feat(server): test-only seed-peer route gated by NODE_ENV+ENABLE_TEST_ROUTES
This commit is contained in:
@@ -355,4 +355,50 @@ export async function adminRoutes(app: FastifyInstance): Promise<void> {
|
|||||||
return reply.code(200).send({ success: true });
|
return reply.code(200).send({ success: true });
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Test-only: seed federation_peers row directly. STRICTLY gated by NODE_ENV='test'
|
||||||
|
// AND ENABLE_TEST_ROUTES='1'. Returns 404 in any other configuration. Used by
|
||||||
|
// the two-instance integration harness to wire up matching HMAC secrets between
|
||||||
|
// home and remote without running the multi-step approval flow.
|
||||||
|
app.post<{ Body: { origin: string; hmacSecret: string; status?: string; instanceName?: string } }>(
|
||||||
|
'/api/admin/test/seed-peer',
|
||||||
|
async (request, reply) => {
|
||||||
|
if (process.env.NODE_ENV !== 'test' || process.env.ENABLE_TEST_ROUTES !== '1') {
|
||||||
|
return reply.code(404).send({ error: 'Not Found', statusCode: 404 });
|
||||||
|
}
|
||||||
|
const { origin, hmacSecret, status, instanceName } = request.body;
|
||||||
|
if (typeof origin !== 'string' || !origin.startsWith('http')) {
|
||||||
|
return reply.code(400).send({ error: 'origin must be an http(s) URL', statusCode: 400 });
|
||||||
|
}
|
||||||
|
if (typeof hmacSecret !== 'string' || hmacSecret.length < 32) {
|
||||||
|
return reply.code(400).send({ error: 'hmacSecret must be a string ≥32 chars', statusCode: 400 });
|
||||||
|
}
|
||||||
|
const validStatuses = new Set(['active', 'pending', 'awaiting_approval', 'rejected', 'revoked', 'needs_attention', 'unreachable', 'accepted']);
|
||||||
|
if (status !== undefined && !validStatuses.has(status)) {
|
||||||
|
return reply.code(400).send({ error: `status must be one of ${[...validStatuses].join(', ')}`, statusCode: 400 });
|
||||||
|
}
|
||||||
|
const db = getDb();
|
||||||
|
const id = crypto.randomUUID();
|
||||||
|
db.insert(schema.federationPeers)
|
||||||
|
.values({
|
||||||
|
id,
|
||||||
|
origin,
|
||||||
|
instanceName: instanceName ?? null,
|
||||||
|
hmacSecret,
|
||||||
|
status: status ?? 'active',
|
||||||
|
consecutiveFailures: 0,
|
||||||
|
consecutiveAuthFailures: 0,
|
||||||
|
lastSyncedAt: 0,
|
||||||
|
nonceSupported: 1,
|
||||||
|
autoRotateIntervalDays: 90,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
})
|
||||||
|
.onConflictDoUpdate({
|
||||||
|
target: schema.federationPeers.origin,
|
||||||
|
set: { hmacSecret, status: status ?? 'active', instanceName: instanceName ?? null },
|
||||||
|
})
|
||||||
|
.run();
|
||||||
|
return reply.code(200).send({ ok: true, id });
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user