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' ;
2025-08-15 22:02:58 +02:00
import { 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' ;
2025-09-08 00:46:57 +02:00
import { 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 ) }
2023-11-19 00:10:30 +01:00
/ >
< 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 >
) ;
} ;