- 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>
113 lines
4.7 KiB
TypeScript
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
|
|
}
|