import { Request, Response, NextFunction } from 'express'; import { pool } from '../db'; declare global { // A namespace is the only way to spell an Express type augmentation — the // interface has to merge into the one Express declares, and Express declares // it inside a namespace. There is no ES module form of this, so the rule is // disabled here rather than worked around. // eslint-disable-next-line @typescript-eslint/no-namespace namespace Express { interface Request { customerId?: number; } } } /** Who a session cookie belongs to, if it is still valid. */ interface SessionOwnerRow { customer_id: number; } export async function attachCustomer(req: Request, _res: Response, next: NextFunction): Promise { const token = req.cookies?.rd_session; if (!token) return next(); // Joined to customers so a disabled account stops resolving at once, rather // than when its 30-day cookie eventually expires. Register, login and // password reset all mint sessions, so checking here covers every path // instead of three separate ones. const { rows } = await pool.query( `SELECT s.customer_id FROM customer_sessions s JOIN customers c ON c.id = s.customer_id WHERE s.token = $1 AND s.expires_at > now() AND c.disabled_at IS NULL`, [token] ); const [session] = rows; if (session) req.customerId = session.customer_id; next(); } export function requireCustomer(req: Request, res: Response, next: NextFunction): void { if (!req.customerId) { res.status(401).json({ error: 'not authenticated' }); return; } next(); }