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' ;
2025-08-16 02:48:46 +02:00
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' ;
2025-08-16 02:48:46 +02:00
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 {
2026-01-25 17:57:30 +01:00
settings : { serverInformAvailableUpdate , serverInformVersionUpdated },
2024-05-07 00:37:01 +02:00
loading : areMetadataServerSettingsLoading ,
request : { error : metadataServerSettingsError , refetch : refetchServerMetadataSettings },
} = useMetadataServerSettings ();
const updateMetadataServerSettings = createUpdateMetadataServerSettings <
2026-01-25 17:57:30 +01:00
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 />
2026-01-25 17:57:30 +01:00
< 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
),
2026-01-25 17:57:30 +01: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
/>
</>
);
}
2025-09-08 00:46:57 +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 )}
2024-04-28 01:01:36 +02:00
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` }
2024-04-28 01:01:36 +02:00
settingValue = { serverSettings . port . toString ()}
2023-11-18 19:55:44 +01:00
handleUpdate = {( port ) => updateSetting ( 'port' , port )}
2024-04-28 01:01:36 +02:00
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` } />
2023-12-09 14:16:59 +01:00
< Switch
edge = "end"
2024-04-28 01:01:36 +02:00
checked = { serverSettings . socksProxyEnabled }
2023-12-09 14:16:59 +01:00
onChange = {( e ) => updateSetting ( 'socksProxyEnabled' , e . target . checked )}
/>
2023-11-18 19:55:44 +01:00
</ ListItem >
2024-02-22 00:25:12 +01:00
< SelectSetting < number >
2026-01-10 19:45:02 +01:00
settingName = { t `SOCKS version` }
2024-04-28 01:01:36 +02:00
value = { serverSettings . socksProxyVersion }
2024-02-22 00:25:12 +01:00
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` }
2024-04-28 01:01:36 +02:00
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` }
2024-04-28 01:01:36 +02:00
value = { serverSettings . socksProxyPort }
2023-11-19 00:10:30 +01:00
handleChange = {( proxyPort ) => updateSetting ( 'socksProxyPort' , proxyPort )}
/>
2024-02-22 00:25:12 +01:00
< TextSetting
2026-01-10 19:45:02 +01:00
settingName = { t `SOCKS username` }
2024-04-28 01:01:36 +02:00
value = { serverSettings . socksProxyUsername }
2024-02-22 00:25:12 +01:00
handleChange = {( proxyUsername ) => updateSetting ( 'socksProxyUsername' , proxyUsername )}
/>
< TextSetting
2026-01-10 19:45:02 +01:00
settingName = { t `SOCKS password` }
2024-04-28 01:01:36 +02:00
value = { serverSettings . socksProxyPassword }
2024-02-22 00:25:12 +01:00
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 >
}
>
2025-07-12 01:08:40 +02:00
< SelectSetting < AuthMode >
2026-01-10 19:45:02 +01:00
settingName = { t `Authentication Mode` }
2025-07-12 01:08:40 +02:00
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 );
});
}}
2025-07-12 01:08:40 +02:00
disabled = { authModeDisabled }
2023-11-19 00:10:30 +01:00
/>
< TextSetting
2026-01-10 19:45:02 +01:00
settingName = { t `Username` }
2025-07-12 01:08:40 +02:00
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` }
2025-07-12 01:08:40 +02:00
value = { serverSettings . authPassword }
2023-11-19 00:10:30 +01:00
isPassword
2025-07-12 01:08:40 +02:00
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"
2024-04-28 01:01:36 +02:00
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` }
2024-04-28 01:01:36 +02:00
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` }
2024-04-28 01:01:36 +02:00
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` }
2024-04-28 01:01:36 +02:00
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` }
2024-04-28 01:01:36 +02:00
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 )}
/>
2024-07-28 22:47:03 +02:00
< 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)` }
2024-07-28 22:47:03 +02:00
/>
< Switch
edge = "end"
checked = { serverSettings . flareSolverrAsResponseFallback }
onChange = {( e ) => updateSetting ( 'flareSolverrAsResponseFallback' , e . target . checked )}
/>
</ ListItem >
2024-01-26 21:20:06 +01:00
</ List >
2025-07-12 04:11:59 +05:30
< List
subheader = {
< ListSubheader component = "div" id = "server-settings-opds" >
2026-01-10 19:45:02 +01:00
{ t `OPDS` }
2025-07-12 04:11:59 +05:30
</ 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)` }
2025-07-12 04:11:59 +05:30
/>
< 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` }
2025-07-12 04:11:59 +05:30
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.` }
2025-07-12 04:11:59 +05:30
value = { serverSettings . opdsItemsPerPage }
defaultValue = { 50 }
minValue = { 10 }
maxValue = { 5000 }
stepSize = { 10 }
showSlider
2026-01-10 19:45:02 +01:00
valueUnit = { t `item` }
2025-07-12 04:11:59 +05:30
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` }
2025-07-12 04:11:59 +05:30
/>
< 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.` }
2025-07-12 04:11:59 +05:30
/>
< 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 haven’ t read yet.` }
2025-07-12 04:11:59 +05:30
/>
< 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.` }
2025-07-12 04:11:59 +05:30
/>
< 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.` }
2025-07-12 04:11:59 +05:30
value = { serverSettings . opdsChapterSortOrder }
values = {[
2026-01-10 19:45:02 +01:00
[
SortOrder . Asc ,
{
text : t `Ascending` ,
},
],
[
SortOrder . Desc ,
{
text : t `Descending` ,
},
],
2025-07-12 04:11:59 +05:30
]}
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 )}
/>
2025-07-12 04:11:59 +05:30
</ 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 >
2025-07-12 01:10:23 +02:00
< List
subheader = {
< ListSubheader component = "div" id = "server-settings-misc" >
2026-01-10 19:45:02 +01:00
{ t `Misc` }
2025-07-12 01:10:23 +02:00
</ ListSubheader >
}
>
< ListItem >
2026-01-10 19:45:02 +01:00
< ListItemText primary = { t `Enable debug logs` } />
2025-07-12 01:10:23 +02:00
< 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` }
2025-07-12 01:10:23 +02:00
/>
< 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` }
2025-07-12 01:10:23 +02:00
settingValue = { getLogFilesCleanupDisplayValue ( serverSettings . maxLogFiles )}
value = { serverSettings . maxLogFiles }
2026-01-10 19:45:02 +01:00
valueUnit = { t `Day` }
2025-07-12 01:10:23 +02:00
handleUpdate = {( maxFiles ) => updateSetting ( 'maxLogFiles' , maxFiles )}
/>
< TextSetting
2026-01-10 19:45:02 +01:00
settingName = { t `Maximum log file size` }
2025-07-12 01:10:23 +02:00
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)` }
2025-07-12 01:10:23 +02:00
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` }
2025-07-12 01:10:23 +02:00
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)` }
2025-07-12 01:10:23 +02:00
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 >
);
};