Files
qipai/backend/src/app.ts
T

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;
}