diff --git a/client/components/modals/ListeningSessionModal.vue b/client/components/modals/ListeningSessionModal.vue index 0a6b556e..cdcf3bd3 100644 --- a/client/components/modals/ListeningSessionModal.vue +++ b/client/components/modals/ListeningSessionModal.vue @@ -89,7 +89,7 @@

{{ $strings.LabelDevice }}

{{ clientDisplayName }}

-

{{ deviceInfo.ipAddress }}

+

{{ _session.ipAddress }}

{{ osDisplayName }}

{{ deviceInfo.browserName }}

{{ deviceDisplayName }}

diff --git a/server/managers/PlaybackSessionManager.js b/server/managers/PlaybackSessionManager.js index fb12feda..6fedfa71 100644 --- a/server/managers/PlaybackSessionManager.js +++ b/server/managers/PlaybackSessionManager.js @@ -181,6 +181,7 @@ class PlaybackSessionManager { // New session from local session = new PlaybackSession(sessionJson) session.deviceInfo = deviceInfo + session.ipAddress = deviceInfo.ipAddress if (session.mediaMetadata == null) { session.mediaMetadata = {} diff --git a/server/migrations/v2.25.2-add-ipaddress-to-playbacksession.js b/server/migrations/v2.25.2-add-ipaddress-to-playbacksession.js new file mode 100644 index 00000000..0d2eb0d0 --- /dev/null +++ b/server/migrations/v2.25.2-add-ipaddress-to-playbacksession.js @@ -0,0 +1,67 @@ +/** + * @typedef MigrationContext + * @property {import('sequelize').QueryInterface} queryInterface - a Sequelize QueryInterface object. + * @property {import('../Logger')} logger - a Logger object. + * + * @typedef MigrationOptions + * @property {MigrationContext} context - an object containing the migration context. + */ + +const migrationVersion = '2.25.2' +const migrationName = `${migrationVersion}-add-ipaddress-to-playbacksession` +const loggerPrefix = `[${migrationVersion} migration]` + +/** + * This migration script adds the ipAddress column to the playbackSessions table. + * + * @param {MigrationOptions} options - an object containing the migration context. + * @returns {Promise} - A promise that resolves when the migration is complete. + */ +async function up({ context: { queryInterface, logger } }) { + logger.info(`${loggerPrefix} UPGRADE BEGIN: ${migrationName}`) + + if (await queryInterface.tableExists('playbackSessions')) { + const tableDescription = await queryInterface.describeTable('playbackSessions') + if (!tableDescription.ipAddress) { + logger.info(`${loggerPrefix} Adding ipAddress column to playbackSessions table`) + await queryInterface.addColumn('playbackSessions', 'ipAddress', { + type: queryInterface.sequelize.Sequelize.DataTypes.STRING, + allowNull: true + }) + logger.info(`${loggerPrefix} Added ipAddress column to playbackSessions table`) + } else { + logger.info(`${loggerPrefix} ipAddress column already exists in playbackSessions table`) + } + } else { + logger.info(`${loggerPrefix} playbackSessions table does not exist`) + } + + logger.info(`${loggerPrefix} UPGRADE END: ${migrationName}`) +} + +/** + * This migration script removes the ipAddress column from the playbackSessions table. + * + * @param {MigrationOptions} options - an object containing the migration context. + * @returns {Promise} - A promise that resolves when the migration is complete. + */ +async function down({ context: { queryInterface, logger } }) { + logger.info(`${loggerPrefix} DOWNGRADE BEGIN: ${migrationName}`) + + if (await queryInterface.tableExists('playbackSessions')) { + const tableDescription = await queryInterface.describeTable('playbackSessions') + if (tableDescription.ipAddress) { + logger.info(`${loggerPrefix} Removing ipAddress column from playbackSessions table`) + await queryInterface.removeColumn('playbackSessions', 'ipAddress') + logger.info(`${loggerPrefix} Removed ipAddress column from playbackSessions table`) + } else { + logger.info(`${loggerPrefix} ipAddress column does not exist in playbackSessions table`) + } + } else { + logger.info(`${loggerPrefix} playbackSessions table does not exist`) + } + + logger.info(`${loggerPrefix} DOWNGRADE END: ${migrationName}`) +} + +module.exports = { up, down } \ No newline at end of file diff --git a/server/models/PlaybackSession.js b/server/models/PlaybackSession.js index 196fbda6..b1ead0f9 100644 --- a/server/models/PlaybackSession.js +++ b/server/models/PlaybackSession.js @@ -16,6 +16,8 @@ class PlaybackSession extends Model { this.displayTitle /** @type {string} */ this.displayAuthor + /** @type {string} */ + this.ipAddress /** @type {number} */ this.duration /** @type {number} */ @@ -93,6 +95,7 @@ class PlaybackSession extends Model { displayAuthor: playbackSessionExpanded.displayAuthor, coverPath: playbackSessionExpanded.coverPath, duration: playbackSessionExpanded.duration, + ipAddress: playbackSessionExpanded.ipAddress, playMethod: playbackSessionExpanded.playMethod, mediaPlayer: playbackSessionExpanded.mediaPlayer, deviceInfo: playbackSessionExpanded.device?.getOldDevice() || null, @@ -141,6 +144,7 @@ class PlaybackSession extends Model { displayTitle: oldPlaybackSession.displayTitle, displayAuthor: oldPlaybackSession.displayAuthor, duration: oldPlaybackSession.duration, + ipAddress: oldPlaybackSession.ipAddress, playMethod: oldPlaybackSession.playMethod, mediaPlayer: oldPlaybackSession.mediaPlayer, startTime: oldPlaybackSession.startTime, @@ -184,6 +188,7 @@ class PlaybackSession extends Model { displayTitle: DataTypes.STRING, displayAuthor: DataTypes.STRING, duration: DataTypes.FLOAT, + ipAddress: DataTypes.STRING, playMethod: DataTypes.INTEGER, mediaPlayer: DataTypes.STRING, startTime: DataTypes.FLOAT, diff --git a/server/objects/PlaybackSession.js b/server/objects/PlaybackSession.js index ba031b66..8c5820d3 100644 --- a/server/objects/PlaybackSession.js +++ b/server/objects/PlaybackSession.js @@ -23,6 +23,7 @@ class PlaybackSession { this.playMethod = null this.mediaPlayer = null this.deviceInfo = null + this.ipAddress = null this.serverVersion = null this.date = null @@ -67,6 +68,7 @@ class PlaybackSession { playMethod: this.playMethod, mediaPlayer: this.mediaPlayer, deviceInfo: this.deviceInfo?.toJSON() || null, + ipAddress: this.ipAddress, serverVersion: this.serverVersion, date: this.date, dayOfWeek: this.dayOfWeek, @@ -101,6 +103,7 @@ class PlaybackSession { playMethod: this.playMethod, mediaPlayer: this.mediaPlayer, deviceInfo: this.deviceInfo?.toJSON() || null, + ipAddress: this.ipAddress, serverVersion: this.serverVersion, date: this.date, dayOfWeek: this.dayOfWeek, @@ -125,6 +128,7 @@ class PlaybackSession { this.duration = session.duration this.playMethod = session.playMethod this.mediaPlayer = session.mediaPlayer || null + this.ipAddress = session.ipAddress || null // Temp do not store old IDs if (this.libraryId?.startsWith('lib_')) { @@ -222,6 +226,7 @@ class PlaybackSession { this.mediaPlayer = mediaPlayer this.deviceInfo = deviceInfo || new DeviceInfo() + this.ipAddress = deviceInfo?.ipAddress || null this.serverVersion = serverVersion this.timeListening = 0 diff --git a/test/server/migrations/v2.25.2-add-ipaddress-to-playbacksession.test.js b/test/server/migrations/v2.25.2-add-ipaddress-to-playbacksession.test.js new file mode 100644 index 00000000..e822767a --- /dev/null +++ b/test/server/migrations/v2.25.2-add-ipaddress-to-playbacksession.test.js @@ -0,0 +1,46 @@ +const chai = require('chai') +const sinon = require('sinon') +const { expect } = chai + +const { DataTypes } = require('sequelize') + +const { up, down } = require('../../../server/migrations/v2.25.2-add-ipaddress-to-playbacksession') + +describe('Migration v2.25.2-add-ipaddress-to-playbacksession', () => { + let queryInterface, logger + + beforeEach(() => { + queryInterface = { + addColumn: sinon.stub().resolves(), + removeColumn: sinon.stub().resolves() + } + + logger = { + info: sinon.stub(), + error: sinon.stub() + } + }) + + describe('up', () => { + it('should add the ipAddress column to playbackSessions table', async () => { + await up(queryInterface, logger) + + expect(queryInterface.addColumn.calledOnce).to.be.true + expect( + queryInterface.addColumn.calledWith('playbackSessions', 'ipAddress', { + type: DataTypes.STRING, + allowNull: true + }) + ).to.be.true + }) + }) + + describe('down', () => { + it('should remove the ipAddress column from playbackSessions table', async () => { + await down(queryInterface, logger) + + expect(queryInterface.removeColumn.calledOnce).to.be.true + expect(queryInterface.removeColumn.calledWith('playbackSessions', 'ipAddress')).to.be.true + }) + }) +})