Compare commits

...

2 Commits

Author SHA1 Message Date
Gauthier
c26f576786 fix: fix ManageSlideOver for blacklist 2024-11-10 17:39:45 +01:00
Gauthier
dd4b6fd859 perf: remove eager load of Blacklist entity from Media entity
Try to resolve some performance issues by removing the eager loading of Blacklist items from the
Media entity
2024-11-10 16:48:32 +01:00
6 changed files with 66 additions and 19 deletions

View File

@@ -4142,6 +4142,21 @@ paths:
'412': '412':
description: Item has already been blacklisted description: Item has already been blacklisted
/blacklist/{tmdbId}: /blacklist/{tmdbId}:
get:
summary: Get media from blacklist
tags:
- blacklist
parameters:
- in: path
name: tmdbId
description: tmdbId ID
required: true
example: '1'
schema:
type: string
responses:
'200':
description: Blacklist details in JSON
delete: delete:
summary: Remove media from blacklist summary: Remove media from blacklist
tags: tags:

View File

@@ -80,12 +80,12 @@ export class Blacklist implements BlacklistItem {
status: MediaStatus.BLACKLISTED, status: MediaStatus.BLACKLISTED,
status4k: MediaStatus.BLACKLISTED, status4k: MediaStatus.BLACKLISTED,
mediaType: blacklistRequest.mediaType, mediaType: blacklistRequest.mediaType,
blacklist: blacklist, blacklist: Promise.resolve(blacklist),
}); });
await mediaRepository.save(media); await mediaRepository.save(media);
} else { } else {
media.blacklist = blacklist; media.blacklist = Promise.resolve(blacklist);
media.status = MediaStatus.BLACKLISTED; media.status = MediaStatus.BLACKLISTED;
media.status4k = MediaStatus.BLACKLISTED; media.status4k = MediaStatus.BLACKLISTED;

View File

@@ -118,10 +118,8 @@ class Media {
@OneToMany(() => Issue, (issue) => issue.media, { cascade: true }) @OneToMany(() => Issue, (issue) => issue.media, { cascade: true })
public issues: Issue[]; public issues: Issue[];
@OneToOne(() => Blacklist, (blacklist) => blacklist.media, { @OneToOne(() => Blacklist, (blacklist) => blacklist.media)
eager: true, public blacklist: Promise<Blacklist>;
})
public blacklist: Blacklist;
@CreateDateColumn() @CreateDateColumn()
public createdAt: Date; public createdAt: Date;

View File

@@ -2,14 +2,13 @@ import { MediaType } from '@server/constants/media';
import { getRepository } from '@server/datasource'; import { getRepository } from '@server/datasource';
import { Blacklist } from '@server/entity/Blacklist'; import { Blacklist } from '@server/entity/Blacklist';
import Media from '@server/entity/Media'; import Media from '@server/entity/Media';
import { NotFoundError } from '@server/entity/Watchlist';
import type { BlacklistResultsResponse } from '@server/interfaces/api/blacklistInterfaces'; import type { BlacklistResultsResponse } from '@server/interfaces/api/blacklistInterfaces';
import { Permission } from '@server/lib/permissions'; import { Permission } from '@server/lib/permissions';
import logger from '@server/logger'; import logger from '@server/logger';
import { isAuthenticated } from '@server/middleware/auth'; import { isAuthenticated } from '@server/middleware/auth';
import { Router } from 'express'; import { Router } from 'express';
import rateLimit from 'express-rate-limit'; import rateLimit from 'express-rate-limit';
import { QueryFailedError } from 'typeorm'; import { EntityNotFoundError, QueryFailedError } from 'typeorm';
import { z } from 'zod'; import { z } from 'zod';
const blacklistRoutes = Router(); const blacklistRoutes = Router();
@@ -71,6 +70,32 @@ blacklistRoutes.get(
} }
); );
blacklistRoutes.get(
'/:id',
isAuthenticated([Permission.MANAGE_BLACKLIST], {
type: 'or',
}),
async (req, res, next) => {
try {
const blacklisteRepository = getRepository(Blacklist);
const blacklistItem = await blacklisteRepository.findOneOrFail({
where: { tmdbId: Number(req.params.id) },
});
return res.status(200).send(blacklistItem);
} catch (e) {
if (e instanceof EntityNotFoundError) {
return next({
status: 401,
message: e.message,
});
}
return next({ status: 500, message: e.message });
}
}
);
blacklistRoutes.post( blacklistRoutes.post(
'/', '/',
isAuthenticated([Permission.MANAGE_BLACKLIST], { isAuthenticated([Permission.MANAGE_BLACKLIST], {
@@ -134,7 +159,7 @@ blacklistRoutes.delete(
return res.status(204).send(); return res.status(204).send();
} catch (e) { } catch (e) {
if (e instanceof NotFoundError) { if (e instanceof EntityNotFoundError) {
return next({ return next({
status: 401, status: 401,
message: e.message, message: e.message,

View File

@@ -1,5 +1,6 @@
import Badge from '@app/components/Common/Badge'; import Badge from '@app/components/Common/Badge';
import Button from '@app/components/Common/Button'; import Button from '@app/components/Common/Button';
import LoadingSpinner from '@app/components/Common/LoadingSpinner';
import Tooltip from '@app/components/Common/Tooltip'; import Tooltip from '@app/components/Common/Tooltip';
import { useUser } from '@app/hooks/useUser'; import { useUser } from '@app/hooks/useUser';
import globalMessages from '@app/i18n/globalMessages'; import globalMessages from '@app/i18n/globalMessages';
@@ -10,6 +11,7 @@ import Link from 'next/link';
import { useState } from 'react'; import { useState } from 'react';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import { useToasts } from 'react-toast-notifications'; import { useToasts } from 'react-toast-notifications';
import useSWR from 'swr';
const messages = defineMessages('component.BlacklistBlock', { const messages = defineMessages('component.BlacklistBlock', {
blacklistedby: 'Blacklisted By', blacklistedby: 'Blacklisted By',
@@ -17,13 +19,13 @@ const messages = defineMessages('component.BlacklistBlock', {
}); });
interface BlacklistBlockProps { interface BlacklistBlockProps {
blacklistItem: Blacklist; tmdbId: number;
onUpdate?: () => void; onUpdate?: () => void;
onDelete?: () => void; onDelete?: () => void;
} }
const BlacklistBlock = ({ const BlacklistBlock = ({
blacklistItem, tmdbId,
onUpdate, onUpdate,
onDelete, onDelete,
}: BlacklistBlockProps) => { }: BlacklistBlockProps) => {
@@ -31,6 +33,7 @@ const BlacklistBlock = ({
const intl = useIntl(); const intl = useIntl();
const [isUpdating, setIsUpdating] = useState(false); const [isUpdating, setIsUpdating] = useState(false);
const { addToast } = useToasts(); const { addToast } = useToasts();
const { data } = useSWR<Blacklist>(`/api/v1/blacklist/${tmdbId}`);
const removeFromBlacklist = async (tmdbId: number, title?: string) => { const removeFromBlacklist = async (tmdbId: number, title?: string) => {
setIsUpdating(true); setIsUpdating(true);
@@ -62,6 +65,14 @@ const BlacklistBlock = ({
setIsUpdating(false); setIsUpdating(false);
}; };
if (!data) {
return (
<>
<LoadingSpinner />
</>
);
}
return ( return (
<div className="px-4 py-3 text-gray-300"> <div className="px-4 py-3 text-gray-300">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
@@ -73,13 +84,13 @@ const BlacklistBlock = ({
<span className="w-40 truncate md:w-auto"> <span className="w-40 truncate md:w-auto">
<Link <Link
href={ href={
blacklistItem.user.id === user?.id data.user.id === user?.id
? '/profile' ? '/profile'
: `/users/${blacklistItem.user.id}` : `/users/${data.user.id}`
} }
> >
<span className="font-semibold text-gray-100 transition duration-300 hover:text-white hover:underline"> <span className="font-semibold text-gray-100 transition duration-300 hover:text-white hover:underline">
{blacklistItem.user.displayName} {data.user.displayName}
</span> </span>
</Link> </Link>
</span> </span>
@@ -91,9 +102,7 @@ const BlacklistBlock = ({
> >
<Button <Button
buttonType="danger" buttonType="danger"
onClick={() => onClick={() => removeFromBlacklist(data.tmdbId, data.title)}
removeFromBlacklist(blacklistItem.tmdbId, blacklistItem.title)
}
disabled={isUpdating} disabled={isUpdating}
> >
<TrashIcon className="icon-sm" /> <TrashIcon className="icon-sm" />
@@ -114,7 +123,7 @@ const BlacklistBlock = ({
<CalendarIcon className="mr-1.5 h-5 w-5 flex-shrink-0" /> <CalendarIcon className="mr-1.5 h-5 w-5 flex-shrink-0" />
</Tooltip> </Tooltip>
<span> <span>
{intl.formatDate(blacklistItem.createdAt, { {intl.formatDate(data.createdAt, {
year: 'numeric', year: 'numeric',
month: 'long', month: 'long',
day: 'numeric', day: 'numeric',

View File

@@ -292,7 +292,7 @@ const ManageSlideOver = ({
</h3> </h3>
<div className="overflow-hidden rounded-md border border-gray-700 shadow"> <div className="overflow-hidden rounded-md border border-gray-700 shadow">
<BlacklistBlock <BlacklistBlock
blacklistItem={data.mediaInfo.blacklist} tmdbId={data.mediaInfo.tmdbId}
onUpdate={() => revalidate()} onUpdate={() => revalidate()}
onDelete={() => onClose()} onDelete={() => onClose()}
/> />