c1adb94abb
CI has been running `prettier --check` against a tree that was never formatted, so the check reported 63 files and failed every run. Nothing here is a behaviour change: `pnpm lint` and the four typecheck builds pass exactly as before. conduit-bringup-artifacts is added to .prettierignore instead. Those files are captured bring-up reports, not maintained sources; reflowing them would only churn a record of what happened.
67 lines
1.8 KiB
TypeScript
67 lines
1.8 KiB
TypeScript
import { Type } from '@sinclair/typebox';
|
|
import type { FastifyInstance } from 'fastify';
|
|
import { eq } from 'drizzle-orm';
|
|
import { nodes } from '@source/database';
|
|
import { AppError } from '../../lib/errors.js';
|
|
|
|
const HeartbeatSchema = {
|
|
body: Type.Object({
|
|
active_servers: Type.Number({ minimum: 0 }),
|
|
total_servers: Type.Number({ minimum: 0 }),
|
|
version: Type.String(),
|
|
}),
|
|
};
|
|
|
|
function extractBearerToken(authHeader?: string): string | null {
|
|
if (!authHeader) return null;
|
|
const [scheme, token] = authHeader.split(' ');
|
|
if (!scheme || !token || scheme.toLowerCase() !== 'bearer') return null;
|
|
return token;
|
|
}
|
|
|
|
export default async function daemonNodeRoutes(app: FastifyInstance) {
|
|
// POST /api/nodes/heartbeat
|
|
app.post('/heartbeat', { schema: HeartbeatSchema }, async (request) => {
|
|
const token = extractBearerToken(
|
|
typeof request.headers.authorization === 'string' ? request.headers.authorization : undefined,
|
|
);
|
|
|
|
if (!token) {
|
|
throw AppError.unauthorized('Missing daemon bearer token', 'DAEMON_AUTH_MISSING');
|
|
}
|
|
|
|
const node = await app.db.query.nodes.findFirst({
|
|
where: eq(nodes.daemonToken, token),
|
|
columns: { id: true },
|
|
});
|
|
|
|
if (!node) {
|
|
throw AppError.unauthorized('Invalid daemon token', 'DAEMON_AUTH_INVALID');
|
|
}
|
|
|
|
const now = new Date();
|
|
await app.db
|
|
.update(nodes)
|
|
.set({
|
|
isOnline: true,
|
|
lastHeartbeat: now,
|
|
updatedAt: now,
|
|
})
|
|
.where(eq(nodes.id, node.id));
|
|
|
|
const body = request.body as {
|
|
active_servers: number;
|
|
total_servers: number;
|
|
version: string;
|
|
};
|
|
|
|
return {
|
|
success: true,
|
|
nodeId: node.id,
|
|
activeServers: body.active_servers,
|
|
totalServers: body.total_servers,
|
|
version: body.version,
|
|
};
|
|
});
|
|
}
|