This commit is contained in:
2026-05-14 19:24:50 +00:00
parent af0628867f
commit a7f218e182
63 changed files with 10539 additions and 0 deletions

114
apps/server/src/index.ts Normal file
View File

@@ -0,0 +1,114 @@
import Fastify from 'fastify';
import fastifyStatic from '@fastify/static';
import fastifyWebsocket from '@fastify/websocket';
import { existsSync } from 'node:fs';
import { resolve } from 'node:path';
import { loadConfig } from './config.js';
import { getSql, applySchema, pingDb, closeDb } from './db.js';
import { registerAuth } from './auth.js';
import { registerProjectRoutes } from './routes/projects.js';
import { registerSessionRoutes } from './routes/sessions.js';
import { registerSettingsRoutes } from './routes/settings.js';
import { registerMessageRoutes } from './routes/messages.js';
import { registerWebSocket } from './routes/ws.js';
import { registerModelRoutes } from './routes/models.js';
import { createInferenceRunner } from './services/inference.js';
import { createBroker } from './services/broker.js';
async function main() {
const config = loadConfig();
const app = Fastify({
logger: { level: config.LOG_LEVEL },
});
const sql = getSql(config);
await applySchema(sql);
app.log.info('database schema applied');
await app.register(fastifyWebsocket);
registerAuth(app);
app.get('/api/health', async () => {
const dbOk = await pingDb(sql);
return { status: dbOk ? 'ok' : 'degraded', db: dbOk };
});
registerProjectRoutes(app, sql, config);
registerSessionRoutes(app, sql, config);
registerSettingsRoutes(app, sql);
registerModelRoutes(app, config);
const broker = createBroker();
const inference = createInferenceRunner({
sql,
config,
log: app.log,
publish: (sessionId, frame) => {
broker.publish(sessionId, frame as unknown as Record<string, unknown> & { type: string });
},
});
registerMessageRoutes(app, sql, {
onSend: (sessionId, _userId, assistantId) => {
inference.enqueue(sessionId, assistantId);
},
publishUserMessage: (sessionId, userMessageId, content) => {
broker.publish(sessionId, {
type: 'message_started',
message_id: userMessageId,
role: 'user',
});
broker.publish(sessionId, {
type: 'delta',
message_id: userMessageId,
content,
});
broker.publish(sessionId, {
type: 'message_complete',
message_id: userMessageId,
});
},
});
registerWebSocket(app, sql, broker);
const webDist = process.env.WEB_DIST_PATH ?? resolve(process.cwd(), '../web/dist');
if (existsSync(webDist)) {
await app.register(fastifyStatic, {
root: webDist,
prefix: '/',
wildcard: false,
});
app.setNotFoundHandler((req, reply) => {
if (req.url.startsWith('/api')) {
reply.code(404).send({ error: 'not found' });
return;
}
reply.sendFile('index.html');
});
app.log.info(`serving static frontend from ${webDist}`);
}
const shutdown = async (signal: string) => {
app.log.info(`received ${signal}, shutting down`);
try {
await app.close();
await closeDb();
process.exit(0);
} catch (err) {
app.log.error(err);
process.exit(1);
}
};
process.on('SIGINT', () => void shutdown('SIGINT'));
process.on('SIGTERM', () => void shutdown('SIGTERM'));
await app.listen({ port: config.PORT, host: config.HOST });
app.log.info(`boocode server listening on http://${config.HOST}:${config.PORT}`);
}
main().catch((err) => {
console.error('Fatal startup error:', err);
process.exit(1);
});