Major UX improvements across multiple pages
Шахматка: - Date/period picker dropdown on navigation button (choose start date + days window) - Cancelled bookings fade out with animation after 1 second Бронирования: - Clickable column headers with sort asc/desc (Гость, Заезд, Выезд, Статус, Источник, Сумма) Страница входа: - Removed role-based account selector — just email + password - System auto-detects role/hotel from credentials Настройки: - New "Бронирование" section with room assignment strategy (spread/together/sequential/manual) - Notifications: SMTP email config + SMS provider config (SMSC, SMS.ru, МТС, etc.) Модули: - Added Housekeeping and Channel Manager as proper modules - Channel Manager lists: Booking.com, Airbnb, Яндекс Путешествия, Островок, Суточно.ру, OneTwoTrip - Housekeeping visible to all roles (including housekeeper) via module status - Sidebar now uses module status to show/hide Уборка and Каналы Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
74
backend/src/app.ts
Normal file
74
backend/src/app.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
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 { config } from './config'
|
||||
import './types' // side-effect: augments fastify types
|
||||
|
||||
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'
|
||||
|
||||
export async function buildApp() {
|
||||
const fastify = Fastify({
|
||||
logger: {
|
||||
level: config.nodeEnv === 'production' ? 'warn' : 'info',
|
||||
...(config.nodeEnv !== 'production' && {
|
||||
transport: { target: 'pino-pretty', options: { colorize: true } },
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
||||
// ── Security ───────────────────────────────────────────────────────────────
|
||||
await fastify.register(helmet, { contentSecurityPolicy: false })
|
||||
|
||||
await fastify.register(cors, {
|
||||
origin: config.cors.origins,
|
||||
credentials: true,
|
||||
methods: ['GET', 'POST', 'PATCH', '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(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)
|
||||
|
||||
return fastify
|
||||
}
|
||||
Reference in New Issue
Block a user