fix(jellyfin): clean up Jellyfin sessions on Jellyseerr logout (#1651)
* fix(jellyfin): clean up Jellyfin sessions on Jellyseerr logout * refactor(auth): remove deleteUserDevice method and handle device deletion directly in logout process * refactor(auth): optimize logout route for Jellyfin/Emby Only query database for Jellyfin/Emby users during logout to avoid unnecessary database queries for Plex/local users. This improves performance by skipping the device deletion process when it's not needed.
This commit is contained in:
@@ -22,6 +22,23 @@ export interface JellyfinUserResponse {
|
|||||||
PrimaryImageTag?: string;
|
PrimaryImageTag?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface JellyfinDevice {
|
||||||
|
Id: string;
|
||||||
|
Name: string;
|
||||||
|
LastUserName: string;
|
||||||
|
AppName: string;
|
||||||
|
AppVersion: string;
|
||||||
|
LastUserId: string;
|
||||||
|
DateLastActivity: string;
|
||||||
|
Capabilities: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JellyfinDevicesResponse {
|
||||||
|
Items: JellyfinDevice[];
|
||||||
|
TotalRecordCount: number;
|
||||||
|
StartIndex: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface JellyfinLoginResponse {
|
export interface JellyfinLoginResponse {
|
||||||
User: JellyfinUserResponse;
|
User: JellyfinUserResponse;
|
||||||
AccessToken: string;
|
AccessToken: string;
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ import logger from '@server/logger';
|
|||||||
import { isAuthenticated } from '@server/middleware/auth';
|
import { isAuthenticated } from '@server/middleware/auth';
|
||||||
import { checkAvatarChanged } from '@server/routes/avatarproxy';
|
import { checkAvatarChanged } from '@server/routes/avatarproxy';
|
||||||
import { ApiError } from '@server/types/error';
|
import { ApiError } from '@server/types/error';
|
||||||
|
import { getAppVersion } from '@server/utils/appVersion';
|
||||||
import { getHostname } from '@server/utils/getHostname';
|
import { getHostname } from '@server/utils/getHostname';
|
||||||
|
import axios from 'axios';
|
||||||
import * as EmailValidator from 'email-validator';
|
import * as EmailValidator from 'email-validator';
|
||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import net from 'net';
|
import net from 'net';
|
||||||
@@ -716,17 +718,79 @@ authRoutes.post('/local', async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
authRoutes.post('/logout', (req, res, next) => {
|
authRoutes.post('/logout', async (req, res, next) => {
|
||||||
req.session?.destroy((err) => {
|
try {
|
||||||
if (err) {
|
const userId = req.session?.userId;
|
||||||
return next({
|
if (!userId) {
|
||||||
status: 500,
|
return res.status(200).json({ status: 'ok' });
|
||||||
message: 'Something went wrong.',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).json({ status: 'ok' });
|
const settings = getSettings();
|
||||||
|
const isJellyfinOrEmby =
|
||||||
|
settings.main.mediaServerType === MediaServerType.JELLYFIN ||
|
||||||
|
settings.main.mediaServerType === MediaServerType.EMBY;
|
||||||
|
|
||||||
|
if (isJellyfinOrEmby) {
|
||||||
|
const user = await getRepository(User)
|
||||||
|
.createQueryBuilder('user')
|
||||||
|
.addSelect(['user.jellyfinUserId', 'user.jellyfinDeviceId'])
|
||||||
|
.where('user.id = :id', { id: userId })
|
||||||
|
.getOne();
|
||||||
|
|
||||||
|
if (user?.jellyfinUserId && user.jellyfinDeviceId) {
|
||||||
|
try {
|
||||||
|
const baseUrl = getHostname();
|
||||||
|
try {
|
||||||
|
await axios.delete(`${baseUrl}/Devices`, {
|
||||||
|
params: { Id: user.jellyfinDeviceId },
|
||||||
|
headers: {
|
||||||
|
'X-Emby-Authorization': `MediaBrowser Client="Jellyseerr", Device="Jellyseerr", DeviceId="jellyseerr", Version="${getAppVersion()}", Token="${
|
||||||
|
settings.jellyfin.apiKey
|
||||||
|
}"`,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to delete Jellyfin device', {
|
||||||
|
label: 'Auth',
|
||||||
|
error: error instanceof Error ? error.message : 'Unknown error',
|
||||||
|
userId: user.id,
|
||||||
|
jellyfinUserId: user.jellyfinUserId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to delete Jellyfin device', {
|
||||||
|
label: 'Auth',
|
||||||
|
error: error instanceof Error ? error.message : 'Unknown error',
|
||||||
|
userId: user.id,
|
||||||
|
jellyfinUserId: user.jellyfinUserId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req.session?.destroy((err: Error | null) => {
|
||||||
|
if (err) {
|
||||||
|
logger.error('Failed to destroy session', {
|
||||||
|
label: 'Auth',
|
||||||
|
error: err.message,
|
||||||
|
userId,
|
||||||
|
});
|
||||||
|
return next({ status: 500, message: 'Failed to destroy session.' });
|
||||||
|
}
|
||||||
|
logger.info('Successfully logged out user', {
|
||||||
|
label: 'Auth',
|
||||||
|
userId,
|
||||||
|
});
|
||||||
|
res.status(200).json({ status: 'ok' });
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error during logout process', {
|
||||||
|
label: 'Auth',
|
||||||
|
error: error instanceof Error ? error.message : 'Unknown error',
|
||||||
|
userId: req.session?.userId,
|
||||||
|
});
|
||||||
|
next({ status: 500, message: 'Error during logout process.' });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
authRoutes.post('/reset-password', async (req, res, next) => {
|
authRoutes.post('/reset-password', async (req, res, next) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user