refactor: checks if the default avatar is cached to avoid creating duplicates for different users

This commit is contained in:
JoaquinOlivero
2024-08-26 18:58:02 +00:00
parent 822ca690da
commit d2f651081a
7 changed files with 173 additions and 116 deletions

View File

@@ -343,6 +343,14 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
}),
userType: UserType.EMBY,
});
if (
user.avatar.includes('https://gravatar.com') &&
user.avatar.includes('default=mm&size=200')
) {
user.avatar = 'https://gravatar.com/avatar/?default=mm&size=200';
}
break;
case MediaServerType.JELLYFIN:
settings.main.mediaServerType = MediaServerType.JELLYFIN;
@@ -361,6 +369,14 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
}),
userType: UserType.JELLYFIN,
});
if (
user.avatar.includes('https://gravatar.com') &&
user.avatar.includes('default=mm&size=200')
) {
user.avatar = 'https://gravatar.com/avatar/?default=mm&size=200';
}
break;
default:
throw new Error('select_server_type');
@@ -415,15 +431,23 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
}
user.avatar = avatar;
} else {
const avatar = gravatarUrl(user.email || account.User.Name, {
let avatar = gravatarUrl(user.email || account.User.Name, {
default: 'mm',
size: 200,
});
if (
avatar.includes('https://gravatar.com') &&
avatar.includes('default=mm&size=200')
) {
avatar = 'https://gravatar.com/avatar/?default=mm&size=200';
}
if (avatar !== user.avatar) {
const avatarProxy = new ImageProxy('avatar', '');
avatarProxy.clearCachedImage(user.avatar);
}
user.avatar = avatar;
}
user.jellyfinUsername = account.User.Name;
@@ -474,6 +498,13 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
? UserType.JELLYFIN
: UserType.EMBY,
});
if (
user.avatar.includes('https://gravatar.com') &&
user.avatar.includes('default=mm&size=200')
) {
user.avatar = 'https://gravatar.com/avatar/?default=mm&size=200';
}
//initialize Jellyfin/Emby users with local login
const passedExplicitPassword = body.password && body.password.length > 0;
if (passedExplicitPassword) {

View File

@@ -7,8 +7,16 @@ const router = Router();
const avatarImageProxy = new ImageProxy('avatar', '');
// Proxy avatar images
router.get('/*', async (req, res) => {
const imagePath = req.url.startsWith('/') ? req.url.slice(1) : req.url;
let imagePath = req.url.startsWith('/') ? req.url.slice(1) : req.url;
try {
if (
imagePath.includes('https://gravatar.com') &&
imagePath.includes('default=mm&size=200')
) {
imagePath = 'https://gravatar.com/avatar/?default=mm&size=200';
}
const imageData = await avatarImageProxy.getImage(imagePath);
res.writeHead(200, {

View File

@@ -122,7 +122,14 @@ router.post(
}
const passedExplicitPassword = body.password && body.password.length > 0;
const avatar = gravatarUrl(email, { default: 'mm', size: 200 });
let avatar = gravatarUrl(email, { default: 'mm', size: 200 });
if (
avatar.includes('https://gravatar.com') &&
avatar.includes('default=mm&size=200')
) {
avatar = 'https://gravatar.com/avatar/?default=mm&size=200';
}
if (
!passedExplicitPassword &&
@@ -557,6 +564,13 @@ router.post(
: UserType.EMBY,
});
if (
newUser.avatar.includes('https://gravatar.com') &&
newUser.avatar.includes('default=mm&size=200')
) {
newUser.avatar = 'https://gravatar.com/avatar/?default=mm&size=200';
}
await userRepository.save(newUser);
createdUsers.push(newUser);
}

View File

@@ -28,7 +28,6 @@ import type Issue from '@server/entity/Issue';
import type { MovieDetails } from '@server/models/Movie';
import type { TvDetails } from '@server/models/Tv';
import { Field, Form, Formik } from 'formik';
import Image from 'next/image';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useState } from 'react';
@@ -264,8 +263,9 @@ const IssueDetails = () => {
</div>
<h1>
<Link
href={`/${issueData.media.mediaType === MediaType.MOVIE ? 'movie' : 'tv'
}/${data.id}`}
href={`/${
issueData.media.mediaType === MediaType.MOVIE ? 'movie' : 'tv'
}/${data.id}`}
className="hover:underline"
>
{title}
@@ -302,7 +302,7 @@ const IssueDetails = () => {
<FormattedRelativeTime
value={Math.floor(
(new Date(issueData.createdAt).getTime() - Date.now()) /
1000
1000
)}
updateIntervalInSeconds={1}
numeric="auto"
@@ -367,7 +367,7 @@ const IssueDetails = () => {
<FormattedRelativeTime
value={Math.floor(
(new Date(issueData.updatedAt).getTime() - Date.now()) /
1000
1000
)}
updateIntervalInSeconds={1}
numeric="auto"
@@ -388,16 +388,16 @@ const IssueDetails = () => {
<PlayIcon />
<span>
{settings.currentSettings.mediaServerType ===
MediaServerType.EMBY
MediaServerType.EMBY
? intl.formatMessage(messages.playonplex, {
mediaServerName: 'Emby',
})
mediaServerName: 'Emby',
})
: settings.currentSettings.mediaServerType ===
MediaServerType.PLEX
? intl.formatMessage(messages.playonplex, {
? intl.formatMessage(messages.playonplex, {
mediaServerName: 'Plex',
})
: intl.formatMessage(messages.playonplex, {
: intl.formatMessage(messages.playonplex, {
mediaServerName: 'Jellyfin',
})}
</span>
@@ -436,16 +436,16 @@ const IssueDetails = () => {
<PlayIcon />
<span>
{settings.currentSettings.mediaServerType ===
MediaServerType.EMBY
MediaServerType.EMBY
? intl.formatMessage(messages.play4konplex, {
mediaServerName: 'Emby',
})
mediaServerName: 'Emby',
})
: settings.currentSettings.mediaServerType ===
MediaServerType.PLEX
? intl.formatMessage(messages.play4konplex, {
? intl.formatMessage(messages.play4konplex, {
mediaServerName: 'Plex',
})
: intl.formatMessage(messages.play4konplex, {
: intl.formatMessage(messages.play4konplex, {
mediaServerName: 'Jellyfin',
})}
</span>
@@ -530,52 +530,52 @@ const IssueDetails = () => {
<div className="mt-4 flex items-center justify-end space-x-2">
{(hasPermission(Permission.MANAGE_ISSUES) ||
belongsToUser) && (
<>
{issueData.status === IssueStatus.OPEN ? (
<Button
type="button"
buttonType="danger"
onClick={async () => {
await updateIssueStatus('resolved');
<>
{issueData.status === IssueStatus.OPEN ? (
<Button
type="button"
buttonType="danger"
onClick={async () => {
await updateIssueStatus('resolved');
if (values.message) {
handleSubmit();
}
}}
>
<CheckCircleIcon />
<span>
{intl.formatMessage(
values.message
? messages.closeissueandcomment
: messages.closeissue
)}
</span>
</Button>
) : (
<Button
type="button"
buttonType="default"
onClick={async () => {
await updateIssueStatus('open');
if (values.message) {
handleSubmit();
}
}}
>
<CheckCircleIcon />
<span>
{intl.formatMessage(
values.message
? messages.closeissueandcomment
: messages.closeissue
)}
</span>
</Button>
) : (
<Button
type="button"
buttonType="default"
onClick={async () => {
await updateIssueStatus('open');
if (values.message) {
handleSubmit();
}
}}
>
<ArrowPathIcon />
<span>
{intl.formatMessage(
values.message
? messages.reopenissueandcomment
: messages.reopenissue
)}
</span>
</Button>
)}
</>
)}
if (values.message) {
handleSubmit();
}
}}
>
<ArrowPathIcon />
<span>
{intl.formatMessage(
values.message
? messages.reopenissueandcomment
: messages.reopenissue
)}
</span>
</Button>
)}
</>
)}
<Button
type="submit"
buttonType="primary"
@@ -641,7 +641,7 @@ const IssueDetails = () => {
<FormattedRelativeTime
value={Math.floor(
(new Date(issueData.updatedAt).getTime() - Date.now()) /
1000
1000
)}
updateIntervalInSeconds={1}
numeric="auto"
@@ -662,16 +662,16 @@ const IssueDetails = () => {
<PlayIcon />
<span>
{settings.currentSettings.mediaServerType ===
MediaServerType.EMBY
MediaServerType.EMBY
? intl.formatMessage(messages.playonplex, {
mediaServerName: 'Emby',
})
mediaServerName: 'Emby',
})
: settings.currentSettings.mediaServerType ===
MediaServerType.PLEX
? intl.formatMessage(messages.playonplex, {
? intl.formatMessage(messages.playonplex, {
mediaServerName: 'Plex',
})
: intl.formatMessage(messages.playonplex, {
: intl.formatMessage(messages.playonplex, {
mediaServerName: 'Jellyfin',
})}
</span>
@@ -709,16 +709,16 @@ const IssueDetails = () => {
<PlayIcon />
<span>
{settings.currentSettings.mediaServerType ===
MediaServerType.EMBY
MediaServerType.EMBY
? intl.formatMessage(messages.play4konplex, {
mediaServerName: 'Emby',
})
mediaServerName: 'Emby',
})
: settings.currentSettings.mediaServerType ===
MediaServerType.PLEX
? intl.formatMessage(messages.play4konplex, {
? intl.formatMessage(messages.play4konplex, {
mediaServerName: 'Plex',
})
: intl.formatMessage(messages.play4konplex, {
: intl.formatMessage(messages.play4konplex, {
mediaServerName: 'Jellyfin',
})}
</span>

View File

@@ -28,7 +28,6 @@ import type { MediaWatchDataResponse } from '@server/interfaces/api/mediaInterfa
import type { RadarrSettings, SonarrSettings } from '@server/lib/settings';
import type { MovieDetails } from '@server/models/Movie';
import type { TvDetails } from '@server/models/Tv';
import Image from 'next/image';
import Link from 'next/link';
import { useIntl } from 'react-intl';
import useSWR from 'swr';
@@ -314,8 +313,8 @@ const ManageSlideOver = ({
{!!watchData?.data && (
<div
className={`grid grid-cols-1 divide-y divide-gray-700 overflow-hidden border-gray-700 text-sm text-gray-300 shadow ${data.mediaInfo?.tautulliUrl
? 'rounded-t-md border-x border-t'
: 'rounded-md border'
? 'rounded-t-md border-x border-t'
: 'rounded-md border'
}`}
>
<div className="grid grid-cols-3 divide-x divide-gray-700">
@@ -471,8 +470,8 @@ const ManageSlideOver = ({
{watchData?.data4k && (
<div
className={`grid grid-cols-1 divide-y divide-gray-700 overflow-hidden border-gray-700 text-sm text-gray-300 shadow ${data.mediaInfo?.tautulliUrl4k
? 'rounded-t-md border-x border-t'
: 'rounded-md border'
? 'rounded-t-md border-x border-t'
: 'rounded-md border'
}`}
>
<div className="grid grid-cols-3 divide-x divide-gray-700">

View File

@@ -6,7 +6,6 @@ import globalMessages from '@app/i18n/globalMessages';
import defineMessages from '@app/utils/defineMessages';
import { MediaServerType } from '@server/constants/server';
import type { UserResultsResponse } from '@server/interfaces/api/userInterfaces';
import Image from 'next/image';
import { useState } from 'react';
import { useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
@@ -158,7 +157,7 @@ const JellyfinImportModal: React.FC<JellyfinImportProps> = ({
title={intl.formatMessage(messages.newJellyfinsigninenabled, {
mediaServerName:
settings.currentSettings.mediaServerType ===
MediaServerType.EMBY
MediaServerType.EMBY
? 'Emby'
: 'Jellyfin',
strong: (msg: React.ReactNode) => (
@@ -190,13 +189,15 @@ const JellyfinImportModal: React.FC<JellyfinImportProps> = ({
>
<span
aria-hidden="true"
className={`${isAllUsers() ? 'bg-indigo-500' : 'bg-gray-800'
} absolute mx-auto h-4 w-9 rounded-full transition-colors duration-200 ease-in-out`}
className={`${
isAllUsers() ? 'bg-indigo-500' : 'bg-gray-800'
} absolute mx-auto h-4 w-9 rounded-full transition-colors duration-200 ease-in-out`}
></span>
<span
aria-hidden="true"
className={`${isAllUsers() ? 'translate-x-5' : 'translate-x-0'
} absolute left-0 inline-block h-5 w-5 transform rounded-full border border-gray-200 bg-white shadow transition-transform duration-200 ease-in-out group-focus:border-blue-300 group-focus:ring`}
className={`${
isAllUsers() ? 'translate-x-5' : 'translate-x-0'
} absolute left-0 inline-block h-5 w-5 transform rounded-full border border-gray-200 bg-white shadow transition-transform duration-200 ease-in-out group-focus:border-blue-300 group-focus:ring`}
></span>
</span>
</th>
@@ -230,17 +231,19 @@ const JellyfinImportModal: React.FC<JellyfinImportProps> = ({
>
<span
aria-hidden="true"
className={`${isSelectedUser(user.id)
? 'bg-indigo-500'
: 'bg-gray-800'
} absolute mx-auto h-4 w-9 rounded-full transition-colors duration-200 ease-in-out`}
className={`${
isSelectedUser(user.id)
? 'bg-indigo-500'
: 'bg-gray-800'
} absolute mx-auto h-4 w-9 rounded-full transition-colors duration-200 ease-in-out`}
></span>
<span
aria-hidden="true"
className={`${isSelectedUser(user.id)
? 'translate-x-5'
: 'translate-x-0'
} absolute left-0 inline-block h-5 w-5 transform rounded-full border border-gray-200 bg-white shadow transition-transform duration-200 ease-in-out group-focus:border-blue-300 group-focus:ring`}
className={`${
isSelectedUser(user.id)
? 'translate-x-5'
: 'translate-x-0'
} absolute left-0 inline-block h-5 w-5 transform rounded-full border border-gray-200 bg-white shadow transition-transform duration-200 ease-in-out group-focus:border-blue-300 group-focus:ring`}
></span>
</span>
</td>

View File

@@ -29,7 +29,6 @@ import { MediaServerType } from '@server/constants/server';
import type { UserResultsResponse } from '@server/interfaces/api/userInterfaces';
import { hasPermission } from '@server/lib/permissions';
import { Field, Form, Formik } from 'formik';
import Image from 'next/image';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
@@ -104,7 +103,8 @@ const UserList = () => {
error,
mutate: revalidate,
} = useSWR<UserResultsResponse>(
`/api/v1/user?take=${currentPageSize}&skip=${pageIndex * currentPageSize
`/api/v1/user?take=${currentPageSize}&skip=${
pageIndex * currentPageSize
}&sort=${currentSort}`
);
@@ -215,9 +215,9 @@ const UserList = () => {
!value
? Yup.string()
: Yup.string().min(
8,
intl.formatMessage(messages.validationpasswordminchars)
)
8,
intl.formatMessage(messages.validationpasswordminchars)
)
),
});
@@ -406,8 +406,9 @@ const UserList = () => {
</div>
</div>
<div
className={`form-row ${passwordGenerationEnabled ? '' : 'opacity-50'
}`}
className={`form-row ${
passwordGenerationEnabled ? '' : 'opacity-50'
}`}
>
<label htmlFor="genpassword" className="checkbox-label">
{intl.formatMessage(messages.autogeneratepassword)}
@@ -426,8 +427,9 @@ const UserList = () => {
</div>
</div>
<div
className={`form-row ${values.genpassword ? 'opacity-50' : ''
}`}
className={`form-row ${
values.genpassword ? 'opacity-50' : ''
}`}
>
<label htmlFor="password" className="text-label">
{intl.formatMessage(messages.password)}
@@ -532,16 +534,16 @@ const UserList = () => {
<InboxArrowDownIcon />
<span>
{settings.currentSettings.mediaServerType ===
MediaServerType.EMBY
MediaServerType.EMBY
? intl.formatMessage(messages.importfrommediaserver, {
mediaServerName: 'Emby',
})
mediaServerName: 'Emby',
})
: settings.currentSettings.mediaServerType ===
MediaServerType.PLEX
? intl.formatMessage(messages.importfrommediaserver, {
? intl.formatMessage(messages.importfrommediaserver, {
mediaServerName: 'Plex',
})
: intl.formatMessage(messages.importfrommediaserver, {
: intl.formatMessage(messages.importfrommediaserver, {
mediaServerName: 'Jellyfin',
})}
</span>
@@ -655,19 +657,19 @@ const UserList = () => {
user.jellyfinUsername ||
user.plexUsername
)?.toLowerCase() !== user.email && (
<div className="text-sm leading-5 text-gray-300">
{user.email}
</div>
)}
<div className="text-sm leading-5 text-gray-300">
{user.email}
</div>
)}
</div>
</div>
</Table.TD>
<Table.TD>
{user.id === currentUser?.id ||
currentHasPermission(
[Permission.MANAGE_REQUESTS, Permission.REQUEST_VIEW],
{ type: 'or' }
) ? (
currentHasPermission(
[Permission.MANAGE_REQUESTS, Permission.REQUEST_VIEW],
{ type: 'or' }
) ? (
<Link
href={`/users/${user.id}/requests`}
className="text-sm leading-5 transition duration-300 hover:underline"
@@ -705,8 +707,8 @@ const UserList = () => {
{user.id === 1
? intl.formatMessage(messages.owner)
: hasPermission(Permission.ADMIN, user.permissions)
? intl.formatMessage(messages.admin)
: intl.formatMessage(messages.user)}
? intl.formatMessage(messages.admin)
: intl.formatMessage(messages.user)}
</Table.TD>
<Table.TD>
{intl.formatDate(user.createdAt, {