Files
fluxer/fluxer_api/src/api/app/MiddlewarePipeline.ts
T

121 lines
4.6 KiB
TypeScript

// SPDX-License-Identifier: AGPL-3.0-or-later
import {ActivityContextMiddleware} from '@app/api/infrastructure/activity/ActivityMeta';
import {AuditLogMiddleware} from '@app/api/middleware/AuditLogMiddleware';
import {ClientFeaturesMiddleware} from '@app/api/middleware/ClientFeaturesMiddleware';
import {ConcurrencyLimitMiddleware} from '@app/api/middleware/ConcurrencyLimitMiddleware';
import ContentFilterMiddleware from '@app/api/middleware/ContentFilterMiddleware';
import {GuildAvailabilityMiddleware} from '@app/api/middleware/GuildAvailabilityMiddleware';
import {IpBanMiddleware} from '@app/api/middleware/IpBanMiddleware';
import {LocaleMiddleware} from '@app/api/middleware/LocaleMiddleware';
import {RequestCacheMiddleware} from '@app/api/middleware/RequestCacheMiddleware';
import {RequestErrorTelemetry} from '@app/api/middleware/RequestErrorTelemetry';
import {RequireClientIpMiddleware} from '@app/api/middleware/RequireClientIpMiddleware';
import {ServiceMiddleware} from '@app/api/middleware/ServiceMiddleware';
import {TrustedClientIpHeaderMiddleware} from '@app/api/middleware/TrustedClientIpHeaderMiddleware';
import {UserMiddleware} from '@app/api/middleware/UserMiddleware';
import type {HonoApp} from '@app/api/types/HonoEnv';
import {PeerAddressMiddleware} from '@app/api/utils/RequestClientIp';
import {Headers as HttpHeaders} from '@fluxer/constants/src/Headers';
import {InvalidApiOriginError} from '@fluxer/errors/src/domains/core/InvalidApiOriginError';
import {cors} from '@fluxer/hono/src/middleware/Cors';
import {applyMiddlewareStack} from '@fluxer/hono/src/middleware/MiddlewareStack';
import {createInfoRequestLogger, requestLogger} from '@fluxer/hono/src/middleware/RequestLogger';
import {resolveClientIpHeaderName} from '@fluxer/ip_utils/src/ClientIp';
import type {LoggerInterface} from '@fluxer/logger/src/LoggerInterface';
interface MiddlewarePipelineOptions {
logger: LoggerInterface;
nodeEnv: string;
corsOrigins: Array<string>;
trustClientIpHeader: boolean;
clientIpHeaderName?: string;
maxInflightRequests: number;
}
export function configureMiddleware(routes: HonoApp, options: MiddlewarePipelineOptions): void {
const {logger, nodeEnv, corsOrigins, trustClientIpHeader, clientIpHeaderName, maxInflightRequests} = options;
const resolvedHeader = resolveClientIpHeaderName(clientIpHeaderName);
routes.use(PeerAddressMiddleware);
routes.use('/webhooks/:webhook_id/:token', cors({origins: '*'}));
routes.use('/webhooks/:webhook_id/:token/messages/:message_id', cors({origins: '*'}));
routes.use(
'/.well-known/fluxer',
cors({
origins: '*',
methods: ['GET', 'HEAD', 'OPTIONS'],
allowedHeaders: [
HttpHeaders.ACCEPT,
HttpHeaders.CONTENT_TYPE,
HttpHeaders.IF_MODIFIED_SINCE,
HttpHeaders.IF_NONE_MATCH,
],
exposedHeaders: [HttpHeaders.ETAG, HttpHeaders.LAST_MODIFIED],
}),
);
applyMiddlewareStack(routes, {
requestId: {},
cors: {
origins: corsOrigins,
allowedHeaders: [
HttpHeaders.CONTENT_TYPE,
HttpHeaders.AUTHORIZATION,
HttpHeaders.X_REQUESTED_WITH,
HttpHeaders.ACCEPT_LANGUAGE,
HttpHeaders.X_REQUEST_ID,
HttpHeaders.IF_NONE_MATCH,
HttpHeaders.X_AUDIT_LOG_REASON,
HttpHeaders.X_CAPTCHA_ID,
HttpHeaders.X_CAPTCHA_TOKEN,
HttpHeaders.X_FLUXER_CLIENT_INSTALLATION_ID,
HttpHeaders.X_FLUXER_FEATURES,
HttpHeaders.X_FLUXER_PLATFORM,
HttpHeaders.X_FLUXER_SUDO_MODE_JWT,
],
exposedHeaders: [HttpHeaders.X_FLUXER_VERSION, HttpHeaders.ETAG],
},
});
routes.use(ConcurrencyLimitMiddleware({maxInflightRequests}));
routes.get('/_health', async (ctx) => ctx.text('OK'));
routes.use(IpBanMiddleware);
routes.use(
requestLogger({
log: createInfoRequestLogger(logger),
skip: ['/_health'],
}),
);
routes.use(RequestErrorTelemetry);
routes.use(RequestCacheMiddleware);
routes.use(ClientFeaturesMiddleware);
if (nodeEnv === 'production') {
routes.use('*', async (ctx, next) => {
const host = ctx.req.header('host');
if (ctx.req.method !== 'GET' && (host === 'web.fluxer.app' || host === 'web.canary.fluxer.app')) {
const origin = ctx.req.header('origin');
if (!origin || origin !== `https://${host}`) {
throw new InvalidApiOriginError();
}
}
await next();
});
}
if (trustClientIpHeader) {
routes.use(
TrustedClientIpHeaderMiddleware({
enabled: true,
logger,
trustClientIpHeader,
clientIpHeaderName: resolvedHeader,
}),
);
}
routes.use(AuditLogMiddleware);
routes.use(RequireClientIpMiddleware());
routes.use(ActivityContextMiddleware);
routes.use(ServiceMiddleware);
routes.use(UserMiddleware);
routes.use(ContentFilterMiddleware);
routes.use(GuildAvailabilityMiddleware);
routes.use(LocaleMiddleware);
}