initial commit
This commit is contained in:
158
server/utils/liveSessions.js
Normal file
158
server/utils/liveSessions.js
Normal file
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* In-memory store for live sharing sessions (camera + location).
|
||||
* Sessions expire after TTL_MS without an update.
|
||||
*/
|
||||
|
||||
import { closeRouter, getProducer, getTransport } from './mediasoup.js'
|
||||
|
||||
const TTL_MS = 60_000 // 60 seconds without update = inactive
|
||||
|
||||
const sessions = new Map()
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* id: string
|
||||
* userId: string
|
||||
* label: string
|
||||
* lat: number
|
||||
* lng: number
|
||||
* updatedAt: number
|
||||
* routerId: string | null
|
||||
* producerId: string | null
|
||||
* transportId: string | null
|
||||
* }} LiveSession
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} userId
|
||||
* @param {string} [label]
|
||||
* @returns {LiveSession} The created live session.
|
||||
*/
|
||||
export function createSession(userId, label = '') {
|
||||
const id = crypto.randomUUID()
|
||||
const now = Date.now()
|
||||
const session = {
|
||||
id,
|
||||
userId,
|
||||
label: (label || 'Live').trim() || 'Live',
|
||||
lat: 0,
|
||||
lng: 0,
|
||||
updatedAt: now,
|
||||
routerId: null,
|
||||
producerId: null,
|
||||
transportId: null,
|
||||
}
|
||||
sessions.set(id, session)
|
||||
return session
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
* @returns {LiveSession | undefined} The session or undefined.
|
||||
*/
|
||||
export function getLiveSession(id) {
|
||||
return sessions.get(id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an existing active session for a user (for replacing with a new one).
|
||||
* @param {string} userId
|
||||
* @returns {LiveSession | undefined} The first active session for the user, or undefined.
|
||||
*/
|
||||
export function getActiveSessionByUserId(userId) {
|
||||
const now = Date.now()
|
||||
for (const [, s] of sessions) {
|
||||
if (s.userId === userId && now - s.updatedAt <= TTL_MS) {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
* @param {{ lat?: number, lng?: number, routerId?: string | null, producerId?: string | null, transportId?: string | null }} updates
|
||||
*/
|
||||
export function updateLiveSession(id, updates) {
|
||||
const session = sessions.get(id)
|
||||
if (!session) return
|
||||
const now = Date.now()
|
||||
if (Number.isFinite(updates.lat)) session.lat = updates.lat
|
||||
if (Number.isFinite(updates.lng)) session.lng = updates.lng
|
||||
if (updates.routerId !== undefined) session.routerId = updates.routerId
|
||||
if (updates.producerId !== undefined) session.producerId = updates.producerId
|
||||
if (updates.transportId !== undefined) session.transportId = updates.transportId
|
||||
session.updatedAt = now
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} id
|
||||
*/
|
||||
export function deleteLiveSession(id) {
|
||||
sessions.delete(id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all sessions (for tests only).
|
||||
*/
|
||||
export function clearSessions() {
|
||||
sessions.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sessions updated within TTL_MS (active only).
|
||||
* Also cleans up expired sessions.
|
||||
* @returns {Promise<Array<{ id: string, userId: string, label: string, lat: number, lng: number, updatedAt: number, hasStream: boolean }>>} Active sessions with hasStream flag.
|
||||
*/
|
||||
export async function getActiveSessions() {
|
||||
const now = Date.now()
|
||||
const result = []
|
||||
const expiredIds = []
|
||||
for (const [id, s] of sessions) {
|
||||
if (now - s.updatedAt <= TTL_MS) {
|
||||
result.push({
|
||||
id: s.id,
|
||||
userId: s.userId,
|
||||
label: s.label,
|
||||
lat: s.lat,
|
||||
lng: s.lng,
|
||||
updatedAt: s.updatedAt,
|
||||
hasStream: Boolean(s.producerId),
|
||||
})
|
||||
}
|
||||
else {
|
||||
expiredIds.push(id)
|
||||
}
|
||||
}
|
||||
// Clean up expired sessions and their WebRTC resources
|
||||
for (const id of expiredIds) {
|
||||
const session = sessions.get(id)
|
||||
if (session) {
|
||||
// Clean up producer if it exists
|
||||
if (session.producerId) {
|
||||
const producer = getProducer(session.producerId)
|
||||
if (producer) {
|
||||
producer.close()
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up transport if it exists
|
||||
if (session.transportId) {
|
||||
const transport = getTransport(session.transportId)
|
||||
if (transport) {
|
||||
transport.close()
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up router
|
||||
if (session.routerId) {
|
||||
await closeRouter(id).catch((err) => {
|
||||
console.error(`[liveSessions] Error closing router for expired session ${id}:`, err)
|
||||
})
|
||||
}
|
||||
|
||||
sessions.delete(id)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user