import { query, withTransaction } from '../../db/client.js' import type { WorkerSessionRow } from './types.js' export type CreateWorkerSessionInput = { sessionId: string workerId: number deviceId: string deviceType: string deviceName: string userAgent: string ipAddress: string issuedAt: string expiresAt: string now: string maxDevices: number } export type CreateWorkerSessionResult = { created: boolean activeSessions: WorkerSessionRow[] session: WorkerSessionRow | null } export async function createWorkerSessionRecord( input: CreateWorkerSessionInput, ): Promise { return withTransaction(async (client) => { // 对账号加行锁,避免并发登录同时绕过设备数量限制。 await client.query('SELECT id FROM worker_users WHERE id = $1 FOR UPDATE', [input.workerId]) await client.query( `UPDATE worker_sessions SET status = 'revoked', revoked_at = $2, updated_at = $2 WHERE worker_id = $1 AND status = 'active' AND expires_at <= $2`, [input.workerId, input.now], ) await client.query( `UPDATE worker_sessions SET status = 'revoked', revoked_at = $3, updated_at = $3 WHERE worker_id = $1 AND device_id = $2 AND status = 'active'`, [input.workerId, input.deviceId, input.now], ) const activeResult = await client.query( `SELECT * FROM worker_sessions WHERE worker_id = $1 AND device_type = $2 AND status = 'active' AND expires_at > $3 ORDER BY last_seen_at DESC, id DESC`, [input.workerId, input.deviceType, input.now], ) const activeSessions = activeResult.rows if (activeSessions.length >= input.maxDevices) { return { created: false, activeSessions, session: null } } const result = await client.query( `INSERT INTO worker_sessions ( session_id, worker_id, device_id, device_type, device_name, user_agent, ip_address, status, issued_at, last_seen_at, expires_at, created_at, updated_at ) VALUES ($1, $2, $3, $4, $5, $6, $7, 'active', $8, $8, $9, $10, $10) RETURNING *`, [ input.sessionId, input.workerId, input.deviceId, input.deviceType, input.deviceName, input.userAgent, input.ipAddress, input.issuedAt, input.expiresAt, input.now, ], ) return { created: true, activeSessions, session: result.rows[0] || null } }) } export async function getWorkerSessionBySessionId( sessionId: string, ): Promise { const result = await query( 'SELECT * FROM worker_sessions WHERE session_id = $1 LIMIT 1', [String(sessionId || '').trim()], ) return result.rows[0] || null } export async function touchWorkerSession(sessionId: string, now: string): Promise { const result = await query( `UPDATE worker_sessions SET last_seen_at = $2, updated_at = $2 WHERE session_id = $1 AND status = 'active' AND expires_at > $2`, [String(sessionId || '').trim(), now], ) return (result.rowCount || 0) > 0 } export async function revokeWorkerSession( workerId: number | string, sessionId: string, now: string, ): Promise { const result = await query( `UPDATE worker_sessions SET status = 'revoked', revoked_at = $3, updated_at = $3 WHERE worker_id = $1 AND session_id = $2 AND status = 'active'`, [Number(workerId), String(sessionId || '').trim(), now], ) return (result.rowCount || 0) > 0 } export async function revokeAllWorkerSessions( workerId: number | string, now: string, ): Promise { const result = await query( `UPDATE worker_sessions SET status = 'revoked', revoked_at = $2, updated_at = $2 WHERE worker_id = $1 AND status = 'active'`, [Number(workerId), now], ) return result.rowCount || 0 } export async function listWorkerSessions( workerId: number | string, now: string, ): Promise { await query( `UPDATE worker_sessions SET status = 'revoked', revoked_at = $2, updated_at = $2 WHERE worker_id = $1 AND status = 'active' AND expires_at <= $2`, [Number(workerId), now], ) const result = await query( `SELECT * FROM worker_sessions WHERE worker_id = $1 AND status = 'active' AND expires_at > $2 ORDER BY device_type ASC, last_seen_at DESC, id DESC`, [Number(workerId), now], ) return result.rows }