chore(deps): upgrade dependencies

This commit is contained in:
gauthier-th
2026-04-14 10:56:08 +02:00
parent cd257bad2a
commit e22f98966d
24 changed files with 3824 additions and 3776 deletions

View File

@@ -34,96 +34,92 @@
},
"license": "MIT",
"dependencies": {
"@dr.pogodin/csurf": "^1.16.6",
"@dr.pogodin/csurf": "^1.16.9",
"@fontsource-variable/inter": "^5.2.8",
"@formatjs/intl": "^4.1.4",
"@formatjs/intl-displaynames": "6.8.13",
"@formatjs/intl-locale": "3.1.1",
"@formatjs/intl-pluralrules": "5.4.6",
"@formatjs/intl-utils": "3.8.4",
"@headlessui/react": "1.7.12",
"@formatjs/intl-displaynames": "7.3.2",
"@formatjs/intl-locale": "5.3.2",
"@formatjs/intl-pluralrules": "6.3.2",
"@headlessui/react": "1.7.19",
"@heroicons/react": "2.2.0",
"@seerr-team/react-tailwindcss-datepicker": "^1.3.4",
"@supercharge/request-ip": "1.2.0",
"@svgr/webpack": "8.1.0",
"@tanem/react-nprogress": "5.0.56",
"@types/ua-parser-js": "^0.7.36",
"@types/wink-jaro-distance": "^2.0.2",
"ace-builds": "1.43.4",
"@tanem/react-nprogress": "6.0.3",
"ace-builds": "1.43.6",
"axios": "1.15.0",
"axios-rate-limit": "1.9.0",
"bcrypt": "6.0.0",
"bowser": "2.13.1",
"connect-typeorm": "1.1.4",
"bowser": "2.14.1",
"connect-typeorm": "2.0.0",
"cookie-parser": "1.4.7",
"copy-to-clipboard": "3.3.3",
"country-flag-icons": "1.6.4",
"cronstrue": "2.23.0",
"date-fns": "2.29.3",
"dns-caching": "^0.2.7",
"email-templates": "12.0.3",
"country-flag-icons": "1.6.16",
"cronstrue": "3.14.0",
"date-fns": "4.1.0",
"dns-caching": "^0.2.9",
"email-templates": "13.0.1",
"express": "4.21.2",
"express-openapi-validator": "4.13.8",
"express-rate-limit": "6.7.0",
"express-session": "1.18.2",
"express-openapi-validator": "5.6.2",
"express-rate-limit": "8.3.2",
"express-session": "1.19.0",
"formik": "^2.4.9",
"gravatar-url": "3.1.0",
"gravatar-url": "4.0.1",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6",
"lodash": "4.17.23",
"mime": "3",
"js-yaml": "^4.1.1",
"lodash": "4.18.1",
"mime": "^4.1.0",
"nanoid": "^5.1.7",
"next": "^14.2.35",
"node-cache": "5.1.2",
"node-gyp": "9.3.1",
"node-gyp": "12.2.0",
"node-schedule": "2.1.1",
"nodemailer": "7.0.12",
"nodemailer": "8.0.5",
"openpgp": "6.3.0",
"pg": "8.17.2",
"pug": "3.0.3",
"pg": "8.20.0",
"pug": "3.0.4",
"react": "^18.3.1",
"react-ace": "10.1.0",
"react-animate-height": "2.1.2",
"react-aria": "3.44.0",
"react-ace": "14.0.1",
"react-animate-height": "3.2.3",
"react-aria": "3.47.0",
"react-dom": "^18.3.1",
"react-intersection-observer": "9.4.3",
"react-intl": "^6.6.8",
"react-markdown": "8.0.5",
"react-intersection-observer": "10.0.3",
"react-intl": "^7.1.14",
"react-markdown": "10.1.0",
"react-popper-tooltip": "4.4.2",
"react-select": "5.10.2",
"react-spring": "9.7.1",
"react-spring": "10.0.3",
"react-toast-notifications": "2.5.1",
"react-transition-group": "^4.4.5",
"react-truncate-markup": "5.1.2",
"react-use-clipboard": "1.0.9",
"reflect-metadata": "0.1.13",
"secure-random-password": "0.2.3",
"semver": "7.7.3",
"sharp": "^0.33.4",
"sqlite3": "5.1.7",
"swagger-ui-express": "4.6.2",
"swr": "2.3.8",
"tailwind-merge": "^2.6.0",
"semver": "7.7.4",
"sharp": "^0.34.5",
"sqlite3": "^6.0.1",
"swagger-ui-express": "5.0.1",
"swr": "2.4.1",
"tailwind-merge": "^2.6.1",
"typeorm": "0.3.28",
"ua-parser-js": "^1.0.35",
"undici": "^7.18.2",
"validator": "^13.15.23",
"ua-parser-js": "^2.0.9",
"undici": "^8.1.0",
"validator": "^13.15.35",
"web-push": "3.6.7",
"wink-jaro-distance": "^2.0.0",
"winston": "3.19.0",
"winston-daily-rotate-file": "4.7.1",
"xml2js": "0.5.0",
"yamljs": "0.3.0",
"yup": "0.32.11",
"winston-daily-rotate-file": "5.0.0",
"xml2js": "0.6.2",
"yup": "1.7.1",
"zod": "4.3.6"
},
"devDependencies": {
"@commitlint/cli": "17.4.4",
"@commitlint/config-conventional": "17.4.4",
"@commitlint/cli": "20.5.0",
"@commitlint/config-conventional": "20.5.0",
"@eslint/js": "9.39.3",
"@next/eslint-plugin-next": "^16.1.6",
"@next/eslint-plugin-next": "^16.2.3",
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/forms": "^0.5.10",
"@tailwindcss/typography": "^0.5.16",
"@tailwindcss/forms": "^0.5.11",
"@tailwindcss/typography": "^0.5.19",
"@types/bcrypt": "6.0.0",
"@types/cookie-parser": "1.4.10",
"@types/country-flag-icons": "1.2.2",
@@ -132,55 +128,55 @@
"@types/eslint-plugin-jsx-a11y": "^6.10.1",
"@types/express": "4.17.17",
"@types/express-session": "1.18.2",
"@types/lodash": "4.17.21",
"@types/mime": "3",
"@types/js-yaml": "^4.0.9",
"@types/lodash": "4.17.24",
"@types/mime": "^3.0.4",
"@types/node": "22.10.5",
"@types/node-schedule": "2.1.8",
"@types/nodemailer": "7",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/nodemailer": "^8.0.0",
"@types/react": "^18.3.28",
"@types/react-dom": "^18.3.7",
"@types/react-transition-group": "4.4.12",
"@types/secure-random-password": "0.2.1",
"@types/semver": "7.7.1",
"@types/supertest": "^6.0.3",
"@types/supertest": "^7.2.0",
"@types/swagger-ui-express": "4.1.8",
"@types/ua-parser-js": "^0.7.36",
"@types/validator": "^13.15.10",
"@types/web-push": "3.6.4",
"@types/wink-jaro-distance": "^2.0.2",
"@types/xml2js": "0.4.14",
"@types/yamljs": "0.2.31",
"@types/yup": "0.29.14",
"autoprefixer": "^10.4.23",
"baseline-browser-mapping": "^2.8.32",
"autoprefixer": "^10.5.0",
"baseline-browser-mapping": "^2.10.18",
"commander": "^14.0.3",
"commitizen": "4.3.1",
"copyfiles": "2.4.1",
"cy-mobile-commands": "0.3.0",
"cypress": "14.5.4",
"cypress": "15.13.1",
"cz-conventional-changelog": "3.3.0",
"eslint": "9.39.3",
"eslint-config-prettier": "10.1.8",
"eslint-plugin-formatjs": "6.2.0",
"eslint-plugin-formatjs": "6.4.5",
"eslint-plugin-jsx-a11y": "6.10.2",
"eslint-plugin-no-relative-import-paths": "1.6.1",
"eslint-plugin-prettier": "4.2.1",
"eslint-plugin-react": "7.37.5",
"eslint-plugin-react-hooks": "7.0.1",
"globals": "^17.3.0",
"globals": "^17.5.0",
"husky": "8.0.3",
"jiti": "^2.6.1",
"lint-staged": "13.1.2",
"nodemon": "3.1.11",
"postcss": "^8.5.6",
"prettier": "3.8.1",
"lint-staged": "16.4.0",
"nodemon": "3.1.14",
"postcss": "^8.5.9",
"prettier": "3.8.2",
"prettier-plugin-organize-imports": "4.3.0",
"prettier-plugin-tailwindcss": "0.6.14",
"prettier-plugin-tailwindcss": "0.7.2",
"supertest": "^7.2.2",
"tailwindcss": "3.4.19",
"ts-node": "10.9.2",
"tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0",
"typescript": "5.4.5",
"typescript-eslint": "^8.56.1"
"typescript-eslint": "^8.58.2"
},
"engines": {
"node": "^22.19.0",
@@ -217,5 +213,8 @@
"sqlite3>node-gyp": "8.4.1",
"@types/express-session": "1.18.2"
}
},
"scarfSettings": {
"enabled": false
}
}
}

