From bcb9bd8a623a1f2afb985c2de120ce4bc6313a87 Mon Sep 17 00:00:00 2001 From: kastov Date: Mon, 6 Jul 2026 08:37:08 +0300 Subject: [PATCH] feat: display raw subscription --- .../external-squads-custom-remarks.widget.tsx | 20 +++++ .../connection-keys.drawer.tsx | 79 ++++++++++++++++--- .../api/hooks/users/users.query.hooks.ts | 16 +++- .../user-identification-card.tsx | 3 +- .../subscription-user-remarks-card.widget.tsx | 21 +++++ 5 files changed, 127 insertions(+), 12 deletions(-) diff --git a/src/shared/_modals/external-squads/external-squads-drawer/tabs/external-squads-custom-remarks.widget.tsx b/src/shared/_modals/external-squads/external-squads-drawer/tabs/external-squads-custom-remarks.widget.tsx index b397c6a2..19194d18 100644 --- a/src/shared/_modals/external-squads/external-squads-drawer/tabs/external-squads-custom-remarks.widget.tsx +++ b/src/shared/_modals/external-squads/external-squads-drawer/tabs/external-squads-custom-remarks.widget.tsx @@ -1,4 +1,6 @@ +import { CodeHighlight } from '@mantine/code-highlight' import { Button, Card, Group, Paper, Stack, Switch, Text, Transition } from '@mantine/core' +import { modals } from '@mantine/modals' import { notifications } from '@mantine/notifications' import { GetExternalSquadByUuidCommand } from '@remnawave/backend-contract' import { RemarksManager } from '@widgets/dashboard/subscription-settings/settings/cards/managers/remarks-manager.widget' @@ -9,6 +11,7 @@ import { TbDeviceFloppy, TbDevices2, TbListLetters, TbX } from 'react-icons/tb' import { queryClient } from '@shared/api' import { QueryKeys, useUpdateExternalSquad } from '@shared/api/hooks' +import { BaseOverlayHeader } from '@shared/ui/overlays/base-overlay-header' interface IProps { externalSquad: GetExternalSquadByUuidCommand.Response['response'] @@ -108,6 +111,23 @@ export const ExternalSquadsCustomRemarksTabWidget = (props: IProps) => { data ) } + }, + onError: (error) => { + if ((error.cause as unknown as { errorCode: string })?.errorCode === 'A231') { + modals.open({ + centered: true, + size: 'auto', + title: ( + + ), + children: + }) + } } } }) diff --git a/src/shared/_modals/users/connection-keys-drawer/connection-keys.drawer.tsx b/src/shared/_modals/users/connection-keys-drawer/connection-keys.drawer.tsx index 39cd52ea..a584d799 100644 --- a/src/shared/_modals/users/connection-keys-drawer/connection-keys.drawer.tsx +++ b/src/shared/_modals/users/connection-keys-drawer/connection-keys.drawer.tsx @@ -9,9 +9,13 @@ import { Stack, TextInput, ThemeIcon, - Text + Text, + Box, + Button } from '@mantine/core' import { modals } from '@mantine/modals' +import { githubDarkTheme } from 'json-edit-react' +import { JsonEditor } from 'json-edit-react' import { useTranslation } from 'react-i18next' import { PiEyeSlashDuotone, @@ -23,9 +27,10 @@ import { PiEmptyDuotone, PiQrCodeDuotone } from 'react-icons/pi' +import { TbJson } from 'react-icons/tb' import { useNiceMantineModal } from '@shared/_modals/use-nice-modal' -import { useGetConnectionKeysByUuid } from '@shared/api/hooks' +import { useGetConnectionKeysByUuid, useGetRawSubscription } from '@shared/api/hooks' import { LoaderModalShared } from '@shared/ui/loader-modal' import { BaseOverlayHeader } from '@shared/ui/overlays/base-overlay-header' import { QrCodeBuilder } from '@shared/ui/qr-code-builder' @@ -34,10 +39,11 @@ import { SectionCardSection } from '@shared/ui/section-card/section-card.section interface IProps { userUuid: string + shortUuid: string } export const ConnectionKeysDrawer = NiceModal.create((props: IProps) => { - const { userUuid } = props + const { userUuid, shortUuid } = props const { t } = useTranslation() const modal = useModal() @@ -52,6 +58,10 @@ export const ConnectionKeysDrawer = NiceModal.create((props: IProps) => { } }) + const { data: rawSubscription, isLoading: isRawSubscriptionLoading } = useGetRawSubscription({ + route: { shortUuid } + }) + const renderQrCode = (link: string, remark: string) => { modals.open({ centered: true, @@ -169,6 +179,44 @@ export const ConnectionKeysDrawer = NiceModal.create((props: IProps) => { ) } + const renderRawSubscription = () => { + modals.open({ + centered: true, + size: 'xl', + title: ( + + ), + children: ( + + { + if (path.length === 0) return false + if (path[0] !== 'resolvedProxyConfigs') return true + return path.length > 2 + }} + data={JSON.parse(JSON.stringify(rawSubscription))} + indent={4} + keySort={([a], [b]) => { + if (a === 'resolvedProxyConfigs') return -1 + if (b === 'resolvedProxyConfigs') return 1 + return 0 + }} + maxWidth="100%" + rootName="" + theme={githubDarkTheme} + viewOnly + jsonStringify={(data) => JSON.stringify(data)} + /> + + ) + }) + } + return ( { /> } > - {isLoading ? ( - - ) : ( - {renderLinks()} - )} + + + {isLoading ? ( + + ) : ( + {renderLinks()} + )} + ) }) diff --git a/src/shared/api/hooks/users/users.query.hooks.ts b/src/shared/api/hooks/users/users.query.hooks.ts index ebece094..8b5bb9b5 100644 --- a/src/shared/api/hooks/users/users.query.hooks.ts +++ b/src/shared/api/hooks/users/users.query.hooks.ts @@ -6,7 +6,8 @@ import { GetUserAccessibleNodesCommand, GetUserByUuidCommand, GetUserMetadataCommand, - GetUserSubscriptionRequestHistoryCommand + GetUserSubscriptionRequestHistoryCommand, + GetRawSubscriptionByShortUuidCommand } from '@remnawave/backend-contract' import { keepPreviousData } from '@tanstack/react-query' @@ -24,6 +25,9 @@ export const usersQueryKeys = createQueryKeys('users', { getConnectionKeysByUuid: (route: GetConnectionKeysByUuidCommand.RequestParam) => ({ queryKey: [route] }), + getRawSubscription: (route: GetRawSubscriptionByShortUuidCommand.RequestParam) => ({ + queryKey: [route] + }), getUserTags: { queryKey: null }, @@ -119,3 +123,13 @@ export const useGetUserMetadata = createGetQueryHook({ staleTime: sToMs(60) } }) + +export const useGetRawSubscription = createGetQueryHook({ + endpoint: GetRawSubscriptionByShortUuidCommand.TSQ_url, + responseSchema: GetRawSubscriptionByShortUuidCommand.ResponseSchema, + routeParamsSchema: GetRawSubscriptionByShortUuidCommand.RequestParamSchema, + getQueryKey: ({ route }) => usersQueryKeys.getRawSubscription(route!).queryKey, + rQueryParams: { + staleTime: sToMs(60) + } +}) diff --git a/src/shared/ui/forms/users/forms-components/user-identification-card.tsx b/src/shared/ui/forms/users/forms-components/user-identification-card.tsx index 2bcc005d..4226b39e 100644 --- a/src/shared/ui/forms/users/forms-components/user-identification-card.tsx +++ b/src/shared/ui/forms/users/forms-components/user-identification-card.tsx @@ -181,7 +181,8 @@ export const UserIdentificationCard = memo((props: IProps) => { color="teal" onClick={() => { showModal('users_connectionKeysDrawer', { - userUuid: user.uuid + userUuid: user.uuid, + shortUuid: user.shortUuid }) }} size="lg" diff --git a/src/widgets/dashboard/subscription-settings/settings/cards/subscription-user-remarks-card.widget.tsx b/src/widgets/dashboard/subscription-settings/settings/cards/subscription-user-remarks-card.widget.tsx index 7c8f39c8..a7039060 100644 --- a/src/widgets/dashboard/subscription-settings/settings/cards/subscription-user-remarks-card.widget.tsx +++ b/src/widgets/dashboard/subscription-settings/settings/cards/subscription-user-remarks-card.widget.tsx @@ -1,5 +1,7 @@ +import { CodeHighlight } from '@mantine/code-highlight' import { Button, Card, Group, Stack } from '@mantine/core' import { useForm, schemaResolver } from '@mantine/form' +import { modals } from '@mantine/modals' import { notifications } from '@mantine/notifications' import { UpdateSubscriptionSettingsCommand } from '@remnawave/backend-contract' import { useState } from 'react' @@ -10,6 +12,7 @@ import Masonry from 'react-layout-masonry' import { queryClient } from '@shared/api' import { QueryKeys, useUpdateSubscriptionSettings } from '@shared/api/hooks' +import { BaseOverlayHeader } from '@shared/ui/overlays/base-overlay-header' import { SettingsCardShared } from '@shared/ui/settings-card' import { handleFormErrors } from '@shared/utils/misc' @@ -77,6 +80,24 @@ export const SubscriptionUserRemarksCardWidget = (props: IProps) => { }, onError(error) { + if ((error.cause as unknown as { errorCode: string })?.errorCode === 'A231') { + modals.open({ + centered: true, + size: 'auto', + title: ( + + ), + children: + }) + + return + } + handleFormErrors(form, error) } }