Skip to content

Commit 2e5828a

Browse files
fix: Documentation URLs
1 parent 0401d74 commit 2e5828a

File tree

4 files changed

+188
-47
lines changed

4 files changed

+188
-47
lines changed

dashboard/src/components/layout/page-header.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Button } from '@/components/ui/button'
22
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
33
import useDirDetection from '@/hooks/use-dir-detection'
44
import { getDocsUrl } from '@/utils/docs-url'
5-
import { LucideIcon, Plus, HelpCircle } from 'lucide-react'
5+
import { HelpCircle, LucideIcon, Plus } from 'lucide-react'
66
import { useTranslation } from 'react-i18next'
77
import { useLocation } from 'react-router'
88

dashboard/src/pages/_dashboard.settings.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { cn } from '@/lib/utils'
44
import { useGetSettings, useModifySettings } from '@/service/api'
55
import { useQueryClient } from '@tanstack/react-query'
66
import { Bell, Database, ListTodo, LucideIcon, MessageCircle, Palette, Send, Settings as SettingsIcon, Webhook } from 'lucide-react'
7-
import { createContext, useContext, useMemo, useCallback } from 'react'
7+
import { createContext, useCallback, useContext, useMemo } from 'react'
88
import { useTranslation } from 'react-i18next'
99
import { Outlet, useLocation, useNavigate } from 'react-router'
1010
import { toast } from 'sonner'
@@ -209,18 +209,12 @@ export default function Settings() {
209209
[is_sudo, settings, isLoading, error, isSaving, handleUpdateSettings],
210210
)
211211

