Files
order_site/device-limit-artifacts/MODIFIED_FILE.ts
T

141 lines
4.4 KiB
TypeScript

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<CreateWorkerSessionResult> {
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<WorkerSessionRow>(
`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<WorkerSessionRow>(
`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<WorkerSessionRow | null> {
const result = await query<WorkerSessionRow>(
'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<boolean> {
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<boolean> {
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<number> {
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<WorkerSessionRow[]> {
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<WorkerSessionRow>(
`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
}