mirror of
https://github.com/remnawave/frontend.git
synced 2026-05-13 12:16:40 +00:00
chore: update @remnawave/backend-contract to version 2.6.23 and enhance node plugins functionality
This commit is contained in:
parent
a47c497263
commit
abb257217f
8 changed files with 194 additions and 14 deletions
8
package-lock.json
generated
8
package-lock.json
generated
|
|
@ -35,7 +35,7 @@
|
|||
"@monaco-editor/react": "^4.7.0",
|
||||
"@noble/post-quantum": "^0.5.4",
|
||||
"@paralleldrive/cuid2": "3.3.0",
|
||||
"@remnawave/backend-contract": "2.6.22",
|
||||
"@remnawave/backend-contract": "2.6.23",
|
||||
"@remnawave/node-plugins": "^0.1.0",
|
||||
"@remnawave/subscription-page-types": "0.4.0",
|
||||
"@simplewebauthn/browser": "^13.2.2",
|
||||
|
|
@ -2988,9 +2988,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@remnawave/backend-contract": {
|
||||
"version": "2.6.22",
|
||||
"resolved": "https://registry.npmjs.org/@remnawave/backend-contract/-/backend-contract-2.6.22.tgz",
|
||||
"integrity": "sha512-1dZIX7/NSX/Y828oFpc5kBbrlccIyH3ZnmGMpV/B7dmFIi5/4Zu+5Ob3iCe9L2mRxzdK0OmzNIORaOogL6p/fA==",
|
||||
"version": "2.6.23",
|
||||
"resolved": "https://registry.npmjs.org/@remnawave/backend-contract/-/backend-contract-2.6.23.tgz",
|
||||
"integrity": "sha512-aJr+At5vUP+nUShMEmJV3+lbiBfVRKtWIS53iKXJS/qxYR+8srg/fTfKJFvsWr/DtuBd0Jy6v+4l2a75ecL/FA==",
|
||||
"license": "AGPL-3.0-only",
|
||||
"dependencies": {
|
||||
"zod": "3.25.76"
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
"@monaco-editor/react": "^4.7.0",
|
||||
"@noble/post-quantum": "^0.5.4",
|
||||
"@paralleldrive/cuid2": "3.3.0",
|
||||
"@remnawave/backend-contract": "2.6.22",
|
||||
"@remnawave/backend-contract": "2.6.23",
|
||||
"@remnawave/node-plugins": "^0.1.0",
|
||||
"@remnawave/subscription-page-types": "0.4.0",
|
||||
"@simplewebauthn/browser": "^13.2.2",
|
||||
|
|
|
|||
|
|
@ -1972,5 +1972,25 @@
|
|||
"nothing-found": "Nothing found...",
|
||||
"select-plugin": "Select plugin"
|
||||
}
|
||||
},
|
||||
"node-plugin-card": {
|
||||
"widget": {
|
||||
"active-on-nodes": "Active on Nodes"
|
||||
}
|
||||
},
|
||||
"adtive-on-nodes": {
|
||||
"modal": {
|
||||
"shared": {
|
||||
"this-plugin-is-not-active-on-any-nodes": "This plugin is not active on any nodes."
|
||||
}
|
||||
}
|
||||
},
|
||||
"node-plugins-grid": {
|
||||
"widget": {
|
||||
"node-plugins-are-an-advanced-feature-please-review-the-documentation-before-use": "Node Plugins are an advanced feature. Please review the documentation before use.",
|
||||
"warning": "Warning",
|
||||
"no-node-plugins-yet": "No node plugins yet",
|
||||
"create-a-plugin-to-extend-node-capabilities-with": "Create a plugin to extend node capabilities with"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { GetNodePluginsCommand } from '@remnawave/backend-contract'
|
||||
import { GetAllNodesCommand, GetNodePluginsCommand } from '@remnawave/backend-contract'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { TbFile } from 'react-icons/tb'
|
||||
import { motion } from 'motion/react'
|
||||
|
|
@ -10,11 +10,12 @@ import { RenameModalShared } from '@shared/ui/modals/rename-modal.shared'
|
|||
import { Page, PageHeaderShared } from '@shared/ui'
|
||||
|
||||
interface Props {
|
||||
nodes: GetAllNodesCommand.Response['response']
|
||||
plugins: GetNodePluginsCommand.Response['response']['nodePlugins']
|
||||
}
|
||||
|
||||
export const NodePluginsBasePageComponent = (props: Props) => {
|
||||
const { plugins } = props
|
||||
const { nodes, plugins } = props
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
|
|
@ -30,7 +31,7 @@ export const NodePluginsBasePageComponent = (props: Props) => {
|
|||
initial={{ opacity: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
>
|
||||
<NodePluginsGridWidget plugins={plugins} />
|
||||
<NodePluginsGridWidget nodes={nodes} plugins={plugins} />
|
||||
</motion.div>
|
||||
|
||||
<NodePluginsSpotlightWidget plugins={plugins} />
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
import { useGetNodePlugins } from '@shared/api/hooks'
|
||||
import { useGetNodePlugins, useGetNodes } from '@shared/api/hooks'
|
||||
import { LoadingScreen } from '@shared/ui'
|
||||
|
||||
import { NodePluginsBasePageComponent } from '../components/node-plugins-base-page.component'
|
||||
|
||||
export function NodePluginsBasePageConnector() {
|
||||
const { data: plugins, isLoading: isPluginsLoading } = useGetNodePlugins({})
|
||||
const { data: nodes, isLoading: isNodesLoading } = useGetNodes()
|
||||
|
||||
if (isPluginsLoading || !plugins) {
|
||||
if (isPluginsLoading || isNodesLoading || !plugins || !nodes) {
|
||||
return <LoadingScreen text="Loading node plugins..." />
|
||||
}
|
||||
|
||||
return <NodePluginsBasePageComponent plugins={plugins.nodePlugins} />
|
||||
return <NodePluginsBasePageComponent nodes={nodes} plugins={plugins.nodePlugins} />
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
import { GetAllNodesCommand } from '@remnawave/backend-contract'
|
||||
import { Center, Stack, Text, ThemeIcon } from '@mantine/core'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { TbServer } from 'react-icons/tb'
|
||||
import { PiCpu } from 'react-icons/pi'
|
||||
|
||||
import { BaseOverlayHeader } from '@shared/ui/overlays/base-overlay-header'
|
||||
import { SectionCard } from '@shared/ui/section-card'
|
||||
|
||||
interface IProps {
|
||||
nodes: GetAllNodesCommand.Response['response']
|
||||
}
|
||||
|
||||
export const ActivePluginsOnNodesModalShared = (props: IProps) => {
|
||||
const { nodes } = props
|
||||
const { t } = useTranslation()
|
||||
|
||||
if (nodes.length === 0) {
|
||||
return (
|
||||
<Center py="xl">
|
||||
<Stack align="center" gap="sm">
|
||||
<ThemeIcon size="xl" variant="gradient-gray">
|
||||
<PiCpu size={24} />
|
||||
</ThemeIcon>
|
||||
<Text c="dimmed" size="sm" ta="center">
|
||||
{t('adtive-on-nodes.modal.shared.this-plugin-is-not-active-on-any-nodes')}
|
||||
</Text>
|
||||
</Stack>
|
||||
</Center>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack gap="md">
|
||||
<SectionCard.Root gap="xs">
|
||||
{nodes.map((node) => (
|
||||
<SectionCard.Section key={node.uuid}>
|
||||
<BaseOverlayHeader
|
||||
countryCode={node.countryCode}
|
||||
IconComponent={TbServer}
|
||||
iconVariant="gradient-blue"
|
||||
subtitle={node.address}
|
||||
title={node.name}
|
||||
titleOrder={5}
|
||||
withCopy={true}
|
||||
/>
|
||||
</SectionCard.Section>
|
||||
))}
|
||||
</SectionCard.Root>
|
||||
</Stack>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { PiCheck, PiCopy, PiPencil, PiTrashDuotone } from 'react-icons/pi'
|
||||
import { PiCheck, PiCopy, PiCpu, PiPencil, PiTrashDuotone } from 'react-icons/pi'
|
||||
import { GetNodePluginsCommand } from '@remnawave/backend-contract'
|
||||
import { TbCopyCheck, TbEdit, TbPlug } from 'react-icons/tb'
|
||||
import { generatePath, useNavigate } from 'react-router-dom'
|
||||
|
|
@ -13,6 +13,7 @@ import { ROUTES } from '@shared/constants'
|
|||
interface IProps {
|
||||
handleCloneNodePlugin: (nodePluginUuid: string) => void
|
||||
handleDeleteNodePlugin: (nodePluginUuid: string) => void
|
||||
handleShowActiveNodes: (nodePluginUuid: string) => void
|
||||
isDragOverlay?: boolean
|
||||
nodePlugin: GetNodePluginsCommand.Response['response']['nodePlugins'][number]
|
||||
}
|
||||
|
|
@ -22,6 +23,7 @@ export function NodePluginCardWidget(props: IProps) {
|
|||
nodePlugin,
|
||||
handleDeleteNodePlugin,
|
||||
handleCloneNodePlugin,
|
||||
handleShowActiveNodes,
|
||||
isDragOverlay = false
|
||||
} = props
|
||||
|
||||
|
|
@ -75,6 +77,13 @@ export function NodePluginCardWidget(props: IProps) {
|
|||
)}
|
||||
</CopyButton>
|
||||
|
||||
<Menu.Item
|
||||
leftSection={<PiCpu size={18} />}
|
||||
onClick={() => handleShowActiveNodes(nodePlugin.uuid)}
|
||||
>
|
||||
{t('node-plugin-card.widget.active-on-nodes')}
|
||||
</Menu.Item>
|
||||
|
||||
<Menu.Item
|
||||
leftSection={<PiPencil size={18} />}
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { GetNodePluginsCommand } from '@remnawave/backend-contract'
|
||||
import { TbAlertTriangle, TbFlame, TbPlug, TbPlugConnectedX, TbShieldX } from 'react-icons/tb'
|
||||
import { GetAllNodesCommand, GetNodePluginsCommand } from '@remnawave/backend-contract'
|
||||
import { Badge, Center, Group, Stack, Text, ThemeIcon } from '@mantine/core'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { modals } from '@mantine/modals'
|
||||
|
||||
|
|
@ -8,18 +10,22 @@ import {
|
|||
useDeleteNodePlugin,
|
||||
useReorderNodePlugins
|
||||
} from '@shared/api/hooks'
|
||||
import { BaseOverlayHeader } from '@shared/ui/overlays/base-overlay-header'
|
||||
import { VirtualizedDndGrid } from '@shared/ui/virtualized-dnd-grid'
|
||||
import { queryClient } from '@shared/api/query-client'
|
||||
import { SectionCard } from '@shared/ui/section-card'
|
||||
|
||||
import { ActivePluginsOnNodesModalShared } from '../active-on-nodes-modal/adtive-on-nodes.modal.shared'
|
||||
import { NodePluginCardWidget } from '../node-plugin-card/node-plugin-card.widget'
|
||||
|
||||
interface IProps {
|
||||
nodes: GetAllNodesCommand.Response['response']
|
||||
plugins: GetNodePluginsCommand.Response['response']['nodePlugins']
|
||||
}
|
||||
|
||||
export function NodePluginsGridWidget(props: IProps) {
|
||||
const { t } = useTranslation()
|
||||
const { plugins } = props
|
||||
const { nodes, plugins } = props
|
||||
|
||||
const { mutate: deleteNodePlugin } = useDeleteNodePlugin({
|
||||
mutationFns: {
|
||||
|
|
@ -27,6 +33,9 @@ export function NodePluginsGridWidget(props: IProps) {
|
|||
queryClient.refetchQueries({
|
||||
queryKey: QueryKeys.nodePlugins.getNodePlugins.queryKey
|
||||
})
|
||||
queryClient.refetchQueries({
|
||||
queryKey: QueryKeys.nodes.getAllNodes.queryKey
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -88,6 +97,92 @@ export function NodePluginsGridWidget(props: IProps) {
|
|||
})
|
||||
}
|
||||
|
||||
const handleShowActiveNodes = (nodePluginUuid: string) => {
|
||||
const activeOnNodes = nodes.filter((node) => node.activePluginUuid === nodePluginUuid)
|
||||
|
||||
modals.open({
|
||||
children: <ActivePluginsOnNodesModalShared nodes={activeOnNodes} />,
|
||||
title: (
|
||||
<BaseOverlayHeader
|
||||
IconComponent={TbPlug}
|
||||
iconVariant="gradient-teal"
|
||||
title={t('node-plugin-card.widget.active-on-nodes')}
|
||||
titleOrder={5}
|
||||
/>
|
||||
),
|
||||
size: 'lg',
|
||||
centered: true
|
||||
})
|
||||
}
|
||||
|
||||
if (!plugins || plugins.length === 0) {
|
||||
return (
|
||||
<SectionCard.Root p="xl">
|
||||
<SectionCard.Section>
|
||||
<BaseOverlayHeader
|
||||
IconComponent={TbAlertTriangle}
|
||||
iconVariant="gradient-orange"
|
||||
subtitle={t(
|
||||
'node-plugins-grid.widget.node-plugins-are-an-advanced-feature-please-review-the-documentation-before-use'
|
||||
)}
|
||||
title={t('node-plugins-grid.widget.warning')}
|
||||
titleOrder={4}
|
||||
/>
|
||||
</SectionCard.Section>
|
||||
|
||||
<SectionCard.Section>
|
||||
<Center py="xl">
|
||||
<Stack align="center" gap="lg">
|
||||
<ThemeIcon radius="xl" size={64} variant="gradient-gray">
|
||||
<TbPlug size={32} />
|
||||
</ThemeIcon>
|
||||
|
||||
<Stack align="center" gap="xs">
|
||||
<Text fw={600} size="lg" ta="center">
|
||||
{t('node-plugins-grid.widget.no-node-plugins-yet')}
|
||||
</Text>
|
||||
<Text c="dimmed" maw={400} size="sm" ta="center">
|
||||
{t(
|
||||
'node-plugins-grid.widget.create-a-plugin-to-extend-node-capabilities-with'
|
||||
)}
|
||||
</Text>
|
||||
</Stack>
|
||||
|
||||
<Group gap="sm" justify="center">
|
||||
<Badge
|
||||
leftSection={<TbFlame size={12} />}
|
||||
radius="md"
|
||||
size="lg"
|
||||
variant="light"
|
||||
>
|
||||
Torrent Blocker
|
||||
</Badge>
|
||||
<Badge
|
||||
color="red"
|
||||
leftSection={<TbShieldX size={12} />}
|
||||
radius="md"
|
||||
size="lg"
|
||||
variant="light"
|
||||
>
|
||||
Blacklist
|
||||
</Badge>
|
||||
<Badge
|
||||
color="violet"
|
||||
leftSection={<TbPlugConnectedX size={12} />}
|
||||
radius="md"
|
||||
size="lg"
|
||||
variant="light"
|
||||
>
|
||||
Connection Drop
|
||||
</Badge>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Center>
|
||||
</SectionCard.Section>
|
||||
</SectionCard.Root>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<VirtualizedDndGrid
|
||||
enableDnd={true}
|
||||
|
|
@ -98,6 +193,7 @@ export function NodePluginsGridWidget(props: IProps) {
|
|||
<NodePluginCardWidget
|
||||
handleCloneNodePlugin={handleCloneNodePlugin}
|
||||
handleDeleteNodePlugin={handleDeleteNodePlugin}
|
||||
handleShowActiveNodes={handleShowActiveNodes}
|
||||
isDragOverlay
|
||||
nodePlugin={nodePlugin}
|
||||
/>
|
||||
|
|
@ -106,6 +202,7 @@ export function NodePluginsGridWidget(props: IProps) {
|
|||
<NodePluginCardWidget
|
||||
handleCloneNodePlugin={handleCloneNodePlugin}
|
||||
handleDeleteNodePlugin={handleDeleteNodePlugin}
|
||||
handleShowActiveNodes={handleShowActiveNodes}
|
||||
nodePlugin={nodePlugin}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue