fix: properly handle server lock and related properties

This commit is contained in:
Kendall Garner
2026-01-08 07:42:59 -08:00
parent 2fdc214c21
commit 7e67d0dac6
12 changed files with 40 additions and 28 deletions
+3 -1
View File
@@ -20,6 +20,8 @@ COPY --chown=nginx:nginx --from=builder /app/out/web /usr/share/nginx/html
COPY ./settings.js.template /etc/nginx/templates/settings.js.template COPY ./settings.js.template /etc/nginx/templates/settings.js.template
COPY ng.conf.template /etc/nginx/templates/default.conf.template COPY ng.conf.template /etc/nginx/templates/default.conf.template
ENV PUBLIC_PATH="/" ENV SERVER_LOCK=false SERVER_NAME="" SERVER_TYPE="" SERVER_URL=""
ENV LEGACY_AUTHENTICATION="" ANALYTICS_DISABLED="" PUBLIC_PATH="/"
EXPOSE 9180 EXPOSE 9180
CMD ["nginx", "-g", "daemon off;"] CMD ["nginx", "-g", "daemon off;"]
+2 -2
View File
@@ -5,9 +5,9 @@ services:
restart: unless-stopped restart: unless-stopped
environment: environment:
- SERVER_NAME=jellyfin # pre-defined server name - SERVER_NAME=jellyfin # pre-defined server name
- SERVER_LOCK=true # When true AND name/type/url are set, only username/password can be toggled - SERVER_LOCK=false # When true AND name/type/url are set, only username/password can be toggled
- SERVER_TYPE=jellyfin # the allowed types are: jellyfin, navidrome, subsonic. These values are case insensitive - SERVER_TYPE=jellyfin # the allowed types are: jellyfin, navidrome, subsonic. These values are case insensitive
- SERVER_URL= # http://address:port or https://address:port - SERVER_URL=http://localhost:8096 # http://address:port or https://address:port
- LEGACY_AUTHENTICATION=false # When SERVER_LOCK is true, sets the legacyauth flag for server authentication (true or false) - LEGACY_AUTHENTICATION=false # When SERVER_LOCK is true, sets the legacyauth flag for server authentication (true or false)
- ANALYTICS_DISABLED=false # Set to true to disable Umami analytics tracking - ANALYTICS_DISABLED=false # Set to true to disable Umami analytics tracking
ports: ports:
+1 -1
View File
@@ -1 +1 @@
"use strict";window.SERVER_URL="${SERVER_URL}";window.SERVER_NAME="${SERVER_NAME}";window.SERVER_TYPE="${SERVER_TYPE}";window.SERVER_LOCK=${SERVER_LOCK};window.LEGACY_AUTHENTICATION=${LEGACY_AUTHENTICATION};window.ANALYTICS_DISABLED="${ANALYTICS_DISABLED}"; "use strict";window.SERVER_URL="${SERVER_URL}";window.SERVER_NAME="${SERVER_NAME}";window.SERVER_TYPE="${SERVER_TYPE}";window.SERVER_LOCK="${SERVER_LOCK}";window.LEGACY_AUTHENTICATION="${LEGACY_AUTHENTICATION}";window.ANALYTICS_DISABLED="${ANALYTICS_DISABLED}";
@@ -3,6 +3,7 @@ import isElectron from 'is-electron';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
import { isServerLock } from '/@/renderer/features/action-required/utils/window-properties';
import JellyfinLogo from '/@/renderer/features/servers/assets/jellyfin.png'; import JellyfinLogo from '/@/renderer/features/servers/assets/jellyfin.png';
import NavidromeLogo from '/@/renderer/features/servers/assets/navidrome.png'; import NavidromeLogo from '/@/renderer/features/servers/assets/navidrome.png';
import OpenSubsonicLogo from '/@/renderer/features/servers/assets/opensubsonic.png'; import OpenSubsonicLogo from '/@/renderer/features/servers/assets/opensubsonic.png';
@@ -28,14 +29,12 @@ const localSettings = isElectron() ? window.api.localSettings : null;
export const ServerRequired = () => { export const ServerRequired = () => {
const serverList = useServerList(); const serverList = useServerList();
const isServerLock = Boolean(window.SERVER_LOCK) || false;
if (Object.keys(serverList).length > 0) { if (Object.keys(serverList).length > 0) {
return ( return (
<ScrollArea> <ScrollArea>
<Stack miw="300px"> <Stack miw="300px">
<ServerSelector /> <ServerSelector />
{!isServerLock && ( {!isServerLock() && (
<> <>
<Divider my="lg" /> <Divider my="lg" />
<AddServerForm onCancel={null} /> <AddServerForm onCancel={null} />
@@ -6,6 +6,7 @@ import { PageHeader } from '/@/renderer/components/page-header/page-header';
import { ActionRequiredContainer } from '/@/renderer/features/action-required/components/action-required-container'; import { ActionRequiredContainer } from '/@/renderer/features/action-required/components/action-required-container';
import { ServerCredentialRequired } from '/@/renderer/features/action-required/components/server-credential-required'; import { ServerCredentialRequired } from '/@/renderer/features/action-required/components/server-credential-required';
import { ServerRequired } from '/@/renderer/features/action-required/components/server-required'; import { ServerRequired } from '/@/renderer/features/action-required/components/server-required';
import { isServerLock } from '/@/renderer/features/action-required/utils/window-properties';
import LoginRoute from '/@/renderer/features/login/routes/login-route'; import LoginRoute from '/@/renderer/features/login/routes/login-route';
import { ServerList } from '/@/renderer/features/servers/components/server-list'; import { ServerList } from '/@/renderer/features/servers/components/server-list';
import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page'; import { AnimatedPage } from '/@/renderer/features/shared/components/animated-page';
@@ -25,8 +26,7 @@ const ActionRequiredRoute = () => {
const isServerRequired = !currentServer; const isServerRequired = !currentServer;
const isCredentialRequired = currentServer && !currentServer.credential; const isCredentialRequired = currentServer && !currentServer.credential;
const isServerLock = Boolean(window.SERVER_LOCK) || false; const isLoginRequired = isServerLock() && !currentServer;
const isLoginRequired = isServerLock && !currentServer;
const checks = [ const checks = [
{ {
@@ -0,0 +1,4 @@
export const isLegacyAuth = () =>
window.LEGACY_AUTHENTICATION === true || window.LEGACY_AUTHENTICATION === 'true';
export const isServerLock = () => window.SERVER_LOCK === true || window.SERVER_LOCK === 'true';
@@ -6,6 +6,10 @@ import { Navigate } from 'react-router';
import { api } from '/@/renderer/api'; import { api } from '/@/renderer/api';
import { PageHeader } from '/@/renderer/components/page-header/page-header'; import { PageHeader } from '/@/renderer/components/page-header/page-header';
import {
isLegacyAuth,
isServerLock,
} from '/@/renderer/features/action-required/utils/window-properties';
import JellyfinIcon from '/@/renderer/features/servers/assets/jellyfin.png'; import JellyfinIcon from '/@/renderer/features/servers/assets/jellyfin.png';
import NavidromeIcon from '/@/renderer/features/servers/assets/navidrome.png'; import NavidromeIcon from '/@/renderer/features/servers/assets/navidrome.png';
import SubsonicIcon from '/@/renderer/features/servers/assets/opensubsonic.png'; import SubsonicIcon from '/@/renderer/features/servers/assets/opensubsonic.png';
@@ -48,17 +52,17 @@ const LoginRoute = () => {
const currentServer = useCurrentServer(); const currentServer = useCurrentServer();
// Check if server lock is configured // Check if server lock is configured
const isServerLock = Boolean(window.SERVER_LOCK) || false; const serverLock = isServerLock();
const serverType = window.SERVER_TYPE ? toServerType(window.SERVER_TYPE) : null; const serverType = window.SERVER_TYPE ? toServerType(window.SERVER_TYPE) : null;
const serverName = window.SERVER_NAME || ''; const serverName = window.SERVER_NAME || '';
const serverUrl = window.SERVER_URL || ''; const serverUrl = window.SERVER_URL || '';
const legacyAuth = isServerLock ? Boolean(window.LEGACY_AUTHENTICATION) || false : false; const legacyAuth = serverLock && isLegacyAuth();
const config = [ const config = [
{ {
isValid: true, isValid: true,
key: 'SERVER_LOCK', key: 'SERVER_LOCK',
value: isServerLock, value: serverLock,
}, },
{ {
isValid: serverType !== null, isValid: serverType !== null,
@@ -3,6 +3,7 @@ import { Dispatch, useCallback } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
import { isServerLock } from '/@/renderer/features/action-required/utils/window-properties';
import { Command, CommandPalettePages } from '/@/renderer/features/search/components/command'; import { Command, CommandPalettePages } from '/@/renderer/features/search/components/command';
import { ServerList } from '/@/renderer/features/servers/components/server-list'; import { ServerList } from '/@/renderer/features/servers/components/server-list';
import { AppRoute } from '/@/renderer/router/routes'; import { AppRoute } from '/@/renderer/router/routes';
@@ -42,8 +43,6 @@ export const ServerCommands = ({ handleClose, setPages, setQuery }: ServerComman
[handleClose, navigate, setCurrentServer, setPages, setQuery], [handleClose, navigate, setCurrentServer, setPages, setQuery],
); );
const isServerLock = Boolean(window.SERVER_LOCK) || false;
return ( return (
<> <>
<Command.Group <Command.Group
@@ -56,7 +55,7 @@ export const ServerCommands = ({ handleClose, setPages, setQuery }: ServerComman
>{`${serverList[key].name}...`}</Command.Item> >{`${serverList[key].name}...`}</Command.Item>
))} ))}
</Command.Group> </Command.Group>
{!isServerLock && ( {!isServerLock() && (
<Command.Group heading={t('common.manage', { postProcess: 'sentenceCase' })}> <Command.Group heading={t('common.manage', { postProcess: 'sentenceCase' })}>
<Command.Item onSelect={handleManageServersModal}> <Command.Item onSelect={handleManageServersModal}>
{t('page.appMenu.manageServers', { postProcess: 'sentenceCase' })}... {t('page.appMenu.manageServers', { postProcess: 'sentenceCase' })}...
@@ -5,6 +5,10 @@ import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { api } from '/@/renderer/api'; import { api } from '/@/renderer/api';
import {
isLegacyAuth,
isServerLock,
} from '/@/renderer/features/action-required/utils/window-properties';
import JellyfinIcon from '/@/renderer/features/servers/assets/jellyfin.png'; import JellyfinIcon from '/@/renderer/features/servers/assets/jellyfin.png';
import NavidromeIcon from '/@/renderer/features/servers/assets/navidrome.png'; import NavidromeIcon from '/@/renderer/features/servers/assets/navidrome.png';
import SubsonicIcon from '/@/renderer/features/servers/assets/opensubsonic.png'; import SubsonicIcon from '/@/renderer/features/servers/assets/opensubsonic.png';
@@ -94,8 +98,8 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
const { addServer, setCurrentServer } = useAuthStoreActions(); const { addServer, setCurrentServer } = useAuthStoreActions();
const { servers: discovered } = useAutodiscovery(); const { servers: discovered } = useAutodiscovery();
const isServerLock = Boolean(window.SERVER_LOCK) || false; const serverLock = isServerLock();
const legacyAuthDefault = isServerLock ? Boolean(window.LEGACY_AUTHENTICATION) || false : false; const legacyAuthDefault = serverLock && isLegacyAuth();
const form = useForm({ const form = useForm({
initialValues: { initialValues: {
@@ -231,7 +235,7 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
<Stack m={5} ref={focusTrapRef}> <Stack m={5} ref={focusTrapRef}>
<SegmentedControl <SegmentedControl
data={ALL_SERVERS} data={ALL_SERVERS}
disabled={isServerLock} disabled={serverLock}
p="md" p="md"
withItemsBorders={false} withItemsBorders={false}
{...form.getInputProps('type')} {...form.getInputProps('type')}
@@ -239,7 +243,7 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
<Group grow> <Group grow>
<TextInput <TextInput
data-autofocus data-autofocus
disabled={isServerLock} disabled={serverLock}
label={t('form.addServer.input', { label={t('form.addServer.input', {
context: 'name', context: 'name',
postProcess: 'titleCase', postProcess: 'titleCase',
@@ -248,7 +252,7 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
{...form.getInputProps('name')} {...form.getInputProps('name')}
/> />
<TextInput <TextInput
disabled={isServerLock} disabled={serverLock}
label={t('form.addServer.input', { label={t('form.addServer.input', {
context: 'url', context: 'url',
postProcess: 'titleCase', postProcess: 'titleCase',
@@ -258,7 +262,7 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
/> />
</Group> </Group>
<TextInput <TextInput
disabled={isServerLock} disabled={serverLock}
label={t('form.addServer.input', { label={t('form.addServer.input', {
context: 'remoteUrl', context: 'remoteUrl',
postProcess: 'titleCase', postProcess: 'titleCase',
@@ -308,7 +312,7 @@ export const AddServerForm = ({ onCancel }: AddServerFormProps) => {
)} )}
{form.values.type === ServerType.SUBSONIC && ( {form.values.type === ServerType.SUBSONIC && (
<Checkbox <Checkbox
disabled={isServerLock} disabled={serverLock}
label={t('form.addServer.input', { label={t('form.addServer.input', {
context: 'legacyAuthentication', context: 'legacyAuthentication',
postProcess: 'titleCase', postProcess: 'titleCase',
@@ -3,6 +3,7 @@ import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
import { isServerLock } from '/@/renderer/features/action-required/utils/window-properties';
import JellyfinLogo from '/@/renderer/features/servers/assets/jellyfin.png'; import JellyfinLogo from '/@/renderer/features/servers/assets/jellyfin.png';
import NavidromeLogo from '/@/renderer/features/servers/assets/navidrome.png'; import NavidromeLogo from '/@/renderer/features/servers/assets/navidrome.png';
import OpenSubsonicLogo from '/@/renderer/features/servers/assets/opensubsonic.png'; import OpenSubsonicLogo from '/@/renderer/features/servers/assets/opensubsonic.png';
@@ -88,8 +89,6 @@ export const ServerSelectorItems = () => {
}); });
}; };
const isServerLock = Boolean(window.SERVER_LOCK) || false;
return ( return (
<> <>
<DropdownMenu.Label> <DropdownMenu.Label>
@@ -123,7 +122,7 @@ export const ServerSelectorItems = () => {
</DropdownMenu.Item> </DropdownMenu.Item>
); );
})} })}
{!isServerLock && ( {!isServerLock() && (
<DropdownMenu.Item <DropdownMenu.Item
leftSection={<Icon icon="edit" />} leftSection={<Icon icon="edit" />}
onClick={handleManageServersModal} onClick={handleManageServersModal}
@@ -6,6 +6,7 @@ import { Link, useNavigate } from 'react-router';
import packageJson from '../../../../../package.json'; import packageJson from '../../../../../package.json';
import { isServerLock } from '/@/renderer/features/action-required/utils/window-properties';
import { ServerList } from '/@/renderer/features/servers/components/server-list'; import { ServerList } from '/@/renderer/features/servers/components/server-list';
import { openSettingsModal } from '/@/renderer/features/settings/utils/open-settings-modal'; import { openSettingsModal } from '/@/renderer/features/settings/utils/open-settings-modal';
import { useAppStore, useAppStoreActions, useCommandPalette } from '/@/renderer/store'; import { useAppStore, useAppStoreActions, useCommandPalette } from '/@/renderer/store';
@@ -175,7 +176,7 @@ export const AppMenu = () => {
type: 'divider', type: 'divider',
}, },
{ {
condition: !window.SERVER_LOCK, condition: !isServerLock(),
id: 'manage-servers', id: 'manage-servers',
item: { item: {
label: t('page.appMenu.manageServers', { postProcess: 'sentenceCase' }), label: t('page.appMenu.manageServers', { postProcess: 'sentenceCase' }),
+2 -2
View File
@@ -1,8 +1,8 @@
declare global { declare global {
interface Window { interface Window {
ANALYTICS_DISABLED?: boolean | string; ANALYTICS_DISABLED?: boolean | string;
LEGACY_AUTHENTICATION?: boolean; LEGACY_AUTHENTICATION?: boolean | string;
SERVER_LOCK?: boolean; SERVER_LOCK?: boolean | string;
SERVER_NAME?: string; SERVER_NAME?: string;
SERVER_TYPE?: string; SERVER_TYPE?: string;
SERVER_URL?: string; SERVER_URL?: string;