mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
feat: issues (#2180)
This commit is contained in:
@@ -14,6 +14,8 @@ import { isPerson } from '../utils/typeHelpers';
|
||||
import authRoutes from './auth';
|
||||
import collectionRoutes from './collection';
|
||||
import discoverRoutes, { createTmdbWithRegionLanguage } from './discover';
|
||||
import issueRoutes from './issue';
|
||||
import issueCommentRoutes from './issueComment';
|
||||
import mediaRoutes from './media';
|
||||
import movieRoutes from './movie';
|
||||
import personRoutes from './person';
|
||||
@@ -108,6 +110,8 @@ router.use('/media', isAuthenticated(), mediaRoutes);
|
||||
router.use('/person', isAuthenticated(), personRoutes);
|
||||
router.use('/collection', isAuthenticated(), collectionRoutes);
|
||||
router.use('/service', isAuthenticated(), serviceRoutes);
|
||||
router.use('/issue', isAuthenticated(), issueRoutes);
|
||||
router.use('/issueComment', isAuthenticated(), issueCommentRoutes);
|
||||
router.use('/auth', authRoutes);
|
||||
|
||||
router.get('/regions', isAuthenticated(), async (req, res) => {
|
||||
|
325
server/routes/issue.ts
Normal file
325
server/routes/issue.ts
Normal file
@@ -0,0 +1,325 @@
|
||||
import { Router } from 'express';
|
||||
import { getRepository } from 'typeorm';
|
||||
import { IssueStatus } from '../constants/issue';
|
||||
import Issue from '../entity/Issue';
|
||||
import IssueComment from '../entity/IssueComment';
|
||||
import Media from '../entity/Media';
|
||||
import { IssueResultsResponse } from '../interfaces/api/issueInterfaces';
|
||||
import { Permission } from '../lib/permissions';
|
||||
import logger from '../logger';
|
||||
import { isAuthenticated } from '../middleware/auth';
|
||||
|
||||
const issueRoutes = Router();
|
||||
|
||||
issueRoutes.get<Record<string, string>, IssueResultsResponse>(
|
||||
'/',
|
||||
isAuthenticated(
|
||||
[
|
||||
Permission.MANAGE_ISSUES,
|
||||
Permission.VIEW_ISSUES,
|
||||
Permission.CREATE_ISSUES,
|
||||
],
|
||||
{ type: 'or' }
|
||||
),
|
||||
async (req, res, next) => {
|
||||
const pageSize = req.query.take ? Number(req.query.take) : 10;
|
||||
const skip = req.query.skip ? Number(req.query.skip) : 0;
|
||||
const createdBy = req.query.createdBy ? Number(req.query.createdBy) : null;
|
||||
|
||||
let sortFilter: string;
|
||||
|
||||
switch (req.query.sort) {
|
||||
case 'modified':
|
||||
sortFilter = 'issue.updatedAt';
|
||||
break;
|
||||
default:
|
||||
sortFilter = 'issue.createdAt';
|
||||
}
|
||||
|
||||
let statusFilter: IssueStatus[];
|
||||
|
||||
switch (req.query.filter) {
|
||||
case 'open':
|
||||
statusFilter = [IssueStatus.OPEN];
|
||||
break;
|
||||
case 'resolved':
|
||||
statusFilter = [IssueStatus.RESOLVED];
|
||||
break;
|
||||
default:
|
||||
statusFilter = [IssueStatus.OPEN, IssueStatus.RESOLVED];
|
||||
}
|
||||
|
||||
let query = getRepository(Issue)
|
||||
.createQueryBuilder('issue')
|
||||
.leftJoinAndSelect('issue.createdBy', 'createdBy')
|
||||
.leftJoinAndSelect('issue.media', 'media')
|
||||
.leftJoinAndSelect('issue.modifiedBy', 'modifiedBy')
|
||||
.where('issue.status IN (:...issueStatus)', {
|
||||
issueStatus: statusFilter,
|
||||
});
|
||||
|
||||
if (
|
||||
!req.user?.hasPermission(
|
||||
[Permission.MANAGE_ISSUES, Permission.VIEW_ISSUES],
|
||||
{ type: 'or' }
|
||||
)
|
||||
) {
|
||||
if (createdBy && createdBy !== req.user?.id) {
|
||||
return next({
|
||||
status: 403,
|
||||
message:
|
||||
'You do not have permission to view issues created by other users',
|
||||
});
|
||||
}
|
||||
query = query.andWhere('createdBy.id = :id', { id: req.user?.id });
|
||||
} else if (createdBy) {
|
||||
query = query.andWhere('createdBy.id = :id', { id: createdBy });
|
||||
}
|
||||
|
||||
const [issues, issueCount] = await query
|
||||
.orderBy(sortFilter, 'DESC')
|
||||
.take(pageSize)
|
||||
.skip(skip)
|
||||
.getManyAndCount();
|
||||
|
||||
return res.status(200).json({
|
||||
pageInfo: {
|
||||
pages: Math.ceil(issueCount / pageSize),
|
||||
pageSize,
|
||||
results: issueCount,
|
||||
page: Math.ceil(skip / pageSize) + 1,
|
||||
},
|
||||
results: issues,
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
issueRoutes.post<
|
||||
Record<string, string>,
|
||||
Issue,
|
||||
{
|
||||
message: string;
|
||||
mediaId: number;
|
||||
issueType: number;
|
||||
problemSeason: number;
|
||||
problemEpisode: number;
|
||||
}
|
||||
>(
|
||||
'/',
|
||||
isAuthenticated([Permission.MANAGE_ISSUES, Permission.CREATE_ISSUES], {
|
||||
type: 'or',
|
||||
}),
|
||||
async (req, res, next) => {
|
||||
// Satisfy typescript here. User is set, we assure you!
|
||||
if (!req.user) {
|
||||
return next({ status: 500, message: 'User missing from request.' });
|
||||
}
|
||||
|
||||
const issueRepository = getRepository(Issue);
|
||||
const mediaRepository = getRepository(Media);
|
||||
|
||||
const media = await mediaRepository.findOne({
|
||||
where: { id: req.body.mediaId },
|
||||
});
|
||||
|
||||
if (!media) {
|
||||
return next({ status: 404, message: 'Media does not exist.' });
|
||||
}
|
||||
|
||||
const issue = new Issue({
|
||||
createdBy: req.user,
|
||||
issueType: req.body.issueType,
|
||||
problemSeason: req.body.problemSeason,
|
||||
problemEpisode: req.body.problemEpisode,
|
||||
media,
|
||||
comments: [
|
||||
new IssueComment({
|
||||
user: req.user,
|
||||
message: req.body.message,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const newIssue = await issueRepository.save(issue);
|
||||
|
||||
return res.status(200).json(newIssue);
|
||||
}
|
||||
);
|
||||
|
||||
issueRoutes.get<{ issueId: string }>(
|
||||
'/:issueId',
|
||||
isAuthenticated(
|
||||
[
|
||||
Permission.MANAGE_ISSUES,
|
||||
Permission.VIEW_ISSUES,
|
||||
Permission.CREATE_ISSUES,
|
||||
],
|
||||
{ type: 'or' }
|
||||
),
|
||||
async (req, res, next) => {
|
||||
const issueRepository = getRepository(Issue);
|
||||
// Satisfy typescript here. User is set, we assure you!
|
||||
if (!req.user) {
|
||||
return next({ status: 500, message: 'User missing from request.' });
|
||||
}
|
||||
|
||||
try {
|
||||
const issue = await issueRepository
|
||||
.createQueryBuilder('issue')
|
||||
.leftJoinAndSelect('issue.comments', 'comments')
|
||||
.leftJoinAndSelect('issue.createdBy', 'createdBy')
|
||||
.leftJoinAndSelect('comments.user', 'user')
|
||||
.leftJoinAndSelect('issue.media', 'media')
|
||||
.where('issue.id = :issueId', { issueId: Number(req.params.issueId) })
|
||||
.getOneOrFail();
|
||||
|
||||
if (
|
||||
issue.createdBy.id !== req.user.id &&
|
||||
!req.user.hasPermission(
|
||||
[Permission.MANAGE_ISSUES, Permission.VIEW_ISSUES],
|
||||
{ type: 'or' }
|
||||
)
|
||||
) {
|
||||
return next({
|
||||
status: 403,
|
||||
message: 'You do not have permission to view this issue.',
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).json(issue);
|
||||
} catch (e) {
|
||||
logger.debug('Failed to retrieve issue.', {
|
||||
label: 'API',
|
||||
errorMessage: e.message,
|
||||
});
|
||||
next({ status: 500, message: 'Issue not found.' });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
issueRoutes.post<{ issueId: string }, Issue, { message: string }>(
|
||||
'/:issueId/comment',
|
||||
isAuthenticated([Permission.MANAGE_ISSUES, Permission.CREATE_ISSUES], {
|
||||
type: 'or',
|
||||
}),
|
||||
async (req, res, next) => {
|
||||
const issueRepository = getRepository(Issue);
|
||||
// Satisfy typescript here. User is set, we assure you!
|
||||
if (!req.user) {
|
||||
return next({ status: 500, message: 'User missing from request.' });
|
||||
}
|
||||
|
||||
try {
|
||||
const issue = await issueRepository.findOneOrFail({
|
||||
where: { id: Number(req.params.issueId) },
|
||||
});
|
||||
|
||||
if (
|
||||
issue.createdBy.id !== req.user.id &&
|
||||
!req.user.hasPermission(Permission.MANAGE_ISSUES)
|
||||
) {
|
||||
return next({
|
||||
status: 403,
|
||||
message: 'You do not have permission to comment on this issue.',
|
||||
});
|
||||
}
|
||||
|
||||
const comment = new IssueComment({
|
||||
message: req.body.message,
|
||||
user: req.user,
|
||||
});
|
||||
|
||||
issue.comments = [...issue.comments, comment];
|
||||
|
||||
await issueRepository.save(issue);
|
||||
|
||||
return res.status(200).json(issue);
|
||||
} catch (e) {
|
||||
logger.debug('Something went wrong creating an issue comment.', {
|
||||
label: 'API',
|
||||
errorMessage: e.message,
|
||||
});
|
||||
next({ status: 500, message: 'Issue not found.' });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
issueRoutes.post<{ issueId: string; status: string }, Issue>(
|
||||
'/:issueId/:status',
|
||||
isAuthenticated(Permission.MANAGE_ISSUES),
|
||||
async (req, res, next) => {
|
||||
const issueRepository = getRepository(Issue);
|
||||
// Satisfy typescript here. User is set, we assure you!
|
||||
if (!req.user) {
|
||||
return next({ status: 500, message: 'User missing from request.' });
|
||||
}
|
||||
|
||||
try {
|
||||
const issue = await issueRepository.findOneOrFail({
|
||||
where: { id: Number(req.params.issueId) },
|
||||
});
|
||||
|
||||
let newStatus: IssueStatus | undefined;
|
||||
|
||||
switch (req.params.status) {
|
||||
case 'resolved':
|
||||
newStatus = IssueStatus.RESOLVED;
|
||||
break;
|
||||
case 'open':
|
||||
newStatus = IssueStatus.OPEN;
|
||||
}
|
||||
|
||||
if (!newStatus) {
|
||||
return next({
|
||||
status: 400,
|
||||
message: 'You must provide a valid status',
|
||||
});
|
||||
}
|
||||
|
||||
issue.status = newStatus;
|
||||
|
||||
await issueRepository.save(issue);
|
||||
|
||||
return res.status(200).json(issue);
|
||||
} catch (e) {
|
||||
logger.debug('Something went wrong creating an issue comment.', {
|
||||
label: 'API',
|
||||
errorMessage: e.message,
|
||||
});
|
||||
next({ status: 500, message: 'Issue not found.' });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
issueRoutes.delete('/:issueId', async (req, res, next) => {
|
||||
const issueRepository = getRepository(Issue);
|
||||
|
||||
try {
|
||||
const issue = await issueRepository.findOneOrFail({
|
||||
where: { id: Number(req.params.issueId) },
|
||||
relations: ['createdBy'],
|
||||
});
|
||||
|
||||
if (
|
||||
!req.user?.hasPermission(Permission.MANAGE_ISSUES) &&
|
||||
issue.createdBy.id !== req.user?.id
|
||||
) {
|
||||
return next({
|
||||
status: 401,
|
||||
message: 'You do not have permission to delete this issue.',
|
||||
});
|
||||
}
|
||||
|
||||
await issueRepository.remove(issue);
|
||||
|
||||
return res.status(204).send();
|
||||
} catch (e) {
|
||||
logger.error('Something went wrong deleting an issue.', {
|
||||
label: 'API',
|
||||
errorMessage: e.message,
|
||||
});
|
||||
next({ status: 404, message: 'Issue not found.' });
|
||||
}
|
||||
});
|
||||
|
||||
export default issueRoutes;
|
132
server/routes/issueComment.ts
Normal file
132
server/routes/issueComment.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import { Router } from 'express';
|
||||
import { getRepository } from 'typeorm';
|
||||
import IssueComment from '../entity/IssueComment';
|
||||
import { Permission } from '../lib/permissions';
|
||||
import logger from '../logger';
|
||||
import { isAuthenticated } from '../middleware/auth';
|
||||
|
||||
const issueCommentRoutes = Router();
|
||||
|
||||
issueCommentRoutes.get<{ commentId: string }, IssueComment>(
|
||||
'/:commentId',
|
||||
isAuthenticated(
|
||||
[
|
||||
Permission.MANAGE_ISSUES,
|
||||
Permission.VIEW_ISSUES,
|
||||
Permission.CREATE_ISSUES,
|
||||
],
|
||||
{
|
||||
type: 'or',
|
||||
}
|
||||
),
|
||||
async (req, res, next) => {
|
||||
const issueCommentRepository = getRepository(IssueComment);
|
||||
|
||||
try {
|
||||
const comment = await issueCommentRepository.findOneOrFail({
|
||||
where: { id: Number(req.params.commentId) },
|
||||
});
|
||||
|
||||
if (
|
||||
!req.user?.hasPermission(
|
||||
[Permission.MANAGE_ISSUES, Permission.VIEW_ISSUES],
|
||||
{ type: 'or' }
|
||||
) &&
|
||||
comment.user.id !== req.user?.id
|
||||
) {
|
||||
return next({
|
||||
status: 403,
|
||||
message: 'You do not have permission to view this comment.',
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).json(comment);
|
||||
} catch (e) {
|
||||
logger.debug('Request for unknown issue comment failed', {
|
||||
label: 'API',
|
||||
errorMessage: e.message,
|
||||
});
|
||||
next({ status: 404, message: 'Issue comment not found.' });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
issueCommentRoutes.put<
|
||||
{ commentId: string },
|
||||
IssueComment,
|
||||
{ message: string }
|
||||
>(
|
||||
'/:commentId',
|
||||
isAuthenticated([Permission.MANAGE_ISSUES, Permission.CREATE_ISSUES], {
|
||||
type: 'or',
|
||||
}),
|
||||
async (req, res, next) => {
|
||||
const issueCommentRepository = getRepository(IssueComment);
|
||||
|
||||
try {
|
||||
const comment = await issueCommentRepository.findOneOrFail({
|
||||
where: { id: Number(req.params.commentId) },
|
||||
});
|
||||
|
||||
if (
|
||||
!req.user?.hasPermission([Permission.MANAGE_ISSUES], { type: 'or' }) &&
|
||||
comment.user.id !== req.user?.id
|
||||
) {
|
||||
return next({
|
||||
status: 403,
|
||||
message: 'You do not have permission to edit this comment.',
|
||||
});
|
||||
}
|
||||
|
||||
comment.message = req.body.message;
|
||||
|
||||
await issueCommentRepository.save(comment);
|
||||
|
||||
return res.status(200).json(comment);
|
||||
} catch (e) {
|
||||
logger.debug('Put request for issue comment failed', {
|
||||
label: 'API',
|
||||
errorMessage: e.message,
|
||||
});
|
||||
next({ status: 404, message: 'Issue comment not found.' });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
issueCommentRoutes.delete<{ commentId: string }, IssueComment>(
|
||||
'/:commentId',
|
||||
isAuthenticated([Permission.MANAGE_ISSUES, Permission.CREATE_ISSUES], {
|
||||
type: 'or',
|
||||
}),
|
||||
async (req, res, next) => {
|
||||
const issueCommentRepository = getRepository(IssueComment);
|
||||
|
||||
try {
|
||||
const comment = await issueCommentRepository.findOneOrFail({
|
||||
where: { id: Number(req.params.commentId) },
|
||||
});
|
||||
|
||||
if (
|
||||
!req.user?.hasPermission([Permission.MANAGE_ISSUES], { type: 'or' }) &&
|
||||
comment.user.id !== req.user?.id
|
||||
) {
|
||||
return next({
|
||||
status: 403,
|
||||
message: 'You do not have permission to delete this comment.',
|
||||
});
|
||||
}
|
||||
|
||||
await issueCommentRepository.remove(comment);
|
||||
|
||||
return res.status(204).send();
|
||||
} catch (e) {
|
||||
logger.debug('Delete request for issue comment failed', {
|
||||
label: 'API',
|
||||
errorMessage: e.message,
|
||||
});
|
||||
next({ status: 404, message: 'Issue comment not found.' });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default issueCommentRoutes;
|
@@ -13,131 +13,134 @@ import { isAuthenticated } from '../middleware/auth';
|
||||
|
||||
const requestRoutes = Router();
|
||||
|
||||
requestRoutes.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const pageSize = req.query.take ? Number(req.query.take) : 10;
|
||||
const skip = req.query.skip ? Number(req.query.skip) : 0;
|
||||
const requestedBy = req.query.requestedBy
|
||||
? Number(req.query.requestedBy)
|
||||
: null;
|
||||
requestRoutes.get<Record<string, unknown>, RequestResultsResponse>(
|
||||
'/',
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const pageSize = req.query.take ? Number(req.query.take) : 10;
|
||||
const skip = req.query.skip ? Number(req.query.skip) : 0;
|
||||
const requestedBy = req.query.requestedBy
|
||||
? Number(req.query.requestedBy)
|
||||
: null;
|
||||
|
||||
let statusFilter: MediaRequestStatus[];
|
||||
let statusFilter: MediaRequestStatus[];
|
||||
|
||||
switch (req.query.filter) {
|
||||
case 'approved':
|
||||
case 'processing':
|
||||
case 'available':
|
||||
statusFilter = [MediaRequestStatus.APPROVED];
|
||||
break;
|
||||
case 'pending':
|
||||
statusFilter = [MediaRequestStatus.PENDING];
|
||||
break;
|
||||
case 'unavailable':
|
||||
statusFilter = [
|
||||
MediaRequestStatus.PENDING,
|
||||
MediaRequestStatus.APPROVED,
|
||||
];
|
||||
break;
|
||||
default:
|
||||
statusFilter = [
|
||||
MediaRequestStatus.PENDING,
|
||||
MediaRequestStatus.APPROVED,
|
||||
MediaRequestStatus.DECLINED,
|
||||
];
|
||||
}
|
||||
switch (req.query.filter) {
|
||||
case 'approved':
|
||||
case 'processing':
|
||||
case 'available':
|
||||
statusFilter = [MediaRequestStatus.APPROVED];
|
||||
break;
|
||||
case 'pending':
|
||||
statusFilter = [MediaRequestStatus.PENDING];
|
||||
break;
|
||||
case 'unavailable':
|
||||
statusFilter = [
|
||||
MediaRequestStatus.PENDING,
|
||||
MediaRequestStatus.APPROVED,
|
||||
];
|
||||
break;
|
||||
default:
|
||||
statusFilter = [
|
||||
MediaRequestStatus.PENDING,
|
||||
MediaRequestStatus.APPROVED,
|
||||
MediaRequestStatus.DECLINED,
|
||||
];
|
||||
}
|
||||
|
||||
let mediaStatusFilter: MediaStatus[];
|
||||
let mediaStatusFilter: MediaStatus[];
|
||||
|
||||
switch (req.query.filter) {
|
||||
case 'available':
|
||||
mediaStatusFilter = [MediaStatus.AVAILABLE];
|
||||
break;
|
||||
case 'processing':
|
||||
case 'unavailable':
|
||||
mediaStatusFilter = [
|
||||
MediaStatus.UNKNOWN,
|
||||
MediaStatus.PENDING,
|
||||
MediaStatus.PROCESSING,
|
||||
MediaStatus.PARTIALLY_AVAILABLE,
|
||||
];
|
||||
break;
|
||||
default:
|
||||
mediaStatusFilter = [
|
||||
MediaStatus.UNKNOWN,
|
||||
MediaStatus.PENDING,
|
||||
MediaStatus.PROCESSING,
|
||||
MediaStatus.PARTIALLY_AVAILABLE,
|
||||
MediaStatus.AVAILABLE,
|
||||
];
|
||||
}
|
||||
switch (req.query.filter) {
|
||||
case 'available':
|
||||
mediaStatusFilter = [MediaStatus.AVAILABLE];
|
||||
break;
|
||||
case 'processing':
|
||||
case 'unavailable':
|
||||
mediaStatusFilter = [
|
||||
MediaStatus.UNKNOWN,
|
||||
MediaStatus.PENDING,
|
||||
MediaStatus.PROCESSING,
|
||||
MediaStatus.PARTIALLY_AVAILABLE,
|
||||
];
|
||||
break;
|
||||
default:
|
||||
mediaStatusFilter = [
|
||||
MediaStatus.UNKNOWN,
|
||||
MediaStatus.PENDING,
|
||||
MediaStatus.PROCESSING,
|
||||
MediaStatus.PARTIALLY_AVAILABLE,
|
||||
MediaStatus.AVAILABLE,
|
||||
];
|
||||
}
|
||||
|
||||
let sortFilter: string;
|
||||
let sortFilter: string;
|
||||
|
||||
switch (req.query.sort) {
|
||||
case 'modified':
|
||||
sortFilter = 'request.updatedAt';
|
||||
break;
|
||||
default:
|
||||
sortFilter = 'request.id';
|
||||
}
|
||||
switch (req.query.sort) {
|
||||
case 'modified':
|
||||
sortFilter = 'request.updatedAt';
|
||||
break;
|
||||
default:
|
||||
sortFilter = 'request.id';
|
||||
}
|
||||
|
||||
let query = getRepository(MediaRequest)
|
||||
.createQueryBuilder('request')
|
||||
.leftJoinAndSelect('request.media', 'media')
|
||||
.leftJoinAndSelect('request.seasons', 'seasons')
|
||||
.leftJoinAndSelect('request.modifiedBy', 'modifiedBy')
|
||||
.leftJoinAndSelect('request.requestedBy', 'requestedBy')
|
||||
.where('request.status IN (:...requestStatus)', {
|
||||
requestStatus: statusFilter,
|
||||
})
|
||||
.andWhere(
|
||||
'((request.is4k = 0 AND media.status IN (:...mediaStatus)) OR (request.is4k = 1 AND media.status4k IN (:...mediaStatus)))',
|
||||
{
|
||||
mediaStatus: mediaStatusFilter,
|
||||
let query = getRepository(MediaRequest)
|
||||
.createQueryBuilder('request')
|
||||
.leftJoinAndSelect('request.media', 'media')
|
||||
.leftJoinAndSelect('request.seasons', 'seasons')
|
||||
.leftJoinAndSelect('request.modifiedBy', 'modifiedBy')
|
||||
.leftJoinAndSelect('request.requestedBy', 'requestedBy')
|
||||
.where('request.status IN (:...requestStatus)', {
|
||||
requestStatus: statusFilter,
|
||||
})
|
||||
.andWhere(
|
||||
'((request.is4k = 0 AND media.status IN (:...mediaStatus)) OR (request.is4k = 1 AND media.status4k IN (:...mediaStatus)))',
|
||||
{
|
||||
mediaStatus: mediaStatusFilter,
|
||||
}
|
||||
);
|
||||
|
||||
if (
|
||||
!req.user?.hasPermission(
|
||||
[Permission.MANAGE_REQUESTS, Permission.REQUEST_VIEW],
|
||||
{ type: 'or' }
|
||||
)
|
||||
) {
|
||||
if (requestedBy && requestedBy !== req.user?.id) {
|
||||
return next({
|
||||
status: 403,
|
||||
message: "You do not have permission to view this user's requests.",
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
if (
|
||||
!req.user?.hasPermission(
|
||||
[Permission.MANAGE_REQUESTS, Permission.REQUEST_VIEW],
|
||||
{ type: 'or' }
|
||||
)
|
||||
) {
|
||||
if (requestedBy && requestedBy !== req.user?.id) {
|
||||
return next({
|
||||
status: 403,
|
||||
message: "You do not have permission to view this user's requests.",
|
||||
query = query.andWhere('requestedBy.id = :id', {
|
||||
id: req.user?.id,
|
||||
});
|
||||
} else if (requestedBy) {
|
||||
query = query.andWhere('requestedBy.id = :id', {
|
||||
id: requestedBy,
|
||||
});
|
||||
}
|
||||
|
||||
query = query.andWhere('requestedBy.id = :id', {
|
||||
id: req.user?.id,
|
||||
});
|
||||
} else if (requestedBy) {
|
||||
query = query.andWhere('requestedBy.id = :id', {
|
||||
id: requestedBy,
|
||||
const [requests, requestCount] = await query
|
||||
.orderBy(sortFilter, 'DESC')
|
||||
.take(pageSize)
|
||||
.skip(skip)
|
||||
.getManyAndCount();
|
||||
|
||||
return res.status(200).json({
|
||||
pageInfo: {
|
||||
pages: Math.ceil(requestCount / pageSize),
|
||||
pageSize,
|
||||
results: requestCount,
|
||||
page: Math.ceil(skip / pageSize) + 1,
|
||||
},
|
||||
results: requests,
|
||||
});
|
||||
} catch (e) {
|
||||
next({ status: 500, message: e.message });
|
||||
}
|
||||
|
||||
const [requests, requestCount] = await query
|
||||
.orderBy(sortFilter, 'DESC')
|
||||
.take(pageSize)
|
||||
.skip(skip)
|
||||
.getManyAndCount();
|
||||
|
||||
return res.status(200).json({
|
||||
pageInfo: {
|
||||
pages: Math.ceil(requestCount / pageSize),
|
||||
pageSize,
|
||||
results: requestCount,
|
||||
page: Math.ceil(skip / pageSize) + 1,
|
||||
},
|
||||
results: requests,
|
||||
} as RequestResultsResponse);
|
||||
} catch (e) {
|
||||
next({ status: 500, message: e.message });
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
requestRoutes.post('/', async (req, res, next) => {
|
||||
const tmdb = new TheMovieDb();
|
||||
@@ -665,7 +668,10 @@ requestRoutes.delete('/:requestId', async (req, res, next) => {
|
||||
|
||||
return res.status(204).send();
|
||||
} catch (e) {
|
||||
logger.error(e.message);
|
||||
logger.error('Something went wrong deleting a request.', {
|
||||
label: 'API',
|
||||
errorMessage: e.message,
|
||||
});
|
||||
next({ status: 404, message: 'Request not found.' });
|
||||
}
|
||||
});
|
||||
|
Reference in New Issue
Block a user