mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
feat(ui): Add local login setting (#817)
This commit is contained in:
@@ -71,6 +71,9 @@ components:
|
|||||||
hideAvailable:
|
hideAvailable:
|
||||||
type: boolean
|
type: boolean
|
||||||
example: false
|
example: false
|
||||||
|
localLogin:
|
||||||
|
type: boolean
|
||||||
|
example: true
|
||||||
defaultPermissions:
|
defaultPermissions:
|
||||||
type: number
|
type: number
|
||||||
example: 32
|
example: 32
|
||||||
|
@@ -10,6 +10,7 @@ export interface PublicSettingsResponse {
|
|||||||
movie4kEnabled: boolean;
|
movie4kEnabled: boolean;
|
||||||
series4kEnabled: boolean;
|
series4kEnabled: boolean;
|
||||||
hideAvailable: boolean;
|
hideAvailable: boolean;
|
||||||
|
localLogin: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CacheItem {
|
export interface CacheItem {
|
||||||
|
@@ -54,6 +54,7 @@ export interface MainSettings {
|
|||||||
csrfProtection: boolean;
|
csrfProtection: boolean;
|
||||||
defaultPermissions: number;
|
defaultPermissions: number;
|
||||||
hideAvailable: boolean;
|
hideAvailable: boolean;
|
||||||
|
localLogin: boolean;
|
||||||
trustProxy: boolean;
|
trustProxy: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +66,7 @@ interface FullPublicSettings extends PublicSettings {
|
|||||||
movie4kEnabled: boolean;
|
movie4kEnabled: boolean;
|
||||||
series4kEnabled: boolean;
|
series4kEnabled: boolean;
|
||||||
hideAvailable: boolean;
|
hideAvailable: boolean;
|
||||||
|
localLogin: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NotificationAgentConfig {
|
export interface NotificationAgentConfig {
|
||||||
@@ -162,6 +164,7 @@ class Settings {
|
|||||||
csrfProtection: false,
|
csrfProtection: false,
|
||||||
defaultPermissions: Permission.REQUEST,
|
defaultPermissions: Permission.REQUEST,
|
||||||
hideAvailable: false,
|
hideAvailable: false,
|
||||||
|
localLogin: true,
|
||||||
trustProxy: false,
|
trustProxy: false,
|
||||||
},
|
},
|
||||||
plex: {
|
plex: {
|
||||||
@@ -296,6 +299,7 @@ class Settings {
|
|||||||
(sonarr) => sonarr.is4k && sonarr.isDefault
|
(sonarr) => sonarr.is4k && sonarr.isDefault
|
||||||
),
|
),
|
||||||
hideAvailable: this.data.main.hideAvailable,
|
hideAvailable: this.data.main.hideAvailable,
|
||||||
|
localLogin: this.data.main.localLogin,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -134,10 +134,13 @@ authRoutes.post('/login', async (req, res, next) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
authRoutes.post('/local', async (req, res, next) => {
|
authRoutes.post('/local', async (req, res, next) => {
|
||||||
|
const settings = getSettings();
|
||||||
const userRepository = getRepository(User);
|
const userRepository = getRepository(User);
|
||||||
const body = req.body as { email?: string; password?: string };
|
const body = req.body as { email?: string; password?: string };
|
||||||
|
|
||||||
if (!body.email || !body.password) {
|
if (!settings.main.localLogin) {
|
||||||
|
return res.status(500).json({ error: 'Local user login is disabled' });
|
||||||
|
} else if (!body.email || !body.password) {
|
||||||
return res
|
return res
|
||||||
.status(500)
|
.status(500)
|
||||||
.json({ error: 'You must provide an email and a password' });
|
.json({ error: 'You must provide an email and a password' });
|
||||||
|
@@ -9,6 +9,7 @@ import Transition from '../Transition';
|
|||||||
import LanguagePicker from '../Layout/LanguagePicker';
|
import LanguagePicker from '../Layout/LanguagePicker';
|
||||||
import LocalLogin from './LocalLogin';
|
import LocalLogin from './LocalLogin';
|
||||||
import Accordion from '../Common/Accordion';
|
import Accordion from '../Common/Accordion';
|
||||||
|
import useSettings from '../../hooks/useSettings';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
signinheader: 'Sign in to continue',
|
signinheader: 'Sign in to continue',
|
||||||
@@ -23,6 +24,7 @@ const Login: React.FC = () => {
|
|||||||
const [authToken, setAuthToken] = useState<string | undefined>(undefined);
|
const [authToken, setAuthToken] = useState<string | undefined>(undefined);
|
||||||
const { user, revalidate } = useUser();
|
const { user, revalidate } = useUser();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const settings = useSettings();
|
||||||
|
|
||||||
// Effect that is triggered when the `authToken` comes back from the Plex OAuth
|
// Effect that is triggered when the `authToken` comes back from the Plex OAuth
|
||||||
// We take the token and attempt to login. If we get a success message, we will
|
// We take the token and attempt to login. If we get a success message, we will
|
||||||
@@ -124,10 +126,14 @@ const Login: React.FC = () => {
|
|||||||
{({ openIndexes, handleClick, AccordionContent }) => (
|
{({ openIndexes, handleClick, AccordionContent }) => (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
className={`text-sm w-full focus:outline-none transition-colors duration-200 py-2 bg-gray-800 hover:bg-gray-700 bg-opacity-70 hover:bg-opacity-70 sm:rounded-t-lg text-center text-gray-400 ${
|
className={`w-full py-2 text-sm text-center text-gray-400 transition-colors duration-200 bg-gray-800 cursor-default focus:outline-none bg-opacity-70 sm:rounded-t-lg ${
|
||||||
openIndexes.includes(0) && 'text-indigo-500'
|
openIndexes.includes(0) && 'text-indigo-500'
|
||||||
|
} ${
|
||||||
|
settings.currentSettings.localLogin &&
|
||||||
|
'hover:bg-gray-700 hover:cursor-pointer'
|
||||||
}`}
|
}`}
|
||||||
onClick={() => handleClick(0)}
|
onClick={() => handleClick(0)}
|
||||||
|
disabled={!settings.currentSettings.localLogin}
|
||||||
>
|
>
|
||||||
{intl.formatMessage(messages.signinwithplex)}
|
{intl.formatMessage(messages.signinwithplex)}
|
||||||
</button>
|
</button>
|
||||||
@@ -139,8 +145,10 @@ const Login: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
|
{settings.currentSettings.localLogin && (
|
||||||
|
<div>
|
||||||
<button
|
<button
|
||||||
className={`text-sm w-full focus:outline-none transition-colors duration-200 py-2 bg-gray-800 hover:bg-gray-700 bg-opacity-70 hover:bg-opacity-70 text-center text-gray-400 ${
|
className={`w-full py-2 text-sm text-center text-gray-400 transition-colors duration-200 bg-gray-800 cursor-default focus:outline-none bg-opacity-70 sm:rounded-t-lg hover:bg-gray-700 hover:cursor-pointer ${
|
||||||
openIndexes.includes(1)
|
openIndexes.includes(1)
|
||||||
? 'text-indigo-500'
|
? 'text-indigo-500'
|
||||||
: 'sm:rounded-b-lg '
|
: 'sm:rounded-b-lg '
|
||||||
@@ -154,6 +162,8 @@ const Login: React.FC = () => {
|
|||||||
<LocalLogin revalidate={revalidate} />
|
<LocalLogin revalidate={revalidate} />
|
||||||
</div>
|
</div>
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
@@ -35,6 +35,9 @@ const messages = defineMessages({
|
|||||||
trustProxy: 'Enable Proxy Support',
|
trustProxy: 'Enable Proxy Support',
|
||||||
trustProxyTip:
|
trustProxyTip:
|
||||||
'Allows Overseerr to correctly register client IP addresses behind a proxy (Overseerr must be reloaded for changes to take effect)',
|
'Allows Overseerr to correctly register client IP addresses behind a proxy (Overseerr must be reloaded for changes to take effect)',
|
||||||
|
localLogin: 'Enable Local User Sign-In',
|
||||||
|
localLoginTip:
|
||||||
|
'Disabling this option only prevents new sign-ins (no user data is deleted)',
|
||||||
});
|
});
|
||||||
|
|
||||||
const SettingsMain: React.FC = () => {
|
const SettingsMain: React.FC = () => {
|
||||||
@@ -83,6 +86,7 @@ const SettingsMain: React.FC = () => {
|
|||||||
csrfProtection: data?.csrfProtection,
|
csrfProtection: data?.csrfProtection,
|
||||||
defaultPermissions: data?.defaultPermissions ?? 0,
|
defaultPermissions: data?.defaultPermissions ?? 0,
|
||||||
hideAvailable: data?.hideAvailable,
|
hideAvailable: data?.hideAvailable,
|
||||||
|
localLogin: data?.localLogin,
|
||||||
trustProxy: data?.trustProxy,
|
trustProxy: data?.trustProxy,
|
||||||
}}
|
}}
|
||||||
enableReinitialize
|
enableReinitialize
|
||||||
@@ -93,6 +97,7 @@ const SettingsMain: React.FC = () => {
|
|||||||
csrfProtection: values.csrfProtection,
|
csrfProtection: values.csrfProtection,
|
||||||
defaultPermissions: values.defaultPermissions,
|
defaultPermissions: values.defaultPermissions,
|
||||||
hideAvailable: values.hideAvailable,
|
hideAvailable: values.hideAvailable,
|
||||||
|
localLogin: values.localLogin,
|
||||||
trustProxy: values.trustProxy,
|
trustProxy: values.trustProxy,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -172,9 +177,7 @@ const SettingsMain: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-row">
|
<div className="form-row">
|
||||||
<label htmlFor="trustProxy" className="checkbox-label">
|
<label htmlFor="trustProxy" className="checkbox-label">
|
||||||
<span className="mr-2">
|
<span>{intl.formatMessage(messages.trustProxy)}</span>
|
||||||
{intl.formatMessage(messages.trustProxy)}
|
|
||||||
</span>
|
|
||||||
<span className="label-tip">
|
<span className="label-tip">
|
||||||
{intl.formatMessage(messages.trustProxyTip)}
|
{intl.formatMessage(messages.trustProxyTip)}
|
||||||
</span>
|
</span>
|
||||||
@@ -236,6 +239,24 @@ const SettingsMain: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="form-row">
|
||||||
|
<label htmlFor="localLogin" className="checkbox-label">
|
||||||
|
<span>{intl.formatMessage(messages.localLogin)}</span>
|
||||||
|
<span className="label-tip">
|
||||||
|
{intl.formatMessage(messages.localLoginTip)}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<div className="form-input">
|
||||||
|
<Field
|
||||||
|
type="checkbox"
|
||||||
|
id="localLogin"
|
||||||
|
name="localLogin"
|
||||||
|
onChange={() => {
|
||||||
|
setFieldValue('localLogin', !values.localLogin);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
role="group"
|
role="group"
|
||||||
aria-labelledby="group-label"
|
aria-labelledby="group-label"
|
||||||
|
@@ -205,7 +205,7 @@ const SettingsNotifications: React.FC = ({ children }) => {
|
|||||||
<Form className="section">
|
<Form className="section">
|
||||||
<div className="form-row">
|
<div className="form-row">
|
||||||
<label htmlFor="name" className="checkbox-label">
|
<label htmlFor="name" className="checkbox-label">
|
||||||
<span className="mr-2">
|
<span>
|
||||||
{intl.formatMessage(messages.enablenotifications)}
|
{intl.formatMessage(messages.enablenotifications)}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
@@ -222,7 +222,7 @@ const SettingsNotifications: React.FC = ({ children }) => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="form-row">
|
<div className="form-row">
|
||||||
<label htmlFor="name" className="checkbox-label">
|
<label htmlFor="name" className="checkbox-label">
|
||||||
<span className="mr-2">
|
<span>
|
||||||
{intl.formatMessage(messages.autoapprovedrequests)}
|
{intl.formatMessage(messages.autoapprovedrequests)}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
|
@@ -351,7 +351,7 @@ const SettingsPlex: React.FC<SettingsPlexProps> = ({ onComplete }) => {
|
|||||||
<div className="form-row">
|
<div className="form-row">
|
||||||
<label htmlFor="name" className="text-label">
|
<label htmlFor="name" className="text-label">
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<span className="mr-2">
|
<span>
|
||||||
<FormattedMessage {...messages.servername} />
|
<FormattedMessage {...messages.servername} />
|
||||||
</span>
|
</span>
|
||||||
<span className="text-gray-500">
|
<span className="text-gray-500">
|
||||||
|
@@ -11,6 +11,7 @@ const defaultSettings = {
|
|||||||
movie4kEnabled: false,
|
movie4kEnabled: false,
|
||||||
series4kEnabled: false,
|
series4kEnabled: false,
|
||||||
hideAvailable: false,
|
hideAvailable: false,
|
||||||
|
localLogin: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SettingsContext = React.createContext<SettingsContextProps>({
|
export const SettingsContext = React.createContext<SettingsContextProps>({
|
||||||
|
@@ -433,6 +433,8 @@
|
|||||||
"components.Settings.hideAvailable": "Hide Available Media",
|
"components.Settings.hideAvailable": "Hide Available Media",
|
||||||
"components.Settings.hostname": "Hostname/IP",
|
"components.Settings.hostname": "Hostname/IP",
|
||||||
"components.Settings.librariesRemaining": "Libraries Remaining: {count}",
|
"components.Settings.librariesRemaining": "Libraries Remaining: {count}",
|
||||||
|
"components.Settings.localLogin": "Enable Local User Sign-In",
|
||||||
|
"components.Settings.localLoginTip": "Disabling this option only prevents new sign-ins (no user data is deleted)",
|
||||||
"components.Settings.manualscan": "Manual Library Scan",
|
"components.Settings.manualscan": "Manual Library Scan",
|
||||||
"components.Settings.manualscanDescription": "Normally, this will only be run once every 24 hours. Overseerr will check your Plex server's recently added more aggressively. If this is your first time configuring Plex, a one-time full manual library scan is recommended!",
|
"components.Settings.manualscanDescription": "Normally, this will only be run once every 24 hours. Overseerr will check your Plex server's recently added more aggressively. If this is your first time configuring Plex, a one-time full manual library scan is recommended!",
|
||||||
"components.Settings.menuAbout": "About",
|
"components.Settings.menuAbout": "About",
|
||||||
|
@@ -142,6 +142,7 @@ CoreApp.getInitialProps = async (initialProps) => {
|
|||||||
hideAvailable: false,
|
hideAvailable: false,
|
||||||
movie4kEnabled: false,
|
movie4kEnabled: false,
|
||||||
series4kEnabled: false,
|
series4kEnabled: false,
|
||||||
|
localLogin: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let locale = 'en';
|
let locale = 'en';
|
||||||
|
Reference in New Issue
Block a user