Add internal daemon routes and service management scripts

This commit is contained in:
2026-02-22 10:16:42 +00:00
parent c9fe2bd9fe
commit 614d25c189
7 changed files with 173 additions and 1 deletions
+2
View File
@@ -7,6 +7,7 @@ import dbPlugin from './plugins/db.js';
import authPlugin from './plugins/auth.js';
import authRoutes from './routes/auth/index.js';
import organizationRoutes from './routes/organizations/index.js';
import internalRoutes from './routes/internal/index.js';
import daemonNodeRoutes from './routes/nodes/daemon.js';
import nodeRoutes from './routes/nodes/index.js';
import serverRoutes from './routes/servers/index.js';
@@ -85,6 +86,7 @@ await app.register(authRoutes, { prefix: '/api/auth' });
await app.register(organizationRoutes, { prefix: '/api/organizations' });
await app.register(adminRoutes, { prefix: '/api/admin' });
await app.register(daemonNodeRoutes, { prefix: '/api/nodes' });
await app.register(internalRoutes, { prefix: '/api/internal' });
// Nested org routes: nodes and servers are scoped to an org
await app.register(
+72
View File
@@ -0,0 +1,72 @@
import { Type } from '@sinclair/typebox';
import type { FastifyInstance, FastifyRequest } from 'fastify';
import { eq } from 'drizzle-orm';
import { nodes } from '@source/database';
import { AppError } from '../../lib/errors.js';
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;
}
async function requireDaemonToken(app: FastifyInstance, request: FastifyRequest): Promise<void> {
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');
}
}
export default async function internalRoutes(app: FastifyInstance) {
app.get('/schedules/due', async (request) => {
await requireDaemonToken(app, request);
return { tasks: [] };
});
app.post(
'/schedules/:taskId/ack',
{
schema: {
params: Type.Object({
taskId: Type.String(),
}),
},
},
async (request) => {
await requireDaemonToken(app, request);
const { taskId } = request.params as { taskId: string };
return { success: true, taskId };
},
);
app.post(
'/servers/:serverUuid/backup',
{
schema: {
params: Type.Object({
serverUuid: Type.String(),
}),
},
},
async (request) => {
await requireDaemonToken(app, request);
const { serverUuid } = request.params as { serverUuid: string };
return { success: true, serverUuid };
},
);
}