6999
pnpm-lock.yaml generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -12,8 +12,8 @@ import { DbAwareColumn, resolveDbType } from '@server/utils/DbColumnHelper';
import { AfterDate } from '@server/utils/dateHelpers';
import bcrypt from 'bcrypt';
import { randomUUID } from 'crypto';
import { nanoid } from 'nanoid';
import path from 'path';
import { default as generatePassword } from 'secure-random-password';
import {
AfterLoad,
Column,
@@ -193,7 +193,7 @@ export class User {
}
public async generatePassword(): Promise<void> {
const password = generatePassword.randomPassword({ length: 16 });
const password = nanoid(16);
await this.setPassword(password);
const { applicationTitle, applicationUrl } = getSettings().main;

View File

@@ -38,12 +38,13 @@ import express from 'express';
import * as OpenApiValidator from 'express-openapi-validator';
import type { Store } from 'express-session';
import session from 'express-session';
import fs from 'fs/promises';
import http from 'http';
import https from 'https';
import yaml from 'js-yaml';
import next from 'next';
import path from 'path';
import swaggerUi from 'swagger-ui-express';
import YAML from 'yamljs';
const API_SPEC_PATH = path.join(__dirname, '../seerr-api.yml');
@@ -221,7 +222,8 @@ app
}).connect(sessionRespository) as Store,
})
);
const apiDocs = YAML.load(API_SPEC_PATH);
const apiSpecContent = await fs.readFile(API_SPEC_PATH, 'utf-8');
const apiDocs = yaml.load(apiSpecContent) as Record<string, unknown>;
server.use('/api-docs', swaggerUi.serve, swaggerUi.setup(apiDocs));
server.use(
OpenApiValidator.middleware({

View File

@@ -1,3 +1,4 @@
/* eslint-disable no-console */
import path from 'path';
import * as winston from 'winston';
import 'winston-daily-rotate-file';
@@ -14,6 +15,42 @@ const hformat = winston.format.printf(
}
);
const seerrFileTransport = new winston.transports.DailyRotateFile({
filename: process.env.CONFIG_DIRECTORY
? `${process.env.CONFIG_DIRECTORY}/logs/seerr-%DATE%.log`
: path.join(__dirname, '../config/logs/seerr-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '7d',
createSymlink: true,
symlinkName: 'seerr.log',
});
const machineLogFileTransport = new winston.transports.DailyRotateFile({
filename: process.env.CONFIG_DIRECTORY
? `${process.env.CONFIG_DIRECTORY}/logs/.machinelogs-%DATE%.json`
: path.join(__dirname, '../config/logs/.machinelogs-%DATE%.json'),
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '1d',
createSymlink: true,
symlinkName: '.machinelogs.json',
format: winston.format.combine(
winston.format.splat(),
winston.format.timestamp(),
winston.format.json()
),
});
seerrFileTransport.on('error', (err) => {
console.error('Error in seerr file transport:', err);
});
machineLogFileTransport.on('error', (err) => {
console.error('Error in machine log file transport:', err);
});
const logger = winston.createLogger({
level: process.env.LOG_LEVEL?.toLowerCase() || 'debug',
format: winston.format.combine(
@@ -30,33 +67,8 @@ const logger = winston.createLogger({
hformat
),
}),
new winston.transports.DailyRotateFile({
filename: process.env.CONFIG_DIRECTORY
? `${process.env.CONFIG_DIRECTORY}/logs/seerr-%DATE%.log`
: path.join(__dirname, '../config/logs/seerr-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '7d',
createSymlink: true,
symlinkName: 'seerr.log',
}),
new winston.transports.DailyRotateFile({
filename: process.env.CONFIG_DIRECTORY
? `${process.env.CONFIG_DIRECTORY}/logs/.machinelogs-%DATE%.json`
: path.join(__dirname, '../config/logs/.machinelogs-%DATE%.json'),
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '1d',
createSymlink: true,
symlinkName: '.machinelogs.json',
format: winston.format.combine(
winston.format.splat(),
winston.format.timestamp(),
winston.format.json()
),
}),
seerrFileTransport,
machineLogFileTransport,
],
});

View File

@@ -55,10 +55,11 @@ const NotificationsDiscord = () => {
webhookUrl: Yup.string()
.when('enabled', {
is: true,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationUrl)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationUrl)),
otherwise: (schema) => schema.nullable(),
})
.url(intl.formatMessage(messages.validationUrl)),
webhookRoleId: Yup.string()

View File

@@ -73,10 +73,11 @@ const NotificationsEmail = () => {
emailFrom: Yup.string()
.when('enabled', {
is: true,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationEmail)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationEmail)),
otherwise: (schema) => schema.nullable(),
})
.test(
'email',
@@ -85,25 +86,28 @@ const NotificationsEmail = () => {
),
smtpHost: Yup.string().when('enabled', {
is: true,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationSmtpHostRequired)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationSmtpHostRequired)),
otherwise: (schema) => schema.nullable(),
}),
smtpPort: Yup.number().when('enabled', {
is: true,
then: Yup.number()
.nullable()
.required(intl.formatMessage(messages.validationSmtpPortRequired)),
otherwise: Yup.number().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationSmtpPortRequired)),
otherwise: (schema) => schema.nullable(),
}),
pgpPrivateKey: Yup.string()
.when('pgpPassword', {
is: (value: unknown) => !!value,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationPgpPrivateKey)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationPgpPrivateKey)),
otherwise: (schema) => schema.nullable(),
})
.matches(
/-----BEGIN PGP PRIVATE KEY BLOCK-----.+-----END PGP PRIVATE KEY BLOCK-----/s,
@@ -111,10 +115,11 @@ const NotificationsEmail = () => {
),
pgpPassword: Yup.string().when('pgpPrivateKey', {
is: (value: unknown) => !!value,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationPgpPassword)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationPgpPassword)),
otherwise: (schema) => schema.nullable(),
}),
},
[['pgpPrivateKey', 'pgpPassword']]

