Compare commits

..

1 Commits

Author SHA1 Message Date
0xsysr3ll
d57af7334c fix(requests): mark requests as failed when Radarr/Sonarr unreachable
This PR fixes requests silently failing when Radarr/Sonarr are unreachable. Requests are now marked as `FAILED` and notifications are sent to alert both users and admins.

Signed-off-by: 0xsysr3ll <0xsysr3ll@pm.me>
2025-11-23 17:42:54 +01:00
14 changed files with 4097 additions and 2929 deletions

View File

@@ -1,33 +1,14 @@
<!--
Please read contributing guide before submitting
your pull request. Please fill in each section below to help us better prioritize your pull request. Thanks!
-->
#### Description
## Description
#### Screenshot (if UI-related)
<!--- Describe your changes in detail -->
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
#### To-Dos
- Fixes #XXXX
## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
## Screenshots / Logs (if applicable)
## Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] I have read and followed the contribution [guidelines](https://github.com/seerr-team/seerr/blob/develop/CONTRIBUTING.md).
- [ ] Disclosed any use of AI (see our [policy](https://github.com/seerr-team/seerr/blob/develop/CONTRIBUTING.md#ai-assistance-notice))
- [ ] I have updated the documentation accordingly.
- [ ] All new and existing tests passed.
- [ ] Successful build `pnpm build`
- [ ] Translation keys `pnpm i18n:extract`
- [ ] Database migration (if required)
#### Issues Fixed or Closed
- Fixes #XXXX

View File

@@ -5,7 +5,7 @@ description: Seerr helm chart for Kubernetes
type: application
version: 3.0.0
# renovate: image=ghcr.io/seerr-team/seerr
appVersion: '3.0.0'
appVersion: '2.7.3'
maintainers:
- name: Seerr Team
url: https://github.com/orgs/seerr-team/people

View File

@@ -1,6 +1,6 @@
# seerr-chart
![Version: 3.0.0](https://img.shields.io/badge/Version-3.0.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 3.0.0](https://img.shields.io/badge/AppVersion-3.0.0-informational?style=flat-square)
![Version: 3.0.0](https://img.shields.io/badge/Version-3.0.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 2.7.3](https://img.shields.io/badge/AppVersion-2.7.3-informational?style=flat-square)
Seerr helm chart for Kubernetes
@@ -22,7 +22,7 @@ Kubernetes: `>=1.23.0-0`
## Installation
Refer to [Seerr kubernetes documentation](https://docs.seerr.dev/getting-started/kubernetes)
Refer to [https://docs.seerr.dev/getting-started/kubernetes](Seerr kubernetes documentation)
## Update Notes

View File

@@ -16,7 +16,7 @@
## Installation
Refer to [Seerr kubernetes documentation](https://docs.seerr.dev/getting-started/kubernetes)
Refer to [https://docs.seerr.dev/getting-started/kubernetes](Seerr kubernetes documentation)
## Update Notes

View File

@@ -26,7 +26,8 @@ sudo mkdir -p /opt/seerr && cd /opt/seerr
```
2. Clone the Seerr repository and checkout the main branch:
```bash
git clone https://github.com/seerr-team/seerr.git .
git clone https://github.com/seerr-team/seerr.git
cd seerr
git checkout main
```
3. Install the dependencies:

View File

@@ -28,7 +28,7 @@ Changes :
If you're migrating from a previous installation, you may need to update the ownership of your config folder:
```bash
docker run --rm -v /path/to/appdata/config:/data alpine chown -R 1000:1000 /data
sudo chown -R 1000:1000 /path/to/appdata/config
```
This ensures the `node` user (UID 1000) owns the config directory and can read and write to it.

View File

@@ -2,7 +2,7 @@
"name": "seerr",
"version": "0.1.0",
"private": true,
"packageManager": "pnpm@10.24.0",
"packageManager": "pnpm@10.17.1",
"scripts": {
"preinstall": "npx only-allow pnpm",
"postinstall": "node postinstall-win.js",
@@ -33,38 +33,38 @@
},
"license": "MIT",
"dependencies": {
"@dr.pogodin/csurf": "^1.16.6",
"@formatjs/intl-displaynames": "6.8.13",
"@dr.pogodin/csurf": "^1.14.1",
"@formatjs/intl-displaynames": "6.2.6",
"@formatjs/intl-locale": "3.1.1",
"@formatjs/intl-pluralrules": "5.4.6",
"@formatjs/intl-pluralrules": "5.1.10",
"@formatjs/intl-utils": "3.8.4",
"@formatjs/swc-plugin-experimental": "^0.4.0",
"@headlessui/react": "2.2.9",
"@heroicons/react": "2.2.0",
"@headlessui/react": "1.7.12",
"@heroicons/react": "2.0.16",
"@supercharge/request-ip": "1.2.0",
"@svgr/webpack": "6.5.1",
"@tanem/react-nprogress": "5.0.56",
"@tanem/react-nprogress": "5.0.30",
"@types/ua-parser-js": "^0.7.36",
"@types/wink-jaro-distance": "^2.0.2",
"ace-builds": "1.43.4",
"axios": "1.13.2",
"axios-rate-limit": "1.4.0",
"ace-builds": "1.15.2",
"axios": "1.10.0",
"axios-rate-limit": "1.3.0",
"bcrypt": "5.1.0",
"bowser": "2.13.1",
"bowser": "2.11.0",
"connect-typeorm": "1.1.4",
"cookie-parser": "1.4.7",
"copy-to-clipboard": "3.3.3",
"country-flag-icons": "1.6.4",
"country-flag-icons": "1.5.5",
"cronstrue": "2.23.0",
"date-fns": "2.29.3",
"dayjs": "1.11.19",
"dayjs": "1.11.7",
"dns-caching": "^0.2.7",
"email-templates": "12.0.3",
"email-templates": "12.0.1",
"express": "4.21.2",
"express-openapi-validator": "4.13.8",
"express-rate-limit": "6.7.0",
"express-session": "1.18.2",
"formik": "^2.4.9",
"express-session": "1.17.3",
"formik": "^2.4.6",
"gravatar-url": "3.1.0",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6",
@@ -76,19 +76,19 @@
"node-schedule": "2.1.1",
"nodemailer": "6.10.0",
"openpgp": "5.11.2",
"pg": "8.16.3",
"pg": "8.11.0",
"plex-api": "5.3.2",
"pug": "3.0.3",
"react": "^18.3.1",
"react-ace": "10.1.0",
"react-animate-height": "2.1.2",
"react-aria": "3.44.0",
"react-aria": "3.23.0",
"react-dom": "^18.3.1",
"react-intersection-observer": "9.4.3",
"react-intl": "^6.6.8",
"react-markdown": "8.0.5",
"react-popper-tooltip": "4.4.2",
"react-select": "5.10.2",
"react-select": "5.7.0",
"react-spring": "9.7.1",
"react-tailwindcss-datepicker-sct": "1.3.4",
"react-toast-notifications": "2.5.1",
@@ -97,19 +97,19 @@
"react-use-clipboard": "1.0.9",
"reflect-metadata": "0.1.13",
"secure-random-password": "0.2.3",
"semver": "7.7.3",
"semver": "7.7.1",
"sharp": "^0.33.4",
"sqlite3": "5.1.7",
"swagger-ui-express": "4.6.2",
"swr": "2.3.7",
"swr": "2.2.5",
"tailwind-merge": "^2.6.0",
"typeorm": "0.3.12",
"ua-parser-js": "^1.0.35",
"undici": "^7.16.0",
"validator": "^13.15.23",
"web-push": "3.6.7",
"undici": "^7.3.0",
"validator": "^13.15.15",
"web-push": "3.5.0",
"wink-jaro-distance": "^2.0.0",
"winston": "3.18.3",
"winston": "3.8.2",
"winston-daily-rotate-file": "4.7.1",
"xml2js": "0.4.23",
"yamljs": "0.3.0",
@@ -123,33 +123,32 @@
"@tailwindcss/forms": "0.5.10",
"@tailwindcss/typography": "0.5.16",
"@types/bcrypt": "5.0.0",
"@types/cookie-parser": "1.4.10",
"@types/country-flag-icons": "1.2.2",
"@types/csurf": "1.11.5",
"@types/cookie-parser": "1.4.3",
"@types/country-flag-icons": "1.2.0",
"@types/csurf": "1.11.2",
"@types/email-templates": "8.0.4",
"@types/express": "4.17.17",
"@types/express-session": "1.18.2",
"@types/lodash": "4.17.21",
"@types/express-session": "1.17.6",
"@types/lodash": "4.14.191",
"@types/mime": "3",
"@types/node": "22.10.5",
"@types/node-schedule": "2.1.8",
"@types/node-schedule": "2.1.0",
"@types/nodemailer": "6.4.7",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/react-transition-group": "4.4.12",
"@types/react-transition-group": "4.4.5",
"@types/secure-random-password": "0.2.1",
"@types/semver": "7.7.1",
"@types/swagger-ui-express": "4.1.8",
"@types/validator": "^13.15.10",
"@types/web-push": "3.6.4",
"@types/semver": "7.3.13",
"@types/swagger-ui-express": "4.1.3",
"@types/validator": "^13.15.3",
"@types/web-push": "3.3.2",
"@types/xml2js": "0.4.11",
"@types/yamljs": "0.2.31",
"@types/yup": "0.29.14",
"@typescript-eslint/eslint-plugin": "5.54.0",
"@typescript-eslint/parser": "5.54.0",
"autoprefixer": "10.4.22",
"baseline-browser-mapping": "^2.8.32",
"commitizen": "4.3.1",
"autoprefixer": "10.4.13",
"commitizen": "4.3.0",
"copyfiles": "2.4.1",
"cy-mobile-commands": "0.3.0",
"cypress": "14.1.0",
@@ -158,22 +157,22 @@
"eslint-config-next": "^14.2.4",
"eslint-config-prettier": "8.6.0",
"eslint-plugin-formatjs": "4.9.0",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-no-relative-import-paths": "1.6.1",
"eslint-plugin-jsx-a11y": "6.7.1",
"eslint-plugin-no-relative-import-paths": "1.5.2",
"eslint-plugin-prettier": "4.2.1",
"eslint-plugin-react": "7.37.5",
"eslint-plugin-react": "7.32.2",
"eslint-plugin-react-hooks": "4.6.0",
"husky": "8.0.3",
"lint-staged": "13.1.2",
"nodemon": "3.1.11",
"postcss": "8.5.6",
"nodemon": "3.1.9",
"postcss": "8.4.31",
"prettier": "2.8.4",
"prettier-plugin-organize-imports": "3.2.2",
"prettier-plugin-tailwindcss": "0.2.3",
"tailwindcss": "3.2.7",
"ts-node": "10.9.2",
"tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0",
"ts-node": "10.9.1",
"tsc-alias": "1.8.2",
"tsconfig-paths": "4.1.2",
"typescript": "4.9.5"
},
"engines": {
@@ -182,7 +181,7 @@
},
"overrides": {
"sqlite3/node-gyp": "8.4.1",
"@types/express-session": "1.18.2"
"@types/express-session": "1.17.6"
},
"config": {
"commitizen": {
@@ -205,11 +204,8 @@
},
"pnpm": {
"onlyBuiltDependencies": [
"@swc/core",
"bcrypt",
"cypress",
"sharp",
"sqlite3"
"sqlite3",
"bcrypt"
]
}
}

6806
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -145,7 +145,7 @@ class JellyfinAPI extends ExternalAPI {
{},
{
headers: {
Authorization: authHeaderVal,
'X-Emby-Authorization': authHeaderVal,
'Content-Type': 'application/json',
Accept: 'application/json',
},

View File

@@ -420,13 +420,32 @@ export class MediaRequestSubscriber
mediaId: entity.media.id,
});
} catch (e) {
logger.error('Something went wrong sending request to Radarr', {
label: 'Media Request',
errorMessage: e.message,
requestId: entity.id,
mediaId: entity.media.id,
const requestRepository = getRepository(MediaRequest);
const mediaRepository = getRepository(Media);
const media = await mediaRepository.findOne({
where: { id: entity.media.id },
});
throw new Error(e.message);
if (media) {
entity.status = MediaRequestStatus.FAILED;
await requestRepository.save(entity);
logger.warn(
'Failed to send movie request to Radarr due to connection or configuration error, marking status as FAILED',
{
label: 'Media Request',
requestId: entity.id,
mediaId: entity.media.id,
errorMessage: e.message,
}
);
MediaRequest.sendNotification(
entity,
media,
Notification.MEDIA_FAILED
);
}
}
}
}
@@ -727,13 +746,32 @@ export class MediaRequestSubscriber
mediaId: entity.media.id,
});
} catch (e) {
logger.error('Something went wrong sending request to Sonarr', {
label: 'Media Request',
errorMessage: e.message,
requestId: entity.id,
mediaId: entity.media.id,
const requestRepository = getRepository(MediaRequest);
const mediaRepository = getRepository(Media);
const media = await mediaRepository.findOne({
where: { id: entity.media.id },
});
throw new Error(e.message);
if (media) {
entity.status = MediaRequestStatus.FAILED;
await requestRepository.save(entity);
logger.warn(
'Failed to send series request to Sonarr due to connection or configuration error, marking status as FAILED',
{
label: 'Media Request',
requestId: entity.id,
mediaId: entity.media.id,
errorMessage: e.message,
}
);
MediaRequest.sendNotification(
entity,
media,
Notification.MEDIA_FAILED
);
}
}
}
}

View File

@@ -25,7 +25,7 @@ const LabeledCheckbox: React.FC<LabeledCheckboxProps> = ({
<Field type="checkbox" id={id} name={id} onChange={onChange} />
</div>
<div className="ml-3 text-sm leading-6">
<label htmlFor="localLogin" className="block" aria-label={label}>
<label htmlFor="localLogin" className="block">
<div className="flex flex-col">
<span className="font-medium text-white">{label}</span>
<span className="font-normal text-gray-400">{description}</span>

View File

@@ -46,7 +46,7 @@ const NotificationType = ({
/>
</div>
<div className="ml-3 text-sm leading-6">
<label htmlFor={option.id} className="block" aria-label={option.name}>
<label htmlFor={option.id} className="block">
<div className="flex flex-col">
<span className="font-medium text-white">{option.name}</span>
<span className="font-normal text-gray-400">

View File

@@ -123,7 +123,7 @@ const PermissionOption = ({
/>
</div>
<div className="ml-3 text-sm leading-6">
<label htmlFor={option.id} className="block" aria-label={option.name}>
<label htmlFor={option.id} className="block">
<div className="flex flex-col">
<span className="font-medium text-white">{option.name}</span>
<span className="font-normal text-gray-400">

View File

@@ -63,7 +63,7 @@ class PlexOAuth {
'X-Plex-Client-Identifier': clientId,
'X-Plex-Model': 'Plex OAuth',
'X-Plex-Platform': browser.getBrowserName(),
'X-Plex-Platform-Version': browser.getBrowserVersion() || 'Unknown',
'X-Plex-Platform-Version': browser.getBrowserVersion(),
'X-Plex-Device': browser.getOSName(),
'X-Plex-Device-Name': `${browser.getBrowserName()} (Seerr)`,
'X-Plex-Device-Screen-Resolution':