Add "ui login" support
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
import { DEFAULT_DEVICE } from '@/features/device/services/Device.ts';
|
||||
import { DEFAULT_SORT_SETTINGS } from '@/features/migration/Migration.constants.ts';
|
||||
import { GlobalUpdateSkipEntriesSettings, MetadataServerSettings } from '@/features/settings/Settings.types.ts';
|
||||
@@ -90,9 +91,13 @@ const AUTH_MODES_TO_TRANSLATION_KEY: { [mode in AuthMode]: SelectSettingValueDis
|
||||
description: 'settings.server.auth.mode.option.basicAuth.label.description',
|
||||
},
|
||||
[AuthMode.SimpleLogin]: {
|
||||
text: 'settings.server.auth.mode.option.simpleLogin.label.title',
|
||||
description: 'settings.server.auth.mode.option.simpleLogin.label.description',
|
||||
disclaimer: 'settings.server.auth.mode.option.simpleLogin.label.info',
|
||||
text: 'settings.server.auth.mode.option.simple_login.label.title',
|
||||
description: 'settings.server.auth.mode.option.simple_login.label.description',
|
||||
disclaimer: 'settings.server.auth.mode.option.simple_login.label.info',
|
||||
},
|
||||
[AuthMode.UiLogin]: {
|
||||
text: 'settings.server.auth.mode.option.ui_login.label.title',
|
||||
description: 'settings.server.auth.mode.option.ui_login.label.description',
|
||||
},
|
||||
};
|
||||
export const AUTH_MODES_SELECT_VALUES: SelectSettingValue<AuthMode>[] = AUTH_MODES.map((mode) => [
|
||||
@@ -183,3 +188,15 @@ export const WEB_UI_UPDATE_INTERVAL = {
|
||||
min: 1,
|
||||
max: 23,
|
||||
};
|
||||
|
||||
export const JWT_ACCESS_TOKEN_EXPIRY = {
|
||||
default: dayjs.duration(5, 'minute').asMinutes(),
|
||||
min: dayjs.duration(1, 'minute').asMinutes(),
|
||||
max: dayjs.duration(4, 'hour').asMinutes(),
|
||||
};
|
||||
|
||||
export const JWT_REFRESH_TOKEN_EXPIRY = {
|
||||
default: dayjs.duration(60, 'day').asDays(),
|
||||
min: dayjs.duration(1, 'day').asDays(),
|
||||
max: dayjs.duration(1, 'year').asDays(),
|
||||
};
|
||||
|
||||
@@ -55,6 +55,9 @@ export type ServerSettingsType = Pick<
|
||||
| 'authMode'
|
||||
| 'authUsername'
|
||||
| 'authPassword'
|
||||
| 'jwtAudience'
|
||||
| 'jwtTokenExpiry'
|
||||
| 'jwtRefreshExpiry'
|
||||
| 'flareSolverrEnabled'
|
||||
| 'flareSolverrTimeout'
|
||||
| 'flareSolverrUrl'
|
||||
|
||||
33
src/features/settings/components/ServerAddressSetting.tsx
Normal file
33
src/features/settings/components/ServerAddressSetting.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) Contributors to the Suwayomi project
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TextSetting } from '@/base/components/settings/text/TextSetting.tsx';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { useLocalStorage } from '@/base/hooks/useStorage.tsx';
|
||||
|
||||
export const ServerAddressSetting = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [serverAddress, setServerAddress] = useLocalStorage<string>('serverBaseURL', window.location.origin);
|
||||
|
||||
const handleServerAddressChange = (address: string) => {
|
||||
const serverBaseUrl = address.replaceAll(/(\/)+$/g, '');
|
||||
setServerAddress(serverBaseUrl);
|
||||
requestManager.reset();
|
||||
};
|
||||
|
||||
return (
|
||||
<TextSetting
|
||||
settingName={t('settings.about.server.label.address')}
|
||||
handleChange={handleServerAddressChange}
|
||||
value={serverAddress}
|
||||
placeholder="http://localhost:4567"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -15,8 +15,8 @@ import ListItemText from '@mui/material/ListItemText';
|
||||
import Switch from '@mui/material/Switch';
|
||||
import ListSubheader from '@mui/material/ListSubheader';
|
||||
import { t as translate } from 'i18next';
|
||||
import dayjs from 'dayjs';
|
||||
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
||||
import { useLocalStorage } from '@/base/hooks/useStorage.tsx';
|
||||
import { TextSetting } from '@/base/components/settings/text/TextSetting.tsx';
|
||||
import { NumberSetting } from '@/base/components/settings/NumberSetting.tsx';
|
||||
import { SelectSetting } from '@/base/components/settings/SelectSetting.tsx';
|
||||
@@ -33,7 +33,13 @@ import { ServerSettings as GqlServerSettings, ServerSettingsType } from '@/featu
|
||||
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
||||
import { useAppTitle } from '@/features/navigation-bar/hooks/useAppTitle.ts';
|
||||
import { AuthMode, SortOrder } from '@/lib/graphql/generated/graphql';
|
||||
import { AUTH_MODES_SELECT_VALUES } from '@/features/settings/Settings.constants.ts';
|
||||
import {
|
||||
AUTH_MODES_SELECT_VALUES,
|
||||
JWT_ACCESS_TOKEN_EXPIRY,
|
||||
JWT_REFRESH_TOKEN_EXPIRY,
|
||||
} from '@/features/settings/Settings.constants.ts';
|
||||
import { ServerAddressSetting } from '@/features/settings/components/ServerAddressSetting.tsx';
|
||||
import { AuthManager } from '@/features/authentication/AuthManager.ts';
|
||||
|
||||
const extractServerSettings = (settings: GqlServerSettings): ServerSettingsType => ({
|
||||
ip: settings.ip,
|
||||
@@ -52,6 +58,9 @@ const extractServerSettings = (settings: GqlServerSettings): ServerSettingsType
|
||||
authMode: settings.authMode,
|
||||
authUsername: settings.authUsername,
|
||||
authPassword: settings.authPassword,
|
||||
jwtAudience: settings.jwtAudience,
|
||||
jwtTokenExpiry: settings.jwtTokenExpiry,
|
||||
jwtRefreshExpiry: settings.jwtRefreshExpiry,
|
||||
flareSolverrEnabled: settings.flareSolverrEnabled,
|
||||
flareSolverrTimeout: settings.flareSolverrTimeout,
|
||||
flareSolverrUrl: settings.flareSolverrUrl,
|
||||
@@ -99,24 +108,18 @@ export const ServerSettings = () => {
|
||||
});
|
||||
const [mutateSettings] = requestManager.useUpdateServerSettings();
|
||||
|
||||
const [serverAddress, setServerAddress] = useLocalStorage<string>(
|
||||
'serverBaseURL',
|
||||
import.meta.env.VITE_SERVER_URL_DEFAULT,
|
||||
);
|
||||
|
||||
const handleServerAddressChange = (address: string) => {
|
||||
const serverBaseUrl = address.replaceAll(/(\/)+$/g, '');
|
||||
setServerAddress(serverBaseUrl);
|
||||
requestManager.reset();
|
||||
};
|
||||
|
||||
const updateSetting = <Setting extends keyof ServerSettingsType>(
|
||||
const updateSetting = async <Setting extends keyof ServerSettingsType>(
|
||||
setting: Setting,
|
||||
value: ServerSettingsType[Setting],
|
||||
onCompletion?: (success: boolean) => void,
|
||||
) => {
|
||||
mutateSettings({ variables: { input: { settings: { [setting]: value } } } }).catch((e) =>
|
||||
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e)),
|
||||
);
|
||||
try {
|
||||
await mutateSettings({ variables: { input: { settings: { [setting]: value } } } });
|
||||
onCompletion?.(true);
|
||||
} catch (e) {
|
||||
makeToast(t('global.error.label.failed_to_save_changes'), 'error', getErrorMessage(e));
|
||||
onCompletion?.(false);
|
||||
}
|
||||
};
|
||||
|
||||
const localSettings = useMemo(
|
||||
@@ -128,12 +131,7 @@ export const ServerSettings = () => {
|
||||
</ListSubheader>
|
||||
}
|
||||
>
|
||||
<TextSetting
|
||||
settingName={t('settings.about.server.label.address')}
|
||||
handleChange={handleServerAddressChange}
|
||||
value={serverAddress}
|
||||
placeholder="http://localhost:4567"
|
||||
/>
|
||||
<ServerAddressSetting />
|
||||
<ListItem>
|
||||
<ListItemText
|
||||
primary={t('global.update.settings.inform.label.title')}
|
||||
@@ -147,7 +145,7 @@ export const ServerSettings = () => {
|
||||
</ListItem>
|
||||
</List>
|
||||
),
|
||||
[serverAddress, serverInformAvailableUpdate],
|
||||
[serverInformAvailableUpdate],
|
||||
);
|
||||
|
||||
const loading = areMetadataServerSettingsLoading || areServerSettingsLoading;
|
||||
@@ -187,8 +185,8 @@ export const ServerSettings = () => {
|
||||
}
|
||||
|
||||
const serverSettings = extractServerSettings(data!.settings);
|
||||
const authModeDisabled = !serverSettings.authUsername.trim() || !serverSettings.authPassword.trim();
|
||||
|
||||
const authModeDisabled = !serverSettings.authUsername?.trim() || !serverSettings.authPassword?.trim();
|
||||
console.log('asdf', serverSettings.jwtTokenExpiry, serverSettings.jwtRefreshExpiry);
|
||||
return (
|
||||
<List sx={{ pt: 0 }}>
|
||||
{localSettings}
|
||||
@@ -271,7 +269,19 @@ export const ServerSettings = () => {
|
||||
settingName={t('settings.server.auth.label.title')}
|
||||
value={serverSettings.authMode}
|
||||
values={AUTH_MODES_SELECT_VALUES}
|
||||
handleChange={(mode) => updateSetting('authMode', mode)}
|
||||
handleChange={(mode) => {
|
||||
updateSetting('authMode', mode, (success) => {
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode !== AuthMode.UiLogin) {
|
||||
AuthManager.removeTokens();
|
||||
}
|
||||
|
||||
AuthManager.setAuthRequired(mode === AuthMode.UiLogin);
|
||||
});
|
||||
}}
|
||||
disabled={authModeDisabled}
|
||||
/>
|
||||
<TextSetting
|
||||
@@ -287,6 +297,41 @@ export const ServerSettings = () => {
|
||||
validate={(value) => serverSettings.authMode === AuthMode.None || !!value.trim()}
|
||||
handleChange={(authPassword) => updateSetting('authPassword', authPassword)}
|
||||
/>
|
||||
{serverSettings.authMode === AuthMode.UiLogin && (
|
||||
<>
|
||||
<TextSetting
|
||||
settingName={t('settings.server.auth.jwt.audience')}
|
||||
value={serverSettings.jwtAudience}
|
||||
handleChange={(audience) => updateSetting('jwtAudience', audience)}
|
||||
/>
|
||||
<NumberSetting
|
||||
settingTitle={t('settings.server.auth.jwt.access_token_expiry')}
|
||||
settingValue={dayjs.duration(serverSettings.jwtTokenExpiry).humanize()}
|
||||
value={dayjs.duration(serverSettings.jwtTokenExpiry).asMinutes()}
|
||||
valueUnit={t('global.time.minutes.minute_other')}
|
||||
defaultValue={JWT_ACCESS_TOKEN_EXPIRY.default}
|
||||
minValue={JWT_ACCESS_TOKEN_EXPIRY.min}
|
||||
maxValue={JWT_ACCESS_TOKEN_EXPIRY.max}
|
||||
handleUpdate={(expiry) =>
|
||||
updateSetting('jwtTokenExpiry', dayjs.duration(expiry, 'minute').toISOString())
|
||||
}
|
||||
showSlider
|
||||
/>
|
||||
<NumberSetting
|
||||
settingTitle={t('settings.server.auth.jwt.refresh_token_expiry')}
|
||||
settingValue={dayjs.duration(serverSettings.jwtRefreshExpiry).humanize()}
|
||||
value={dayjs.duration(serverSettings.jwtRefreshExpiry).asDays()}
|
||||
valueUnit={t('global.time.days.day_other')}
|
||||
defaultValue={JWT_REFRESH_TOKEN_EXPIRY.default}
|
||||
minValue={JWT_REFRESH_TOKEN_EXPIRY.min}
|
||||
maxValue={JWT_REFRESH_TOKEN_EXPIRY.max}
|
||||
handleUpdate={(expiry) =>
|
||||
updateSetting('jwtRefreshExpiry', dayjs.duration(expiry, 'day').toISOString())
|
||||
}
|
||||
showSlider
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</List>
|
||||
<List
|
||||
subheader={
|
||||
|
||||
Reference in New Issue
Block a user