diff --git a/server/lib/notifications/agents/email.ts b/server/lib/notifications/agents/email.ts index a1dd7e4e4..cbed472fa 100644 --- a/server/lib/notifications/agents/email.ts +++ b/server/lib/notifications/agents/email.ts @@ -13,6 +13,7 @@ import { NotificationAgentKey, } from '../../settings'; import { BaseAgent, NotificationAgent, NotificationPayload } from './agent'; +import * as EmailValidator from 'email-validator'; class EmailAgent extends BaseAgent @@ -215,14 +216,23 @@ class EmailAgent this.getSettings(), payload.notifyUser.settings?.pgpKey ); - await email.send( - this.buildMessage( - type, - payload, - payload.notifyUser.email, - payload.notifyUser.displayName - ) - ); + if (EmailValidator.validate(payload.notifyUser.email)) { + await email.send( + this.buildMessage( + type, + payload, + payload.notifyUser.email, + payload.notifyUser.displayName + ) + ); + } else { + logger.warn('Invalid email address provided for user', { + label: 'Notifications', + recipient: payload.notifyUser.displayName, + type: Notification[type], + subject: payload.subject, + }); + } } catch (e) { logger.error('Error sending email notification', { label: 'Notifications', @@ -268,9 +278,18 @@ class EmailAgent this.getSettings(), user.settings?.pgpKey ); - await email.send( - this.buildMessage(type, payload, user.email, user.displayName) - ); + if (EmailValidator.validate(user.email)) { + await email.send( + this.buildMessage(type, payload, user.email, user.displayName) + ); + } else { + logger.warn('Invalid email address provided for user', { + label: 'Notifications', + recipient: user.displayName, + type: Notification[type], + subject: payload.subject, + }); + } } catch (e) { logger.error('Error sending email notification', { label: 'Notifications', diff --git a/server/routes/user/index.ts b/server/routes/user/index.ts index a45391acd..667efca20 100644 --- a/server/routes/user/index.ts +++ b/server/routes/user/index.ts @@ -1,6 +1,6 @@ import { Router } from 'express'; import gravatarUrl from 'gravatar-url'; -import { findIndex, sortBy } from 'lodash'; +import { findIndex, forEach, sortBy } from 'lodash'; import { getRepository, In, Not } from 'typeorm'; import JellyfinAPI from '../../api/jellyfin'; import PlexTvAPI from '../../api/plextv'; @@ -492,62 +492,44 @@ router.post( ); jellyfinClient.setUserId(admin.jellyfinUserId ?? ''); - const jellyfinUsersResponse = await jellyfinClient.getUsers(); + //const jellyfinUsersResponse = await jellyfinClient.getUsers(); const createdUsers: User[] = []; const { hostname, externalHostname } = getSettings().jellyfin; const jellyfinHost = externalHostname && externalHostname.length > 0 ? externalHostname : hostname; - for (const account of jellyfinUsersResponse.users) { - if (account.Name) { - const user = await userRepository - .createQueryBuilder('user') - .where('user.jellyfinUserId = :id', { id: account.Id }) - .orWhere('user.email = :email', { - email: account.Name, - }) - .getOne(); - const avatar = account.PrimaryImageTag - ? `${jellyfinHost}/Users/${account.Id}/Images/Primary/?tag=${account.PrimaryImageTag}&quality=90` - : '/os_logo_square.png'; + forEach(body.jellyfinUserIds, async (jellyfinUserId) => { + jellyfinClient.setUserId(jellyfinUserId); + const jellyfinUser = await jellyfinClient.getUser(); - if (user) { - // Update the user's avatar with their Jellyfin thumbnail, in case it changed - user.avatar = avatar; - user.email = account.Name; - user.jellyfinUsername = account.Name; + const user = await userRepository.findOne({ + select: ['id', 'jellyfinUserId'], + where: { jellyfinUserId: jellyfinUserId }, + }); - // In case the user was previously a local account - if (user.userType === UserType.LOCAL) { - user.userType = UserType.JELLYFIN; - user.jellyfinUserId = account.Id; - } - await userRepository.save(user); - } else if (!body || body.jellyfinUserIds.includes(account.Id)) { - // logger.error('CREATED USER', { - // label: 'API', - // }); - - const newUser = new User({ - jellyfinUsername: account.Name, - jellyfinUserId: account.Id, - jellyfinDeviceId: Buffer.from( - `BOT_overseerr_${account.Name ?? ''}` - ).toString('base64'), - email: account.Name, - permissions: settings.main.defaultPermissions, - avatar, - userType: UserType.JELLYFIN, - }); - await userRepository.save(newUser); - createdUsers.push(newUser); - } + if (!user) { + const newUser = new User({ + jellyfinUsername: jellyfinUser.Name, + jellyfinUserId: jellyfinUser.Id, + jellyfinDeviceId: Buffer.from( + `BOT_jellyseerr_${jellyfinUser.Name ?? ''}` + ).toString('base64'), + email: jellyfinUser.Name, + permissions: settings.main.defaultPermissions, + avatar: jellyfinUser.PrimaryImageTag + ? `${jellyfinHost}/Users/${jellyfinUser.Id}/Images/Primary/?tag=${jellyfinUser.PrimaryImageTag}&quality=90` + : '/os_logo_square.png', + userType: UserType.JELLYFIN, + }); + + await userRepository.save(newUser); + createdUsers.push(newUser); } - } - return res.status(201).json(User.filterMany(createdUsers)); + return res.status(201).json(User.filterMany(createdUsers)); + }); } catch (e) { next({ status: 500, message: e.message }); } diff --git a/src/components/Setup/LoginWithPlex.tsx b/src/components/Setup/LoginWithPlex.tsx index 90d4425b4..4e5a10265 100644 --- a/src/components/Setup/LoginWithPlex.tsx +++ b/src/components/Setup/LoginWithPlex.tsx @@ -5,7 +5,7 @@ import { useUser } from '../../hooks/useUser'; import PlexLoginButton from '../PlexLoginButton'; const messages = defineMessages({ - welcome: 'Welcome to Overseerr', + welcome: 'Welcome to Jellyseerr', signinMessage: 'Get started by signing in with your Plex account', }); diff --git a/src/components/Setup/SetupLogin.tsx b/src/components/Setup/SetupLogin.tsx index d3d581b8a..09e48e161 100644 --- a/src/components/Setup/SetupLogin.tsx +++ b/src/components/Setup/SetupLogin.tsx @@ -9,7 +9,7 @@ import { MediaServerType } from '../../../server/constants/server'; import getConfig from 'next/config'; const messages = defineMessages({ - welcome: 'Welcome to Overseerr', + welcome: 'Welcome to Jellyseerr', signinMessage: 'Get started by signing in', signinWithJellyfin: 'Use your {mediaServerName} account', signinWithPlex: 'Use your Plex account', diff --git a/src/components/UserList/JellyfinImportModal.tsx b/src/components/UserList/JellyfinImportModal.tsx index 3e2f7ddf6..c3cf12a5f 100644 --- a/src/components/UserList/JellyfinImportModal.tsx +++ b/src/components/UserList/JellyfinImportModal.tsx @@ -9,6 +9,7 @@ import globalMessages from '../../i18n/globalMessages'; import Alert from '../Common/Alert'; import Modal from '../Common/Modal'; import getConfig from 'next/config'; +import { UserResultsResponse } from '../../../server/interfaces/api/userInterfaces'; interface JellyfinImportProps { onCancel?: () => void; @@ -30,6 +31,7 @@ const messages = defineMessages({ const JellyfinImportModal: React.FC = ({ onCancel, onComplete, + children, }) => { const intl = useIntl(); const settings = useSettings(); @@ -117,6 +119,20 @@ const JellyfinImportModal: React.FC = ({ } }; + const { data: existingUsers } = useSWR( + `/api/v1/user?take=${children}` + ); + + data?.forEach((user, pos) => { + if ( + existingUsers?.results.some( + (existingUser) => existingUser.jellyfinUserId === user.id + ) + ) { + delete data[pos]; + } + }); + return ( { setShowImportModal(false); revalidate(); }} - /> + > + {data.pageInfo.results} + )}