gitscope
← marzneshin / Diff · 16e5ea1 → 16e5ea1
Changes 16e5ea1..16e5ea1
Patch commit
diff --git a/app/dashboard/package-lock.json b/app/dashboard/package-lock.json
index 7d4dc5e..fb4cc88 100644
--- a/app/dashboard/package-lock.json
+++ b/app/dashboard/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "marzneshin-dashboard",
- "version": "0.1.1",
+ "version": "0.1.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "marzneshin-dashboard",
- "version": "0.1.1",
+ "version": "0.1.3",
"hasInstallScript": true,
"dependencies": {
"@chakra-ui/icons": "^2.1.1",
@@ -16,6 +16,7 @@
"@heroicons/react": "^2.0.12",
"@hookform/devtools": "^4.3.1",
"@hookform/resolvers": "^2.9.10",
+ "@tanstack/react-query-devtools": "^5.24.8",
"apexcharts": "^3.40.0",
"classnames": "^2.3.2",
"dayjs": "^1.11.6",
@@ -2510,6 +2511,57 @@
"@svgr/core": "^6.0.0"
}
},
+ "node_modules/@tanstack/query-core": {
+ "version": "5.24.8",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.24.8.tgz",
+ "integrity": "sha512-yH7KnfXMf10p1U5GffTQzFi2Miiw6WJZImGYGdV7eqa5ZbKO8qVx9lOA9SfhIaJXomrMp1Yz5w/CBhVM3yWeTA==",
+ "peer": true,
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/query-devtools": {
+ "version": "5.24.0",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.24.0.tgz",
+ "integrity": "sha512-pThim455t69zrZaQKa7IRkEIK8UBTS+gHVAdNfhO72Xh4rWpMc63ovRje5/n6iw63+d6QiJzVadsJVdPoodSeQ==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/react-query": {
+ "version": "5.24.8",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.24.8.tgz",
+ "integrity": "sha512-jB3JS9SzDmBySk9VVOTPt/0ixWEb3K3dy9IExlVl/1NouY3N7HzAqG/1d4m6E9eFfKJoLvA/hBksaLu0lw627A==",
+ "peer": true,
+ "dependencies": {
+ "@tanstack/query-core": "5.24.8"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0"
+ }
+ },
+ "node_modules/@tanstack/react-query-devtools": {
+ "version": "5.24.8",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.24.8.tgz",
+ "integrity": "sha512-OfNbyOF07/3PXH1fdLs/2BxjpXv2hFI777iym2L0lVGQbyVSJAUbGXYaCvgjZIdan/vfiXf3sjCVkO8mD9hf0Q==",
+ "dependencies": {
+ "@tanstack/query-devtools": "5.24.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "@tanstack/react-query": "^5.24.8",
+ "react": "^18.0.0"
+ }
+ },
"node_modules/@tootallnate/once": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
@@ -3013,7 +3065,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
- "devOptional": true,
+ "dev": true,
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
@@ -3326,7 +3378,7 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
- "devOptional": true,
+ "dev": true,
"engines": {
"node": ">=8"
}
@@ -3344,7 +3396,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
- "devOptional": true,
+ "dev": true,
"dependencies": {
"fill-range": "^7.0.1"
},
@@ -3565,7 +3617,7 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
- "devOptional": true,
+ "dev": true,
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@@ -4348,6 +4400,7 @@
"cpu": [
"x64"
],
+ "dev": true,
"optional": true,
"os": [
"linux"
@@ -4962,7 +5015,7 @@
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
- "devOptional": true,
+ "dev": true,
"dependencies": {
"to-regex-range": "^5.0.1"
},
@@ -5267,7 +5320,7 @@
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "devOptional": true,
+ "dev": true,
"dependencies": {
"is-glob": "^4.0.1"
},
@@ -5665,7 +5718,7 @@
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz",
"integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==",
- "devOptional": true
+ "dev": true
},
"node_modules/import-fresh": {
"version": "3.3.0",
@@ -5802,7 +5855,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "devOptional": true,
+ "dev": true,
"dependencies": {
"binary-extensions": "^2.0.0"
},
@@ -5956,7 +6009,7 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "devOptional": true,
+ "dev": true,
"engines": {
"node": ">=0.12.0"
}
@@ -7098,7 +7151,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "devOptional": true,
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -8112,7 +8165,7 @@
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "devOptional": true,
+ "dev": true,
"dependencies": {
"picomatch": "^2.2.1"
},
@@ -8382,7 +8435,7 @@
"version": "1.71.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz",
"integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==",
- "devOptional": true,
+ "dev": true,
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
@@ -9042,7 +9095,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "devOptional": true,
+ "dev": true,
"dependencies": {
"is-number": "^7.0.0"
},
diff --git a/app/dashboard/package.json b/app/dashboard/package.json
index 22d423b..6a28ad1 100644
--- a/app/dashboard/package.json
+++ b/app/dashboard/package.json
@@ -1,7 +1,7 @@
{
"name": "marzneshin-dashboard",
"private": true,
- "version": "0.1.1",
+ "version": "0.1.3",
"scripts": {
"dev": "vite --port 3000",
"build": "tsc && vite build",
@@ -19,6 +19,7 @@
"@heroicons/react": "^2.0.12",
"@hookform/devtools": "^4.3.1",
"@hookform/resolvers": "^2.9.10",
+ "@tanstack/react-query-devtools": "^5.24.8",
"apexcharts": "^3.40.0",
"classnames": "^2.3.2",
"dayjs": "^1.11.6",
diff --git a/app/dashboard/src/index.tsx b/app/dashboard/src/index.tsx
index 4a8667e..2dd49f0 100644
--- a/app/dashboard/src/index.tsx
+++ b/app/dashboard/src/index.tsx
@@ -8,12 +8,13 @@ import utc from 'dayjs/plugin/utc';
import 'locales/i18n';
import React from 'react';
import ReactDOM from 'react-dom/client';
-import { QueryClientProvider } from 'react-query';
+import { QueryClientProvider } from '@tanstack/react-query';
import { queryClient } from 'service/react-query';
import { updateThemeColor } from 'utils/themeColor';
import { theme } from '../chakra.config';
import App from './App';
import 'index.scss';
+import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
dayjs.extend(Timezone);
dayjs.extend(LocalizedFormat);
@@ -28,6 +29,7 @@ ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<ChakraProvider theme={theme}>
<QueryClientProvider client={queryClient}>
<App />
+ <ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
</ChakraProvider>
</React.StrictMode>
diff --git a/app/dashboard/src/pages/home/statistics.tsx b/app/dashboard/src/pages/home/statistics.tsx
index 8e3c166..8762a0f 100644
--- a/app/dashboard/src/pages/home/statistics.tsx
+++ b/app/dashboard/src/pages/home/statistics.tsx
@@ -7,9 +7,10 @@ import {
import { useDashboard } from 'stores';
import { FC } from 'react';
import { useTranslation } from 'react-i18next';
-import { useQuery } from 'react-query';
+import { useQuery } from '@tanstack/react-query';
import { fetch } from 'service/http';
import { formatBytes, numberWithCommas } from 'utils/formatByte';
+import { useEffect } from 'react';
import { StatisticsCardInLine, StatisticsQueryKey } from 'components/statistics-card';
const TotalUsersIcon = chakra(UsersIcon, {
@@ -41,16 +42,18 @@ const MemoryIcon = chakra(ChartPieIcon, {
export const Statistics: FC<BoxProps> = (props) => {
- const { version } = useDashboard();
+ const { version: currentVersion } = useDashboard();
const { data: systemData } = useQuery({
- queryKey: StatisticsQueryKey,
+ queryKey: [StatisticsQueryKey],
queryFn: () => fetch('/system'),
refetchInterval: 5000,
- onSuccess: ({ version: currentVersion }) => {
- if (version !== currentVersion)
- useDashboard.setState({ version: currentVersion });
- },
});
+
+ useEffect(() => {
+ if (systemData?.version !== currentVersion)
+ useDashboard.setState({ version: systemData.version })
+ } )
+
const { t } = useTranslation();
return (
<HStack
diff --git a/app/dashboard/src/pages/hosts/container.tsx b/app/dashboard/src/pages/hosts/container.tsx
index e9329cf..f90f769 100644
--- a/app/dashboard/src/pages/hosts/container.tsx
+++ b/app/dashboard/src/pages/hosts/container.tsx
@@ -1,20 +1,45 @@
-import React from 'react'
+import React, { useEffect } from 'react'
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'
import { InboundsHostsFilters } from './filters'
import { InboundsSidebar } from './inbounds-sidebar'
import { HostsTable } from './table'
+import { queryIds } from 'constants/query-ids';
+import { useQuery } from '@tanstack/react-query';
+import { fetchInbounds, fetchInboundHosts, useInbounds } from 'stores';
export const InboundsHostsManager = () => {
+ const { selectedInbound, refetchHosts } = useInbounds();
+
+ const { data: inbounds } = useQuery({
+ queryKey: [queryIds.inbounds],
+ initialData: [],
+ queryFn: () => {
+ return fetchInbounds();
+ }
+ });
+
+ const { data: hosts } = useQuery({
+ queryKey: [queryIds.hosts, selectedInbound?.id],
+ initialData: [],
+ queryFn: () => {
+ return fetchInboundHosts(selectedInbound?.id);
+ }
+ });
+
+ useEffect(() => {
+ refetchHosts();
+ }, [selectedInbound])
+
return (
<>
<InboundsHostsFilters />
<PanelGroup direction="horizontal">
<Panel id="sidebar" minSize={20} order={1}>
- <InboundsSidebar />
+ <InboundsSidebar inbounds={inbounds} />
</Panel>
<PanelResizeHandle />
<Panel minSize={60} order={2}>
- <HostsTable />
+ <HostsTable hosts={hosts} />
</Panel>
</PanelGroup>
</>
diff --git a/app/dashboard/src/pages/hosts/inbounds-sidebar/index.tsx b/app/dashboard/src/pages/hosts/inbounds-sidebar/index.tsx
index cd6bb3c..1086ed0 100644
--- a/app/dashboard/src/pages/hosts/inbounds-sidebar/index.tsx
+++ b/app/dashboard/src/pages/hosts/inbounds-sidebar/index.tsx
@@ -2,21 +2,26 @@ import { Table, Thead, Tbody, Th, Td, Tr, HStack } from '@chakra-ui/react';
import classNames from 'classnames';
import { InboundCard } from 'components/inbounds-card';
import { handleSort } from 'components/table';
+import { FC } from 'react';
import { useTranslation } from 'react-i18next';
-import { useQuery } from 'react-query';
-import { fetchInbounds, useInbounds } from 'stores';
+import { useInbounds } from 'stores';
+import { InboundType } from 'types';
-export const InboundsSidebar = () => {
+type InboundsSidebarProps = {
+ inbounds: InboundType[];
+}
+
+export const InboundsSidebar: FC<InboundsSidebarProps> = ({
+ inbounds,
+}) => {
const {
onInboundsFilterChange,
inboundsFilters: filters,
selectedInbound,
selectInbound,
+ refetchHosts,
} = useInbounds();
- const { data: inbounds } = useQuery('hosts', () => {
- return fetchInbounds();
- });
const { t } = useTranslation();
@@ -45,7 +50,10 @@ export const InboundsSidebar = () => {
className={classNames('interactive')}
bg={active ? 'gray.300' : undefined}
_dark={{ bg: active ? 'gray.700' : undefined }}
- onClick={() => { selectInbound(inbound) }}>
+ onClick={() => {
+ selectInbound(inbound);
+ refetchHosts();
+ }}>
<Td>
<InboundCard
tag={inbound.tag}
diff --git a/app/dashboard/src/pages/hosts/table/index.tsx b/app/dashboard/src/pages/hosts/table/index.tsx
index 4033be2..4bdfd28 100644
--- a/app/dashboard/src/pages/hosts/table/index.tsx
+++ b/app/dashboard/src/pages/hosts/table/index.tsx
@@ -1,222 +1,49 @@
import {
- Accordion,
- AccordionButton,
- AccordionItem,
- AccordionPanel,
Box,
HStack,
- IconButton,
Table,
TableProps,
Tbody,
Td,
- Text,
Th,
Thead,
- Tooltip,
Tr,
- useBreakpointValue,
- VStack,
} from '@chakra-ui/react';
import classNames from 'classnames';
-import { FC, Fragment, useEffect, useState } from 'react';
+import { FC, useEffect, } from 'react';
import { useTranslation } from 'react-i18next';
import {
EmptySection,
- AccordionArrowIcon,
- EditIcon,
Sort,
handleSort,
} from 'components/table';
-import { useInbounds, fetchInboundHosts } from 'stores';
-import { useQuery } from 'react-query';
+import { useInbounds, HostType } from 'stores';
-type ExpandedIndex = number | number[];
-type NodesTableProps = {} & TableProps;
+type NodesTableProps = {
+ hosts: HostType[];
+} & TableProps;
-export const HostsTable: FC<NodesTableProps> = (props) => {
+export const HostsTable: FC<NodesTableProps> = ({ hosts }) => {
const {
hostsFilters: filters,
selectedInbound,
+ refetchHosts,
+ onHostsFilterChange,
onEditingHost,
onCreatingHost,
- onHostsFilterChange,
- refetchHosts,
} = useInbounds();
- const { data: hosts } = useQuery('hosts', () => {
- if (selectedInbound !== null) {
- return fetchInboundHosts(selectedInbound.id);
- }
- });
- useEffect(() => { refetchHosts() }, [selectedInbound]);
+ useEffect(() => {
+ refetchHosts();
+ }, [selectedInbound]);
const { t } = useTranslation();
- const [selectedRow, setSelectedRow] = useState<ExpandedIndex | undefined>(
- undefined
- );
-
- const useTable = useBreakpointValue({ base: false, md: true });
-
- // TODO: Find a different mechancism to to detect ftiler
- // const isFiltered = users.length !== total;
- const toggleAccordion = (index: number) => {
- setSelectedRow(index === selectedRow ? undefined : index);
- };
return (
<Box id="hosts-table" borderTopStartRadius="0" overflowX={{ base: 'unset', md: 'unset' }} >
- <Accordion
- allowMultiple
- display={{ base: 'block', md: 'none' }}
- index={selectedRow}
- >
- <Table orientation="vertical" zIndex="docked" {...props}>
- <Thead zIndex="docked" position="relative">
- <Tr>
- <Th
- minW="220px"
- pl={4}
- pr={4}
- cursor={'pointer'}
- onClick={handleSort.bind(null, filters, 'remark', onHostsFilterChange)}
- >
- <HStack>
- <span>{t('remark')}</span>
- <Sort sort={filters.sort} column="remark" />
- </HStack>
- </Th>
- <Th
- minW="100px"
- cursor={'pointer'}
- pr={0}
- onClick={handleSort.bind(null, filters, 'address', onHostsFilterChange)}
- >
- <HStack>
- <span>{t('hostsTable.address')}</span>
- <Sort sort={filters.sort} column="address" />
- </HStack>
- </Th>
- <Th
- minW="100px"
- cursor={'pointer'}
- pr={0}
- onClick={handleSort.bind(null, filters, 'port', onHostsFilterChange)}
- >
- <HStack>
- <span>{t('hostsTable.port')}</span>
- <Sort sort={filters.sort} column="port" />
- </HStack>
- </Th>
- {/* Action buttons (certificate, core settings)*/}
- </Tr>
- </Thead>
- <Tbody>
- {!useTable && hosts?.map((host, i) => {
- return (
- <Fragment key={i}>
- <Tr
- onClick={toggleAccordion.bind(null, i)}
- cursor="pointer"
- >
- <Td borderBottom={0} minW="100px" pl={4} pr={4}>
- {host.remark}
- </Td>
- <Td borderBottom={0} minW="100px" pr={0}>
- {host.address}
- </Td>
- <Td p={0} borderBottom={0} w="32px" minW="32px">
- <AccordionArrowIcon
- color="gray.600"
- _dark={{
- color: 'gray.400',
- }}
- transition="transform .2s ease-out"
- transform={
- selectedRow === i ? 'rotate(180deg)' : '0deg'
- }
- />
- </Td>
- </Tr>
- <Tr
- className="collapsible"
- onClick={toggleAccordion.bind(null, i)}
- >
- <Td p={0} colSpan={4}>
- <AccordionItem border={0}>
- <AccordionButton display="none"></AccordionButton>
- <AccordionPanel
- border={0}
- cursor="pointer"
- px={6}
- py={3}
- >
- <VStack justifyContent="space-between" spacing="4">
- <VStack
- alignItems="flex-start"
- w="full"
- spacing={-1}
- >
- <Text
- textTransform="capitalize"
- fontSize="xs"
- fontWeight="bold"
- color="gray.600"
- _dark={{
- color: 'gray.400',
- }}
- >
- {t('nodesTable.ipPort')}
- </Text>
- <Box width="full" minW="230px">
- {host.port}
- </Box>
- </VStack>
- <HStack w="full" justifyContent="space-between">
- <HStack>
- <Tooltip
- label={t('nodesDialog.editNode')}
- placement="top"
- >
- <IconButton
- p="0 !important"
- aria-label="Edit user"
- bg="transparent"
- _dark={{
- _hover: {
- bg: 'gray.700',
- },
- }}
- size={{
- base: 'sm',
- md: 'md',
- }}
- onClick={(e) => {
- e.stopPropagation();
- onEditingHost(true, host);
- }}
- >
- <EditIcon />
- </IconButton>
- </Tooltip>
- </HStack>
- </HStack>
- </VStack>
- </AccordionPanel>
- </AccordionItem>
- </Td>
- </Tr>
- </Fragment>
- );
- })}
- </Tbody>
- </Table>
- </Accordion>
<Table
orientation="vertical"
- display={{ base: 'none', md: 'table' }}
- {...props}
>
<Thead zIndex="docked" position="relative">
<Tr>
@@ -257,7 +84,7 @@ export const HostsTable: FC<NodesTableProps> = (props) => {
</Tr>
</Thead>
<Tbody overflowY="auto">
- {useTable && hosts?.map((host, i) => {
+ {hosts?.map((host, i) => {
return (
<Tr
key={i}
diff --git a/app/dashboard/src/pages/nodes/modals/core-settings-modal.tsx b/app/dashboard/src/pages/nodes/modals/core-settings-modal.tsx
index fbbbe60..98767dd 100644
--- a/app/dashboard/src/pages/nodes/modals/core-settings-modal.tsx
+++ b/app/dashboard/src/pages/nodes/modals/core-settings-modal.tsx
@@ -32,7 +32,7 @@ import debounce from 'lodash.debounce';
import { FC, useCallback, useEffect, useRef, useState } from 'react';
import { Controller, useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
-import { useMutation } from 'react-query';
+import { useMutation } from '@tanstack/react-query';
import { ReadyState } from 'react-use-websocket';
import { useWebSocket } from 'react-use-websocket/dist/lib/use-websocket';
import { getAuthToken } from 'service/auth-storage';
@@ -169,8 +169,10 @@ const CoreSettingModalContent: FC = () => {
const status = getStatus(readyState.toString());
- const { mutate: handleRestartCore, isLoading: isRestarting } =
- useMutation(restartCore);
+ const {
+ mutate: handleRestartCore,
+ isPending: isRestarting
+ } = useMutation({ mutationFn: restartCore });
const handleOnSave = ({ config }: any) => {
updateConfig(config)
diff --git a/app/dashboard/src/pages/nodes/table/index.tsx b/app/dashboard/src/pages/nodes/table/index.tsx
index ecb5c6e..f951624 100644
--- a/app/dashboard/src/pages/nodes/table/index.tsx
+++ b/app/dashboard/src/pages/nodes/table/index.tsx
@@ -35,7 +35,7 @@ import {
handleSort,
} from 'components/table';
import { fetchNodes, useNodes } from 'stores';
-import { useQuery } from 'react-query';
+import { useQuery } from '@tanstack/react-query';
import { queryIds } from 'constants/query-ids';
type ExpandedIndex = number | number[];
@@ -49,7 +49,7 @@ export const NodesTable: FC<NodesTableProps> = (props) => {
onAddingNode,
onFilterChange,
} = useNodes();
- const { data: nodes } = useQuery(queryIds.nodes, () => { return fetchNodes() })
+ const { data: nodes } = useQuery({ queryKey: [queryIds.nodes], queryFn: fetchNodes });
const total = nodes?.length || 0;
const { t } = useTranslation();
diff --git a/app/dashboard/src/pages/services/dialog/index.tsx b/app/dashboard/src/pages/services/dialog/index.tsx
index e5e37fc..4f31a3d 100644
--- a/app/dashboard/src/pages/services/dialog/index.tsx
+++ b/app/dashboard/src/pages/services/dialog/index.tsx
@@ -33,7 +33,7 @@ import { InboundsField } from './inbounds-field';
import { NameField } from './name-field';
import { InboundType, Service, ServiceCreate } from 'types';
import { fetchInbounds, useInbounds, useServices } from 'stores';
-import { useQuery } from 'react-query';
+import { useQuery } from '@tanstack/react-query';
import { queryIds } from 'constants/query-ids';
const formatService = (service: Service): FormType => {
@@ -61,7 +61,7 @@ export const ServiceDialog: FC<ServiceDialogProps> = () => {
const { refetchInbounds } = useInbounds();
const {
data: inbounds,
- } = useQuery(queryIds.inbounds, () => fetchInbounds());
+ } = useQuery({ queryKey: [queryIds.inbounds], queryFn: () => fetchInbounds() });
const isEditing = !!editingService;
const isOpen = isCreatingNewService || isEditing;
const [loading, setLoading] = useState(false);
diff --git a/app/dashboard/src/pages/services/table.tsx b/app/dashboard/src/pages/services/table.tsx
index ba31756..281e6b4 100644
--- a/app/dashboard/src/pages/services/table.tsx
+++ b/app/dashboard/src/pages/services/table.tsx
@@ -1,6 +1,5 @@
import {
- Accordion,
Box,
HStack,
Table,
@@ -10,11 +9,10 @@ import {
Th,
Thead,
Tr,
- useBreakpointValue,
} from '@chakra-ui/react';
import classNames from 'classnames';
-import { FC, useState, } from 'react';
+import { FC } from 'react';
import { useTranslation } from 'react-i18next';
import {
EmptySection,
@@ -24,10 +22,9 @@ import {
} from 'components/table';
import { fetchServices, useServices } from 'stores';
import { pageSizeManagers } from 'utils/userPreferenceStorage';
-import { useQuery } from 'react-query';
+import { useQuery } from '@tanstack/react-query';
import { queryIds } from 'constants/query-ids';
-type ExpandedIndex = number | number[];
type ServicesTableProps = {} & TableProps;
export const ServicesTable: FC<ServicesTableProps> = (props) => {
@@ -37,94 +34,14 @@ export const ServicesTable: FC<ServicesTableProps> = (props) => {
onCreateService,
onFilterChange,
} = useServices();
- const { data: services } = useQuery(queryIds.services, () => { return fetchServices(filters) });
- const useTable = useBreakpointValue({ base: false, md: true });
+ const { data: services } = useQuery({
+ queryKey: [queryIds.services],
+ queryFn: () => { return fetchServices(filters) }
+ });
const { t } = useTranslation();
- const [selectedRow,] = useState<ExpandedIndex | undefined>(
- undefined
- );
return (
<Box id="services-table" h="45vh" overflowX={{ base: 'unset', md: 'unset' }} >
- <Accordion
- allowMultiple
- display={{ base: 'block', md: 'none' }}
- index={selectedRow}
- >
- <Table orientation="vertical" zIndex="docked" {...props}>
- <Thead zIndex="docked" position="relative">
- <Tr>
- <Tr>
- <Th
- position="sticky"
- minW="200px"
- pl={4}
- pr={4}
- cursor={'pointer'}
- onClick={handleSort.bind(null, filters, 'name', onFilterChange)}
- >
- <HStack>
- <span>{t('services')}</span>
- <Sort sort={filters.sort} column="name" />
- </HStack>
- </Th>
- <Th
- position="sticky"
- minW="50px"
- cursor={'pointer'}
- pr={0}
- onClick={handleSort.bind(null, filters, 'users_number', onFilterChange)}
- >
- <HStack>
- <span>{t('servicesTable.usersNumber')}</span>
- <Sort sort={filters.sort} column="users_number" />
- </HStack>
- </Th>
- <Th
- position="sticky"
- minW="50px"
- cursor={'pointer'}
- pr={0}
- onClick={handleSort.bind(null, filters, 'inbounds_number', onFilterChange)}
- >
- <HStack>
- <span>{t('servicesTable.inboundsNumber')}</span>
- <Sort sort={filters.sort} column="inbounds_number" />
- </HStack>
- </Th>
- </Tr>
- </Tr>
- </Thead>
- <Tbody>
- {!useTable &&
- services?.map((service, i) => {
- return (
- <Tr
- cursor="pointer"
- key={service.id}
- className={classNames('interactive', {
- 'last-row': i === (services.length - 1),
- })}
- onClick={(e) => {
- e.stopPropagation();
- onEditingService(service);
- }}
- >
- <Td borderBottom={0} minW="100px" pl={4} pr={4}>
- {service.name}
- </Td>
- <Td borderBottom={0} minW="50px" pl={6} pr={6}>
- {service.users.length}
- </Td>
- <Td borderBottom={0} minW="50px" pr={0}>
- {service.inbounds.length}
- </Td>
- </Tr>
- );
- })}
- </Tbody>
- </Table>
- </Accordion>
<Table orientation="vertical" zIndex="docked" {...props}>
<Thead zIndex="docked" position="relative">
<Tr>
@@ -168,32 +85,31 @@ export const ServicesTable: FC<ServicesTableProps> = (props) => {
</Tr>
</Thead>
<Tbody>
- {useTable &&
- services?.map((service, i) => {
- return (
- <Tr
- cursor="pointer"
- key={service.id}
- className={classNames('interactive', {
- 'last-row': i === (services.length - 1),
- })}
- onClick={(e) => {
- e.stopPropagation();
- onEditingService(service);
- }}
- >
- <Td borderBottom={0} minW="100px" pl={4} pr={4}>
- {service.name}
- </Td>
- <Td borderBottom={0} minW="50px" pl={6} pr={6}>
- {service.users.length}
- </Td>
- <Td borderBottom={0} minW="50px" pr={0}>
- {service.inbounds.length}
- </Td>
- </Tr>
- );
- })}
+ {services?.map((service, i) => {
+ return (
+ <Tr
+ cursor="pointer"
+ key={service.id}
+ className={classNames('interactive', {
+ 'last-row': i === (services.length - 1),
+ })}
+ onClick={(e) => {
+ e.stopPropagation();
+ onEditingService(service);
+ }}
+ >
+ <Td borderBottom={0} minW="100px" pl={4} pr={4}>
+ {service.name}
+ </Td>
+ <Td borderBottom={0} minW="50px" pl={6} pr={6}>
+ {service.users.length}
+ </Td>
+ <Td borderBottom={0} minW="50px" pr={0}>
+ {service.inbounds.length}
+ </Td>
+ </Tr>
+ );
+ })}
{services?.length == 0 && (
<Tr>
<Td colSpan={3}>
diff --git a/app/dashboard/src/pages/users/dialog/index.tsx b/app/dashboard/src/pages/users/dialog/index.tsx
index e9022a0..d2ac165 100644
--- a/app/dashboard/src/pages/users/dialog/index.tsx
+++ b/app/dashboard/src/pages/users/dialog/index.tsx
@@ -51,7 +51,7 @@ import { ExpireDateField } from './expire-date-field';
import { NoteField } from './note-field';
import { useUsers, useServices, fetchServices } from 'stores';
import { queryIds } from 'constants/query-ids';
-import { useQuery } from 'react-query';
+import { useQuery } from '@tanstack/react-query';
const formatUser = (user: User): FormType => {
const services: number[] = user.services.map((service: number | Service): number => {
@@ -81,7 +81,7 @@ export const UserDialog: FC<UserDialogProps> = () => {
} = useUsers();
const { refetchServices, servicesFilters: filters } = useServices();
const { data: services } = useQuery({
- queryKey: queryIds.services,
+ queryKey: [queryIds.services],
initialData: [],
queryFn: () => {
return fetchServices(filters);
diff --git a/app/dashboard/src/pages/users/table/index.tsx b/app/dashboard/src/pages/users/table/index.tsx
index d19e863..123ada5 100644
--- a/app/dashboard/src/pages/users/table/index.tsx
+++ b/app/dashboard/src/pages/users/table/index.tsx
@@ -39,7 +39,7 @@ import { UsageSlider, UsageSliderCompact } from './usage-slider';
import { fetchUsers, useUsers } from 'stores';
import { ActionButtons } from './action-buttons';
import { pageSizeManagers } from 'utils/userPreferenceStorage';
-import { useQuery } from 'react-query';
+import { useQuery } from '@tanstack/react-query';
import { User } from 'types';
import { queryIds } from 'constants/query-ids';
@@ -55,7 +55,7 @@ export const UsersTable: FC<UsersTableProps> = (props) => {
onFilterChange,
} = useUsers();
const { data } = useQuery({
- queryKey: queryIds.users,
+ queryKey: [queryIds.users],
queryFn: () => { return fetchUsers(filters) },
initialData: { total: 0, users: [] },
});
diff --git a/app/dashboard/src/service/react-query.ts b/app/dashboard/src/service/react-query.ts
index 0fd037d..6d46de5 100644
--- a/app/dashboard/src/service/react-query.ts
+++ b/app/dashboard/src/service/react-query.ts
@@ -1,3 +1,3 @@
-import { QueryClient } from 'react-query';
+import { QueryClient } from '@tanstack/react-query';
export const queryClient = new QueryClient();
diff --git a/app/dashboard/src/stores/inbounds.store.ts b/app/dashboard/src/stores/inbounds.store.ts
index b8645d9..4f9ce72 100644
--- a/app/dashboard/src/stores/inbounds.store.ts
+++ b/app/dashboard/src/stores/inbounds.store.ts
@@ -44,10 +44,12 @@ export const fetchInbounds = async () => {
});
};
-export const fetchInboundHosts = async (id: number): Promise<Hosts> => {
+export const fetchInboundHosts = async (id: number | undefined): Promise<Hosts> => {
useDashboard.setState({ loading: true });
return fetch(`/inbounds/${id}/hosts`)
- .then((hosts: Hosts) => hosts)
+ .then((hosts: Hosts) => {
+ return hosts
+ })
.finally(() => {
useDashboard.setState({ loading: false });
});
@@ -58,7 +60,7 @@ type InboundsStateType = {
loading: boolean;
setLoading: (value: boolean) => void;
// Inbounds
- selectedInbound: InboundType | null;
+ selectedInbound: InboundType | undefined;
selectInbound: (inbound: InboundType) => void;
refetchInbounds: () => void;
// Hosts
@@ -83,7 +85,7 @@ type InboundsStateType = {
export const useInbounds = create(
subscribeWithSelector<InboundsStateType>((set, get) => ({
selectedHost: null,
- selectedInbound: null,
+ selectedInbound: undefined,
loading: false,
isDeletingHost: false,
isCreatingHost: false,
@@ -118,10 +120,10 @@ export const useInbounds = create(
set({ loading: value })
},
refetchInbounds: () => {
- queryClient.invalidateQueries(queryIds.inbounds);
+ queryClient.invalidateQueries({ queryKey: [queryIds.inbounds] });
},
- refetchHosts: () => {
- queryClient.invalidateQueries(queryIds.hosts);
+ refetchHosts: async () => {
+ queryClient.invalidateQueries({ queryKey: [queryIds.hosts] });
},
selectInbound: (host): void => {
set({ selectedInbound: host })
@@ -149,7 +151,7 @@ export const useInbounds = create(
createHost: async (inboundId: number | undefined, body: HostSchema): Promise<boolean | void> => {
if (inboundId !== undefined) {
useDashboard.setState({ loading: true });
- return fetch(`/inbounds/${inboundId}/hosts/${body}`, { method: 'POST', body })
+ return fetch(`/inbounds/${inboundId}/hosts`, { method: 'POST', body })
.then(() => true)
.catch(() => false)
.finally(() => {
diff --git a/app/dashboard/src/stores/nodes.store.ts b/app/dashboard/src/stores/nodes.store.ts
index 9cecb45..91b747e 100644
--- a/app/dashboard/src/stores/nodes.store.ts
+++ b/app/dashboard/src/stores/nodes.store.ts
@@ -1,4 +1,4 @@
-import { useQuery } from 'react-query';
+import { useQuery } from '@tanstack/react-query';
import { fetch } from 'service/http';
import { create } from 'zustand';
import { z } from 'zod';
@@ -95,9 +95,11 @@ export type NodeStore = {
onShowingNodesUsage: (isShowingNodesUsage: boolean) => void;
};
+// TODO: Refactor all useQuery for inbounds, hosts,
+// services and uses to be a custom hook like this.
export const useNodesQuery = () => {
return useQuery({
- queryKey: FetchNodesQueryKey,
+ queryKey: [FetchNodesQueryKey],
queryFn: fetchNodes,
refetchInterval: useNodes.getState().editingNode ? 3000 : undefined,
refetchOnWindowFocus: false,
@@ -130,7 +132,7 @@ export const useNodes = create<NodeStore>((set, get) => ({
return fetch('/nodes', { method: 'POST', body }).then(() => { get().refetchCertificate() });
},
refetchNodes: () => {
- queryClient.invalidateQueries(queryIds.nodes);
+ queryClient.invalidateQueries({ queryKey: [queryIds.nodes] });
},
refetchCertificate: () => {
fetch('/nodes/settings').then((res) => {
diff --git a/app/dashboard/src/stores/services.store.ts b/app/dashboard/src/stores/services.store.ts
index a4a088e..b76f795 100644
--- a/app/dashboard/src/stores/services.store.ts
+++ b/app/dashboard/src/stores/services.store.ts
@@ -1,5 +1,4 @@
import { queryClient } from 'service/react-query';
-import { StatisticsQueryKey } from 'components/statistics-card';
import { Service, ServiceCreate } from 'types/service';
import { pageSizeManagers } from 'utils/userPreferenceStorage';
import { create } from 'zustand';
@@ -60,7 +59,7 @@ export const useServices = create(
get().refetchServices();
},
refetchServices: () => {
- queryClient.invalidateQueries(queryIds.services);
+ queryClient.invalidateQueries({ queryKey: [queryIds.services] });
},
onCreateService: (isCreatingNewService) => set({ isCreatingNewService }),
onEditingService: (editingService) => {
@@ -75,14 +74,12 @@ export const useServices = create(
set({ deletingService: null });
get().refetchServices();
useUsers.getState().refetchUsers();
- queryClient.invalidateQueries(StatisticsQueryKey);
});
},
createService: async (body: ServiceCreate) => {
return fetch('/services', { method: 'POST', body }).then(() => {
set({ editingService: null });
get().refetchServices();
- queryClient.invalidateQueries(StatisticsQueryKey);
});
},
editService: async (body: ServiceCreate) => {
diff --git a/app/dashboard/src/stores/users.store.ts b/app/dashboard/src/stores/users.store.ts
index 8a4c3df..e8c2238 100644
--- a/app/dashboard/src/stores/users.store.ts
+++ b/app/dashboard/src/stores/users.store.ts
@@ -79,7 +79,7 @@ export const useUsers = create(subscribeWithSelector<UsersStateType>((set, get)
set({ subscribeUrl });
},
refetchUsers: () => {
- queryClient.invalidateQueries(queryIds.users);
+ queryClient.invalidateQueries({ queryKey: [queryIds.users] });
},
resetAllUsage: async () => {
return fetch('/users/reset', { method: 'POST' }).then(() => {
@@ -109,7 +109,7 @@ export const useUsers = create(subscribeWithSelector<UsersStateType>((set, get)
return fetch(`/users/${user.username}`, { method: 'DELETE' }).then(() => {
set({ deletingUser: null });
get().refetchUsers();
- queryClient.invalidateQueries(StatisticsQueryKey);
+ queryClient.invalidateQueries({ queryKey: [StatisticsQueryKey] });
});
},
createUser: async (body: UserCreate) => {
@@ -117,7 +117,7 @@ export const useUsers = create(subscribeWithSelector<UsersStateType>((set, get)
set({ editingUser: null });
get().refetchUsers();
useServices.getState().refetchServices();
- queryClient.invalidateQueries(StatisticsQueryKey);
+ queryClient.invalidateQueries({ queryKey: [StatisticsQueryKey] });
});
},
editUser: async (body: UserCreate) => {
diff --git a/makefile b/makefile
index 4b63f02..053e7a4 100644
--- a/makefile
+++ b/makefile
@@ -3,11 +3,11 @@ start:
alembic upgrade head
python main.py
-dashboard-build:
- cd app/dashboard/
+install-deps:
npm install --prefix './app/dashboard'
+
+dashboard-build: install-deps
VITE_BASE_API=/api/ npm run build --prefix './app/dashboard' --if-present -- --outDir dist --base '/dashboard/'
- ls
cp ./app/dashboard/dist/index.html ./app/dashboard/dist/404.html
dashboard-dev:
@@ -17,5 +17,13 @@ dashboard-dev:
--base ./app/dashboard/ \
--clearScreen false
+dashboard-preview:
+ VITE_BASE_API=http://0.0.0.0:8000/api/ npm run preview \
+ --prefix './app/dashboard/' \
+ -- --host 0.0.0.0 \
+ --base ./app/dashboard/ \
+ --clearScreen false
+
+
dashboard-cleanup:
rm -rf app/dashboard/node_modules/