mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-10 12:42:27 +09:00
79 lines
3.0 KiB
TypeScript
79 lines
3.0 KiB
TypeScript
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
import {createInitializer, createShutdown} from '@app/api/app/APILifecycle';
|
|
import {registerControllers} from '@app/api/app/ControllerRegistry';
|
|
import {configureMiddleware} from '@app/api/app/MiddlewarePipeline';
|
|
import type {APIConfig} from '@app/api/config/APIConfig';
|
|
import type {ILogger} from '@app/api/ILogger';
|
|
import {recordRequestStatus} from '@app/api/middleware/RequestErrorTelemetry';
|
|
import type {HonoApp, HonoEnv} from '@app/api/types/HonoEnv';
|
|
import {AppErrorHandler, AppNotFoundHandler} from '@fluxer/errors/src/domains/core/ErrorHandlers';
|
|
import {IpBannedError} from '@fluxer/errors/src/domains/moderation/IpBannedError';
|
|
import {resolveErrorStatus} from '@fluxer/errors/src/error_handling/ErrorIntrospection';
|
|
import {createMetricsMiddleware} from '@fluxer/hono/src/middleware/Metrics';
|
|
import {LOCKED_DOWN_PERMISSIONS_POLICY, securityHeaders} from '@fluxer/hono/src/middleware/SecurityHeaders';
|
|
import {setIsDevelopment} from '@fluxer/schema/src/primitives/UrlValidators';
|
|
import type {Context} from 'hono';
|
|
import {Hono} from 'hono';
|
|
|
|
interface CreateAPIAppOptions {
|
|
config: APIConfig;
|
|
logger: ILogger;
|
|
registerRoutes?: (routes: HonoApp) => void;
|
|
}
|
|
|
|
interface APIAppResult {
|
|
app: HonoApp;
|
|
initialize: () => Promise<void>;
|
|
shutdown: () => Promise<void>;
|
|
}
|
|
|
|
function TelemetryAwareAppErrorHandler(err: Error, ctx: Context<HonoEnv>): Response | Promise<Response> {
|
|
if (!(err instanceof IpBannedError)) {
|
|
const status = resolveErrorStatus(err);
|
|
if (status !== null && !ctx.get('user')) {
|
|
recordRequestStatus(ctx.req.raw, status);
|
|
}
|
|
}
|
|
return AppErrorHandler(err, ctx);
|
|
}
|
|
|
|
export async function createAPIApp(options: CreateAPIAppOptions): Promise<APIAppResult> {
|
|
const {config, logger} = options;
|
|
const shutdownApiLifecycle = createShutdown(config, logger);
|
|
setIsDevelopment(config.nodeEnv === 'development');
|
|
const routes = new Hono<HonoEnv>({strict: true});
|
|
configureMiddleware(routes, {
|
|
logger,
|
|
nodeEnv: config.nodeEnv,
|
|
corsOrigins: [...config.endpoints.webAppOrigins, config.endpoints.marketing],
|
|
trustClientIpHeader: config.proxy.trust_client_ip_header,
|
|
clientIpHeaderName: config.proxy.client_ip_header,
|
|
maxInflightRequests: config.maxInflightRequests,
|
|
});
|
|
routes.onError(TelemetryAwareAppErrorHandler);
|
|
routes.notFound(AppNotFoundHandler);
|
|
registerControllers(routes, config);
|
|
options.registerRoutes?.(routes);
|
|
const app = new Hono<HonoEnv>({strict: true});
|
|
const {middleware: metricsMiddleware, metricsHandler} = createMetricsMiddleware('api');
|
|
app.use('*', metricsMiddleware);
|
|
app.get('/_metrics', metricsHandler);
|
|
app.use(
|
|
'*',
|
|
securityHeaders({
|
|
contentSecurityPolicy: "default-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'",
|
|
permissionsPolicy: LOCKED_DOWN_PERMISSIONS_POLICY,
|
|
}),
|
|
);
|
|
app.route('/v1', routes);
|
|
app.route('/', routes);
|
|
app.onError(TelemetryAwareAppErrorHandler);
|
|
app.notFound(AppNotFoundHandler);
|
|
return {
|
|
app,
|
|
initialize: createInitializer(config, logger),
|
|
shutdown: shutdownApiLifecycle,
|
|
};
|
|
}
|