212-
// Generate tutorial URL for the current settings tab
213-
const getTutorialUrl = () => {
214-
const locale = i18n.language || 'en'
215-
return `https://docs.pasarguard.org/${locale}/panel/settings`
216-
}
217-
218212
// Always render the provider to ensure context is available for child routes
219213
// This prevents issues during HMR when components might render before parent is ready
220214
return (
221215
<SettingsContext.Provider value={settingsContextValue}>
222216
<div className="flex w-full flex-col items-start gap-0">
223-
<PageHeader title={t(`settings.${activeTab}.title`)} description="manageSettings" tutorialUrl={getTutorialUrl()} />
217+
<PageHeader title={t(`settings.${activeTab}.title`)} description="manageSettings" />
224218

225219
<div className="relative w-full">
226220
<div className="flex border-b">

dashboard/src/service/api/index.ts

Lines changed: 177 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -181,17 +181,17 @@ export type XrayMuxSettingsOutputXudpConcurrency = number | null
181181

182182
export type XrayMuxSettingsOutputConcurrency = number | null
183183

184+
export interface XrayMuxSettingsOutput {
185+
enabled?: boolean
186+
concurrency?: XrayMuxSettingsOutputConcurrency
187+
xudpConcurrency?: XrayMuxSettingsOutputXudpConcurrency
188+
xudpProxyUDP443?: Xudp
189+
}
190+
184191
export type XrayMuxSettingsInputXudpConcurrency = number | null
185192

186193
export type XrayMuxSettingsInputConcurrency = number | null
187194

188-
export interface XrayMuxSettingsInput {
189-
enabled?: boolean
190-
concurrency?: XrayMuxSettingsInputConcurrency
191-
xudp_concurrency?: XrayMuxSettingsInputXudpConcurrency
192-
xudp_proxy_udp_443?: Xudp
193-
}
194-
195195
export interface XrayFragmentSettings {
196196
/** @pattern ^(:?tlshello|[\d-]{1,16})$ */
197197
packets: string
@@ -210,11 +210,11 @@ export const Xudp = {
210210
skip: 'skip',
211211
} as const
212212

213-
export interface XrayMuxSettingsOutput {
213+
export interface XrayMuxSettingsInput {
214214
enabled?: boolean
215-
concurrency?: XrayMuxSettingsOutputConcurrency
216-
xudpConcurrency?: XrayMuxSettingsOutputXudpConcurrency
217-
xudpProxyUDP443?: Xudp
215+
concurrency?: XrayMuxSettingsInputConcurrency
216+
xudp_concurrency?: XrayMuxSettingsInputXudpConcurrency
217+
xudp_proxy_udp_443?: Xudp
218218
}
219219

220220
export type XTLSFlows = (typeof XTLSFlows)[keyof typeof XTLSFlows]
@@ -674,6 +674,13 @@ export interface UserModify {
674674
status?: UserModifyStatus
675675
}
676676

677+
/**
678+
* User IP lists for all nodes
679+
*/
680+
export interface UserIPListAll {
681+
nodes: UserIPListAllNodes
682+
}
683+
677684
export type UserIPListIps = { [key: string]: number }
678685

679686
/**
@@ -685,13 +692,6 @@ export interface UserIPList {
685692

686693
export type UserIPListAllNodes = { [key: string]: UserIPList | null }
687694

688-
/**
689-
* User IP lists for all nodes
690-
*/
691-
export interface UserIPListAll {
692-
nodes: UserIPListAllNodes
693-
}
694-
695695
export type UserCreateStatus = UserStatusCreate | null
696696

697697
export type UserCreateNextPlan = NextPlanModel | null
@@ -1114,19 +1114,6 @@ export type NotificationSettingsOutputTelegramChatId = number | null
11141114

11151115
export type NotificationSettingsOutputTelegramApiToken = string | null
11161116

1117-
export interface NotificationSettingsOutput {
1118-
notify_telegram?: boolean
1119-
notify_discord?: boolean
1120-
telegram_api_token?: NotificationSettingsOutputTelegramApiToken
1121-
telegram_chat_id?: NotificationSettingsOutputTelegramChatId
1122-
telegram_topic_id?: NotificationSettingsOutputTelegramTopicId
1123-
discord_webhook_url?: NotificationSettingsOutputDiscordWebhookUrl
1124-
channels?: NotificationChannels
1125-
proxy_url?: NotificationSettingsOutputProxyUrl
1126-
/** */
1127-
max_retries: number
1128-
}
1129-
11301117
export type NotificationSettingsInputProxyUrl = string | null
11311118

11321119
export type NotificationSettingsInputDiscordWebhookUrl = string | null
@@ -1175,6 +1162,19 @@ export interface NotificationChannels {
11751162
user_template?: NotificationChannel
11761163
}
11771164

1165+
export interface NotificationSettingsOutput {
1166+
notify_telegram?: boolean
1167+
notify_discord?: boolean
1168+
telegram_api_token?: NotificationSettingsOutputTelegramApiToken
1169+
telegram_chat_id?: NotificationSettingsOutputTelegramChatId
1170+
telegram_topic_id?: NotificationSettingsOutputTelegramTopicId
1171+
discord_webhook_url?: NotificationSettingsOutputDiscordWebhookUrl
1172+
channels?: NotificationChannels
1173+
proxy_url?: NotificationSettingsOutputProxyUrl
1174+
/** */
1175+
max_retries: number
1176+
}
1177+
11781178
export type NotificationChannelDiscordWebhookUrl = string | null
11791179

11801180
export type NotificationChannelTelegramTopicId = number | null
@@ -1272,6 +1272,7 @@ export interface NodeResponse {
12721272
name: string
12731273
address: string
12741274
port?: number
1275+
api_port?: number
12751276
/** */
12761277
usage_coefficient?: number
12771278
connection_type: NodeConnectionType
@@ -1342,8 +1343,6 @@ export type NodeModifyKeepAlive = number | null
13421343

13431344
export type NodeModifyServerCa = string | null
13441345

1345-
export type NodeModifyConnectionType = NodeConnectionType | null
1346-
13471346
export type NodeModifyUsageCoefficient = number | null
13481347

13491348
export type NodeModifyPort = number | null
@@ -1356,6 +1355,7 @@ export interface NodeModify {
13561355
name?: NodeModifyName
13571356
address?: NodeModifyAddress
13581357
port?: NodeModifyPort
1358+
api_port?: number
13591359
usage_coefficient?: NodeModifyUsageCoefficient
13601360
connection_type?: NodeModifyConnectionType
13611361
server_ca?: NodeModifyServerCa
@@ -1370,6 +1370,15 @@ export interface NodeModify {
13701370
status?: NodeModifyStatus
13711371
}
13721372

1373+
export interface NodeGeoFilesUpdate {
1374+
region?: GeoFilseRegion
1375+
}
1376+
1377+
export interface NodeCoreUpdate {
1378+
/** @pattern ^(latest|v?\d+\.\d+\.\d+)$ */
1379+
core_version?: string
1380+
}
1381+
13731382
export type NodeConnectionType = (typeof NodeConnectionType)[keyof typeof NodeConnectionType]
13741383

13751384
// eslint-disable-next-line @typescript-eslint/no-redeclare
@@ -1378,10 +1387,13 @@ export const NodeConnectionType = {
13781387
rest: 'rest',
13791388
} as const
13801389

1390+
export type NodeModifyConnectionType = NodeConnectionType | null
1391+
13811392
export interface NodeCreate {
13821393
name: string
13831394
address: string
13841395
port?: number
1396+
api_port?: number
13851397
/** */
13861398
usage_coefficient?: number
13871399
connection_type: NodeConnectionType
@@ -1575,6 +1587,15 @@ export interface GroupCreate {
15751587
is_disabled?: boolean
15761588
}
15771589

1590+
export type GeoFilseRegion = (typeof GeoFilseRegion)[keyof typeof GeoFilseRegion]
1591+
1592+
// eslint-disable-next-line @typescript-eslint/no-redeclare
1593+
export const GeoFilseRegion = {
1594+
iran: 'iran',
1595+
china: 'china',
1596+
russia: 'russia',
1597+
} as const
1598+
15781599
export interface General {
15791600
default_flow?: XTLSFlows
15801601
default_method?: ShadowsocksMethods
@@ -4384,6 +4405,129 @@ export const useRemoveNode = <TData = Awaited<ReturnType<typeof removeNode>>, TE
43844405
return useMutation(mutationOptions)
43854406
}
43864407

4408+
/**
4409+
* @summary Update Node
4410+
*/
4411+
export const updateNode = (nodeId: number, signal?: AbortSignal) => {
4412+
return orvalFetcher<unknown>({ url: `/api/node/${nodeId}/update`, method: 'POST', signal })
4413+
}
4414+
4415+
export const getUpdateNodeMutationOptions = <TData = Awaited<ReturnType<typeof updateNode>>, TError = ErrorType<Unauthorized | Forbidden | HTTPValidationError>, TContext = unknown>(options?: {
4416+
mutation?: UseMutationOptions<TData, TError, { nodeId: number }, TContext>
4417+
}) => {
4418+
const mutationKey = ['updateNode']
4419+
const { mutation: mutationOptions } = options
4420+
? options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey
4421+
? options
4422+
: { ...options, mutation: { ...options.mutation, mutationKey } }
4423+
: { mutation: { mutationKey } }
4424+
4425+
const mutationFn: MutationFunction<Awaited<ReturnType<typeof updateNode>>, { nodeId: number }> = props => {
4426+
const { nodeId } = props ?? {}
4427+
4428+
return updateNode(nodeId)
4429+
}
4430+
4431+
return { mutationFn, ...mutationOptions } as UseMutationOptions<TData, TError, { nodeId: number }, TContext>
4432+
}
4433+
4434+
export type UpdateNodeMutationResult = NonNullable<Awaited<ReturnType<typeof updateNode>>>
4435+
4436+
export type UpdateNodeMutationError = ErrorType<Unauthorized | Forbidden | HTTPValidationError>
4437+
4438+
/**
4439+
* @summary Update Node
4440+
*/
4441+
export const useUpdateNode = <TData = Awaited<ReturnType<typeof updateNode>>, TError = ErrorType<Unauthorized | Forbidden | HTTPValidationError>, TContext = unknown>(options?: {
4442+
mutation?: UseMutationOptions<TData, TError, { nodeId: number }, TContext>
4443+
}): UseMutationResult<TData, TError, { nodeId: number }, TContext> => {
4444+
const mutationOptions = getUpdateNodeMutationOptions(options)
4445+
4446+
return useMutation(mutationOptions)
4447+
}
4448+
4449+
/**
4450+
* @summary Update Core
4451+
*/
4452+
export const updateCore = (nodeId: number, nodeCoreUpdate: BodyType<NodeCoreUpdate>, signal?: AbortSignal) => {
4453+
return orvalFetcher<unknown>({ url: `/api/node/${nodeId}/core_update`, method: 'POST', headers: { 'Content-Type': 'application/json' }, data: nodeCoreUpdate, signal })
4454+
}
4455+
4456+
export const getUpdateCoreMutationOptions = <TData = Awaited<ReturnType<typeof updateCore>>, TError = ErrorType<Unauthorized | Forbidden | HTTPValidationError>, TContext = unknown>(options?: {
4457+
mutation?: UseMutationOptions<TData, TError, { nodeId: number; data: BodyType<NodeCoreUpdate> }, TContext>
4458+
}) => {
4459+
const mutationKey = ['updateCore']
4460+
const { mutation: mutationOptions } = options
4461+
? options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey
4462+
? options
4463+
: { ...options, mutation: { ...options.mutation, mutationKey } }
4464+
: { mutation: { mutationKey } }
4465+
4466+
const mutationFn: MutationFunction<Awaited<ReturnType<typeof updateCore>>, { nodeId: number; data: BodyType<NodeCoreUpdate> }> = props => {
4467+
const { nodeId, data } = props ?? {}
4468+
4469+
return updateCore(nodeId, data)
4470+
}
4471+
4472+
return { mutationFn, ...mutationOptions } as UseMutationOptions<TData, TError, { nodeId: number; data: BodyType<NodeCoreUpdate> }, TContext>
4473+
}
4474+
4475+
export type UpdateCoreMutationResult = NonNullable<Awaited<ReturnType<typeof updateCore>>>
4476+
export type UpdateCoreMutationBody = BodyType<NodeCoreUpdate>
4477+
export type UpdateCoreMutationError = ErrorType<Unauthorized | Forbidden | HTTPValidationError>
4478+
4479+
/**
4480+
* @summary Update Core
4481+
*/
4482+
export const useUpdateCore = <TData = Awaited<ReturnType<typeof updateCore>>, TError = ErrorType<Unauthorized | Forbidden | HTTPValidationError>, TContext = unknown>(options?: {
4483+
mutation?: UseMutationOptions<TData, TError, { nodeId: number; data: BodyType<NodeCoreUpdate> }, TContext>
4484+
}): UseMutationResult<TData, TError, { nodeId: number; data: BodyType<NodeCoreUpdate> }, TContext> => {
4485+
const mutationOptions = getUpdateCoreMutationOptions(options)
4486+
4487+
return useMutation(mutationOptions)
4488+
}
4489+
4490+
/**
4491+
* @summary Update Geofiles
4492+
*/
4493+
export const updateGeofiles = (nodeId: number, nodeGeoFilesUpdate: BodyType<NodeGeoFilesUpdate>, signal?: AbortSignal) => {
4494+
return orvalFetcher<unknown>({ url: `/api/node/${nodeId}/geofiles`, method: 'POST', headers: { 'Content-Type': 'application/json' }, data: nodeGeoFilesUpdate, signal })
4495+
}
4496+
4497+
export const getUpdateGeofilesMutationOptions = <TData = Awaited<ReturnType<typeof updateGeofiles>>, TError = ErrorType<Unauthorized | Forbidden | HTTPValidationError>, TContext = unknown>(options?: {
4498+
mutation?: UseMutationOptions<TData, TError, { nodeId: number; data: BodyType<NodeGeoFilesUpdate> }, TContext>
4499+
}) => {
4500+
const mutationKey = ['updateGeofiles']
4501+
const { mutation: mutationOptions } = options
4502+
? options.mutation && 'mutationKey' in options.mutation && options.mutation.mutationKey
4503+
? options
4504+
: { ...options, mutation: { ...options.mutation, mutationKey } }
4505+
: { mutation: { mutationKey } }
4506+
4507+
const mutationFn: MutationFunction<Awaited<ReturnType<typeof updateGeofiles>>, { nodeId: number; data: BodyType<NodeGeoFilesUpdate> }> = props => {
4508+
const { nodeId, data } = props ?? {}
4509+
4510+
return updateGeofiles(nodeId, data)
4511+
}
4512+
4513+
return { mutationFn, ...mutationOptions } as UseMutationOptions<TData, TError, { nodeId: number; data: BodyType<NodeGeoFilesUpdate> }, TContext>
4514+
}
4515+
4516+
export type UpdateGeofilesMutationResult = NonNullable<Awaited<ReturnType<typeof updateGeofiles>>>
4517+
export type UpdateGeofilesMutationBody = BodyType<NodeGeoFilesUpdate>
4518+
export type UpdateGeofilesMutationError = ErrorType<Unauthorized | Forbidden | HTTPValidationError>
4519+
4520+
/**
4521+
* @summary Update Geofiles
4522+
*/
4523+
export const useUpdateGeofiles = <TData = Awaited<ReturnType<typeof updateGeofiles>>, TError = ErrorType<Unauthorized | Forbidden | HTTPValidationError>, TContext = unknown>(options?: {
4524+
mutation?: UseMutationOptions<TData, TError, { nodeId: number; data: BodyType<NodeGeoFilesUpdate> }, TContext>
4525+
}): UseMutationResult<TData, TError, { nodeId: number; data: BodyType<NodeGeoFilesUpdate> }, TContext> => {
4526+
const mutationOptions = getUpdateGeofilesMutationOptions(options)
4527+
4528+
return useMutation(mutationOptions)
4529+
}
4530+
43874531
/**
43884532
* Reset node traffic usage (uplink and downlink).
43894533
Creates a log entry in node_usage_reset_logs table.

dashboard/src/utils/docs-url.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,24 @@ export function getDocsUrl(pagePath: string): string {
1515
const normalizedLocale = locale.split('-')[0]
1616

1717
// Special case: node documentation uses /node/ instead of /panel/nodes
18-
if (pagePath.startsWith('/nodes')) {
18+
if (pagePath === '/nodes') {
1919
return `${DOCUMENTATION}/${normalizedLocale}/node/`
2020
}
21-
21+
if (pagePath.startsWith('/settings')) {
22+
console.log(normalizedLocale)
23+
return `${DOCUMENTATION}/${normalizedLocale}/panel/settings`
24+
}
2225
// Map route paths to documentation paths
2326
const pathMap: Record<string, string> = {
2427
'/': 'dashboard',
2528
'/users': 'users',
2629
'/statistics': 'statistics',
27-
'/hosts': 'hosts',
30+
'/hosts': 'host',
2831
'/groups': 'groups',
29-
'/templates': 'templates',
32+
'/templates': 'user_template',
3033
'/admins': 'admins',
31-
'/settings': 'settings',
3234
'/bulk': 'bulk',
35+
'/nodes/cores': 'core',
3336
}
3437

3538
// Handle nested routes - find the longest matching route

0 commit comments

Comments
 (0)