feat(email validation): email requirement and validation + better importer
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
|||||||
NotificationAgentKey,
|
NotificationAgentKey,
|
||||||
} from '../../settings';
|
} from '../../settings';
|
||||||
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
|
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
|
||||||
|
import * as EmailValidator from 'email-validator';
|
||||||
|
|
||||||
class EmailAgent
|
class EmailAgent
|
||||||
extends BaseAgent<NotificationAgentEmail>
|
extends BaseAgent<NotificationAgentEmail>
|
||||||
@@ -215,14 +216,23 @@ class EmailAgent
|
|||||||
this.getSettings(),
|
this.getSettings(),
|
||||||
payload.notifyUser.settings?.pgpKey
|
payload.notifyUser.settings?.pgpKey
|
||||||
);
|
);
|
||||||
await email.send(
|
if (EmailValidator.validate(payload.notifyUser.email)) {
|
||||||
this.buildMessage(
|
await email.send(
|
||||||
type,
|
this.buildMessage(
|
||||||
payload,
|
type,
|
||||||
payload.notifyUser.email,
|
payload,
|
||||||
payload.notifyUser.displayName
|
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) {
|
} catch (e) {
|
||||||
logger.error('Error sending email notification', {
|
logger.error('Error sending email notification', {
|
||||||
label: 'Notifications',
|
label: 'Notifications',
|
||||||
@@ -268,9 +278,18 @@ class EmailAgent
|
|||||||
this.getSettings(),
|
this.getSettings(),
|
||||||
user.settings?.pgpKey
|
user.settings?.pgpKey
|
||||||
);
|
);
|
||||||
await email.send(
|
if (EmailValidator.validate(user.email)) {
|
||||||
this.buildMessage(type, payload, user.email, user.displayName)
|
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) {
|
} catch (e) {
|
||||||
logger.error('Error sending email notification', {
|
logger.error('Error sending email notification', {
|
||||||
label: 'Notifications',
|
label: 'Notifications',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import gravatarUrl from 'gravatar-url';
|
import gravatarUrl from 'gravatar-url';
|
||||||
import { findIndex, sortBy } from 'lodash';
|
import { findIndex, forEach, sortBy } from 'lodash';
|
||||||
import { getRepository, In, Not } from 'typeorm';
|
import { getRepository, In, Not } from 'typeorm';
|
||||||
import JellyfinAPI from '../../api/jellyfin';
|
import JellyfinAPI from '../../api/jellyfin';
|
||||||
import PlexTvAPI from '../../api/plextv';
|
import PlexTvAPI from '../../api/plextv';
|
||||||
@@ -492,62 +492,44 @@ router.post(
|
|||||||
);
|
);
|
||||||
jellyfinClient.setUserId(admin.jellyfinUserId ?? '');
|
jellyfinClient.setUserId(admin.jellyfinUserId ?? '');
|
||||||
|
|
||||||
const jellyfinUsersResponse = await jellyfinClient.getUsers();
|
//const jellyfinUsersResponse = await jellyfinClient.getUsers();
|
||||||
const createdUsers: User[] = [];
|
const createdUsers: User[] = [];
|
||||||
const { hostname, externalHostname } = getSettings().jellyfin;
|
const { hostname, externalHostname } = getSettings().jellyfin;
|
||||||
const jellyfinHost =
|
const jellyfinHost =
|
||||||
externalHostname && externalHostname.length > 0
|
externalHostname && externalHostname.length > 0
|
||||||
? externalHostname
|
? externalHostname
|
||||||
: hostname;
|
: 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
|
forEach(body.jellyfinUserIds, async (jellyfinUserId) => {
|
||||||
? `${jellyfinHost}/Users/${account.Id}/Images/Primary/?tag=${account.PrimaryImageTag}&quality=90`
|
jellyfinClient.setUserId(jellyfinUserId);
|
||||||
: '/os_logo_square.png';
|
const jellyfinUser = await jellyfinClient.getUser();
|
||||||
|
|
||||||
if (user) {
|
const user = await userRepository.findOne({
|
||||||
// Update the user's avatar with their Jellyfin thumbnail, in case it changed
|
select: ['id', 'jellyfinUserId'],
|
||||||
user.avatar = avatar;
|
where: { jellyfinUserId: jellyfinUserId },
|
||||||
user.email = account.Name;
|
});
|
||||||
user.jellyfinUsername = account.Name;
|
|
||||||
|
|
||||||
// In case the user was previously a local account
|
if (!user) {
|
||||||
if (user.userType === UserType.LOCAL) {
|
const newUser = new User({
|
||||||
user.userType = UserType.JELLYFIN;
|
jellyfinUsername: jellyfinUser.Name,
|
||||||
user.jellyfinUserId = account.Id;
|
jellyfinUserId: jellyfinUser.Id,
|
||||||
}
|
jellyfinDeviceId: Buffer.from(
|
||||||
await userRepository.save(user);
|
`BOT_jellyseerr_${jellyfinUser.Name ?? ''}`
|
||||||
} else if (!body || body.jellyfinUserIds.includes(account.Id)) {
|
).toString('base64'),
|
||||||
// logger.error('CREATED USER', {
|
email: jellyfinUser.Name,
|
||||||
// label: 'API',
|
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,
|
||||||
|
});
|
||||||
|
|
||||||
const newUser = new User({
|
await userRepository.save(newUser);
|
||||||
jellyfinUsername: account.Name,
|
createdUsers.push(newUser);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return res.status(201).json(User.filterMany(createdUsers));
|
return res.status(201).json(User.filterMany(createdUsers));
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
next({ status: 500, message: e.message });
|
next({ status: 500, message: e.message });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useUser } from '../../hooks/useUser';
|
|||||||
import PlexLoginButton from '../PlexLoginButton';
|
import PlexLoginButton from '../PlexLoginButton';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
welcome: 'Welcome to Overseerr',
|
welcome: 'Welcome to Jellyseerr',
|
||||||
signinMessage: 'Get started by signing in with your Plex account',
|
signinMessage: 'Get started by signing in with your Plex account',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { MediaServerType } from '../../../server/constants/server';
|
|||||||
import getConfig from 'next/config';
|
import getConfig from 'next/config';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
welcome: 'Welcome to Overseerr',
|
welcome: 'Welcome to Jellyseerr',
|
||||||
signinMessage: 'Get started by signing in',
|
signinMessage: 'Get started by signing in',
|
||||||
signinWithJellyfin: 'Use your {mediaServerName} account',
|
signinWithJellyfin: 'Use your {mediaServerName} account',
|
||||||
signinWithPlex: 'Use your Plex account',
|
signinWithPlex: 'Use your Plex account',
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import globalMessages from '../../i18n/globalMessages';
|
|||||||
import Alert from '../Common/Alert';
|
import Alert from '../Common/Alert';
|
||||||
import Modal from '../Common/Modal';
|
import Modal from '../Common/Modal';
|
||||||
import getConfig from 'next/config';
|
import getConfig from 'next/config';
|
||||||
|
import { UserResultsResponse } from '../../../server/interfaces/api/userInterfaces';
|
||||||
|
|
||||||
interface JellyfinImportProps {
|
interface JellyfinImportProps {
|
||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
@@ -30,6 +31,7 @@ const messages = defineMessages({
|
|||||||
const JellyfinImportModal: React.FC<JellyfinImportProps> = ({
|
const JellyfinImportModal: React.FC<JellyfinImportProps> = ({
|
||||||
onCancel,
|
onCancel,
|
||||||
onComplete,
|
onComplete,
|
||||||
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const settings = useSettings();
|
const settings = useSettings();
|
||||||
@@ -117,6 +119,20 @@ const JellyfinImportModal: React.FC<JellyfinImportProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { data: existingUsers } = useSWR<UserResultsResponse>(
|
||||||
|
`/api/v1/user?take=${children}`
|
||||||
|
);
|
||||||
|
|
||||||
|
data?.forEach((user, pos) => {
|
||||||
|
if (
|
||||||
|
existingUsers?.results.some(
|
||||||
|
(existingUser) => existingUser.jellyfinUserId === user.id
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
delete data[pos];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
loading={!data && !error}
|
loading={!data && !error}
|
||||||
|
|||||||
@@ -482,7 +482,9 @@ const UserList: React.FC = () => {
|
|||||||
setShowImportModal(false);
|
setShowImportModal(false);
|
||||||
revalidate();
|
revalidate();
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
|
{data.pageInfo.results}
|
||||||
|
</JellyfinImportModal>
|
||||||
)}
|
)}
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user