mirror of
https://github.com/fallenbagel/jellyseerr.git
synced 2026-01-01 12:18:35 -05:00
Adding Jellyfin Setting for Custom "Forgot Password" URL
Adding Jellyfin Setting for Custom "Forgot Password" URL. Useful in cases where you are using a custom authentication provider such as the LDAP plugin, Authelia, lldap, or any other external auth scheme with its own password reset page.
This commit is contained in:
@@ -368,6 +368,9 @@ components:
|
|||||||
externalHostname:
|
externalHostname:
|
||||||
type: string
|
type: string
|
||||||
example: 'http://my.jellyfin.host'
|
example: 'http://my.jellyfin.host'
|
||||||
|
jellyfinForgotPasswordUrl:
|
||||||
|
type: string
|
||||||
|
example: 'http://my.jellyfin.host/web/index.html#!/forgotpassword.html'
|
||||||
adminUser:
|
adminUser:
|
||||||
type: string
|
type: string
|
||||||
example: 'admin'
|
example: 'admin'
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export interface PublicSettingsResponse {
|
|||||||
locale: string;
|
locale: string;
|
||||||
emailEnabled: boolean;
|
emailEnabled: boolean;
|
||||||
newPlexLogin: boolean;
|
newPlexLogin: boolean;
|
||||||
|
jellyfinForgotPasswordUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CacheItem {
|
export interface CacheItem {
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export interface JellyfinSettings {
|
|||||||
externalHostname?: string;
|
externalHostname?: string;
|
||||||
libraries: Library[];
|
libraries: Library[];
|
||||||
serverId: string;
|
serverId: string;
|
||||||
|
jellyfinForgotPasswordUrl: string;
|
||||||
}
|
}
|
||||||
export interface TautulliSettings {
|
export interface TautulliSettings {
|
||||||
hostname?: string;
|
hostname?: string;
|
||||||
@@ -124,6 +125,7 @@ interface FullPublicSettings extends PublicSettings {
|
|||||||
applicationUrl: string;
|
applicationUrl: string;
|
||||||
hideAvailable: boolean;
|
hideAvailable: boolean;
|
||||||
localLogin: boolean;
|
localLogin: boolean;
|
||||||
|
jellyfinForgotPasswordUrl: string;
|
||||||
movie4kEnabled: boolean;
|
movie4kEnabled: boolean;
|
||||||
series4kEnabled: boolean;
|
series4kEnabled: boolean;
|
||||||
region: string;
|
region: string;
|
||||||
@@ -331,6 +333,7 @@ class Settings {
|
|||||||
name: '',
|
name: '',
|
||||||
hostname: '',
|
hostname: '',
|
||||||
externalHostname: '',
|
externalHostname: '',
|
||||||
|
jellyfinForgotPasswordUrl: '',
|
||||||
libraries: [],
|
libraries: [],
|
||||||
serverId: '',
|
serverId: '',
|
||||||
},
|
},
|
||||||
@@ -534,6 +537,7 @@ class Settings {
|
|||||||
applicationUrl: this.data.main.applicationUrl,
|
applicationUrl: this.data.main.applicationUrl,
|
||||||
hideAvailable: this.data.main.hideAvailable,
|
hideAvailable: this.data.main.hideAvailable,
|
||||||
localLogin: this.data.main.localLogin,
|
localLogin: this.data.main.localLogin,
|
||||||
|
jellyfinForgotPasswordUrl: this.data.jellyfin.jellyfinForgotPasswordUrl,
|
||||||
movie4kEnabled: this.data.radarr.some(
|
movie4kEnabled: this.data.radarr.some(
|
||||||
(radarr) => radarr.is4k && radarr.isDefault
|
(radarr) => radarr.is4k && radarr.isDefault
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -222,6 +222,7 @@ const JellyfinLogin: React.FC<JellyfinLoginProps> = ({
|
|||||||
const baseUrl = settings.currentSettings.jellyfinExternalHost
|
const baseUrl = settings.currentSettings.jellyfinExternalHost
|
||||||
? settings.currentSettings.jellyfinExternalHost
|
? settings.currentSettings.jellyfinExternalHost
|
||||||
: settings.currentSettings.jellyfinHost;
|
: settings.currentSettings.jellyfinHost;
|
||||||
|
const jellyfinForgotPasswordUrl = settings.currentSettings.jellyfinForgotPasswordUrl;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Formik
|
<Formik
|
||||||
@@ -298,11 +299,11 @@ const JellyfinLogin: React.FC<JellyfinLoginProps> = ({
|
|||||||
<Button
|
<Button
|
||||||
as="a"
|
as="a"
|
||||||
buttonType="ghost"
|
buttonType="ghost"
|
||||||
href={`${baseUrl}/web/index.html#!/${
|
href={jellyfinForgotPasswordUrl ? `${jellyfinForgotPasswordUrl}` :
|
||||||
process.env.JELLYFIN_TYPE === 'emby'
|
`${baseUrl}/web/index.html#!/${process.env.JELLYFIN_TYPE === 'emby'
|
||||||
? 'startup/'
|
? 'startup/'
|
||||||
: ''
|
: ''
|
||||||
}forgotpassword.html`}
|
}forgotpassword.html`}
|
||||||
>
|
>
|
||||||
{intl.formatMessage(messages.forgotpassword)}
|
{intl.formatMessage(messages.forgotpassword)}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -30,9 +30,10 @@ const messages = defineMessages({
|
|||||||
jellyfinSettingsSuccess: '{mediaServerName} settings saved successfully!',
|
jellyfinSettingsSuccess: '{mediaServerName} settings saved successfully!',
|
||||||
jellyfinSettings: '{mediaServerName} Settings',
|
jellyfinSettings: '{mediaServerName} Settings',
|
||||||
jellyfinSettingsDescription:
|
jellyfinSettingsDescription:
|
||||||
'Optionally configure the internal and external endpoints for your {mediaServerName} server. In most cases, the external URL is different to the internal URL.',
|
'Optionally configure the internal and external endpoints for your {mediaServerName} server. In most cases, the external URL is different to the internal URL. A custom password reset URL can also be set for {mediaServerName} login, in case you would like to redirect to a different password reset page.',
|
||||||
externalUrl: 'External URL',
|
externalUrl: 'External URL',
|
||||||
internalUrl: 'Internal URL',
|
internalUrl: 'Internal URL',
|
||||||
|
jellyfinForgotPasswordUrl: 'Forgot Password URL',
|
||||||
validationUrl: 'You must provide a valid URL',
|
validationUrl: 'You must provide a valid URL',
|
||||||
syncing: 'Syncing',
|
syncing: 'Syncing',
|
||||||
syncJellyfin: 'Sync Libraries',
|
syncJellyfin: 'Sync Libraries',
|
||||||
@@ -94,6 +95,10 @@ const SettingsJellyfin: React.FC<SettingsJellyfinProps> = ({
|
|||||||
/^(https?:\/\/)?(?:[\w-]+\.)*[\w-]+(?::\d{2,5})?(?:\/[\w-]+)*(?:\/)?$/gm,
|
/^(https?:\/\/)?(?:[\w-]+\.)*[\w-]+(?::\d{2,5})?(?:\/[\w-]+)*(?:\/)?$/gm,
|
||||||
intl.formatMessage(messages.validationUrl)
|
intl.formatMessage(messages.validationUrl)
|
||||||
),
|
),
|
||||||
|
jellyfinForgotPasswordUrl: Yup.string().matches(
|
||||||
|
/^(https?:\/\/)?(?:[\w-]+\.)*[\w-]+(?::\d{2,5})?(?:\/[\w-]+)*(?:\/)?$/gm,
|
||||||
|
intl.formatMessage(messages.validationUrl)
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
const activeLibraries =
|
const activeLibraries =
|
||||||
@@ -171,20 +176,20 @@ const SettingsJellyfin: React.FC<SettingsJellyfinProps> = ({
|
|||||||
<h3 className="heading">
|
<h3 className="heading">
|
||||||
{publicRuntimeConfig.JELLYFIN_TYPE == 'emby'
|
{publicRuntimeConfig.JELLYFIN_TYPE == 'emby'
|
||||||
? intl.formatMessage(messages.jellyfinlibraries, {
|
? intl.formatMessage(messages.jellyfinlibraries, {
|
||||||
mediaServerName: 'Emby',
|
mediaServerName: 'Emby',
|
||||||
})
|
})
|
||||||
: intl.formatMessage(messages.jellyfinlibraries, {
|
: intl.formatMessage(messages.jellyfinlibraries, {
|
||||||
mediaServerName: 'Jellyfin',
|
mediaServerName: 'Jellyfin',
|
||||||
})}
|
})}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="description">
|
<p className="description">
|
||||||
{publicRuntimeConfig.JELLYFIN_TYPE == 'emby'
|
{publicRuntimeConfig.JELLYFIN_TYPE == 'emby'
|
||||||
? intl.formatMessage(messages.jellyfinlibrariesDescription, {
|
? intl.formatMessage(messages.jellyfinlibrariesDescription, {
|
||||||
mediaServerName: 'Emby',
|
mediaServerName: 'Emby',
|
||||||
})
|
})
|
||||||
: intl.formatMessage(messages.jellyfinlibrariesDescription, {
|
: intl.formatMessage(messages.jellyfinlibrariesDescription, {
|
||||||
mediaServerName: 'Jellyfin',
|
mediaServerName: 'Jellyfin',
|
||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="section">
|
<div className="section">
|
||||||
@@ -223,11 +228,11 @@ const SettingsJellyfin: React.FC<SettingsJellyfinProps> = ({
|
|||||||
<p className="description">
|
<p className="description">
|
||||||
{publicRuntimeConfig.JELLYFIN_TYPE == 'emby'
|
{publicRuntimeConfig.JELLYFIN_TYPE == 'emby'
|
||||||
? intl.formatMessage(messages.manualscanDescriptionJellyfin, {
|
? intl.formatMessage(messages.manualscanDescriptionJellyfin, {
|
||||||
mediaServerName: 'Emby',
|
mediaServerName: 'Emby',
|
||||||
})
|
})
|
||||||
: intl.formatMessage(messages.manualscanDescriptionJellyfin, {
|
: intl.formatMessage(messages.manualscanDescriptionJellyfin, {
|
||||||
mediaServerName: 'Jellyfin',
|
mediaServerName: 'Jellyfin',
|
||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="section">
|
<div className="section">
|
||||||
@@ -271,11 +276,11 @@ const SettingsJellyfin: React.FC<SettingsJellyfinProps> = ({
|
|||||||
values={{
|
values={{
|
||||||
count: dataSync.currentLibrary
|
count: dataSync.currentLibrary
|
||||||
? dataSync.libraries.slice(
|
? dataSync.libraries.slice(
|
||||||
dataSync.libraries.findIndex(
|
dataSync.libraries.findIndex(
|
||||||
(library) =>
|
(library) =>
|
||||||
library.id === dataSync.currentLibrary?.id
|
library.id === dataSync.currentLibrary?.id
|
||||||
) + 1
|
) + 1
|
||||||
).length
|
).length
|
||||||
: 0,
|
: 0,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -333,26 +338,27 @@ const SettingsJellyfin: React.FC<SettingsJellyfinProps> = ({
|
|||||||
<h3 className="heading">
|
<h3 className="heading">
|
||||||
{publicRuntimeConfig.JELLYFIN_TYPE == 'emby'
|
{publicRuntimeConfig.JELLYFIN_TYPE == 'emby'
|
||||||
? intl.formatMessage(messages.jellyfinSettings, {
|
? intl.formatMessage(messages.jellyfinSettings, {
|
||||||
mediaServerName: 'Emby',
|
mediaServerName: 'Emby',
|
||||||
})
|
})
|
||||||
: intl.formatMessage(messages.jellyfinSettings, {
|
: intl.formatMessage(messages.jellyfinSettings, {
|
||||||
mediaServerName: 'Jellyfin',
|
mediaServerName: 'Jellyfin',
|
||||||
})}
|
})}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="description">
|
<p className="description">
|
||||||
{publicRuntimeConfig.JELLYFIN_TYPE == 'emby'
|
{publicRuntimeConfig.JELLYFIN_TYPE == 'emby'
|
||||||
? intl.formatMessage(messages.jellyfinSettingsDescription, {
|
? intl.formatMessage(messages.jellyfinSettingsDescription, {
|
||||||
mediaServerName: 'Emby',
|
mediaServerName: 'Emby',
|
||||||
})
|
})
|
||||||
: intl.formatMessage(messages.jellyfinSettingsDescription, {
|
: intl.formatMessage(messages.jellyfinSettingsDescription, {
|
||||||
mediaServerName: 'Jellyfin',
|
mediaServerName: 'Jellyfin',
|
||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={{
|
initialValues={{
|
||||||
jellyfinInternalUrl: data?.hostname || '',
|
jellyfinInternalUrl: data?.hostname || '',
|
||||||
jellyfinExternalUrl: data?.externalHostname || '',
|
jellyfinExternalUrl: data?.externalHostname || '',
|
||||||
|
jellyfinForgotPasswordUrl: data?.jellyfinForgotPasswordUrl || '',
|
||||||
}}
|
}}
|
||||||
validationSchema={JellyfinSettingsSchema}
|
validationSchema={JellyfinSettingsSchema}
|
||||||
onSubmit={async (values) => {
|
onSubmit={async (values) => {
|
||||||
@@ -360,6 +366,7 @@ const SettingsJellyfin: React.FC<SettingsJellyfinProps> = ({
|
|||||||
await axios.post('/api/v1/settings/jellyfin', {
|
await axios.post('/api/v1/settings/jellyfin', {
|
||||||
hostname: values.jellyfinInternalUrl,
|
hostname: values.jellyfinInternalUrl,
|
||||||
externalHostname: values.jellyfinExternalUrl,
|
externalHostname: values.jellyfinExternalUrl,
|
||||||
|
jellyfinForgotPasswordUrl: values.jellyfinForgotPasswordUrl,
|
||||||
} as JellyfinSettings);
|
} as JellyfinSettings);
|
||||||
|
|
||||||
addToast(
|
addToast(
|
||||||
@@ -437,6 +444,27 @@ const SettingsJellyfin: React.FC<SettingsJellyfinProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="form-row">
|
||||||
|
<label htmlFor="jellyfinForgotPasswordUrl" className="text-label">
|
||||||
|
{intl.formatMessage(messages.jellyfinForgotPasswordUrl)}
|
||||||
|
</label>
|
||||||
|
<div className="form-input-area">
|
||||||
|
<div className="form-input-field">
|
||||||
|
<Field
|
||||||
|
type="text"
|
||||||
|
inputMode="url"
|
||||||
|
id="jellyfinForgotPasswordUrl"
|
||||||
|
name="jellyfinForgotPasswordUrl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{errors.jellyfinForgotPasswordUrl &&
|
||||||
|
touched.jellyfinForgotPasswordUrl && (
|
||||||
|
<div className="error">
|
||||||
|
{errors.jellyfinForgotPasswordUrl}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<span className="ml-3 inline-flex rounded-md shadow-sm">
|
<span className="ml-3 inline-flex rounded-md shadow-sm">
|
||||||
|
|||||||
@@ -938,7 +938,7 @@
|
|||||||
"components.Settings.internalUrl": "Internal URL",
|
"components.Settings.internalUrl": "Internal URL",
|
||||||
"components.Settings.is4k": "4K",
|
"components.Settings.is4k": "4K",
|
||||||
"components.Settings.jellyfinSettings": "{mediaServerName} Settings",
|
"components.Settings.jellyfinSettings": "{mediaServerName} Settings",
|
||||||
"components.Settings.jellyfinSettingsDescription": "Optionally configure the internal and external endpoints for your {mediaServerName} server. In most cases, the external URL is different to the internal URL.",
|
"components.Settings.jellyfinSettingsDescription": "Optionally configure the internal and external endpoints for your {mediaServerName} server. In most cases, the external URL is different to the internal URL. A custom password reset URL can also be set for {mediaServerName} login, in case you would like to redirect to a different password reset page.",
|
||||||
"components.Settings.jellyfinSettingsFailure": "Something went wrong while saving {mediaServerName} settings.",
|
"components.Settings.jellyfinSettingsFailure": "Something went wrong while saving {mediaServerName} settings.",
|
||||||
"components.Settings.jellyfinSettingsSuccess": "{mediaServerName} settings saved successfully!",
|
"components.Settings.jellyfinSettingsSuccess": "{mediaServerName} settings saved successfully!",
|
||||||
"components.Settings.jellyfinlibraries": "{mediaServerName} Libraries",
|
"components.Settings.jellyfinlibraries": "{mediaServerName} Libraries",
|
||||||
|
|||||||
Reference in New Issue
Block a user