View File

@@ -48,10 +48,11 @@ const NotificationsGotify = () => {
url: Yup.string()
.when('enabled', {
is: true,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationUrlRequired)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationUrlRequired)),
otherwise: (schema) => schema.nullable(),
})
.test(
'valid-url',
@@ -65,19 +66,21 @@ const NotificationsGotify = () => {
),
token: Yup.string().when('enabled', {
is: true,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationTokenRequired)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationTokenRequired)),
otherwise: (schema) => schema.nullable(),
}),
priority: Yup.string().when('enabled', {
is: true,
then: Yup.string()
.nullable()
.min(0)
.max(9)
.required(intl.formatMessage(messages.validationPriorityRequired)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.min(0)
.max(9)
.required(intl.formatMessage(messages.validationPriorityRequired)),
otherwise: (schema) => schema.nullable(),
}),
});

View File

@@ -55,10 +55,11 @@ const NotificationsNtfy = () => {
url: Yup.string()
.when('enabled', {
is: true,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationNtfyUrl)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationNtfyUrl)),
otherwise: (schema) => schema.nullable(),
})
.test(
'valid-url',
@@ -68,19 +69,22 @@ const NotificationsNtfy = () => {
topic: Yup.string()
.when('enabled', {
is: true,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationNtfyUrl)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationNtfyTopic)),
otherwise: (schema) => schema.nullable(),
})
.defined(intl.formatMessage(messages.validationNtfyTopic)),
priority: Yup.number().when('enabled', {
is: true,
then: Yup.number()
.min(1)
.max(5)
.required(intl.formatMessage(messages.validationPriorityRequired)),
otherwise: Yup.number().nullable(),
then: (schema) =>
schema
.nullable()
.min(1)
.max(5)
.required(intl.formatMessage(messages.validationPriorityRequired)),
otherwise: (schema) => schema.nullable(),
}),
});

