Files
hotelsync/backend/src/app.ts
HotelSync 0e3a7347e0 fix: allow cross-origin image loading + hide legend on small screens
- Set crossOriginResourcePolicy: cross-origin in helmet so browsers
  can load uploaded images from api.hotelsync.ru when app runs on
  app.hotelsync.ru (was blocked by same-origin CORP header)
- Hide booking status legend squares below lg breakpoint (hidden lg:flex)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 20:08:48 +03:00

113 lines
4.7 KiB
TypeScript

import Fastify, { FastifyRequest, FastifyReply } from 'fastify'
import jwt from '@fastify/jwt'
import cookie from '@fastify/cookie'
import cors from '@fastify/cors'
import helmet from '@fastify/helmet'
import rateLimit from '@fastify/rate-limit'
import multipart from '@fastify/multipart'
import staticFiles from '@fastify/static'
import { join } from 'path'
import { config } from './config'
import './types' // side-effect: augments fastify types
import wsRoutes from './routes/ws'
import authRoutes from './routes/auth'
import hotelsRoutes from './routes/hotels'
import roomsRoutes from './routes/rooms'
import bookingsRoutes from './routes/bookings'
import housekeepingRoutes from './routes/housekeeping'
import channelsRoutes from './routes/channels'
import usersRoutes from './routes/users'
import netupRoutes from './routes/netup'
import guestsRoutes from './routes/guests'
import bookingGuestsRoutes from './routes/booking-guests'
import hotelSettingsRoutes from './routes/hotel-settings'
import rentalRoutes from './routes/rental'
import categoriesRoutes from './routes/categories'
import tariffsRoutes from './routes/tariffs'
import ratePeriodsRoutes from './routes/rate-periods'
import rateOverridesRoutes from './routes/rate-overrides'
import uploadRoutes from './routes/upload'
import housekeepingSettingsRoutes from './routes/housekeeping-settings'
import notificationsRoutes from './routes/notifications'
import { startJobs } from './jobs'
export async function buildApp() {
const fastify = Fastify({
logger: {
level: config.nodeEnv === 'production' ? 'warn' : 'info',
...(config.nodeEnv !== 'production' && {
transport: { target: 'pino-pretty', options: { colorize: true } },
}),
},
bodyLimit: 20 * 1024 * 1024, // 20MB — for base64 photo uploads
})
// ── File uploads & static ─────────────────────────────────────────────────
await fastify.register(multipart)
const uploadsDir = process.env.UPLOADS_DIR ?? join(process.cwd(), 'uploads')
await fastify.register(staticFiles, { root: uploadsDir, prefix: '/uploads/' })
// ── Security ───────────────────────────────────────────────────────────────
await fastify.register(helmet, { contentSecurityPolicy: false, crossOriginResourcePolicy: { policy: 'cross-origin' } })
await fastify.register(cors, {
origin: config.cors.origins,
credentials: true,
methods: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'OPTIONS'],
})
await fastify.register(rateLimit, {
max: 200,
timeWindow: '1 minute',
})
// ── Cookies & JWT ──────────────────────────────────────────────────────────
await fastify.register(cookie)
await fastify.register(jwt, {
secret: config.jwt.secret,
})
// Authenticate decorator used as onRequest hook in routes
fastify.decorate(
'authenticate',
async (request: FastifyRequest, reply: FastifyReply) => {
try {
await request.jwtVerify()
} catch (err) {
reply.code(401).send({ error: 'Unauthorized' })
}
},
)
// ── Health check ───────────────────────────────────────────────────────────
fastify.get('/health', async () => ({ status: 'ok', ts: new Date().toISOString() }))
// ── Routes ─────────────────────────────────────────────────────────────────
await fastify.register(wsRoutes)
await fastify.register(authRoutes)
await fastify.register(hotelsRoutes)
await fastify.register(roomsRoutes)
await fastify.register(bookingsRoutes)
await fastify.register(housekeepingRoutes)
await fastify.register(channelsRoutes)
await fastify.register(usersRoutes)
await fastify.register(netupRoutes)
await fastify.register(guestsRoutes)
await fastify.register(bookingGuestsRoutes)
await fastify.register(hotelSettingsRoutes)
await fastify.register(rentalRoutes)
await fastify.register(categoriesRoutes)
await fastify.register(tariffsRoutes)
await fastify.register(ratePeriodsRoutes)
await fastify.register(rateOverridesRoutes)
await fastify.register(uploadRoutes)
await fastify.register(housekeepingSettingsRoutes)
await fastify.register(notificationsRoutes)
startJobs()
return fastify
}