more potential fixes for server lock duplication

This commit is contained in:
jeffvli
2026-06-15 20:49:17 -07:00
parent ba4664e797
commit f7adcb8533
3 changed files with 68 additions and 18 deletions
@@ -0,0 +1,34 @@
import { ServerListItemWithCredential } from '/@/shared/types/domain-types';
import { ServerType } from '/@/shared/types/types';
export const normalizeServerUrl = (url: string) => url.replace(/\/$/, '');
export const findExistingServerLockServer = (
serverList: Record<string, ServerListItemWithCredential>,
configuredUrl: string,
serverType?: null | ServerType,
): ServerListItemWithCredential | undefined => {
const servers = Object.values(serverList);
if (servers.length === 0) {
return undefined;
}
const normalizedUrl = normalizeServerUrl(configuredUrl);
const byUrl = servers.find((server) => normalizeServerUrl(server.url) === normalizedUrl);
if (byUrl) {
return byUrl;
}
// Server lock allows only one server — reuse the existing entry even if the URL changed.
if (servers.length === 1) {
return servers[0];
}
if (serverType) {
return servers.find((server) => server.type === serverType);
}
return undefined;
};
@@ -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 {
findExistingServerLockServer,
normalizeServerUrl,
} from '/@/renderer/features/action-required/utils/server-lock';
import { import {
isLegacyAuth, isLegacyAuth,
isServerLock, isServerLock,
@@ -19,6 +23,7 @@ import { PageErrorBoundary } from '/@/renderer/features/shared/components/page-e
import { AppRoute } from '/@/renderer/router/routes'; import { AppRoute } from '/@/renderer/router/routes';
import { import {
getServerById, getServerById,
useAuthStore,
useAuthStoreActions, useAuthStoreActions,
useCurrentServer, useCurrentServer,
useServerList, useServerList,
@@ -51,12 +56,10 @@ const SERVER_NAMES: Record<ServerType, string> = {
[ServerType.SUBSONIC]: 'OpenSubsonic', [ServerType.SUBSONIC]: 'OpenSubsonic',
}; };
const normalizeUrl = (url: string) => url.replace(/\/$/, '');
const LoginRoute = () => { const LoginRoute = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { addServer, setCurrentServer, updateServer } = useAuthStoreActions(); const { addServer, deleteServer, setCurrentServer, updateServer } = useAuthStoreActions();
const currentServer = useCurrentServer(); const currentServer = useCurrentServer();
const serverList = useServerList(); const serverList = useServerList();
@@ -151,15 +154,16 @@ const LoginRoute = () => {
}); });
} }
const normalizedUrl = normalizeUrl(serverUrl); const normalizedUrl = normalizeServerUrl(serverUrl);
const normalizedRemoteURL = normalizeUrl(remoteUrl); const normalizedRemoteURL = normalizeServerUrl(remoteUrl);
const existingServer = const existingServer = serverLock
serverLock && ? findExistingServerLockServer(serverList, normalizedUrl, serverType)
Object.values(serverList).find((s) => normalizeUrl(s.url) === normalizedUrl); : undefined;
const serverId = existingServer?.id ?? nanoid();
const serverItem: ServerListItemWithCredential = { const serverItem: ServerListItemWithCredential = {
credential: data.credential, credential: data.credential,
id: nanoid(), id: serverId,
isAdmin: data.isAdmin, isAdmin: data.isAdmin,
name: serverName, name: serverName,
remoteUrl: normalizedRemoteURL, remoteUrl: normalizedRemoteURL,
@@ -173,6 +177,9 @@ const LoginRoute = () => {
const updates: Partial<ServerListItemWithCredential> = { const updates: Partial<ServerListItemWithCredential> = {
credential: data.credential, credential: data.credential,
isAdmin: data.isAdmin, isAdmin: data.isAdmin,
name: serverName,
remoteUrl: normalizedRemoteURL,
url: normalizedUrl,
userId: data.userId, userId: data.userId,
username: data.username, username: data.username,
}; };
@@ -190,12 +197,20 @@ const LoginRoute = () => {
setCurrentServer(serverItem); setCurrentServer(serverItem);
} }
if (serverLock) {
Object.values(useAuthStore.getState().serverList).forEach((server) => {
if (server.id !== serverId) {
deleteServer(server.id);
}
});
}
toast.success({ toast.success({
message: t('form.addServer.success'), message: t('form.addServer.success'),
}); });
if (localSettings && values.password) { if (localSettings && values.password) {
const saved = await localSettings.passwordSet(values.password, serverItem.id); const saved = await localSettings.passwordSet(values.password, serverId);
if (!saved) { if (!saved) {
toast.error({ toast.error({
message: t('form.addServer.error', { message: t('form.addServer.error', {
+9 -8
View File
@@ -2,12 +2,11 @@ import { useEffect, useMemo } from 'react';
import { Navigate, Outlet } from 'react-router'; import { Navigate, Outlet } from 'react-router';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { normalizeServerUrl } from '/@/renderer/features/action-required/utils/server-lock';
import { isServerLock } from '/@/renderer/features/action-required/utils/window-properties'; import { isServerLock } from '/@/renderer/features/action-required/utils/window-properties';
import { AppRoute } from '/@/renderer/router/routes'; import { AppRoute } from '/@/renderer/router/routes';
import { useAuthStore, useAuthStoreActions } from '/@/renderer/store'; import { useAuthStore, useAuthStoreActions } from '/@/renderer/store';
const normalizeUrl = (url: string) => url.replace(/\/$/, '');
export const AppOutlet = () => { export const AppOutlet = () => {
const currentServer = useAuthStore( const currentServer = useAuthStore(
(state) => (state) =>
@@ -19,25 +18,27 @@ export const AppOutlet = () => {
: null, : null,
shallow, shallow,
); );
const { deleteServer, setCurrentServer } = useAuthStoreActions(); const { setCurrentServer, updateServer } = useAuthStoreActions();
const hasServerLockMismatch = useMemo(() => { const hasServerLockMismatch = useMemo(() => {
if (!isServerLock() || !currentServer || !window.SERVER_URL) { if (!isServerLock() || !currentServer || !window.SERVER_URL) {
return false; return false;
} }
const configuredUrl = normalizeUrl(window.SERVER_URL); const configuredUrl = normalizeServerUrl(window.SERVER_URL);
const persistedUrl = normalizeUrl(currentServer.url); const persistedUrl = normalizeServerUrl(currentServer.url);
return configuredUrl !== persistedUrl; return configuredUrl !== persistedUrl;
}, [currentServer]); }, [currentServer]);
useEffect(() => { useEffect(() => {
if (hasServerLockMismatch && currentServer) { if (hasServerLockMismatch && currentServer && window.SERVER_URL) {
deleteServer(currentServer.id); updateServer(currentServer.id, {
url: normalizeServerUrl(window.SERVER_URL),
});
setCurrentServer(null); setCurrentServer(null);
} }
}, [currentServer, deleteServer, hasServerLockMismatch, setCurrentServer]); }, [currentServer, hasServerLockMismatch, setCurrentServer, updateServer]);
const isActionsRequired = !currentServer || hasServerLockMismatch; const isActionsRequired = !currentServer || hasServerLockMismatch;