mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-07 04:20:12 +02:00
Update server management
This commit is contained in:
@@ -8,7 +8,9 @@ import { AddServerUrlForm } from '@/renderer/features/servers/components/add-ser
|
||||
import { EditServerForm } from '@/renderer/features/servers/components/edit-server-form';
|
||||
import { ServerSection } from '@/renderer/features/servers/components/server-section';
|
||||
import { useDeleteServerUrl } from '@/renderer/features/servers/mutations/use-delete-server-url';
|
||||
import { useDisableServerFolder } from '@/renderer/features/servers/mutations/use-disable-server-folder';
|
||||
import { useDisableServerUrl } from '@/renderer/features/servers/mutations/use-disable-server-url';
|
||||
import { useEnableServerFolder } from '@/renderer/features/servers/mutations/use-enable-server-folder';
|
||||
import { useEnableServerUrl } from '@/renderer/features/servers/mutations/use-enable-server-url';
|
||||
import { usePermissions } from '@/renderer/features/shared';
|
||||
import { useAuthStore } from '@/renderer/store';
|
||||
@@ -26,6 +28,8 @@ export const ServerListItem = ({ server }: ServerListItemProps) => {
|
||||
const enableServerUrl = useEnableServerUrl();
|
||||
const disableServerUrl = useDisableServerUrl();
|
||||
const deleteServerUrl = useDeleteServerUrl();
|
||||
const enableServerFolder = useEnableServerFolder();
|
||||
const disableServerFolder = useDisableServerFolder();
|
||||
const serverCredentials = useAuthStore((state) => state.serverCredentials);
|
||||
|
||||
const enableServerCredential = useAuthStore(
|
||||
@@ -69,6 +73,18 @@ export const ServerListItem = ({ server }: ServerListItemProps) => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleToggleFolder = (folderId: string, enabled: boolean) => {
|
||||
if (enabled) {
|
||||
return disableServerFolder.mutate({
|
||||
query: { folderId, serverId: server.id },
|
||||
});
|
||||
}
|
||||
|
||||
return enableServerFolder.mutate({
|
||||
query: { folderId, serverId: server.id },
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack spacing="xl">
|
||||
@@ -104,7 +120,35 @@ export const ServerListItem = ({ server }: ServerListItemProps) => {
|
||||
</Group>
|
||||
)}
|
||||
</ServerSection>
|
||||
<ServerSection title="Server URLs">
|
||||
<ServerSection title="Music Folders">
|
||||
<Stack>
|
||||
{server.serverFolders?.map((folder) => (
|
||||
<Group position="apart">
|
||||
<Group>
|
||||
<Text>{folder.name}</Text>
|
||||
</Group>
|
||||
<Group>
|
||||
<Button
|
||||
compact
|
||||
radius="lg"
|
||||
variant={folder.enabled ? 'filled' : 'subtle'}
|
||||
onClick={() =>
|
||||
handleToggleFolder(folder.id, folder.enabled)
|
||||
}
|
||||
>
|
||||
{folder.enabled ? 'Enabled' : 'Disabled'}
|
||||
</Button>
|
||||
{permissions.deleteServerFolder && (
|
||||
<Button compact disabled radius="xl" variant="subtle">
|
||||
<RiDeleteBin2Fill />
|
||||
</Button>
|
||||
)}
|
||||
</Group>
|
||||
</Group>
|
||||
))}
|
||||
</Stack>
|
||||
</ServerSection>
|
||||
<ServerSection title="URLs">
|
||||
{addUrl ? (
|
||||
<AddServerUrlForm
|
||||
serverId={server.id}
|
||||
@@ -119,14 +163,13 @@ export const ServerListItem = ({ server }: ServerListItemProps) => {
|
||||
<Group>
|
||||
<Button
|
||||
compact
|
||||
px={10}
|
||||
radius="lg"
|
||||
variant={serverUrl.enabled ? 'filled' : 'subtle'}
|
||||
onClick={() =>
|
||||
handleToggleUrl(serverUrl.id, serverUrl.enabled)
|
||||
}
|
||||
>
|
||||
{serverUrl.enabled ? 'Disable' : 'Enable'}
|
||||
{serverUrl.enabled ? 'Enabled' : 'Disabled'}
|
||||
</Button>
|
||||
{permissions.deleteServerUrl && (
|
||||
<Button
|
||||
@@ -155,7 +198,7 @@ export const ServerListItem = ({ server }: ServerListItemProps) => {
|
||||
</>
|
||||
)}
|
||||
</ServerSection>
|
||||
<ServerSection title="Server Credentials">
|
||||
<ServerSection title="Credentials">
|
||||
{addCredential ? (
|
||||
<AddServerCredentialForm
|
||||
server={server}
|
||||
@@ -170,7 +213,6 @@ export const ServerListItem = ({ server }: ServerListItemProps) => {
|
||||
<Group>
|
||||
<Button
|
||||
compact
|
||||
px={10}
|
||||
radius="lg"
|
||||
variant={credential.enabled ? 'filled' : 'subtle'}
|
||||
onClick={() =>
|
||||
|
||||
@@ -18,9 +18,12 @@ const Section = styled.div`
|
||||
export const ServerSection = ({ title, children }: ServerSectionProps) => {
|
||||
return (
|
||||
<Container>
|
||||
<Text font={Font.EPILOGUE} size="sm">
|
||||
{title}
|
||||
</Text>
|
||||
{React.isValidElement(title) ? (
|
||||
title
|
||||
) : (
|
||||
<Text font={Font.EPILOGUE}>{title}</Text>
|
||||
)}
|
||||
|
||||
<Section>{children}</Section>
|
||||
</Container>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { AxiosError } from 'axios';
|
||||
import { api } from '@/renderer/api';
|
||||
import { queryKeys } from '@/renderer/api/query-keys';
|
||||
import { ServerListResponse } from '@/renderer/api/servers.api';
|
||||
import { ApiError, NullResponse } from '@/renderer/api/types';
|
||||
|
||||
export const useDisableServerFolder = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const mutation = useMutation<
|
||||
NullResponse,
|
||||
AxiosError<ApiError>,
|
||||
{ query: { folderId: string; serverId: string } },
|
||||
{ previous: ServerListResponse | undefined }
|
||||
>({
|
||||
mutationFn: ({ query }) => api.servers.disableFolder(query),
|
||||
onError: (_err, _variables, context) => {
|
||||
if (!context?.previous) return;
|
||||
queryClient.setQueryData(queryKeys.servers.list(), context.previous);
|
||||
},
|
||||
onMutate: async (variables) => {
|
||||
const queryKey = queryKeys.servers.list();
|
||||
|
||||
await queryClient.cancelQueries(queryKey);
|
||||
const previous = queryClient.getQueryData<ServerListResponse>(queryKey);
|
||||
|
||||
if (!previous) return undefined;
|
||||
|
||||
const data = previous.data.map((server) => {
|
||||
if (server.id === variables.query.serverId) {
|
||||
return {
|
||||
...server,
|
||||
serverFolders: server.serverFolders?.map((folder) =>
|
||||
folder.id === variables.query.folderId
|
||||
? { ...folder, enabled: false }
|
||||
: folder
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return server;
|
||||
});
|
||||
|
||||
queryClient.setQueryData(queryKey, { ...previous, data });
|
||||
|
||||
return { previous };
|
||||
},
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries(queryKeys.servers.list());
|
||||
},
|
||||
});
|
||||
|
||||
return mutation;
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { AxiosError } from 'axios';
|
||||
import { api } from '@/renderer/api';
|
||||
import { queryKeys } from '@/renderer/api/query-keys';
|
||||
import { ServerListResponse } from '@/renderer/api/servers.api';
|
||||
import { ApiError, NullResponse } from '@/renderer/api/types';
|
||||
|
||||
export const useEnableServerFolder = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const mutation = useMutation<
|
||||
NullResponse,
|
||||
AxiosError<ApiError>,
|
||||
{ query: { folderId: string; serverId: string } },
|
||||
{ previous: ServerListResponse | undefined }
|
||||
>({
|
||||
mutationFn: ({ query }) => api.servers.enableFolder(query),
|
||||
onError: (_err, _variables, context) => {
|
||||
if (!context?.previous) return;
|
||||
queryClient.setQueryData(queryKeys.servers.list(), context.previous);
|
||||
},
|
||||
onMutate: async (variables) => {
|
||||
const queryKey = queryKeys.servers.list();
|
||||
|
||||
await queryClient.cancelQueries(queryKey);
|
||||
const previous = queryClient.getQueryData<ServerListResponse>(queryKey);
|
||||
|
||||
if (!previous) return undefined;
|
||||
|
||||
const data = previous.data.map((server) => {
|
||||
if (server.id === variables.query.serverId) {
|
||||
return {
|
||||
...server,
|
||||
serverFolders: server.serverFolders?.map((folder) =>
|
||||
folder.id === variables.query.folderId
|
||||
? { ...folder, enabled: true }
|
||||
: folder
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
return server;
|
||||
});
|
||||
|
||||
queryClient.setQueryData(queryKey, { ...previous, data });
|
||||
|
||||
return { previous };
|
||||
},
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries(queryKeys.servers.list());
|
||||
},
|
||||
});
|
||||
|
||||
return mutation;
|
||||
};
|
||||
@@ -20,7 +20,7 @@ export const validateServerCredential = async (options: {
|
||||
if (legacyAuth) {
|
||||
token = `u=${username}&p=${password}`;
|
||||
testConnection = await axios.get(
|
||||
`${cleanServerUrl}/rest/ping.view?v=1.13.0&c=sonixd&f=json&${token}`
|
||||
`${cleanServerUrl}/rest/ping.view?v=1.13.0&c=Feishin&f=json&${token}`
|
||||
);
|
||||
} else {
|
||||
const salt = randomString();
|
||||
@@ -28,7 +28,7 @@ export const validateServerCredential = async (options: {
|
||||
token = `u=${username}&s=${salt}&t=${hash}`;
|
||||
|
||||
testConnection = await axios.get(
|
||||
`${cleanServerUrl}/rest/ping.view?v=1.13.0&c=sonixd&f=json&${token}`
|
||||
`${cleanServerUrl}/rest/ping.view?v=1.13.0&c=Feishin&f=json&${token}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export const validateServerCredential = async (options: {
|
||||
{ pw: password, username },
|
||||
{
|
||||
headers: {
|
||||
'X-Emby-Authorization': `MediaBrowser Client="Sonixd", Device="PC", DeviceId="Sonixd", Version="1.0.0-alpha1"`,
|
||||
'X-Emby-Authorization': `MediaBrowser Client="Feishin", Device="PC", DeviceId="Feishin", Version="1.0.0-alpha1"`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user