import { randomBytes } from 'crypto' import { pool } from '../db.js' import { pushNavigate } from './device-stream.js' const SESSION_TTL_MINUTES = 30 // Shared by routes/staff.js (terminal-key auth, the extension's real call) // and routes/devices.js's admin test-push (staff JWT auth) — both just need // "mint a session for this booking on this device", the auth tier differs, // the logic doesn't. See project_checkin's "Submission flow sequencing". export async function pushBookingToDevice(deviceId, bookingId) { const { rows: existing } = await pool.query('SELECT * FROM devices WHERE device_id = $1', [deviceId]) if (!existing.length) return { ok: false, status: 404, error: 'Device not found' } // Atomic claim — the UPDATE's own WHERE status='idle' is the source of // truth, not the SELECT above (which is only used for a friendly device // name in the 409 message), so two near-simultaneous pushes to the same // device can't both "win". const { rows: claimed } = await pool.query( `UPDATE devices SET status = 'busy' WHERE device_id = $1 AND status = 'idle' RETURNING *`, [deviceId] ) if (!claimed.length) { return { ok: false, status: 409, error: `${existing[0].name} is busy with another check-in` } } // Single-hotel-per-stack deployment (see project_checkin memory) — no // per-request "which hotel" lookup needed, this whole app instance already // is one hotel. hotel_id here is purely a local audit-row label, sourced // from the same env var lib/newbook.js sends to the NewBook API itself // (not a separate app-level setting — a local `newbook_hotel_id` Settings // field existed here before 2026-09-01 but was removed as vestigial // multi-hotel-model plumbing that no longer did anything real). const hotelId = process.env.NEWBOOK_LOCATION_ID || '' const token = randomBytes(24).toString('hex') const expiresAt = new Date(Date.now() + SESSION_TTL_MINUTES * 60 * 1000) await pool.query( `INSERT INTO checkin_sessions (token, device_id, booking_id, hotel_id, expires_at) VALUES ($1,$2,$3,$4,$5)`, [token, deviceId, String(bookingId), hotelId, expiresAt] ) const delivered = pushNavigate(deviceId, token) return { ok: true, token, delivered } }