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:
|
||||
type: boolean
|
||||
example: false
|
||||
localLogin:
|
||||
type: boolean
|
||||
example: true
|
||||
defaultPermissions:
|
||||
type: number
|
||||
example: 32
|
||||
|
@@ -10,6 +10,7 @@ export interface PublicSettingsResponse {
|
||||
movie4kEnabled: boolean;
|
||||
series4kEnabled: boolean;
|
||||
hideAvailable: boolean;
|
||||
localLogin: boolean;
|
||||
}
|
||||
|
||||
export interface CacheItem {
|
||||
|
@@ -54,6 +54,7 @@ export interface MainSettings {
|
||||
csrfProtection: boolean;
|
||||
defaultPermissions: number;
|
||||
hideAvailable: boolean;
|
||||
localLogin: boolean;
|
||||
trustProxy: boolean;
|
||||
}
|
||||
|
||||
@@ -65,6 +66,7 @@ interface FullPublicSettings extends PublicSettings {
|
||||
movie4kEnabled: boolean;
|
||||
series4kEnabled: boolean;
|
||||
hideAvailable: boolean;
|
||||
localLogin: boolean;
|
||||
}
|
||||
|
||||
export interface NotificationAgentConfig {
|
||||
@@ -162,6 +164,7 @@ class Settings {
|
||||
csrfProtection: false,
|
||||
defaultPermissions: Permission.REQUEST,
|
||||
hideAvailable: false,
|
||||
localLogin: true,
|
||||
trustProxy: false,
|
||||
},
|
||||
plex: {
|
||||
@@ -296,6 +299,7 @@ class Settings {
|
||||
(sonarr) => sonarr.is4k && sonarr.isDefault
|
||||
),
|
||||
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) => {
|
||||
const settings = getSettings();
|
||||
const userRepository = getRepository(User);
|
||||
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
|
||||
.status(500)
|
||||
.json({ error: 'You must provide an email and a password' });
|
||||
|
@@ -9,6 +9,7 @@ import Transition from '../Transition';
|
||||
import LanguagePicker from '../Layout/LanguagePicker';
|
||||
import LocalLogin from './LocalLogin';
|
||||
import Accordion from '../Common/Accordion';
|
||||
import useSettings from '../../hooks/useSettings';
|
||||
|
||||
const messages = defineMessages({
|
||||
signinheader: 'Sign in to continue',
|
||||
@@ -23,6 +24,7 @@ const Login: React.FC = () => {
|
||||
const [authToken, setAuthToken] = useState<string | undefined>(undefined);
|
||||
const { user, revalidate } = useUser();
|
||||
const router = useRouter();
|
||||
const settings = useSettings();
|
||||
|
||||
// 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
|
||||
@@ -124,10 +126,14 @@ const Login: React.FC = () => {
|
||||
{({ openIndexes, handleClick, AccordionContent }) => (
|
||||
<>
|
||||
<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'
|
||||
} ${
|
||||
settings.currentSettings.localLogin &&
|
||||
'hover:bg-gray-700 hover:cursor-pointer'
|
||||
}`}
|
||||
onClick={() => handleClick(0)}
|
||||
disabled={!settings.currentSettings.localLogin}
|
||||
>
|
||||
{intl.formatMessage(messages.signinwithplex)}
|
||||
</button>
|
||||
@@ -139,21 +145,25 @@ const Login: React.FC = () => {
|
||||
/>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
<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 ${
|
||||
openIndexes.includes(1)
|
||||
? 'text-indigo-500'
|
||||
: 'sm:rounded-b-lg '
|
||||
}`}
|
||||
onClick={() => handleClick(1)}
|
||||
>
|
||||
{intl.formatMessage(messages.signinwithoverseerr)}
|
||||
</button>
|
||||
<AccordionContent isOpen={openIndexes.includes(1)}>
|
||||
<div className="px-10 py-8">
|
||||
<LocalLogin revalidate={revalidate} />
|
||||
{settings.currentSettings.localLogin && (
|
||||
<div>
|
||||
<button
|
||||
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)
|
||||
? 'text-indigo-500'
|
||||
: 'sm:rounded-b-lg '
|
||||
}`}
|
||||
onClick={() => handleClick(1)}
|
||||
>
|
||||
{intl.formatMessage(messages.signinwithoverseerr)}
|
||||
</button>
|
||||
<AccordionContent isOpen={openIndexes.includes(1)}>
|
||||
<div className="px-10 py-8">
|
||||
<LocalLogin revalidate={revalidate} />
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Accordion>
|
||||
|
@@ -35,6 +35,9 @@ const messages = defineMessages({
|
||||
trustProxy: 'Enable Proxy Support',
|
||||
trustProxyTip:
|
||||
'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 = () => {
|
||||
@@ -83,6 +86,7 @@ const SettingsMain: React.FC = () => {
|
||||
csrfProtection: data?.csrfProtection,
|
||||
defaultPermissions: data?.defaultPermissions ?? 0,
|
||||
hideAvailable: data?.hideAvailable,
|
||||
localLogin: data?.localLogin,
|
||||
trustProxy: data?.trustProxy,
|
||||
}}
|
||||
enableReinitialize
|
||||
@@ -93,6 +97,7 @@ const SettingsMain: React.FC = () => {
|
||||
csrfProtection: values.csrfProtection,
|
||||
defaultPermissions: values.defaultPermissions,
|
||||
hideAvailable: values.hideAvailable,
|
||||
localLogin: values.localLogin,
|
||||
trustProxy: values.trustProxy,
|
||||
});
|
||||
|
||||
@@ -172,9 +177,7 @@ const SettingsMain: React.FC = () => {
|
||||
</div>
|
||||
<div className="form-row">
|
||||
<label htmlFor="trustProxy" className="checkbox-label">
|
||||
<span className="mr-2">
|
||||
{intl.formatMessage(messages.trustProxy)}
|
||||
</span>
|
||||
<span>{intl.formatMessage(messages.trustProxy)}</span>
|
||||
<span className="label-tip">
|
||||
{intl.formatMessage(messages.trustProxyTip)}
|
||||
</span>
|
||||
@@ -236,6 +239,24 @@ const SettingsMain: React.FC = () => {
|
||||
/>
|
||||
</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
|
||||
role="group"
|
||||
aria-labelledby="group-label"
|
||||
|
@@ -205,7 +205,7 @@ const SettingsNotifications: React.FC = ({ children }) => {
|
||||
<Form className="section">
|
||||
<div className="form-row">
|
||||
<label htmlFor="name" className="checkbox-label">
|
||||
<span className="mr-2">
|
||||
<span>
|
||||
{intl.formatMessage(messages.enablenotifications)}
|
||||
</span>
|
||||
</label>
|
||||
@@ -222,7 +222,7 @@ const SettingsNotifications: React.FC = ({ children }) => {
|
||||
</div>
|
||||
<div className="form-row">
|
||||
<label htmlFor="name" className="checkbox-label">
|
||||
<span className="mr-2">
|
||||
<span>
|
||||
{intl.formatMessage(messages.autoapprovedrequests)}
|
||||
</span>
|
||||
</label>
|
||||
|
@@ -351,7 +351,7 @@ const SettingsPlex: React.FC<SettingsPlexProps> = ({ onComplete }) => {
|
||||
<div className="form-row">
|
||||
<label htmlFor="name" className="text-label">
|
||||
<div className="flex flex-col">
|
||||
<span className="mr-2">
|
||||
<span>
|
||||
<FormattedMessage {...messages.servername} />
|
||||
</span>
|
||||
<span className="text-gray-500">
|
||||
|
@@ -11,6 +11,7 @@ const defaultSettings = {
|
||||
movie4kEnabled: false,
|
||||
series4kEnabled: false,
|
||||
hideAvailable: false,
|
||||
localLogin: false,
|
||||
};
|
||||
|
||||
export const SettingsContext = React.createContext<SettingsContextProps>({
|
||||
|
@@ -433,6 +433,8 @@
|
||||
"components.Settings.hideAvailable": "Hide Available Media",
|
||||
"components.Settings.hostname": "Hostname/IP",
|
||||
"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.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",
|
||||
|
@@ -142,6 +142,7 @@ CoreApp.getInitialProps = async (initialProps) => {
|
||||
hideAvailable: false,
|
||||
movie4kEnabled: false,
|
||||
series4kEnabled: false,
|
||||
localLogin: true,
|
||||
};
|
||||
|
||||
let locale = 'en';
|
||||
|
Reference in New Issue
Block a user