View File

@@ -46,10 +46,11 @@ const NotificationsPushbullet = () => {
const NotificationsPushbulletSchema = Yup.object().shape({
accessToken: Yup.string().when('enabled', {
is: true,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationAccessTokenRequired)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationAccessTokenRequired)),
otherwise: (schema) => schema.nullable(),
}),
});

View File

@@ -61,10 +61,13 @@ const NotificationsPushover = () => {
accessToken: Yup.string()
.when('enabled', {
is: true,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationAccessTokenRequired)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(
intl.formatMessage(messages.validationAccessTokenRequired)
),
otherwise: (schema) => schema.nullable(),
})
.matches(
/^[a-z\d]{30}$/i,
@@ -73,10 +76,11 @@ const NotificationsPushover = () => {
userToken: Yup.string()
.when('enabled', {
is: true,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationUserTokenRequired)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationUserTokenRequired)),
otherwise: (schema) => schema.nullable(),
})
.matches(
/^[a-z\d]{30}$/i,

View File

@@ -45,10 +45,11 @@ const NotificationsSlack = () => {
webhookUrl: Yup.string()
.when('enabled', {
is: true,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationWebhookUrl)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationWebhookUrl)),
otherwise: (schema) => schema.nullable(),
})
.url(intl.formatMessage(messages.validationWebhookUrl)),
});

