mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	first draft
This commit is contained in:
		
							parent
							
								
									25c7e95a64
								
							
						
					
					
						commit
						04ba949182
					
				@ -2,6 +2,7 @@ const SocketIO = require('socket.io')
 | 
			
		||||
const Logger = require('./Logger')
 | 
			
		||||
const Database = require('./Database')
 | 
			
		||||
const Auth = require('./Auth')
 | 
			
		||||
const NotificationManager = require('./managers/NotificationManager')
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @typedef SocketClient
 | 
			
		||||
@ -15,11 +16,25 @@ class SocketAuthority {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    this.Server = null
 | 
			
		||||
    this.socketIoServers = []
 | 
			
		||||
    this.emittedNotifications = new Set(['item_created', 'item_updated']);
 | 
			
		||||
 | 
			
		||||
    /** @type {Object.<string, SocketClient>} */
 | 
			
		||||
    this.clients = {}
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Fires a notification if enabled and the event is whitelisted
 | 
			
		||||
   * @param {string} event - The event name fired. Needs to be whitelisted in this.emitted_notifications
 | 
			
		||||
   * @param {any} payload - The payload to send with the event. For user-specific events, this includes the userId
 | 
			
		||||
   */
 | 
			
		||||
  _fireNotification(event, payload) {
 | 
			
		||||
    // Should be O(1) so no real performance hit
 | 
			
		||||
    if (!this.emittedNotifications.has(event)) return
 | 
			
		||||
    Logger.debug(`[SocketAuthority] fireNotification - ${event}`)
 | 
			
		||||
 | 
			
		||||
    NotificationManager.fireNotificationFromSocket(event, payload)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * returns an array of User.toJSONForPublic with `connections` for the # of socket connections
 | 
			
		||||
   *  a user can have many socket connections
 | 
			
		||||
@ -53,6 +68,7 @@ class SocketAuthority {
 | 
			
		||||
   * @param {Function} [filter] optional filter function to only send event to specific users
 | 
			
		||||
   */
 | 
			
		||||
  emitter(evt, data, filter = null) {
 | 
			
		||||
    void this._fireNotification(evt, data)
 | 
			
		||||
    for (const socketId in this.clients) {
 | 
			
		||||
      if (this.clients[socketId].user) {
 | 
			
		||||
        if (filter && !filter(this.clients[socketId].user)) continue
 | 
			
		||||
@ -64,6 +80,7 @@ class SocketAuthority {
 | 
			
		||||
 | 
			
		||||
  // Emits event to all clients for a specific user
 | 
			
		||||
  clientEmitter(userId, evt, data) {
 | 
			
		||||
    void this._fireNotification(evt, data)
 | 
			
		||||
    const clients = this.getClientsForUser(userId)
 | 
			
		||||
    if (!clients.length) {
 | 
			
		||||
      return Logger.debug(`[SocketAuthority] clientEmitter - no clients found for user ${userId}`)
 | 
			
		||||
@ -77,6 +94,7 @@ class SocketAuthority {
 | 
			
		||||
 | 
			
		||||
  // Emits event to all admin user clients
 | 
			
		||||
  adminEmitter(evt, data) {
 | 
			
		||||
    void this._fireNotification(evt, data);
 | 
			
		||||
    for (const socketId in this.clients) {
 | 
			
		||||
      if (this.clients[socketId].user?.isAdminOrUp) {
 | 
			
		||||
        this.clients[socketId].socket.emit(evt, data)
 | 
			
		||||
@ -92,6 +110,7 @@ class SocketAuthority {
 | 
			
		||||
   * @param {import('./models/LibraryItem')} libraryItem
 | 
			
		||||
   */
 | 
			
		||||
  libraryItemEmitter(evt, libraryItem) {
 | 
			
		||||
    void this._fireNotification(evt, libraryItem)
 | 
			
		||||
    for (const socketId in this.clients) {
 | 
			
		||||
      if (this.clients[socketId].user?.checkCanAccessLibraryItem(libraryItem)) {
 | 
			
		||||
        this.clients[socketId].socket.emit(evt, libraryItem.toOldJSONExpanded())
 | 
			
		||||
@ -107,6 +126,7 @@ class SocketAuthority {
 | 
			
		||||
   * @param {import('./models/LibraryItem')[]} libraryItems
 | 
			
		||||
   */
 | 
			
		||||
  libraryItemsEmitter(evt, libraryItems) {
 | 
			
		||||
    void this._fireNotification(evt, libraryItems)
 | 
			
		||||
    for (const socketId in this.clients) {
 | 
			
		||||
      if (this.clients[socketId].user) {
 | 
			
		||||
        const libraryItemsAccessibleToUser = libraryItems.filter((li) => this.clients[socketId].user.checkCanAccessLibraryItem(li))
 | 
			
		||||
 | 
			
		||||
@ -90,6 +90,14 @@ class NotificationManager {
 | 
			
		||||
    this.triggerNotification('onBackupFailed', eventData)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @param
 | 
			
		||||
   */
 | 
			
		||||
  async onItemUpdated(libraryItem) {
 | 
			
		||||
    console.log('onItemUpdated', libraryItem)
 | 
			
		||||
    this.triggerNotification('onItemUpdated', libraryItem)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onTest() {
 | 
			
		||||
    this.triggerNotification('onTest')
 | 
			
		||||
  }
 | 
			
		||||
@ -124,7 +132,7 @@ class NotificationManager {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    await Database.updateSetting(Database.notificationSettings)
 | 
			
		||||
    SocketAuthority.emitter('notifications_updated', Database.notificationSettings.toJSON())
 | 
			
		||||
    //SocketAuthority.emitter('notifications_updated', Database.notificationSettings.toJSON())
 | 
			
		||||
 | 
			
		||||
    this.notificationFinished()
 | 
			
		||||
  }
 | 
			
		||||
@ -184,5 +192,26 @@ class NotificationManager {
 | 
			
		||||
        return false
 | 
			
		||||
      })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fireNotificationFromSocket(eventName, eventData) {
 | 
			
		||||
    if (!Database.notificationSettings.isUseable) return
 | 
			
		||||
 | 
			
		||||
    const eventNameModified = eventName.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
 | 
			
		||||
    const eventKey = `on${eventNameModified.charAt(0).toUpperCase()}${eventNameModified.slice(1)}`;
 | 
			
		||||
 | 
			
		||||
    if (!Database.notificationSettings.getHasActiveNotificationsForEvent(eventKey)) {
 | 
			
		||||
      // No logging to prevent console spam
 | 
			
		||||
      Logger.debug(`[NotificationManager] fireSocketNotification: No active notifications`)
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Logger.debug(`[NotificationManager] fireNotificationFromSocket: ${eventKey} event fired`)
 | 
			
		||||
 | 
			
		||||
    switch (eventKey) {
 | 
			
		||||
      case 'onItemUpdated':
 | 
			
		||||
        void this.onItemUpdated(eventData)
 | 
			
		||||
        break
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
module.exports = new NotificationManager()
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,61 @@
 | 
			
		||||
const { version } = require('../../package.json')
 | 
			
		||||
const LibraryItem = require('../models/LibraryItem')
 | 
			
		||||
 | 
			
		||||
const libraryItemVariables = [
 | 
			
		||||
  'id',
 | 
			
		||||
  'ino',
 | 
			
		||||
  'path',
 | 
			
		||||
  'relPath',
 | 
			
		||||
  'mediaId',
 | 
			
		||||
  'mediaType',
 | 
			
		||||
  'isFile',
 | 
			
		||||
  'isMissing',
 | 
			
		||||
  'isInvalid',
 | 
			
		||||
  'mtime',
 | 
			
		||||
  'ctime',
 | 
			
		||||
  'birthtime',
 | 
			
		||||
  'size',
 | 
			
		||||
  'lastScan',
 | 
			
		||||
  'lastScanVersion',
 | 
			
		||||
  'libraryFiles',
 | 
			
		||||
  'extraData',
 | 
			
		||||
  'title',
 | 
			
		||||
  'titleIgnorePrefix',
 | 
			
		||||
  'authorNamesFirstLast',
 | 
			
		||||
  'authorNamesLastFirst',
 | 
			
		||||
  'createdAt',
 | 
			
		||||
  'updatedAt',
 | 
			
		||||
  'libraryId',
 | 
			
		||||
  'libraryFolderId',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
const libraryItemTestData = {
 | 
			
		||||
  id: '123e4567-e89b-12d3-a456-426614174000',
 | 
			
		||||
  ino: '9876543',
 | 
			
		||||
  path: '/audiobooks/Frank Herbert/Dune',
 | 
			
		||||
  relPath: 'Frank Herbert/Dune',
 | 
			
		||||
  mediaId: 'abcdef12-3456-7890-abcd-ef1234567890',
 | 
			
		||||
  mediaType: 'book',
 | 
			
		||||
  isFile: true,
 | 
			
		||||
  isMissing: false,
 | 
			
		||||
  isInvalid: false,
 | 
			
		||||
  mtime: new Date('2023-11-15T10:20:30.400Z'),
 | 
			
		||||
  ctime: new Date('2023-11-15T10:20:30.400Z'),
 | 
			
		||||
  birthtime: new Date('2023-11-15T10:20:30.390Z'),
 | 
			
		||||
  size: 987654321,
 | 
			
		||||
  lastScan: new Date('2024-01-10T08:15:00.000Z'),
 | 
			
		||||
  lastScanVersion: '3.2.0',
 | 
			
		||||
  title: 'Dune',
 | 
			
		||||
  titleIgnorePrefix: 'Dune',
 | 
			
		||||
  authorNamesFirstLast: 'Frank Herbert',
 | 
			
		||||
  authorNamesLastFirst: 'Herbert, Frank',
 | 
			
		||||
  createdAt: new Date('2023-11-15T10:21:00.000Z'),
 | 
			
		||||
  updatedAt: new Date('2024-05-15T18:30:36.940Z'),
 | 
			
		||||
  libraryId: 'fedcba98-7654-3210-fedc-ba9876543210',
 | 
			
		||||
  libraryFolderId: '11223344-5566-7788-99aa-bbccddeeff00'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
module.exports.notificationData = {
 | 
			
		||||
  events: [
 | 
			
		||||
@ -60,6 +117,22 @@ module.exports.notificationData = {
 | 
			
		||||
        errorMsg: 'Example error message'
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    // Sockets - Silently crying because not using typescript
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
      name: 'onItemUpdated',
 | 
			
		||||
      requiresLibrary: true,
 | 
			
		||||
      description: 'Triggered when an item is updated',
 | 
			
		||||
      descriptionKey: 'NotificationOnItemUpdatedDescription',
 | 
			
		||||
      variables: libraryItemVariables,
 | 
			
		||||
      defaults: {
 | 
			
		||||
        title: 'Item Updated: {{title}}',
 | 
			
		||||
        body: 'Item {{title}} has been updated.\n\nPath: {{path}}\nSize: {{size}} bytes\nLast Scan: {{lastScan}}\nLibrary ID: {{libraryId}}\nLibrary Folder ID: {{libraryFolderId}}'
 | 
			
		||||
      },
 | 
			
		||||
      testData: libraryItemTestData
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Test
 | 
			
		||||
    {
 | 
			
		||||
      name: 'onTest',
 | 
			
		||||
      requiresLibrary: false,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user