refactor progress
This commit is contained in:
198
fluxer_server/src/Routes.tsx
Normal file
198
fluxer_server/src/Routes.tsx
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type {Config} from '@app/Config';
|
||||
import {createHealthCheckHandler, createLivenessCheckHandler, createReadinessCheckHandler} from '@app/HealthCheck';
|
||||
import {createComponentLogger} from '@app/Logger';
|
||||
import {
|
||||
type InitializedServices,
|
||||
initializeAllServices,
|
||||
runServiceInitialization,
|
||||
type ServiceInitializer,
|
||||
shutdownAllServices,
|
||||
startBackgroundServices,
|
||||
} from '@app/ServiceInitializer';
|
||||
import {getBuildMetadata} from '@fluxer/config/src/BuildMetadata';
|
||||
import {AppErrorHandler, AppNotFoundHandler} from '@fluxer/errors/src/domains/core/ErrorHandlers';
|
||||
import {applyMiddlewareStack} from '@fluxer/hono/src/middleware/MiddlewareStack';
|
||||
import {createServiceTelemetry} from '@fluxer/hono/src/middleware/TelemetryAdapters';
|
||||
import type {BaseHonoEnv} from '@fluxer/hono_types/src/HonoTypes';
|
||||
import {Hono} from 'hono';
|
||||
import {trimTrailingSlash} from 'hono/trailing-slash';
|
||||
|
||||
export interface MountedRoutes {
|
||||
app: Hono<BaseHonoEnv>;
|
||||
services: InitializedServices;
|
||||
initialize: () => Promise<void>;
|
||||
start: () => Promise<void>;
|
||||
shutdown: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface MountRoutesOptions {
|
||||
config: Config;
|
||||
staticDir?: string | undefined;
|
||||
}
|
||||
|
||||
const startTime = Date.now();
|
||||
const BUILD_METADATA = getBuildMetadata();
|
||||
|
||||
export async function mountRoutes(options: MountRoutesOptions): Promise<MountedRoutes> {
|
||||
const {config, staticDir} = options;
|
||||
const logger = createComponentLogger('routes');
|
||||
const VERSION = BUILD_METADATA.buildNumber ?? '0.0.0';
|
||||
|
||||
logger.info('Starting route mounting and service initialization');
|
||||
|
||||
const app = new Hono<BaseHonoEnv>();
|
||||
|
||||
app.use(trimTrailingSlash());
|
||||
|
||||
const telemetry = createServiceTelemetry({
|
||||
serviceName: 'fluxer-server',
|
||||
skipPaths: ['/_health', '/_ready', '/_live'],
|
||||
});
|
||||
|
||||
applyMiddlewareStack(app, {
|
||||
requestId: {},
|
||||
tracing: telemetry.tracing,
|
||||
metrics: {
|
||||
enabled: true,
|
||||
collector: telemetry.metricsCollector,
|
||||
skipPaths: ['/_health', '/_ready', '/_live'],
|
||||
},
|
||||
logger: {
|
||||
log: (data) => {
|
||||
logger.info(
|
||||
{
|
||||
method: data.method,
|
||||
path: data.path,
|
||||
status: data.status,
|
||||
durationMs: data.durationMs,
|
||||
},
|
||||
'Request completed',
|
||||
);
|
||||
},
|
||||
skip: ['/_health', '/_ready', '/_live'],
|
||||
},
|
||||
skipErrorHandler: true,
|
||||
});
|
||||
let initializers: Array<ServiceInitializer> = [];
|
||||
let services: InitializedServices = {};
|
||||
|
||||
try {
|
||||
const result = await initializeAllServices({
|
||||
config,
|
||||
logger,
|
||||
staticDir,
|
||||
});
|
||||
|
||||
initializers = result.initializers;
|
||||
services = result.services;
|
||||
|
||||
if (services.s3 !== undefined) {
|
||||
app.route('/s3', services.s3.app);
|
||||
logger.info(config.isMonolith ? 'S3 service mounted at /s3 (restricted mode)' : 'S3 service mounted at /s3');
|
||||
}
|
||||
|
||||
if (services.queue !== undefined && !config.isMonolith) {
|
||||
app.route('/queue', services.queue.app);
|
||||
logger.info('Queue service mounted at /queue');
|
||||
} else if (services.queue !== undefined) {
|
||||
logger.info('Queue service available internally only (monolith mode)');
|
||||
}
|
||||
|
||||
if (services.mediaProxy !== undefined) {
|
||||
app.route('/media', services.mediaProxy.app);
|
||||
logger.info(
|
||||
config.isMonolith
|
||||
? 'Media Proxy service mounted at /media (public-only mode)'
|
||||
: 'Media Proxy service mounted at /media',
|
||||
);
|
||||
}
|
||||
|
||||
if (services.admin !== undefined) {
|
||||
app.route('/admin', services.admin.app);
|
||||
logger.info('Admin service mounted at /admin');
|
||||
}
|
||||
|
||||
if (services.api !== undefined) {
|
||||
const apiService = services.api;
|
||||
app.route('/api', apiService.app);
|
||||
app.get('/.well-known/fluxer', (ctx) => apiService.app.fetch(ctx.req.raw));
|
||||
logger.info('API service mounted at /api');
|
||||
}
|
||||
|
||||
const healthHandler = createHealthCheckHandler({
|
||||
services,
|
||||
staticDir,
|
||||
version: VERSION,
|
||||
startTime,
|
||||
latencyThresholdMs: config.healthCheck.latencyThresholdMs,
|
||||
});
|
||||
app.get('/_health', healthHandler);
|
||||
logger.info('Health check endpoint mounted at /_health');
|
||||
|
||||
const readinessHandler = createReadinessCheckHandler({
|
||||
services,
|
||||
staticDir,
|
||||
version: VERSION,
|
||||
startTime,
|
||||
latencyThresholdMs: config.healthCheck.latencyThresholdMs,
|
||||
});
|
||||
app.get('/_ready', readinessHandler);
|
||||
logger.info('Readiness check endpoint mounted at /_ready');
|
||||
|
||||
const livenessHandler = createLivenessCheckHandler();
|
||||
app.get('/_live', livenessHandler);
|
||||
logger.info('Liveness check endpoint mounted at /_live');
|
||||
|
||||
if (services.appServer !== undefined) {
|
||||
app.route('/', services.appServer.app);
|
||||
logger.info('SPA App server mounted at /');
|
||||
}
|
||||
|
||||
app.onError(AppErrorHandler);
|
||||
app.notFound(AppNotFoundHandler);
|
||||
|
||||
logger.info({serviceCount: initializers.length}, 'All services mounted successfully');
|
||||
} catch (error) {
|
||||
logger.error({error: error instanceof Error ? error.message : 'Unknown error'}, 'Failed to mount routes');
|
||||
throw error;
|
||||
}
|
||||
|
||||
const initialize = async (): Promise<void> => {
|
||||
await runServiceInitialization(initializers, logger);
|
||||
};
|
||||
|
||||
const start = async (): Promise<void> => {
|
||||
await startBackgroundServices(initializers, logger);
|
||||
};
|
||||
|
||||
const shutdown = async (): Promise<void> => {
|
||||
await shutdownAllServices(initializers, logger);
|
||||
};
|
||||
|
||||
return {
|
||||
app,
|
||||
services,
|
||||
initialize,
|
||||
start,
|
||||
shutdown,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user