mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-07-31 13:51:30 +02:00
Merge 0749a55deb
into 878f0787ba
This commit is contained in:
commit
0faa419624
@ -37,6 +37,7 @@ const CronManager = require('./managers/CronManager')
|
||||
const ApiCacheManager = require('./managers/ApiCacheManager')
|
||||
const BinaryManager = require('./managers/BinaryManager')
|
||||
const ShareManager = require('./managers/ShareManager')
|
||||
const LastSeenManager = require('./managers/LastSeenManager')
|
||||
const LibraryScanner = require('./scanner/LibraryScanner')
|
||||
|
||||
//Import the main Passport and Express-Session library
|
||||
@ -107,6 +108,7 @@ class Server {
|
||||
this.cronManager = new CronManager(this.podcastManager, this.playbackSessionManager)
|
||||
this.apiCacheManager = new ApiCacheManager()
|
||||
this.binaryManager = new BinaryManager()
|
||||
this.lastSeenManager = new LastSeenManager()
|
||||
|
||||
// Routers
|
||||
this.apiRouter = new ApiRouter(this)
|
||||
@ -130,6 +132,20 @@ class Server {
|
||||
this.auth.isAuthenticated(req, res, next)
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware to track user activity for lastSeen updates
|
||||
*
|
||||
* @param {import('express').Request} req
|
||||
* @param {import('express').Response} res
|
||||
* @param {import('express').NextFunction} next
|
||||
*/
|
||||
lastSeenMiddleware(req, res, next) {
|
||||
if (req.user && req.user.id) {
|
||||
this.lastSeenManager.addActiveUser(req.user.id)
|
||||
}
|
||||
next()
|
||||
}
|
||||
|
||||
cancelLibraryScan(libraryId) {
|
||||
LibraryScanner.setCancelLibraryScan(libraryId)
|
||||
}
|
||||
@ -171,6 +187,7 @@ class Server {
|
||||
const libraries = await Database.libraryModel.getAllWithFolders()
|
||||
await this.cronManager.init(libraries)
|
||||
this.apiCacheManager.init()
|
||||
this.lastSeenManager.init()
|
||||
|
||||
if (Database.serverSettings.scannerDisableWatcher) {
|
||||
Logger.info(`[Server] Watcher is disabled`)
|
||||
@ -310,7 +327,7 @@ class Server {
|
||||
// Skip JSON parsing for internal-api routes
|
||||
router.use(/^(?!\/internal-api).*/, express.json({ limit: '10mb' }))
|
||||
|
||||
router.use('/api', this.auth.ifAuthNeeded(this.authMiddleware.bind(this)), this.apiRouter.router)
|
||||
router.use('/api', this.auth.ifAuthNeeded(this.authMiddleware.bind(this)), this.lastSeenMiddleware.bind(this), this.apiRouter.router)
|
||||
router.use('/hls', this.hlsRouter.router)
|
||||
router.use('/public', this.publicRouter.router)
|
||||
|
||||
@ -499,6 +516,13 @@ class Server {
|
||||
*/
|
||||
async stop() {
|
||||
Logger.info('=== Stopping Server ===')
|
||||
|
||||
// Cleanup LastSeenManager first to flush any pending updates
|
||||
if (this.lastSeenManager) {
|
||||
await this.lastSeenManager.cleanup()
|
||||
Logger.info('[Server] LastSeenManager Cleaned Up')
|
||||
}
|
||||
|
||||
Watcher.close()
|
||||
Logger.info('[Server] Watcher Closed')
|
||||
await SocketAuthority.close()
|
||||
|
@ -282,9 +282,11 @@ class SocketAuthority {
|
||||
client.user = user
|
||||
this.adminEmitter('user_online', client.user.toJSONForPublic(this.Server.playbackSessionManager.sessions))
|
||||
|
||||
// Update user lastSeen without firing sequelize bulk update hooks
|
||||
user.lastSeen = Date.now()
|
||||
await user.save({ hooks: false })
|
||||
if (this.Server.lastSeenManager) {
|
||||
this.Server.lastSeenManager.addActiveUser(user.id)
|
||||
// To ensure actual lastSeen behaviour does not change, we just flush when connecting. This should not add much overhead as this is only for the authenticated socket.
|
||||
this.Server.lastSeenManager.flushActiveUsers()
|
||||
}
|
||||
|
||||
const initialPayload = {
|
||||
userId: client.user.id,
|
||||
|
114
server/managers/LastSeenManager.js
Normal file
114
server/managers/LastSeenManager.js
Normal file
@ -0,0 +1,114 @@
|
||||
const Logger = require('../Logger')
|
||||
const Database = require('../Database')
|
||||
|
||||
/**
|
||||
* Manager for handling lastSeen updates
|
||||
*/
|
||||
class LastSeenManager {
|
||||
constructor() {
|
||||
/** @type {Set<string>} Set of user IDs that have made requests */
|
||||
this.activeUsers = new Set()
|
||||
|
||||
/** @type {NodeJS.Timeout} Flush interval timer */
|
||||
this.flushInterval = null
|
||||
|
||||
/** @type {number} Flush interval */
|
||||
this.flushIntervalMs = 1000 * 60 * 5
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the LastSeenManager
|
||||
* Start the periodic flush process
|
||||
*/
|
||||
init() {
|
||||
Logger.info('[LastSeenManager] Initializing')
|
||||
this.startFlushInterval()
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the periodic flush interval
|
||||
*/
|
||||
startFlushInterval() {
|
||||
if (this.flushInterval) {
|
||||
clearInterval(this.flushInterval)
|
||||
}
|
||||
|
||||
this.flushInterval = setInterval(() => {
|
||||
this.flushActiveUsers()
|
||||
}, this.flushIntervalMs)
|
||||
|
||||
Logger.info(`[LastSeenManager] Started flush interval every ${this.flushIntervalMs / 1000} seconds`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the flush interval
|
||||
*/
|
||||
stopFlushInterval() {
|
||||
if (this.flushInterval) {
|
||||
clearInterval(this.flushInterval)
|
||||
this.flushInterval = null
|
||||
Logger.info('[LastSeenManager] Stopped flush interval')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a user to the active users set
|
||||
* @param {string} userId - User ID
|
||||
*/
|
||||
addActiveUser(userId) {
|
||||
if (userId) {
|
||||
this.activeUsers.add(userId)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush all active users to the database and clear the set
|
||||
* Updates lastSeen timestamp for all users in the set
|
||||
*/
|
||||
async flushActiveUsers() {
|
||||
if (this.activeUsers.size === 0) {
|
||||
Logger.debug('[LastSeenManager] No active users to flush')
|
||||
return
|
||||
}
|
||||
|
||||
const userIds = Array.from(this.activeUsers)
|
||||
const currentTime = Date.now()
|
||||
|
||||
Logger.debug(`[LastSeenManager] Flushing ${userIds.length} active users to database`)
|
||||
|
||||
try {
|
||||
const affectedRows = await Database.userModel.update(
|
||||
{ lastSeen: new Date(currentTime) },
|
||||
{
|
||||
where: {
|
||||
id: userIds
|
||||
},
|
||||
hooks: false
|
||||
}
|
||||
)
|
||||
|
||||
Logger.debug(`[LastSeenManager] Successfully updated lastSeen for ${affectedRows[0]} users`)
|
||||
|
||||
// Clear the active users set
|
||||
this.activeUsers.clear()
|
||||
} catch (error) {
|
||||
Logger.error(`[LastSeenManager] Failed to flush active users:`, error)
|
||||
}
|
||||
}
|
||||
|
||||
async forceFlush() {
|
||||
Logger.info('[LastSeenManager] Force flushing active users')
|
||||
await this.flushActiveUsers()
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup and stop all processes
|
||||
*/
|
||||
async cleanup() {
|
||||
Logger.info('[LastSeenManager] Cleaning up')
|
||||
this.stopFlushInterval()
|
||||
await this.forceFlush()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LastSeenManager
|
Loading…
Reference in New Issue
Block a user