181 lines
5.9 KiB
TypeScript
181 lines
5.9 KiB
TypeScript
import { randomUUID } from 'node:crypto';
|
|
import cors from '@fastify/cors';
|
|
import rateLimit from '@fastify/rate-limit';
|
|
import Fastify, { type FastifyInstance } from 'fastify';
|
|
import { loadConfig, type AppConfig } from './config.js';
|
|
import { registerHealthRoutes, type MqttHealthProvider } from './routes/health.js';
|
|
import {
|
|
registerPlatformBootstrapRoutes,
|
|
type PlatformConfigResolver
|
|
} from './routes/platform-bootstrap.js';
|
|
import { registerAuthRoutes, type AuthRouteOptions } from './routes/auth.js';
|
|
import {
|
|
registerUserManagementRoutes,
|
|
type UserManagementRouteOptions
|
|
} from './routes/user-management.js';
|
|
import {
|
|
registerStoreRoomRoutes,
|
|
type StoreRoomRouteOptions
|
|
} from './routes/store-room-management.js';
|
|
import {
|
|
registerContentRoutes,
|
|
type ContentRouteOptions
|
|
} from './routes/content-management.js';
|
|
import {
|
|
registerStoreDiscoveryRoutes,
|
|
type StoreDiscoveryRouteOptions
|
|
} from './routes/store-discovery.js';
|
|
import {
|
|
registerStoreAccessRoutes,
|
|
type StoreAccessRouteOptions
|
|
} from './routes/store-access.js';
|
|
import { registerPricingRoutes, type PricingRouteOptions } from './routes/pricing.js';
|
|
import {
|
|
registerOrderStateRoutes, type OrderStateRouteOptions
|
|
} from './routes/order-state.js';
|
|
import {
|
|
registerOrderQueryRoutes, type OrderQueryRouteOptions
|
|
} from './routes/order-query.js';
|
|
import {
|
|
registerOrderManagementRoutes, type OrderManagementRouteOptions
|
|
} from './routes/order-management.js';
|
|
import { registerOrderShareRoutes, type OrderShareRouteOptions } from './routes/order-share.js';
|
|
import { registerPaymentRoutes, type PaymentRouteOptions } from './routes/payments.js';
|
|
import {
|
|
registerThirdPartyRoutes, type ThirdPartyRouteOptions
|
|
} from './routes/third-party.js';
|
|
import { registerDeviceRoutes, type DeviceRouteOptions } from './routes/devices.js';
|
|
import {
|
|
registerDeviceControlRoutes, type DeviceControlRouteOptions
|
|
} from './routes/device-control.js';
|
|
import { registerMemberRoutes, type MemberRouteOptions } from './routes/members.js';
|
|
import { registerRechargeRoutes, type RechargeRouteOptions } from './routes/recharge.js';
|
|
import { registerCleaningRoutes, type CleaningRouteOptions } from './routes/cleaning.js';
|
|
|
|
export interface BuildAppOptions {
|
|
config?: AppConfig;
|
|
platformConfigRepository?: PlatformConfigResolver;
|
|
auth?: AuthRouteOptions;
|
|
userManagement?: UserManagementRouteOptions;
|
|
storeRoom?: StoreRoomRouteOptions;
|
|
content?: ContentRouteOptions;
|
|
storeDiscovery?: StoreDiscoveryRouteOptions;
|
|
storeAccess?: StoreAccessRouteOptions;
|
|
pricing?: PricingRouteOptions;
|
|
orderState?: OrderStateRouteOptions;
|
|
orderQuery?: OrderQueryRouteOptions;
|
|
orderManagement?: OrderManagementRouteOptions;
|
|
orderShare?: OrderShareRouteOptions;
|
|
payment?: PaymentRouteOptions;
|
|
thirdParty?: ThirdPartyRouteOptions;
|
|
mqtt?: MqttHealthProvider;
|
|
devices?: DeviceRouteOptions;
|
|
deviceControl?: DeviceControlRouteOptions;
|
|
members?: MemberRouteOptions;
|
|
recharge?: RechargeRouteOptions;
|
|
cleaning?: CleaningRouteOptions;
|
|
}
|
|
|
|
declare module 'fastify' {
|
|
interface FastifyRequest {
|
|
traceId: string;
|
|
rawBody: string;
|
|
}
|
|
}
|
|
|
|
export async function buildApp(options: BuildAppOptions = {}): Promise<FastifyInstance> {
|
|
const config = options.config ?? loadConfig();
|
|
const app = Fastify({
|
|
logger: {
|
|
level: config.nodeEnv === 'test' ? 'silent' : 'info'
|
|
},
|
|
genReqId: () => randomUUID()
|
|
});
|
|
|
|
app.decorateRequest('traceId', '');
|
|
app.decorateRequest('rawBody', '');
|
|
app.addHook('onRequest', async (request, reply) => {
|
|
const traceHeader = request.headers['x-trace-id'];
|
|
request.traceId = Array.isArray(traceHeader) ? traceHeader[0] : traceHeader || request.id;
|
|
reply.header('x-trace-id', request.traceId);
|
|
});
|
|
|
|
await app.register(cors, {
|
|
origin: config.corsOrigins
|
|
});
|
|
await app.register(rateLimit, {
|
|
max: 120,
|
|
timeWindow: '1 minute'
|
|
});
|
|
|
|
app.setErrorHandler((error, request, reply) => {
|
|
request.log.error({ err: error, traceId: request.traceId }, 'request failed');
|
|
reply.status(500).send({
|
|
code: 'INTERNAL_ERROR',
|
|
message: 'Internal server error',
|
|
traceId: request.traceId
|
|
});
|
|
});
|
|
|
|
await registerHealthRoutes(app, config, options.mqtt);
|
|
if (options.platformConfigRepository) {
|
|
await registerPlatformBootstrapRoutes(app, options.platformConfigRepository);
|
|
}
|
|
if (options.auth) {
|
|
await registerAuthRoutes(app, options.auth);
|
|
}
|
|
if (options.userManagement) {
|
|
await registerUserManagementRoutes(app, options.userManagement);
|
|
}
|
|
if (options.storeRoom) {
|
|
await registerStoreRoomRoutes(app, options.storeRoom);
|
|
}
|
|
if (options.content) {
|
|
await registerContentRoutes(app, options.content);
|
|
}
|
|
if (options.storeDiscovery) {
|
|
await registerStoreDiscoveryRoutes(app, options.storeDiscovery);
|
|
}
|
|
if (options.storeAccess) {
|
|
await registerStoreAccessRoutes(app, options.storeAccess);
|
|
}
|
|
if (options.pricing) {
|
|
await registerPricingRoutes(app, options.pricing);
|
|
}
|
|
if (options.orderState) {
|
|
await registerOrderStateRoutes(app, options.orderState);
|
|
}
|
|
if (options.orderQuery) {
|
|
await registerOrderQueryRoutes(app, options.orderQuery);
|
|
}
|
|
if (options.orderManagement) {
|
|
await registerOrderManagementRoutes(app, options.orderManagement);
|
|
}
|
|
if (options.orderShare) {
|
|
await registerOrderShareRoutes(app, options.orderShare);
|
|
}
|
|
if (options.payment) {
|
|
await registerPaymentRoutes(app, options.payment);
|
|
}
|
|
if (options.thirdParty) {
|
|
await registerThirdPartyRoutes(app, options.thirdParty);
|
|
}
|
|
if (options.devices) {
|
|
await registerDeviceRoutes(app, options.devices);
|
|
}
|
|
if (options.deviceControl) {
|
|
await registerDeviceControlRoutes(app, options.deviceControl);
|
|
}
|
|
if (options.members) {
|
|
await registerMemberRoutes(app, options.members);
|
|
}
|
|
if (options.recharge) {
|
|
await registerRechargeRoutes(app, options.recharge);
|
|
}
|
|
if (options.cleaning) {
|
|
await registerCleaningRoutes(app, options.cleaning);
|
|
}
|
|
|
|
return app;
|
|
}
|