Remove pull integration; persist push log to DB (last 50 per hotel)

- Remove TravelLine/netup-pms pull route (travelline.ts unused)
- Remove pull integration section from TvWelcomePage (token field, API URL, etc.)
- Migration 008: netup_push_log table (hotel_id, action, status, request/response body)
- recordPush() now writes to DB in addition to in-memory buffer
- getLog reads from DB so logs survive server restarts
- clearLog deletes from DB
- Limit enforced: 50 rows per hotel (oldest auto-deleted)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-19 12:55:39 +03:00
parent 32115399ee
commit ed397dbcd9
4 changed files with 200 additions and 363 deletions

View File

@@ -1,30 +1,47 @@
import type { FastifyPluginAsync } from 'fastify'
import { db } from '../db'
import { captured } from './travelline'
type SlugParam = { Params: { slug: string } }
type SlugIdParam = { Params: { slug: string; id: string } }
// ── Push event log (check-in / check-out calls to NetUP) ─────────────────────
export type PushEvent = {
ts: string
action: 'check-in' | 'check-out' | 'message'
hotelId: string
roomNumber: string // PMS room number
netupRoom: string // NetUP room number
url: string
requestBody?: unknown // what we sent
status: 'ok' | 'error' | 'skipped'
httpStatus?: number
responseBody?: string // what NetUP replied
error?: string
ts: string
action: 'check-in' | 'check-out' | 'message'
hotelId: string
roomNumber: string
netupRoom: string
url: string
requestBody?: unknown
status: 'ok' | 'error' | 'skipped'
httpStatus?: number
responseBody?: string
error?: string
}
const MAX_PUSH = 100
// In-memory fallback (used before DB write completes; also keeps last 50)
export const pushLog: PushEvent[] = []
function recordPush(e: PushEvent) {
pushLog.push(e)
if (pushLog.length > MAX_PUSH) pushLog.shift()
if (pushLog.length > 50) pushLog.shift()
// Persist to DB (fire-and-forget); also enforce 50-row limit per hotel
db.query(
`INSERT INTO netup_push_log
(hotel_id, ts, action, room_number, netup_room, url, request_body, status, http_status, response_body, error)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`,
[e.hotelId, e.ts, e.action, e.roomNumber, e.netupRoom, e.url,
e.requestBody ? JSON.stringify(e.requestBody) : null,
e.status, e.httpStatus ?? null, e.responseBody ?? null, e.error ?? null],
).then(() =>
// Keep only last 50 rows per hotel
db.query(
`DELETE FROM netup_push_log WHERE hotel_id = $1 AND id NOT IN (
SELECT id FROM netup_push_log WHERE hotel_id = $1 ORDER BY ts DESC LIMIT 50
)`,
[e.hotelId],
)
).catch(() => { /* best-effort */ })
}
const netup: FastifyPluginAsync = async (fastify) => {
@@ -270,13 +287,25 @@ const netup: FastifyPluginAsync = async (fastify) => {
return reply.code(403).send({ error: 'Forbidden' })
}
const hotelId = await getHotelId(slug)
// Push events filtered to this hotel
const pushEvents = [...pushLog]
.filter(e => e.hotelId === hotelId)
.reverse()
// Pull requests (from NetUP TravelLine polls) — all, no hotel filter possible
const pullRequests = [...captured].reverse()
return { pushEvents, pullRequests }
const { rows } = await db.query(
`SELECT ts, action, room_number, netup_room, url, request_body,
status, http_status, response_body, error
FROM netup_push_log WHERE hotel_id = $1 ORDER BY ts DESC LIMIT 50`,
[hotelId],
)
const pushEvents = rows.map((r: Record<string, unknown>) => ({
ts: (r.ts as Date).toISOString(),
action: r.action,
roomNumber: r.room_number,
netupRoom: r.netup_room,
url: r.url,
requestBody: r.request_body,
status: r.status,
httpStatus: r.http_status,
responseBody: r.response_body,
error: r.error,
}))
return { pushEvents, pullRequests: [] }
},
)
@@ -289,7 +318,8 @@ const netup: FastifyPluginAsync = async (fastify) => {
if (!canAccess(request.user.hotelSlug, request.user.role, slug)) {
return reply.code(403).send({ error: 'Forbidden' })
}
captured.length = 0
const hotelId2 = await getHotelId(slug)
if (hotelId2) await db.query('DELETE FROM netup_push_log WHERE hotel_id = $1', [hotelId2])
pushLog.length = 0
return { ok: true }
},