import { FastifyPluginAsync } from 'fastify' import { db } from '../db' const hotels: FastifyPluginAsync = async (fastify) => { // ── GET /api/hotels ──────────────────────────────────────────────────────── fastify.get( '/api/hotels', { onRequest: [fastify.authenticate] }, async (request, reply) => { if (request.user.role !== 'super_admin') { return reply.code(403).send({ error: 'Forbidden' }) } const { rows } = await db.query( `SELECT h.*, count(u.id)::int AS user_count, count(r.id)::int AS room_count FROM hotels h LEFT JOIN users u ON u.hotel_id = h.id LEFT JOIN rooms r ON r.hotel_id = h.id GROUP BY h.id ORDER BY h.created_at`, ) return rows }, ) // ── POST /api/hotels ─────────────────────────────────────────────────────── fastify.post<{ Body: { name: string; slug: string; plan?: string; timezone?: string; currency?: string } }>( '/api/hotels', { onRequest: [fastify.authenticate] }, async (request, reply) => { if (request.user.role !== 'super_admin') { return reply.code(403).send({ error: 'Forbidden' }) } const { name, slug, plan = 'starter', timezone = 'Europe/Moscow', currency = 'RUB' } = request.body const { rows } = await db.query( `INSERT INTO hotels (name, slug, plan, timezone, currency) VALUES ($1, $2, $3, $4, $5) RETURNING *`, [name, slug, plan, timezone, currency], ) return reply.code(201).send(rows[0]) }, ) // ── GET /api/hotels/:slug ────────────────────────────────────────────────── fastify.get<{ Params: { slug: string } }>( '/api/hotels/:slug', { onRequest: [fastify.authenticate] }, async (request, reply) => { const { slug } = request.params if (request.user.role !== 'super_admin' && request.user.hotelSlug !== slug) { return reply.code(403).send({ error: 'Forbidden' }) } const { rows } = await db.query( `SELECT h.*, (SELECT count(*)::int FROM rooms r WHERE r.hotel_id = h.id) AS room_count, (SELECT count(*)::int FROM users u WHERE u.hotel_id = h.id) AS user_count, (SELECT count(*)::int FROM bookings b WHERE b.hotel_id = h.id AND b.status IN ('confirmed','checked_in')) AS active_bookings FROM hotels h WHERE h.slug = $1`, [slug], ) if (!rows[0]) return reply.code(404).send({ error: 'Hotel not found' }) return rows[0] }, ) // ── PATCH /api/hotels/:slug ──────────────────────────────────────────────── fastify.patch<{ Params: { slug: string }; Body: Record }>( '/api/hotels/:slug', { onRequest: [fastify.authenticate] }, async (request, reply) => { const { slug } = request.params if (request.user.role !== 'super_admin' && request.user.hotelSlug !== slug) { return reply.code(403).send({ error: 'Forbidden' }) } if (request.user.role === 'housekeeper') { return reply.code(403).send({ error: 'Forbidden' }) } const allowed = ['name', 'plan', 'timezone', 'currency'] const updates: string[] = [] const values: unknown[] = [] let idx = 1 for (const key of allowed) { if (request.body[key] !== undefined) { updates.push(`${key} = $${idx}`) values.push(request.body[key]) idx++ } } if (updates.length === 0) return reply.code(400).send({ error: 'Nothing to update' }) updates.push(`updated_at = NOW()`) values.push(slug) const { rows } = await db.query( `UPDATE hotels SET ${updates.join(', ')} WHERE slug = $${idx} RETURNING *`, values, ) if (!rows[0]) return reply.code(404).send({ error: 'Hotel not found' }) return rows[0] }, ) } export default hotels