Files
suwayomi-material-you-webui/src/features/settings/screens/ServerSettings.tsx
T

645 lines
29 KiB
TypeScript
Raw Normal View History

2023-11-18 19:55:44 +01:00
/*
* 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/.
*/
2025-05-03 13:24:55 +02:00
import { useMemo } from 'react';
2024-04-14 15:50:12 +02:00
import Link from '@mui/material/Link';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';
import Switch from '@mui/material/Switch';
2023-11-18 19:55:44 +01:00
import ListSubheader from '@mui/material/ListSubheader';
2025-10-11 00:39:44 +02:00
import { d } from 'koration';
2026-01-10 19:45:02 +01:00
import { Trans, useLingui } from '@lingui/react/macro';
import { plural, t as translate } from '@lingui/core/macro';
2024-10-06 01:27:51 +02:00
import { requestManager } from '@/lib/requests/RequestManager.ts';
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';
import { LoadingPlaceholder } from '@/base/components/feedback/LoadingPlaceholder.tsx';
import { EmptyViewAbsoluteCentered } from '@/base/components/feedback/EmptyViewAbsoluteCentered.tsx';
2024-10-05 16:09:34 +02:00
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
2024-05-07 00:37:01 +02:00
import {
createUpdateMetadataServerSettings,
useMetadataServerSettings,
2025-08-15 22:02:58 +02:00
} from '@/features/settings/services/ServerSettingsMetadata.ts';
import { makeToast } from '@/base/utils/Toast.ts';
2026-03-11 00:11:29 +01:00
import type { MetadataUpdateSettings } from '@/features/app-updates/AppUpdateChecker.types.ts';
2024-12-20 17:24:40 +01:00
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
2025-08-15 22:02:58 +02:00
import { useAppTitle } from '@/features/navigation-bar/hooks/useAppTitle.ts';
2025-12-06 17:29:52 +01:00
import { AuthMode, CbzMediaType, DatabaseType, SortOrder } from '@/lib/graphql/generated/graphql';
2025-07-27 00:03:51 +02:00
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';
2026-03-11 00:11:29 +01:00
import type { ServerSettings as ServerSettingsType } from '@/features/settings/Settings.types.ts';
2025-12-06 12:59:39 -06:00
import { KoreaderSyncSettings } from '@/features/settings/components/koreaderSync/KoreaderSyncSettings.tsx';
2023-11-18 19:55:44 +01:00
2024-09-01 03:04:34 +02:00
const getLogFilesCleanupDisplayValue = (ttl: number): string => {
if (ttl === 0) {
2026-01-10 19:45:02 +01:00
return translate`Never`;
2024-09-01 03:04:34 +02:00
}
2026-01-10 19:45:02 +01:00
return plural(ttl, {
one: 'Delete log files that are older than # day',
other: 'Delete log files that are older than # days',
});
2024-09-01 03:04:34 +02:00
};
2023-11-18 19:55:44 +01:00
export const ServerSettings = () => {
2026-01-10 19:45:02 +01:00
const { t } = useLingui();
2023-11-18 19:55:44 +01:00
2026-01-10 19:45:02 +01:00
useAppTitle(t`Server`);
2023-11-18 19:55:44 +01:00
2024-05-07 00:37:01 +02:00
const {
settings: { serverInformAvailableUpdate, serverInformVersionUpdated },
2024-05-07 00:37:01 +02:00
loading: areMetadataServerSettingsLoading,
request: { error: metadataServerSettingsError, refetch: refetchServerMetadataSettings },
} = useMetadataServerSettings();
const updateMetadataServerSettings = createUpdateMetadataServerSettings<
keyof Pick<MetadataUpdateSettings, 'serverInformAvailableUpdate' | 'serverInformVersionUpdated'>
2026-01-10 19:45:02 +01:00
>((e) => makeToast(t`Failed to save changes`, 'error', getErrorMessage(e)));
2024-05-07 00:37:01 +02:00
const {
data,
loading: areServerSettingsLoading,
error: serverSettingsError,
refetch: refetchServerSettings,
} = requestManager.useGetServerSettings({
2024-04-27 22:28:55 +02:00
notifyOnNetworkStatusChange: true,
});
2023-11-18 19:55:44 +01:00
const [mutateSettings] = requestManager.useUpdateServerSettings();
2025-12-06 12:59:39 -06:00
const koSyncStatus = requestManager.useKoSyncStatus();
2025-07-27 00:03:51 +02:00
const updateSetting = async <Setting extends keyof ServerSettingsType>(
2023-11-18 19:55:44 +01:00
setting: Setting,
value: ServerSettingsType[Setting],
2025-07-27 00:03:51 +02:00
onCompletion?: (success: boolean) => void,
2023-11-18 19:55:44 +01:00
) => {
2025-07-27 00:03:51 +02:00
try {
await mutateSettings({ variables: { input: { settings: { [setting]: value } } } });
onCompletion?.(true);
} catch (e) {
2026-01-10 19:45:02 +01:00
makeToast(t`Failed to save changes`, 'error', getErrorMessage(e));
2025-07-27 00:03:51 +02:00
onCompletion?.(false);
}
2023-11-18 19:55:44 +01:00
};
2024-05-05 01:00:23 +02:00
const localSettings = useMemo(
() => (
2023-11-18 19:55:44 +01:00
<List
subheader={
<ListSubheader component="div" id="server-settings-client">
2026-01-10 19:45:02 +01:00
{t`Client`}
2023-11-18 19:55:44 +01:00
</ListSubheader>
}
>
2025-07-27 00:03:51 +02:00
<ServerAddressSetting />
<ListItem>
<ListItemText
primary={t`Inform about version update`}
secondary={t`Shows a dialog in case the version of the server has changed`}
/>
<Switch
edge="end"
checked={serverInformVersionUpdated}
onChange={(e) => updateMetadataServerSettings('serverInformVersionUpdated', e.target.checked)}
/>
</ListItem>
2024-05-07 00:37:01 +02:00
<ListItem>
<ListItemText
2026-01-10 19:45:02 +01:00
primary={t`Inform about available update`}
secondary={t`Shows a dialog in case a new version is available`}
2024-05-07 00:37:01 +02:00
/>
<Switch
edge="end"
checked={serverInformAvailableUpdate}
onChange={(e) => updateMetadataServerSettings('serverInformAvailableUpdate', e.target.checked)}
/>
</ListItem>
2023-11-18 19:55:44 +01:00
</List>
2024-05-05 01:00:23 +02:00
),
[serverInformVersionUpdated, serverInformAvailableUpdate],
2024-05-05 01:00:23 +02:00
);
2025-12-06 12:59:39 -06:00
const loading = areMetadataServerSettingsLoading || areServerSettingsLoading || koSyncStatus.loading;
2024-05-05 01:00:23 +02:00
if (loading) {
return (
<>
{localSettings}
<LoadingPlaceholder />
</>
);
}
2025-12-06 12:59:39 -06:00
const error = metadataServerSettingsError ?? serverSettingsError ?? koSyncStatus.error;
2024-05-05 01:00:23 +02:00
if (error) {
return (
<>
{localSettings}
<EmptyViewAbsoluteCentered
2026-01-10 19:45:02 +01:00
message={t`Unable to load data`}
2024-12-21 23:27:03 +01:00
messageExtra={getErrorMessage(error)}
2024-05-07 00:37:01 +02:00
retry={() => {
if (metadataServerSettingsError) {
refetchServerMetadataSettings().catch(
defaultPromiseErrorHandler('ServerSettings::refetchServerMetadataSettings'),
);
}
if (serverSettingsError) {
refetchServerSettings().catch(
defaultPromiseErrorHandler('ServerSettings::refetchServerSettings'),
);
}
2025-12-06 12:59:39 -06:00
if (koSyncStatus.error) {
koSyncStatus
.refetch()
.catch(defaultPromiseErrorHandler('ServerSettings::koSyncStatus.refetch'));
}
2024-05-07 00:37:01 +02:00
}}
2024-05-05 01:00:23 +02:00
/>
</>
);
}
const serverSettings = data!.settings;
2025-12-06 12:59:39 -06:00
const koreaderSyncStatus = koSyncStatus.data!.koSyncStatus;
2025-07-27 00:03:51 +02:00
const authModeDisabled = !serverSettings.authUsername?.trim() || !serverSettings.authPassword?.trim();
2025-09-08 00:37:02 +02:00
const isH2Database = serverSettings.databaseType === DatabaseType.H2;
2025-08-27 01:24:12 +02:00
2024-05-05 01:00:23 +02:00
return (
2024-09-05 00:34:13 +02:00
<List sx={{ pt: 0 }}>
2024-05-05 01:00:23 +02:00
{localSettings}
2023-11-18 19:55:44 +01:00
<List
subheader={
<ListSubheader component="div" id="server-settings-server-address">
2026-01-10 19:45:02 +01:00
{t`Server bindings`}
2023-11-18 19:55:44 +01:00
</ListSubheader>
}
>
<TextSetting
2026-01-10 19:45:02 +01:00
settingName={t`IP`}
2023-11-18 19:55:44 +01:00
handleChange={(ip) => updateSetting('ip', ip)}
value={serverSettings.ip}
2023-11-18 19:55:44 +01:00
placeholder="0.0.0.0"
/>
<NumberSetting
2026-01-10 19:45:02 +01:00
settingTitle={t`Port`}
settingValue={serverSettings.port.toString()}
2023-11-18 19:55:44 +01:00
handleUpdate={(port) => updateSetting('port', port)}
value={serverSettings.port}
2023-11-18 19:55:44 +01:00
defaultValue={4567}
2026-01-10 19:45:02 +01:00
valueUnit={t`Port`}
2023-11-18 19:55:44 +01:00
/>
</List>
<List
subheader={
<ListSubheader component="div" id="server-settings-socks-proxy">
2026-01-10 19:45:02 +01:00
{t`SOCKS proxy`}
2023-11-18 19:55:44 +01:00
</ListSubheader>
}
>
<ListItem>
2026-01-10 19:45:02 +01:00
<ListItemText primary={t`Use SOCKS proxy`} />
<Switch
edge="end"
checked={serverSettings.socksProxyEnabled}
onChange={(e) => updateSetting('socksProxyEnabled', e.target.checked)}
/>
2023-11-18 19:55:44 +01:00
</ListItem>
<SelectSetting<number>
2026-01-10 19:45:02 +01:00
settingName={t`SOCKS version`}
value={serverSettings.socksProxyVersion}
values={[
[4, { text: '4' }],
[5, { text: '5' }],
]}
handleChange={(socksProxyVersion) => updateSetting('socksProxyVersion', socksProxyVersion)}
/>
2023-11-19 00:10:30 +01:00
<TextSetting
2026-01-10 19:45:02 +01:00
settingName={t`SOCKS host`}
value={serverSettings.socksProxyHost}
2023-11-19 00:10:30 +01:00
handleChange={(proxyHost) => updateSetting('socksProxyHost', proxyHost)}
/>
<TextSetting
2026-01-10 19:45:02 +01:00
settingName={t`SOCKS port`}
value={serverSettings.socksProxyPort}
2023-11-19 00:10:30 +01:00
handleChange={(proxyPort) => updateSetting('socksProxyPort', proxyPort)}
/>
<TextSetting
2026-01-10 19:45:02 +01:00
settingName={t`SOCKS username`}
value={serverSettings.socksProxyUsername}
handleChange={(proxyUsername) => updateSetting('socksProxyUsername', proxyUsername)}
/>
<TextSetting
2026-01-10 19:45:02 +01:00
settingName={t`SOCKS password`}
value={serverSettings.socksProxyPassword}
handleChange={(proxyPassword) => updateSetting('socksProxyPassword', proxyPassword)}
isPassword
/>
2023-11-18 19:55:44 +01:00
</List>
<List
subheader={
<ListSubheader component="div" id="server-settings-auth">
2026-01-10 19:45:02 +01:00
{t`Authentication`}
2023-11-18 19:55:44 +01:00
</ListSubheader>
}
>
<SelectSetting<AuthMode>
2026-01-10 19:45:02 +01:00
settingName={t`Authentication Mode`}
value={serverSettings.authMode}
values={AUTH_MODES_SELECT_VALUES}
2025-07-27 00:03:51 +02:00
handleChange={(mode) => {
updateSetting('authMode', mode, (success) => {
if (!success) {
return;
}
if (mode !== AuthMode.UiLogin) {
AuthManager.removeTokens();
}
AuthManager.setAuthRequired(mode === AuthMode.UiLogin);
});
}}
disabled={authModeDisabled}
2023-11-19 00:10:30 +01:00
/>
<TextSetting
2026-01-10 19:45:02 +01:00
settingName={t`Username`}
value={serverSettings.authUsername}
validate={(value) => serverSettings.authMode === AuthMode.None || !!value.trim()}
handleChange={(authUsername) => updateSetting('authUsername', authUsername)}
/>
<TextSetting
2026-01-10 19:45:02 +01:00
settingName={t`Password`}
value={serverSettings.authPassword}
2023-11-19 00:10:30 +01:00
isPassword
validate={(value) => serverSettings.authMode === AuthMode.None || !!value.trim()}
handleChange={(authPassword) => updateSetting('authPassword', authPassword)}
2023-11-19 00:10:30 +01:00
/>
2025-07-27 00:03:51 +02:00
{serverSettings.authMode === AuthMode.UiLogin && (
<>
<TextSetting
2026-01-10 19:45:02 +01:00
settingName={t`JWT audience claim`}
2025-07-27 00:03:51 +02:00
value={serverSettings.jwtAudience}
handleChange={(audience) => updateSetting('jwtAudience', audience)}
/>
<NumberSetting
2026-01-10 19:45:02 +01:00
settingTitle={t`JWT access token expiry`}
2025-10-11 00:39:44 +02:00
settingValue={d(serverSettings.jwtTokenExpiry).minutes.humanize()}
value={d(serverSettings.jwtTokenExpiry).minutes.inWholeMinutes}
2026-01-10 19:45:02 +01:00
valueUnit={t`Minute`}
2025-07-27 00:03:51 +02:00
defaultValue={JWT_ACCESS_TOKEN_EXPIRY.default}
minValue={JWT_ACCESS_TOKEN_EXPIRY.min}
maxValue={JWT_ACCESS_TOKEN_EXPIRY.max}
2025-10-11 00:39:44 +02:00
handleUpdate={(expiry) => updateSetting('jwtTokenExpiry', d(expiry).minutes.toISOString())}
2025-07-27 00:03:51 +02:00
showSlider
/>
<NumberSetting
2026-01-10 19:45:02 +01:00
settingTitle={t`JWT refresh token expiry`}
2025-10-11 00:39:44 +02:00
settingValue={d(serverSettings.jwtRefreshExpiry).days.humanize()}
value={d(serverSettings.jwtRefreshExpiry).days.inWholeDays}
2026-01-10 19:45:02 +01:00
valueUnit={t`Day`}
2025-07-27 00:03:51 +02:00
defaultValue={JWT_REFRESH_TOKEN_EXPIRY.default}
minValue={JWT_REFRESH_TOKEN_EXPIRY.min}
maxValue={JWT_REFRESH_TOKEN_EXPIRY.max}
2025-10-11 00:39:44 +02:00
handleUpdate={(expiry) => updateSetting('jwtRefreshExpiry', d(expiry).days.toISOString())}
2025-07-27 00:03:51 +02:00
showSlider
/>
</>
)}
2023-11-18 19:55:44 +01:00
</List>
2024-01-26 21:20:06 +01:00
<List
subheader={
<ListSubheader component="div" id="server-settings-clouadflare-bypass">
2026-01-10 19:45:02 +01:00
{t`Cloudflare bypass`}
2024-01-26 21:20:06 +01:00
</ListSubheader>
}
>
<ListItem>
<ListItemText
2026-01-10 19:45:02 +01:00
primary={t`FlareSolverr enabled`}
2024-01-26 21:20:06 +01:00
secondary={
2026-01-10 19:45:02 +01:00
<Trans>
2024-05-20 14:59:02 +02:00
See{' '}
2024-01-26 21:20:06 +01:00
<Link
href="https://github.com/FlareSolverr/FlareSolverr?tab=readme-ov-file#installation"
target="_blank"
rel="noreferrer"
>
FlareSolverr
2024-05-20 14:59:02 +02:00
</Link>{' '}
2024-01-26 21:20:06 +01:00
for information on how to set it up
</Trans>
}
/>
<Switch
edge="end"
checked={serverSettings.flareSolverrEnabled}
2024-01-26 21:20:06 +01:00
onChange={(e) => updateSetting('flareSolverrEnabled', e.target.checked)}
/>
</ListItem>
<TextSetting
2026-01-10 19:45:02 +01:00
settingName={t`FlareSolverr server url`}
dialogDescription={t`The address of the FlareSolverr server`}
value={serverSettings.flareSolverrUrl}
2024-01-26 21:20:06 +01:00
handleChange={(url) => updateSetting('flareSolverrUrl', url)}
/>
<NumberSetting
2026-01-10 19:45:02 +01:00
settingTitle={t`FlareSolverr request timeout`}
settingValue={plural(serverSettings.flareSolverrTimeout, {
one: '# second',
other: '# seconds',
})}
dialogDescription={t`How much time FlareSolverr has to handle the request`}
value={serverSettings.flareSolverrTimeout}
2024-01-26 21:20:06 +01:00
defaultValue={60}
minValue={20}
maxValue={60 * 5}
stepSize={1}
showSlider
2026-01-10 19:45:02 +01:00
valueUnit={t`Second`}
2024-01-26 21:20:06 +01:00
handleUpdate={(timeout) => updateSetting('flareSolverrTimeout', timeout)}
/>
<TextSetting
2026-01-10 19:45:02 +01:00
settingName={t`FlareSolverr session name`}
value={serverSettings.flareSolverrSessionName}
2024-01-26 21:20:06 +01:00
handleChange={(sessionName) => updateSetting('flareSolverrSessionName', sessionName)}
/>
<NumberSetting
2026-01-10 19:45:02 +01:00
settingTitle={t`FlareSolverr session TTL`}
settingValue={plural(serverSettings.flareSolverrSessionTtl, {
one: '# minute',
other: '# minutes',
})}
dialogDescription={t`FlareSolverr will automatically rotate expired sessions based on the TTL provided in minutes`}
value={serverSettings.flareSolverrSessionTtl}
2024-01-26 21:20:06 +01:00
defaultValue={15}
minValue={1}
maxValue={60}
stepSize={1}
showSlider
2026-01-10 19:45:02 +01:00
valueUnit={t`Minute`}
2024-01-26 21:20:06 +01:00
handleUpdate={(sessionTTL) => updateSetting('flareSolverrSessionTtl', sessionTTL)}
/>
<ListItem>
<ListItemText
2026-01-10 19:45:02 +01:00
primary={t`Response fallback`}
secondary={t`Use the FlareSolverr response in case the server runs into a Cloudflare challenge while FlareSolverr does not and is therefore unable to solve the challenge (does not work for images)`}
/>
<Switch
edge="end"
checked={serverSettings.flareSolverrAsResponseFallback}
onChange={(e) => updateSetting('flareSolverrAsResponseFallback', e.target.checked)}
/>
</ListItem>
2024-01-26 21:20:06 +01:00
</List>
<List
subheader={
<ListSubheader component="div" id="server-settings-opds">
2026-01-10 19:45:02 +01:00
{t`OPDS`}
</ListSubheader>
}
>
<ListItem>
<ListItemText
2026-01-10 19:45:02 +01:00
primary={t`Binary file size`}
secondary={t`Display file sizes in binary (KiB, MiB, GiB) instead of decimal (KB, MB, GB)`}
/>
<Switch
edge="end"
checked={serverSettings.opdsUseBinaryFileSizes}
onChange={(e) => updateSetting('opdsUseBinaryFileSizes', e.target.checked)}
/>
</ListItem>
<NumberSetting
2026-01-10 19:45:02 +01:00
settingTitle={t`Items per page`}
settingValue={serverSettings.opdsItemsPerPage.toString()}
2026-01-10 19:45:02 +01:00
dialogDescription={t`Number of items per page in OPDS feeds (e.g., Library History, Manga Chapters).\nHigher values may affect client performance.`}
value={serverSettings.opdsItemsPerPage}
defaultValue={50}
minValue={10}
maxValue={5000}
stepSize={10}
showSlider
2026-01-10 19:45:02 +01:00
valueUnit={t`item`}
handleUpdate={(value) => updateSetting('opdsItemsPerPage', value)}
/>
<ListItem>
<ListItemText
2026-01-10 19:45:02 +01:00
primary={t`Enable page read progress`}
secondary={t`Track and update your reading progress by page for each chapter during page streaming`}
/>
<Switch
edge="end"
checked={serverSettings.opdsEnablePageReadProgress}
onChange={(e) => updateSetting('opdsEnablePageReadProgress', e.target.checked)}
/>
</ListItem>
<ListItem>
<ListItemText
2026-01-10 19:45:02 +01:00
primary={t`Mark chapters as read on download`}
secondary={t`Automatically mark chapters as read when you download them.`}
/>
<Switch
edge="end"
checked={serverSettings.opdsMarkAsReadOnDownload}
onChange={(e) => updateSetting('opdsMarkAsReadOnDownload', e.target.checked)}
/>
</ListItem>
<ListItem>
<ListItemText
2026-01-10 19:45:02 +01:00
primary={t`Show only unread chapters`}
secondary={t`Filter manga feed to display only chapters you havent read yet.`}
/>
<Switch
edge="end"
checked={serverSettings.opdsShowOnlyUnreadChapters}
onChange={(e) => updateSetting('opdsShowOnlyUnreadChapters', e.target.checked)}
/>
</ListItem>
<ListItem>
<ListItemText
2026-01-10 19:45:02 +01:00
primary={t`Show only downloaded chapters`}
secondary={t`Filter manga feed to display only chapters you have downloaded.`}
/>
<Switch
edge="end"
checked={serverSettings.opdsShowOnlyDownloadedChapters}
onChange={(e) => updateSetting('opdsShowOnlyDownloadedChapters', e.target.checked)}
/>
</ListItem>
<SelectSetting<SortOrder>
2026-01-10 19:45:02 +01:00
settingName={t`Chapter sort order`}
dialogDescription={t`Choose the order in which chapters are displayed.`}
value={serverSettings.opdsChapterSortOrder}
values={[
2026-01-10 19:45:02 +01:00
[
SortOrder.Asc,
{
text: t`Ascending`,
},
],
[
SortOrder.Desc,
{
text: t`Descending`,
},
],
]}
handleChange={(value) => updateSetting('opdsChapterSortOrder', value)}
/>
2025-12-06 17:29:52 +01:00
<SelectSetting<CbzMediaType>
2026-01-10 19:45:02 +01:00
settingName={t`CBZ MIME-Type`}
dialogDescription={t`Controls the MimeType that Suwayomi sends in OPDS entries for CBZ archives. Also affects global CBZ download.\nModern follows recent IANA standard (2017), while LEGACY (deprecated mimetype for .cbz) and COMPATIBLE (deprecated mimetype for all comic archives) might be more compatible with older clients.`}
2025-12-06 17:29:52 +01:00
value={serverSettings.opdsCbzMimetype}
values={[
2026-01-10 19:45:02 +01:00
[
CbzMediaType.Legacy,
{
text: t`Legacy`,
},
],
[
CbzMediaType.Modern,
{
text: t`Modern`,
},
],
[
CbzMediaType.Compatible,
{
text: t`Compatible`,
},
],
2025-12-06 17:29:52 +01:00
]}
handleChange={(value) => updateSetting('opdsCbzMimetype', value)}
/>
</List>
2025-12-06 12:59:39 -06:00
<KoreaderSyncSettings
settings={serverSettings}
serverAddress={koreaderSyncStatus.serverAddress}
username={koreaderSyncStatus.username}
isLoggedIn={koreaderSyncStatus.isLoggedIn}
updateSetting={updateSetting}
/>
2025-09-08 00:37:02 +02:00
<List
subheader={
<ListSubheader component="div" id="server-settings-database">
2026-01-10 19:45:02 +01:00
{t`Database`}
2025-09-08 00:37:02 +02:00
</ListSubheader>
}
>
<SelectSetting<DatabaseType>
2026-01-10 19:45:02 +01:00
settingName={t`Type`}
2025-09-08 00:37:02 +02:00
value={serverSettings.databaseType}
values={[
2026-01-10 19:45:02 +01:00
[
DatabaseType.H2,
{
text: t`H2`,
},
],
[
DatabaseType.Postgresql,
{
text: t`PostgreSQL`,
},
],
2025-09-08 00:37:02 +02:00
]}
handleChange={(value) => updateSetting('databaseType', value)}
/>
<TextSetting
2026-01-10 19:45:02 +01:00
settingName={t`Database url`}
2025-09-08 00:37:02 +02:00
handleChange={(url) => updateSetting('databaseUrl', url)}
value={serverSettings.databaseUrl}
placeholder="postgresql://localhost:5432/suwayomi"
disabled={isH2Database}
/>
<TextSetting
2026-01-10 19:45:02 +01:00
settingName={t`Username`}
2025-09-08 00:37:02 +02:00
value={serverSettings.databaseUsername}
handleChange={(username) => updateSetting('databaseUsername', username)}
disabled={isH2Database}
/>
<TextSetting
2026-01-10 19:45:02 +01:00
settingName={t`Password`}
2025-09-08 00:37:02 +02:00
value={serverSettings.databasePassword}
handleChange={(password) => updateSetting('databasePassword', password)}
disabled={isH2Database}
isPassword
/>
2025-12-06 17:18:48 +01:00
<ListItem>
<ListItemText
2026-01-10 19:45:02 +01:00
primary={t`Hikari connection pool`}
secondary={t`Improves performance, but may cause issues for some installations where data cannot be read from the database`}
2025-12-06 17:18:48 +01:00
/>
<Switch
edge="end"
checked={serverSettings.useHikariConnectionPool}
onChange={(e) => updateSetting('useHikariConnectionPool', e.target.checked)}
/>
</ListItem>
2025-09-08 00:37:02 +02:00
</List>
<List
subheader={
<ListSubheader component="div" id="server-settings-misc">
2026-01-10 19:45:02 +01:00
{t`Misc`}
</ListSubheader>
}
>
<ListItem>
2026-01-10 19:45:02 +01:00
<ListItemText primary={t`Enable debug logs`} />
<Switch
edge="end"
checked={serverSettings.debugLogsEnabled}
onChange={(e) => updateSetting('debugLogsEnabled', e.target.checked)}
/>
</ListItem>
<ListItem>
<ListItemText
2026-01-10 19:45:02 +01:00
primary={t`Show icon in system tray`}
secondary={t`This icon will be shown on the system that is running the server`}
/>
<Switch
edge="end"
checked={serverSettings.systemTrayEnabled}
onChange={(e) => updateSetting('systemTrayEnabled', e.target.checked)}
/>
</ListItem>
<NumberSetting
2026-01-10 19:45:02 +01:00
settingTitle={t`Log file cleanup`}
settingValue={getLogFilesCleanupDisplayValue(serverSettings.maxLogFiles)}
value={serverSettings.maxLogFiles}
2026-01-10 19:45:02 +01:00
valueUnit={t`Day`}
handleUpdate={(maxFiles) => updateSetting('maxLogFiles', maxFiles)}
/>
<TextSetting
2026-01-10 19:45:02 +01:00
settingName={t`Maximum log file size`}
value={serverSettings.maxLogFileSize}
2026-01-10 19:45:02 +01:00
dialogDescription={t`Example for possible values: 1 (bytes), 1KB (kilobytes), 1MB (megabytes), 1GB (gigabytes)`}
validate={(value) => !!value.match(/^[0-9]+(|kb|KB|mb|MB|gb|GB)$/g)}
handleChange={(maxLogFileSize) => updateSetting('maxLogFileSize', maxLogFileSize)}
/>
<TextSetting
2026-01-10 19:45:02 +01:00
settingName={t`Maximum size of all log files`}
value={serverSettings.maxLogFolderSize}
2026-01-10 19:45:02 +01:00
dialogDescription={t`Example for possible values: 1 (bytes), 1KB (kilobytes), 1MB (megabytes), 1GB (gigabytes)`}
validate={(value) => !!value.match(/^[0-9]+(|kb|KB|mb|MB|gb|GB)$/g)}
handleChange={(maxLogFolderSize) => updateSetting('maxLogFolderSize', maxLogFolderSize)}
/>
</List>
2023-11-18 19:55:44 +01:00
</List>
);
};