View File

@@ -53,31 +53,28 @@ const NotificationsTelegram = () => {
const NotificationsTelegramSchema = Yup.object().shape({
botAPI: Yup.string().when('enabled', {
is: true,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationBotAPIRequired)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationBotAPIRequired)),
otherwise: (schema) => schema.nullable(),
}),
chatId: Yup.string()
.when(['enabled', 'types'], {
is: (enabled: boolean, types: number) => enabled && !!types,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationChatIdRequired)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationChatIdRequired)),
otherwise: (schema) => schema.nullable(),
})
.matches(
/^-?\d+$/,
intl.formatMessage(messages.validationChatIdRequired)
),
messageThreadId: Yup.string()
.when(['types'], {
is: (enabled: boolean, types: number) => enabled && !!types,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationMessageThreadId)),
otherwise: Yup.string().nullable(),
})
.transform((v) => v || null)
.nullable()
.matches(/^\d+$/, intl.formatMessage(messages.validationMessageThreadId)),
});

View File

@@ -127,10 +127,11 @@ const NotificationsWebhook = () => {
webhookUrl: Yup.string()
.when('enabled', {
is: true,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationWebhookUrl)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationWebhookUrl)),
otherwise: (schema) => schema.nullable(),
})
.test(
'valid-url',
@@ -183,10 +184,13 @@ const NotificationsWebhook = () => {
jsonPayload: Yup.string()
.when('enabled', {
is: true,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationJsonPayloadRequired)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(
intl.formatMessage(messages.validationJsonPayloadRequired)
),
otherwise: (schema) => schema.nullable(),
})
.test(
'validate-json',

View File

@@ -116,13 +116,15 @@ const SettingsJellyfin: React.FC<SettingsJellyfinProps> = ({
.required(intl.formatMessage(messages.validationHostnameRequired)),
port: Yup.number().when(['hostname'], {
is: (value: unknown) => !!value,
then: Yup.number()
.typeError(intl.formatMessage(messages.validationPortRequired))
.nullable()
.required(intl.formatMessage(messages.validationPortRequired)),
otherwise: Yup.number()
.typeError(intl.formatMessage(messages.validationPortRequired))
.nullable(),
then: (schema) =>
schema
.typeError(intl.formatMessage(messages.validationPortRequired))
.nullable()
.required(intl.formatMessage(messages.validationPortRequired)),
otherwise: (schema) =>
schema
.typeError(intl.formatMessage(messages.validationPortRequired))
.nullable(),
}),
urlBase: Yup.string()
.test(

View File

@@ -74,26 +74,32 @@ const SettingsNetwork = () => {
const NetworkSettingsSchema = Yup.object().shape({
dnsCacheForceMinTtl: Yup.number().when('dnsCacheEnabled', {
is: true,
then: Yup.number()
.typeError(intl.formatMessage(messages.validationDnsCacheMinTtl))
.required(intl.formatMessage(messages.validationDnsCacheMinTtl))
.min(0),
then: (schema) =>
schema
.typeError(intl.formatMessage(messages.validationDnsCacheMinTtl))
.required(intl.formatMessage(messages.validationDnsCacheMinTtl))
.min(0),
otherwise: (schema) => schema.nullable(),
}),
dnsCacheForceMaxTtl: Yup.number().when('dnsCacheEnabled', {
is: true,
then: Yup.number()
.typeError(intl.formatMessage(messages.validationDnsCacheMaxTtl))
.required(intl.formatMessage(messages.validationDnsCacheMaxTtl))
.min(-1),
then: (schema) =>
schema
.typeError(intl.formatMessage(messages.validationDnsCacheMaxTtl))
.required(intl.formatMessage(messages.validationDnsCacheMaxTtl))
.min(-1),
otherwise: (schema) => schema.nullable(),
}),
proxyPort: Yup.number().when('proxyEnabled', {
is: (proxyEnabled: boolean) => proxyEnabled,
then: Yup.number()
.typeError(intl.formatMessage(messages.validationProxyPort))
.integer(intl.formatMessage(messages.validationProxyPort))
.min(1, intl.formatMessage(messages.validationProxyPort))
.max(65535, intl.formatMessage(messages.validationProxyPort))
.required(intl.formatMessage(messages.validationProxyPort)),
then: (schema) =>
schema
.typeError(intl.formatMessage(messages.validationProxyPort))
.integer(intl.formatMessage(messages.validationProxyPort))
.min(1, intl.formatMessage(messages.validationProxyPort))
.max(65535, intl.formatMessage(messages.validationProxyPort))
.required(intl.formatMessage(messages.validationProxyPort)),
otherwise: (schema) => schema.nullable(),
}),
apiRequestTimeout: Yup.number()
.typeError(intl.formatMessage(messages.validationApiRequestTimeout))

View File

@@ -150,10 +150,13 @@ const SettingsPlex = ({ onComplete }: SettingsPlexProps) => {
tautulliHostname: Yup.string()
.when(['tautulliPort', 'tautulliApiKey'], {
is: (value: unknown) => !!value,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationHostnameRequired)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(
intl.formatMessage(messages.validationHostnameRequired)
),
otherwise: (schema) => schema.nullable(),
})
.matches(
/^(([a-z]|\d|_|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*)?([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])$/i,
@@ -161,13 +164,15 @@ const SettingsPlex = ({ onComplete }: SettingsPlexProps) => {
),
tautulliPort: Yup.number().when(['tautulliHostname', 'tautulliApiKey'], {
is: (value: unknown) => !!value,
then: Yup.number()
.typeError(intl.formatMessage(messages.validationPortRequired))
.nullable()
.required(intl.formatMessage(messages.validationPortRequired)),
otherwise: Yup.number()
.typeError(intl.formatMessage(messages.validationPortRequired))
.nullable(),
then: (schema) =>
schema
.typeError(intl.formatMessage(messages.validationPortRequired))
.nullable()
.required(intl.formatMessage(messages.validationPortRequired)),
otherwise: (schema) =>
schema
.typeError(intl.formatMessage(messages.validationPortRequired))
.nullable(),
}),
tautulliUrlBase: Yup.string()
.test(
@@ -182,10 +187,11 @@ const SettingsPlex = ({ onComplete }: SettingsPlexProps) => {
),
tautulliApiKey: Yup.string().when(['tautulliHostname', 'tautulliPort'], {
is: (value: unknown) => !!value,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationApiKey)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationApiKey)),
otherwise: (schema) => schema.nullable(),
}),
tautulliExternalUrl: Yup.string()
.test(

View File

@@ -63,9 +63,9 @@ const SettingsUsers = () => {
.test({
name: 'atLeastOneAuth',
test: function (values) {
const isValid = ['localLogin', 'mediaServerLogin'].some(
(field) => !!values[field]
);
const isValid = (
['localLogin', 'mediaServerLogin'] as (keyof typeof values)[]
).some((field) => !!values[field]);
if (isValid) return true;
return this.createError({

View File

@@ -44,10 +44,11 @@ const UserNotificationsDiscord = () => {
discordId: Yup.string()
.when('types', {
is: (types: number) => !!types,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationDiscordId)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationDiscordId)),
otherwise: (schema) => schema.nullable(),
})
.matches(/^\d{17,19}$/, intl.formatMessage(messages.validationDiscordId)),
});

View File

@@ -44,10 +44,13 @@ const UserPushbulletSettings = () => {
const UserNotificationsPushbulletSchema = Yup.object().shape({
pushbulletAccessToken: Yup.string().when('types', {
is: (types: number) => !!types,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationPushbulletAccessToken)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(
intl.formatMessage(messages.validationPushbulletAccessToken)
),
otherwise: (schema) => schema.nullable(),
}),
});

View File

@@ -57,12 +57,13 @@ const UserPushoverSettings = () => {
pushoverApplicationToken: Yup.string()
.when('types', {
is: (types: number) => !!types,
then: Yup.string()
.nullable()
.required(
intl.formatMessage(messages.validationPushoverApplicationToken)
),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(
intl.formatMessage(messages.validationPushoverApplicationToken)
),
otherwise: (schema) => schema.nullable(),
})
.matches(
/^[a-z\d]{30}$/i,
@@ -71,10 +72,11 @@ const UserPushoverSettings = () => {
pushoverUserKey: Yup.string()
.when('types', {
is: (types: number) => !!types,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationPushoverUserKey)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationPushoverUserKey)),
otherwise: (schema) => schema.nullable(),
})
.matches(
/^[a-z\d]{30}$/i,

View File

@@ -50,25 +50,19 @@ const UserTelegramSettings = () => {
telegramChatId: Yup.string()
.when('types', {
is: (types: number) => !!types,
then: Yup.string()
.nullable()
.required(intl.formatMessage(messages.validationTelegramChatId)),
otherwise: Yup.string().nullable(),
then: (schema) =>
schema
.nullable()
.required(intl.formatMessage(messages.validationTelegramChatId)),
otherwise: (schema) => schema.nullable(),
})
.matches(
/^-?\d+$/,
intl.formatMessage(messages.validationTelegramChatId)
),
telegramMessageThreadId: Yup.string()
.when(['types'], {
is: (enabled: boolean, types: number) => enabled && !!types,
then: Yup.string()
.nullable()
.required(
intl.formatMessage(messages.validationTelegramMessageThreadId)
),
otherwise: Yup.string().nullable(),
})
.transform((v) => v || null)
.nullable()
.matches(
/^\d+$/,
intl.formatMessage(messages.validationTelegramMessageThreadId)

View File

@@ -74,7 +74,7 @@ const UserPasswordChange = () => {
confirmPassword: Yup.string()
.required(intl.formatMessage(messages.validationConfirmPassword))
.oneOf(
[Yup.ref('newPassword'), null],
[Yup.ref('newPassword'), ''],
intl.formatMessage(messages.validationConfirmPasswordSame)
),
});

View File

@@ -1,10 +1,10 @@
import { shouldPolyfill as shouldPolyfillDisplayNames } from '@formatjs/intl-displaynames/should-polyfill';
import { shouldPolyfill as shouldPolyfillLocale } from '@formatjs/intl-locale/should-polyfill';
import { shouldPolyfill as shouldPolyfillPluralrules } from '@formatjs/intl-pluralrules/should-polyfill';
import { shouldPolyfill as shouldPolyfillDisplayNames } from '@formatjs/intl-displaynames/should-polyfill.js';
import { shouldPolyfill as shouldPolyfillLocale } from '@formatjs/intl-locale/should-polyfill.js';
import { shouldPolyfill as shouldPolyfillPluralrules } from '@formatjs/intl-pluralrules/should-polyfill.js';
const polyfillLocale = async () => {
if (shouldPolyfillLocale()) {
await import('@formatjs/intl-locale/polyfill');
await import('@formatjs/intl-locale/polyfill.js');
}
};
@@ -15,8 +15,10 @@ const polyfillPluralRules = async (locale: string) => {
return;
}
// Load the polyfill 1st BEFORE loading data
await import('@formatjs/intl-pluralrules/polyfill-force');
await import(`@formatjs/intl-pluralrules/locale-data/${unsupportedLocale}`);
await import('@formatjs/intl-pluralrules/polyfill-force.js');
await import(
`@formatjs/intl-pluralrules/locale-data/${unsupportedLocale}.js`
);
};
const polyfillDisplayNames = async (locale: string) => {
@@ -26,8 +28,10 @@ const polyfillDisplayNames = async (locale: string) => {
return;
}
// Load the polyfill 1st BEFORE loading data
await import('@formatjs/intl-displaynames/polyfill-force');
await import(`@formatjs/intl-displaynames/locale-data/${unsupportedLocale}`);
await import('@formatjs/intl-displaynames/polyfill-force.js');
await import(
`@formatjs/intl-displaynames/locale-data/${unsupportedLocale}.js`
);
};
export const polyfillIntl = async (locale: string) => {