mirror of
https://github.com/fallenbagel/jellyseerr.git
synced 2026-01-01 04:08:45 -05:00
feat(requests): add request quotas (#1277)
* feat(quotas): rebased * feat: add getQuota() method to User entity * feat(ui): add default quota setting options * feat: user quota settings * feat: quota display in request modals * fix: only show user quotas on own profile or with manage users permission * feat: add request progress circles to profile page * feat: add migration * fix: add missing restricted field to api schema * fix: dont show auto approve message for movie request when restricted * fix(lang): change enable checkbox langauge to "enable override" Co-authored-by: Jakob Ankarhem <jakob.ankarhem@outlook.com> Co-authored-by: TheCatLady <52870424+TheCatLady@users.noreply.github.com>
This commit is contained in:
@@ -1,15 +1,15 @@
|
||||
import { Router } from 'express';
|
||||
import { isAuthenticated } from '../middleware/auth';
|
||||
import { Permission } from '../lib/permissions';
|
||||
import { getRepository } from 'typeorm';
|
||||
import { MediaRequest } from '../entity/MediaRequest';
|
||||
import TheMovieDb from '../api/themoviedb';
|
||||
import { MediaRequestStatus, MediaStatus, MediaType } from '../constants/media';
|
||||
import Media from '../entity/Media';
|
||||
import { MediaStatus, MediaRequestStatus, MediaType } from '../constants/media';
|
||||
import { MediaRequest } from '../entity/MediaRequest';
|
||||
import SeasonRequest from '../entity/SeasonRequest';
|
||||
import logger from '../logger';
|
||||
import { RequestResultsResponse } from '../interfaces/api/requestInterfaces';
|
||||
import { User } from '../entity/User';
|
||||
import { RequestResultsResponse } from '../interfaces/api/requestInterfaces';
|
||||
import { Permission } from '../lib/permissions';
|
||||
import logger from '../logger';
|
||||
import { isAuthenticated } from '../middleware/auth';
|
||||
|
||||
const requestRoutes = Router();
|
||||
|
||||
@@ -154,8 +154,29 @@ requestRoutes.post(
|
||||
});
|
||||
}
|
||||
|
||||
if (!requestUser) {
|
||||
return next({
|
||||
status: 500,
|
||||
message: 'User missing from request context.',
|
||||
});
|
||||
}
|
||||
|
||||
const quotas = await requestUser.getQuota();
|
||||
|
||||
if (req.body.mediaType === MediaType.MOVIE && quotas.movie.restricted) {
|
||||
return next({
|
||||
status: 403,
|
||||
message: 'Movie Quota Exceeded',
|
||||
});
|
||||
} else if (req.body.mediaType === MediaType.TV && quotas.tv.restricted) {
|
||||
return next({
|
||||
status: 403,
|
||||
message: 'Series Quota Exceeded',
|
||||
});
|
||||
}
|
||||
|
||||
const tmdbMedia =
|
||||
req.body.mediaType === 'movie'
|
||||
req.body.mediaType === MediaType.MOVIE
|
||||
? await tmdb.getMovie({ movieId: req.body.mediaId })
|
||||
: await tmdb.getTvShow({ tvId: req.body.mediaId });
|
||||
|
||||
@@ -182,7 +203,7 @@ requestRoutes.post(
|
||||
}
|
||||
}
|
||||
|
||||
if (req.body.mediaType === 'movie') {
|
||||
if (req.body.mediaType === MediaType.MOVIE) {
|
||||
const existing = await requestRepository.findOne({
|
||||
where: {
|
||||
media: {
|
||||
@@ -247,7 +268,7 @@ requestRoutes.post(
|
||||
|
||||
await requestRepository.save(request);
|
||||
return res.status(201).json(request);
|
||||
} else if (req.body.mediaType === 'tv') {
|
||||
} else if (req.body.mediaType === MediaType.TV) {
|
||||
const requestedSeasons = req.body.seasons as number[];
|
||||
let existingSeasons: number[] = [];
|
||||
|
||||
@@ -458,14 +479,14 @@ requestRoutes.put<{ requestId: string }>(
|
||||
});
|
||||
}
|
||||
|
||||
if (req.body.mediaType === 'movie') {
|
||||
if (req.body.mediaType === MediaType.MOVIE) {
|
||||
request.serverId = req.body.serverId;
|
||||
request.profileId = req.body.profileId;
|
||||
request.rootFolder = req.body.rootFolder;
|
||||
request.requestedBy = requestUser as User;
|
||||
|
||||
requestRepository.save(request);
|
||||
} else if (req.body.mediaType === 'tv') {
|
||||
} else if (req.body.mediaType === MediaType.TV) {
|
||||
const mediaRepository = getRepository(Media);
|
||||
request.serverId = req.body.serverId;
|
||||
request.profileId = req.body.profileId;
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
import { Router } from 'express';
|
||||
import gravatarUrl from 'gravatar-url';
|
||||
import { getRepository, Not } from 'typeorm';
|
||||
import PlexTvAPI from '../../api/plextv';
|
||||
import { UserType } from '../../constants/user';
|
||||
import { MediaRequest } from '../../entity/MediaRequest';
|
||||
import { User } from '../../entity/User';
|
||||
import {
|
||||
QuotaResponse,
|
||||
UserRequestsResponse,
|
||||
UserResultsResponse,
|
||||
} from '../../interfaces/api/userInterfaces';
|
||||
import { hasPermission, Permission } from '../../lib/permissions';
|
||||
import { getSettings } from '../../lib/settings';
|
||||
import logger from '../../logger';
|
||||
import gravatarUrl from 'gravatar-url';
|
||||
import { UserType } from '../../constants/user';
|
||||
import { isAuthenticated } from '../../middleware/auth';
|
||||
import { UserResultsResponse } from '../../interfaces/api/userInterfaces';
|
||||
import { UserRequestsResponse } from '../../interfaces/api/userInterfaces';
|
||||
import userSettingsRoutes from './usersettings';
|
||||
|
||||
const router = Router();
|
||||
@@ -380,4 +383,36 @@ router.post(
|
||||
}
|
||||
);
|
||||
|
||||
router.get<{ id: string }, QuotaResponse>(
|
||||
'/:id/quota',
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const userRepository = getRepository(User);
|
||||
|
||||
if (
|
||||
Number(req.params.id) !== req.user?.id &&
|
||||
!req.user?.hasPermission(
|
||||
[Permission.MANAGE_USERS, Permission.MANAGE_REQUESTS],
|
||||
{ type: 'and' }
|
||||
)
|
||||
) {
|
||||
return next({
|
||||
status: 403,
|
||||
message: 'You do not have permission to access this endpoint.',
|
||||
});
|
||||
}
|
||||
|
||||
const user = await userRepository.findOneOrFail({
|
||||
where: { id: Number(req.params.id) },
|
||||
});
|
||||
|
||||
const quotas = await user.getQuota();
|
||||
|
||||
return res.status(200).json(quotas);
|
||||
} catch (e) {
|
||||
next({ status: 404, message: e.message });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -2,13 +2,13 @@ import { Router } from 'express';
|
||||
import { getRepository } from 'typeorm';
|
||||
import { canMakePermissionsChange } from '.';
|
||||
import { User } from '../../entity/User';
|
||||
import { getSettings } from '../../lib/settings';
|
||||
import { UserSettings } from '../../entity/UserSettings';
|
||||
import {
|
||||
UserSettingsGeneralResponse,
|
||||
UserSettingsNotificationsResponse,
|
||||
} from '../../interfaces/api/userSettingsInterfaces';
|
||||
import { Permission } from '../../lib/permissions';
|
||||
import { getSettings } from '../../lib/settings';
|
||||
import logger from '../../logger';
|
||||
import { isAuthenticated } from '../../middleware/auth';
|
||||
|
||||
@@ -35,6 +35,9 @@ userSettingsRoutes.get<{ id: string }, UserSettingsGeneralResponse>(
|
||||
'/main',
|
||||
isOwnProfileOrAdmin(),
|
||||
async (req, res, next) => {
|
||||
const {
|
||||
main: { defaultQuotas },
|
||||
} = getSettings();
|
||||
const userRepository = getRepository(User);
|
||||
|
||||
try {
|
||||
@@ -50,6 +53,14 @@ userSettingsRoutes.get<{ id: string }, UserSettingsGeneralResponse>(
|
||||
username: user.username,
|
||||
region: user.settings?.region,
|
||||
originalLanguage: user.settings?.originalLanguage,
|
||||
movieQuotaLimit: user.movieQuotaLimit,
|
||||
movieQuotaDays: user.movieQuotaDays,
|
||||
tvQuotaLimit: user.tvQuotaLimit,
|
||||
tvQuotaDays: user.tvQuotaDays,
|
||||
globalMovieQuotaDays: defaultQuotas.movie.quotaDays,
|
||||
globalMovieQuotaLimit: defaultQuotas.movie.quotaLimit,
|
||||
globalTvQuotaDays: defaultQuotas.tv.quotaDays,
|
||||
globalTvQuotaLimit: defaultQuotas.tv.quotaLimit,
|
||||
});
|
||||
} catch (e) {
|
||||
next({ status: 500, message: e.message });
|
||||
@@ -82,6 +93,18 @@ userSettingsRoutes.post<
|
||||
}
|
||||
|
||||
user.username = req.body.username;
|
||||
|
||||
// Update quota values only if the user has the correct permissions
|
||||
if (
|
||||
!user.hasPermission(Permission.MANAGE_USERS) &&
|
||||
req.user?.id !== user.id
|
||||
) {
|
||||
user.movieQuotaDays = req.body.movieQuotaDays;
|
||||
user.movieQuotaLimit = req.body.movieQuotaLimit;
|
||||
user.tvQuotaDays = req.body.tvQuotaDays;
|
||||
user.tvQuotaLimit = req.body.tvQuotaLimit;
|
||||
}
|
||||
|
||||
if (!user.settings) {
|
||||
user.settings = new UserSettings({
|
||||
user: req.user,
|
||||
|
||||
Reference in New Issue
Block a user