import fastifyCookie from '@fastify/cookie';
import fastifyMultipart from '@fastify/multipart';
import fastifyStatic from '@fastify/static';
import fastifyWebsocket from '@fastify/websocket';
import Fastify from 'fastify';
import path from 'node:path';
import { registerAuthRoutes } from './routes/authRoutes.js';
import { registerFileRoutes } from './routes/fileRoutes.js';
import { registerTerminalLayoutRoutes } from './routes/terminalLayoutRoutes.js';
import { registerTerminalRoutes } from './routes/terminalRoutes.js';
import { registerTerminalWebSocket } from './routes/terminalWebSocket.js';
import { registerUploadRoutes } from './routes/uploadRoutes.js';
import { registerTotpRoutes } from './routes/totpRoutes.js';
import { registerSecurityRoutes } from './routes/securityRoutes.js';
import { registerUpdateRoutes } from './routes/updateRoutes.js';
import { registerAgentRoutes } from './agent/agentRoutes.js';
import { registerAgentManagementRoutes } from './routes/agentManagementRoutes.js';
import { registerNotificationAssetRoutes } from './routes/notificationAssetRoutes.js';
import { registerClientDiagnosticsRoutes } from './routes/clientDiagnosticsRoutes.js';
export async function buildApp(config, services) {
    const app = Fastify({ logger: true, trustProxy: config.trustProxy });
    const contentSecurityPolicy = buildContentSecurityPolicy(config);
    await app.register(fastifyCookie, {
        secret: config.sessionSecret
    });
    await app.register(fastifyWebsocket);
    await app.register(fastifyMultipart, {
        throwFileSizeLimit: false,
        limits: {
            files: config.uploadMaxFiles,
            fileSize: config.uploadMaxFileBytes + 1,
            parts: config.uploadMaxFiles + 1,
            fields: 1
        }
    });
    app.addHook('onRequest', async (_request, reply) => {
        setSecurityHeaders(reply, contentSecurityPolicy);
    });
    await registerAuthRoutes(app, config, services);
    await registerTotpRoutes(app, config, services);
    await registerSecurityRoutes(app, config, services);
    await registerUpdateRoutes(app, config, services);
    await registerTerminalRoutes(app, config, services);
    await registerTerminalLayoutRoutes(app, config, services);
    await registerUploadRoutes(app, config, services);
    await registerFileRoutes(app, config, services);
    await registerNotificationAssetRoutes(app, config, services);
    await registerClientDiagnosticsRoutes(app, config, services);
    await registerTerminalWebSocket(app, config, services);
    if (config.agentToken) {
        await registerAgentRoutes(app, config, services);
    }
    await registerAgentManagementRoutes(app, config, services);
    app.addHook('onSend', async (_request, reply, payload) => {
        const contentType = reply.getHeader('content-type');
        if (typeof contentType === 'string' && contentType.includes('text/html')) {
            reply.header('Cache-Control', 'no-store');
        }
        return payload;
    });
    await app.register(fastifyStatic, {
        root: config.staticRoot,
        prefix: '/'
    });
    app.setNotFoundHandler(async (request, reply) => {
        const requestPath = request.url.split('?', 1)[0] ?? request.url;
        if (requestPath === '/api' || requestPath.startsWith('/api/')) {
            return reply.code(404).send({ error: 'not_found' });
        }
        reply.header('Cache-Control', 'no-store');
        return reply.sendFile('index.html', path.resolve(config.staticRoot));
    });
    return app;
}
function setSecurityHeaders(reply, contentSecurityPolicy) {
    reply.header('Content-Security-Policy', contentSecurityPolicy);
    reply.header('X-Content-Type-Options', 'nosniff');
    reply.header('X-Frame-Options', 'DENY');
    reply.header('Referrer-Policy', 'no-referrer');
}
function buildContentSecurityPolicy(config) {
    const websocketSources = websocketSourcesForOrigins(config.allowedOrigins);
    return [
        "default-src 'self'",
        "base-uri 'self'",
        "object-src 'none'",
        "frame-ancestors 'none'",
        "form-action 'self'",
        "script-src 'self'",
        "style-src 'self' 'unsafe-inline'",
        "img-src 'self' data: blob:",
        "media-src 'self' blob:",
        "font-src 'self' data:",
        `connect-src 'self'${websocketSources.length ? ` ${websocketSources.join(' ')}` : ''}`,
        "frame-src 'self' blob:",
        "worker-src 'self' blob:"
    ].join('; ');
}
function websocketSourcesForOrigins(origins) {
    const sources = new Set();
    for (const origin of origins) {
        try {
            const parsed = new URL(origin);
            if (parsed.protocol === 'http:') {
                sources.add(`ws://${parsed.host}`);
            }
            else if (parsed.protocol === 'https:') {
                sources.add(`wss://${parsed.host}`);
            }
        }
        catch {
            // Ignore malformed values here; origin validation still rejects them at request time.
        }
    }
    return [...sources];
}
//# sourceMappingURL=http.js.map