diff --git a/.gitignore b/.gitignore index d294bc0914..20aef36132 100644 --- a/.gitignore +++ b/.gitignore @@ -72,5 +72,7 @@ tsconfig.tsbuildinfo # Config Cache Directory config/cache +# Allow server directory for Telescope find_files +!/server # Docker compose compose.override.yaml diff --git a/.vscode/settings.json b/.vscode/settings.json index dbb4a2ccf8..92ad828392 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,8 +20,6 @@ "files.associations": { "globals.css": "tailwindcss" }, - "i18n-ally.localesPaths": [ - "src/i18n/locale" - ], + "i18n-ally.localesPaths": ["src/i18n/locale"], "yaml.format.singleQuote": true } diff --git a/seerr-api.yml b/seerr-api.yml index bf9d882712..6a70188a8d 100644 --- a/seerr-api.yml +++ b/seerr-api.yml @@ -6843,7 +6843,7 @@ paths: /media/{mediaId}: delete: summary: Delete media item - description: Removes a media item. The `MANAGE_REQUESTS` permission is required to perform this action. + description: Removes a media item. The `MANAGE_REQUESTS` or `DELETE_MEDIA` permission is required to perform this action. tags: - media parameters: @@ -6860,7 +6860,7 @@ paths: /media/{mediaId}/file: delete: summary: Delete media file - description: Removes a media file from radarr/sonarr. The `ADMIN` permission is required to perform this action. + description: Removes a media file from radarr/sonarr. The `ADMIN` or `DELETE_MEDIA` permission is required to perform this action. tags: - media parameters: @@ -6929,7 +6929,7 @@ paths: description: | Returns play count, play duration, and users who have watched the media. - Requires the `ADMIN` permission. + Requires the `ADMIN` or `DELETE_MEDIA` permission. tags: - media parameters: diff --git a/server/lib/permissions.ts b/server/lib/permissions.ts index bc477169c0..e8ea85256e 100644 --- a/server/lib/permissions.ts +++ b/server/lib/permissions.ts @@ -28,6 +28,7 @@ export enum Permission { RECENT_VIEW = 67108864, WATCHLIST_VIEW = 134217728, MANAGE_BLACKLIST = 268435456, + DELETE_MEDIA = 536870912, VIEW_BLACKLIST = 1073741824, } diff --git a/server/routes/index.ts b/server/routes/index.ts index 6155f3508f..dfa83d1689 100644 --- a/server/routes/index.ts +++ b/server/routes/index.ts @@ -146,6 +146,24 @@ router.get( } } ); + +router.get( + '/settings/radarr', + isAuthenticated([Permission.ADMIN, Permission.DELETE_MEDIA], { type: 'or' }), + (_req, res) => { + const settings = getSettings(); + return res.status(200).json(settings.radarr); + } +); + +router.get( + '/settings/sonarr', + isAuthenticated([Permission.ADMIN, Permission.DELETE_MEDIA], { type: 'or' }), + (_req, res) => { + const settings = getSettings(); + return res.status(200).json(settings.sonarr); + } +); router.use('/settings', isAuthenticated(Permission.ADMIN), settingsRoutes); router.use('/search', isAuthenticated(), searchRoutes); router.use('/discover', isAuthenticated(), discoverRoutes); diff --git a/server/routes/media.ts b/server/routes/media.ts index 8f52efae86..393d90a006 100644 --- a/server/routes/media.ts +++ b/server/routes/media.ts @@ -165,7 +165,9 @@ mediaRoutes.post< mediaRoutes.delete( '/:id', - isAuthenticated(Permission.MANAGE_REQUESTS), + isAuthenticated([Permission.MANAGE_REQUESTS, Permission.DELETE_MEDIA], { + type: 'or', + }), async (req, res, next) => { try { const mediaRepository = getRepository(Media); @@ -189,7 +191,9 @@ mediaRoutes.delete( mediaRoutes.delete( '/:id/file', - isAuthenticated(Permission.MANAGE_REQUESTS), + isAuthenticated([Permission.ADMIN, Permission.DELETE_MEDIA], { + type: 'or', + }), async (req, res, next) => { try { const settings = getSettings(); @@ -282,7 +286,7 @@ mediaRoutes.delete( mediaRoutes.get<{ id: string }, MediaWatchDataResponse>( '/:id/watch_data', - isAuthenticated(Permission.ADMIN), + isAuthenticated([Permission.ADMIN, Permission.DELETE_MEDIA], { type: 'or' }), async (req, res, next) => { const settings = getSettings().tautulli; diff --git a/server/routes/settings/radarr.ts b/server/routes/settings/radarr.ts index efa5866582..cc927e3a00 100644 --- a/server/routes/settings/radarr.ts +++ b/server/routes/settings/radarr.ts @@ -6,12 +6,6 @@ import { Router } from 'express'; const radarrRoutes = Router(); -radarrRoutes.get('/', (_req, res) => { - const settings = getSettings(); - - res.status(200).json(settings.radarr); -}); - radarrRoutes.post('/', async (req, res) => { const settings = getSettings(); diff --git a/server/routes/settings/sonarr.ts b/server/routes/settings/sonarr.ts index 84bf4d7932..e0d7f5efc9 100644 --- a/server/routes/settings/sonarr.ts +++ b/server/routes/settings/sonarr.ts @@ -6,12 +6,6 @@ import { Router } from 'express'; const sonarrRoutes = Router(); -sonarrRoutes.get('/', (_req, res) => { - const settings = getSettings(); - - res.status(200).json(settings.sonarr); -}); - sonarrRoutes.post('/', async (req, res) => { const settings = getSettings(); diff --git a/src/components/ManageSlideOver/index.tsx b/src/components/ManageSlideOver/index.tsx index 7a2c1047e3..b5c10d036f 100644 --- a/src/components/ManageSlideOver/index.tsx +++ b/src/components/ManageSlideOver/index.tsx @@ -99,15 +99,19 @@ const ManageSlideOver = ({ const { data: watchData } = useSWR( settings.currentSettings.mediaServerType === MediaServerType.PLEX && data.mediaInfo && - hasPermission(Permission.ADMIN) + hasPermission([Permission.ADMIN, Permission.DELETE_MEDIA], { type: 'or' }) ? `/api/v1/media/${data.mediaInfo.id}/watch_data` : null ); const { data: radarrData } = useSWR( - hasPermission(Permission.ADMIN) ? '/api/v1/settings/radarr' : null + hasPermission([Permission.ADMIN, Permission.DELETE_MEDIA], { type: 'or' }) + ? '/api/v1/settings/radarr' + : null ); const { data: sonarrData } = useSWR( - hasPermission(Permission.ADMIN) ? '/api/v1/settings/sonarr' : null + hasPermission([Permission.ADMIN, Permission.DELETE_MEDIA], { type: 'or' }) + ? '/api/v1/settings/sonarr' + : null ); const deleteMedia = async () => { @@ -312,7 +316,9 @@ const ManageSlideOver = ({ )} - {hasPermission(Permission.ADMIN) && + {hasPermission([Permission.ADMIN, Permission.DELETE_MEDIA], { + type: 'or', + }) && (data.mediaInfo?.serviceUrl || data.mediaInfo?.tautulliUrl || watchData?.data) && ( @@ -435,8 +441,9 @@ const ManageSlideOver = ({ )} - - {hasPermission(Permission.ADMIN) && + {hasPermission([Permission.ADMIN, Permission.DELETE_MEDIA], { + type: 'or', + }) && data?.mediaInfo?.serviceUrl && isDefaultService() && (
diff --git a/src/components/MovieDetails/index.tsx b/src/components/MovieDetails/index.tsx index 85574fe991..56a6567894 100644 --- a/src/components/MovieDetails/index.tsx +++ b/src/components/MovieDetails/index.tsx @@ -648,7 +648,10 @@ const MovieDetails = ({ movie }: MovieDetailsProps) => { )} - {hasPermission(Permission.MANAGE_REQUESTS) && + {hasPermission( + [Permission.MANAGE_REQUESTS, Permission.DELETE_MEDIA], + { type: 'or' } + ) && data.mediaInfo && (data.mediaInfo.jellyfinMediaId || data.mediaInfo.jellyfinMediaId4k || diff --git a/src/components/PermissionEdit/index.tsx b/src/components/PermissionEdit/index.tsx index 5a861de8ae..68f4b75c5e 100644 --- a/src/components/PermissionEdit/index.tsx +++ b/src/components/PermissionEdit/index.tsx @@ -17,6 +17,9 @@ export const messages = defineMessages('components.PermissionEdit', { managerequests: 'Manage Requests', managerequestsDescription: 'Grant permission to manage media requests. All requests made by a user with this permission will be automatically approved.', + deletemedia: 'Delete Media', + deletemediaDescription: + 'Grant permission to delete media from Sonarr and Radarr.', request: 'Request', requestDescription: 'Grant permission to submit requests for non-4K media.', requestMovies: 'Request Movies', @@ -164,6 +167,12 @@ export const PermissionEdit = ({ }, ], }, + { + id: 'deletemedia', + name: intl.formatMessage(messages.deletemedia), + description: intl.formatMessage(messages.deletemediaDescription), + permission: Permission.DELETE_MEDIA, + }, { id: 'request', name: intl.formatMessage(messages.request), diff --git a/src/components/TvDetails/index.tsx b/src/components/TvDetails/index.tsx index 5aa3a67dfb..89eef03654 100644 --- a/src/components/TvDetails/index.tsx +++ b/src/components/TvDetails/index.tsx @@ -692,33 +692,37 @@ const TvDetails = ({ tv }: TvDetailsProps) => { )} - {hasPermission(Permission.MANAGE_REQUESTS) && data.mediaInfo && ( - - - - )} + {hasPermission( + [Permission.MANAGE_REQUESTS, Permission.DELETE_MEDIA], + { type: 'or' } + ) && + data.mediaInfo && ( + + + + )}
diff --git a/src/i18n/locale/en.json b/src/i18n/locale/en.json index 34963834d2..35cb3eb896 100644 --- a/src/i18n/locale/en.json +++ b/src/i18n/locale/en.json @@ -415,6 +415,8 @@ "components.PermissionEdit.blacklistedItemsDescription": "Grant permission to blacklist media.", "components.PermissionEdit.createissues": "Report Issues", "components.PermissionEdit.createissuesDescription": "Grant permission to report media issues.", + "components.PermissionEdit.deletemedia": "Delete Media", + "components.PermissionEdit.deletemediaDescription": "Grant permission to delete media from Sonarr and Radarr.", "components.PermissionEdit.manageblacklist": "Manage Blacklist", "components.PermissionEdit.manageblacklistDescription": "Grant permission to manage blacklisted media.", "components.PermissionEdit.manageissues": "Manage Issues",