← Files BranchaARCHIVED FILE
skills/brancha/app/src/server/app.ts
2.73 KB · Oct 3, 2026 · 06:30 UTC
import type { IncomingMessage, ServerResponse } from 'node:http';
import { HttpError } from './http/errors.js';
import { Router } from './http/router.js';
import { sendJson } from './http/response.js';
import { serveStatic } from './http/static.js';
import { isAllowedHost } from './http/security.js';
import { NodeRepository } from './nodes/node.repository.js';
import { registerNodeRoutes } from './nodes/node.routes.js';
import { NodeService } from './nodes/node.service.js';
import { ProjectRepository } from './projects/project.repository.js';
import { registerProjectRoutes } from './projects/project.routes.js';
import { ProjectService } from './projects/project.service.js';
import { projectEvents } from './realtime/events.js';
import { startChangeWatcher } from './realtime/change-watcher.js';
import { ArtifactStore } from './artifacts/artifact.store.js';
import { registerSettingsRoutes } from './settings/settings.routes.js';
import { SettingsStore } from './settings/settings.store.js';
export function createRequestHandler() {
const projects = new ProjectRepository();
const nodes = new NodeRepository();
const artifacts = new ArtifactStore();
const settings = new SettingsStore();
const router = new Router();
startChangeWatcher();
router.get('/api/health', ({ res }) => sendJson(res, 200, {
service: 'brancha',
version: process.env.BRANCHA_VERSION || 'development',
pid: process.pid,
instanceId: process.env.BRANCHA_INSTANCE_ID || null,
capabilities: ['batch-nodes', 'optimistic-concurrency', 'server-seq', 'direct-sql', 'direct-artifacts', 'external-change-events', 'theme-settings', 'locale-settings'],
}));
registerSettingsRoutes(router, settings);
registerProjectRoutes(router, new ProjectService(projects, nodes, artifacts));
registerNodeRoutes(router, new NodeService(nodes, projects, artifacts));
router.get('/api/events', ({ req, res }) => projectEvents.connect(req, res));
return async function handleRequest(req: IncomingMessage, res: ServerResponse) {
const path = new URL(req.url || '/', 'http://localhost').pathname;
try {
if (!isAllowedHost(req)) throw new HttpError(403, 'host is not allowed', 'HOST_NOT_ALLOWED');
if (path.startsWith('/api/')) {
if (!await router.handle(req, res, path)) sendJson(res, 404, { error: 'not found', code: 'ROUTE_NOT_FOUND' });
return;
}
serveStatic(res, path);
} catch (error) {
const status = error instanceof HttpError ? error.status : 500;
if (status === 500) console.error('[server]', error);
if (!res.headersSent) sendJson(res, status, {
error: error instanceof Error ? error.message : String(error),
code: error instanceof HttpError ? error.code : 'INTERNAL_ERROR',
});
}
};
}
SHA-256: d2182e0f2784ba61e0296d4ddceccbf8640622c54fc0ee7166f176d5adc5d55f