Merge pull request #136 from NicolaiVdS/email-validation-and-requirement

feat(userprofile): email requirement and validation + import user button overhaul
This commit is contained in:
Fallenbagel
2022-06-15 01:29:47 +05:00
committed by GitHub
19 changed files with 216 additions and 61 deletions

View File

@@ -137,6 +137,8 @@ export class User {
@UpdateDateColumn()
public updatedAt: Date;
public warnings: string[] = [];
constructor(init?: Partial<User>) {
Object.assign(this, init);
}

View File

@@ -13,6 +13,7 @@ import {
NotificationAgentKey,
} from '../../settings';
import { BaseAgent, NotificationAgent, NotificationPayload } from './agent';
import * as EmailValidator from 'email-validator';
class EmailAgent
extends BaseAgent<NotificationAgentEmail>
@@ -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',

View File

@@ -134,6 +134,7 @@ interface FullPublicSettings extends PublicSettings {
enablePushRegistration: boolean;
locale: string;
emailEnabled: boolean;
userEmailRequired: boolean;
newPlexLogin: boolean;
}
@@ -159,6 +160,7 @@ export interface NotificationAgentSlack extends NotificationAgentConfig {
export interface NotificationAgentEmail extends NotificationAgentConfig {
options: {
userEmailRequired: boolean;
emailFrom: string;
smtpHost: string;
smtpPort: number;
@@ -335,6 +337,7 @@ class Settings {
email: {
enabled: false,
options: {
userEmailRequired: false,
emailFrom: '',
smtpHost: '',
smtpPort: 587,
@@ -529,6 +532,8 @@ class Settings {
enablePushRegistration: this.data.notifications.agents.webpush.enabled,
locale: this.data.main.locale,
emailEnabled: this.data.notifications.agents.email.enabled,
userEmailRequired:
this.data.notifications.agents.email.options.userEmailRequired,
newPlexLogin: this.data.main.newPlexLogin,
};
}

View File

@@ -9,6 +9,7 @@ import { Permission } from '../lib/permissions';
import { getSettings } from '../lib/settings';
import logger from '../logger';
import { isAuthenticated } from '../middleware/auth';
import * as EmailValidator from 'email-validator';
const authRoutes = Router();
@@ -24,6 +25,16 @@ authRoutes.get('/me', isAuthenticated(), async (req, res) => {
where: { id: req.user.id },
});
// check if email is required in settings and if user has an valid email
const settings = await getSettings();
if (
settings.notifications.agents.email.options.userEmailRequired &&
!EmailValidator.validate(user.email)
) {
user.warnings.push('userEmailRequired');
logger.warn(`User ${user.username} has no valid email address`);
}
return res.status(200).json(user);
});

View File

@@ -492,61 +492,46 @@ 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';
jellyfinClient.setUserId(admin.jellyfinUserId ?? '');
const jellyfinUsers = await jellyfinClient.getUsers();
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;
for (const jellyfinUserId of body.jellyfinUserIds) {
const jellyfinUser = jellyfinUsers.users.find(
(user) => user.Id === 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 user = await userRepository.findOne({
select: ['id', 'jellyfinUserId'],
where: { jellyfinUserId: jellyfinUserId },
});
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));
} catch (e) {
next({ status: 500, message: e.message });