mirror of
https://github.com/VaalaCat/frp-panel.git
synced 2025-09-26 19:31:18 +08:00
feat: rpc url and api url settings on front
This commit is contained in:
@@ -41,12 +41,12 @@ func FRPsAuthOption(cfg Config, isDefault bool) v1.HTTPPluginOptions {
|
||||
if isDefault {
|
||||
port = cfg.Master.APIPort
|
||||
} else {
|
||||
port = cfg.Master.InternalFRPAuthServerPort
|
||||
port = cfg.Server.InternalFRPAuthServerPort
|
||||
}
|
||||
authUrl, err := url.Parse(fmt.Sprintf("http://%s:%d%s",
|
||||
cfg.Master.InternalFRPAuthServerHost,
|
||||
cfg.Server.InternalFRPAuthServerHost,
|
||||
port,
|
||||
cfg.Master.InternalFRPAuthServerPath))
|
||||
cfg.Server.InternalFRPAuthServerPath))
|
||||
if err != nil {
|
||||
logger.Logger(context.Background()).WithError(err).Fatalf("parse auth url error")
|
||||
}
|
||||
|
@@ -29,19 +29,19 @@ type Config struct {
|
||||
GithubProxyUrl string `env:"GITHUB_PROXY_URL" env-default:"https://ghfast.top/" env-description:"github proxy url"`
|
||||
} `env-prefix:"APP_"`
|
||||
Master struct {
|
||||
APIPort int `env:"API_PORT" env-default:"9000" env-description:"master api port"`
|
||||
APIHost string `env:"API_HOST" env-description:"master host, can behind proxy like cdn"`
|
||||
APIScheme string `env:"API_SCHEME" env-default:"http" env-description:"master api scheme"`
|
||||
CacheSize int `env:"CACHE_SIZE" env-default:"10" env-description:"cache size in MB"`
|
||||
RPCHost string `env:"RPC_HOST" env-default:"127.0.0.1" env-description:"master host, is a public ip or domain"`
|
||||
RPCPort int `env:"RPC_PORT" env-default:"9001" env-description:"master rpc port"`
|
||||
InternalFRPServerHost string `env:"INTERNAL_FRP_SERVER_HOST" env-description:"internal frp server host, used for client connection"`
|
||||
APIPort int `env:"API_PORT" env-default:"9000" env-description:"master api port"`
|
||||
APIHost string `env:"API_HOST" env-description:"master host, can behind proxy like cdn"`
|
||||
APIScheme string `env:"API_SCHEME" env-default:"http" env-description:"master api scheme"`
|
||||
CacheSize int `env:"CACHE_SIZE" env-default:"10" env-description:"cache size in MB"`
|
||||
RPCHost string `env:"RPC_HOST" env-default:"127.0.0.1" env-description:"master host, is a public ip or domain"`
|
||||
RPCPort int `env:"RPC_PORT" env-default:"9001" env-description:"master rpc port"`
|
||||
InternalFRPServerHost string `env:"INTERNAL_FRP_SERVER_HOST" env-description:"internal frp server host, used for client connection"`
|
||||
} `env-prefix:"MASTER_"`
|
||||
Server struct {
|
||||
APIPort int `env:"API_PORT" env-default:"8999" env-description:"server api port"`
|
||||
InternalFRPAuthServerHost string `env:"INTERNAL_FRP_AUTH_SERVER_HOST" env-default:"127.0.0.1" env-description:"internal frp auth server host"`
|
||||
InternalFRPAuthServerPort int `env:"INTERNAL_FRP_AUTH_SERVER_PORT" env-default:"8999" env-description:"internal frp auth server port"`
|
||||
InternalFRPAuthServerPath string `env:"INTERNAL_FRP_AUTH_SERVER_PATH" env-default:"/auth" env-description:"internal frp auth server path"`
|
||||
} `env-prefix:"MASTER_"`
|
||||
Server struct {
|
||||
APIPort int `env:"API_PORT" env-default:"8999" env-description:"server api port"`
|
||||
} `env-prefix:"SERVER_"`
|
||||
DB struct {
|
||||
Type string `env:"TYPE" env-default:"sqlite3" env-description:"db type, mysql or sqlite3 and so on"`
|
||||
|
@@ -4,6 +4,10 @@ Server 推荐使用 docker 部署!不推荐直接安装到服务器中
|
||||
|
||||
注意 ⚠️:client 和 server 的启动指令可能会随着项目更新而改变,虽然在项目迭代时会注意前后兼容,但仍难以完全适配,因此 client 和 server 的启动指令以 master 生成为准
|
||||
|
||||
> `default` 服务端禁止单独部署,直接在 webui 中配置即可。重复部署会造成 `default` 服务端无法正常工作。
|
||||
|
||||
> `server` 会占用 8999 端口,请确保该端口未被占用。如果冲突,请修改 `server` 的 `SERVER_API_PORT` 和 `INTERNAL_FRP_AUTH_SERVER_PORT` 环境变量,要确保两个端口一致。`default` 服务端也会占用这个端口,因此不能在同一台机器以**默认配置**部署 `server` 和 `default` 服务端,需要修改 `server` 的环境变量来避免冲突。
|
||||
|
||||
> 如果只有一台公网服务器需要管理,那么使用 `master` 自带的 `default` 服务端即可,无需单独部署 `server`,但要注意在 `master` 启动后要配置 `default` 服务端
|
||||
|
||||
## 在 Linux 上部署
|
||||
|
@@ -4,6 +4,10 @@ We recommend deploying the Server via Docker! Direct installation on the host is
|
||||
|
||||
**Note ⚠️:** The startup commands for `client` and `server` may change as the project evolves. Although we strive for backward compatibility, the commands generated by the Master’s web UI should be treated as authoritative.
|
||||
|
||||
> The `default` server cannot be deployed separately. It should be configured in the webui. Repeated deployment will cause the `default` server to malfunction.
|
||||
|
||||
> The `server` will occupy port 8999. Please ensure that this port is not occupied. If there is a conflict, please modify the `SERVER_API_PORT` and `INTERNAL_FRP_AUTH_SERVER_PORT` environment variables of the `server`, ensuring that the two ports are the same. The `default` server will also occupy this port, so you cannot deploy the `server` and `default` server on the same machine with **default configuration**. You need to modify the environment variables of the `server` to avoid conflicts.
|
||||
|
||||
> If you only have one public-facing server to manage, you can use the Master’s built-in `default` server without deploying a separate Server. Remember to configure the `default` server after starting the Master.
|
||||
|
||||
## Deploying on Linux
|
||||
|
@@ -26,7 +26,7 @@ import { useMutation, useQuery } from '@tanstack/react-query'
|
||||
import { deleteClient, listClient } from '@/api/client'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { $frontendPreference, $platformInfo, $useServerGithubProxyUrl } from '@/store/user'
|
||||
import { $frontendPreference, $platformInfo } from '@/store/user'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
import { getClientsStatus } from '@/api/platform'
|
||||
import { Client, ClientType } from '@/lib/pb/common'
|
||||
@@ -125,7 +125,6 @@ export const columns: ColumnDef<ClientTableSchema>[] = [
|
||||
export const ClientID = ({ client }: { client: ClientTableSchema }) => {
|
||||
const { t } = useTranslation()
|
||||
const platformInfo = useStore($platformInfo)
|
||||
const useGithubProxyUrl = useStore($useServerGithubProxyUrl)
|
||||
const frontendPreference = useStore($frontendPreference)
|
||||
|
||||
if (!platformInfo) {
|
||||
@@ -152,7 +151,9 @@ export const ClientID = ({ client }: { client: ClientTableSchema }) => {
|
||||
<div className="grid gap-2">
|
||||
<div className="grid grid-cols-2 items-center gap-4 justify-items-center">
|
||||
<Label>{t('client.install.use_github_proxy_url')}</Label>
|
||||
<Checkbox onCheckedChange={$useServerGithubProxyUrl.set} defaultChecked={useGithubProxyUrl} />
|
||||
<Checkbox onCheckedChange={(checked) => {
|
||||
$frontendPreference.set({ ...frontendPreference, useServerGithubProxyUrl: checked === 'indeterminate' ? false : checked })
|
||||
}} defaultChecked={frontendPreference.useServerGithubProxyUrl} />
|
||||
</div>
|
||||
<div className="grid grid-cols-2 items-center gap-4 justify-items-center">
|
||||
<Label>{t('client.install.github_proxy_url')}</Label>
|
||||
@@ -165,8 +166,8 @@ export const ClientID = ({ client }: { client: ClientTableSchema }) => {
|
||||
navigator.clipboard.writeText(
|
||||
WindowsInstallCommand('client', client, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl),
|
||||
githubProxyUrl: frontendPreference.useServerGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, frontendPreference.useServerGithubProxyUrl),
|
||||
)
|
||||
}
|
||||
disabled={!platformInfo}
|
||||
@@ -179,8 +180,8 @@ export const ClientID = ({ client }: { client: ClientTableSchema }) => {
|
||||
readOnly
|
||||
value={WindowsInstallCommand('client', client, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl)}
|
||||
githubProxyUrl: frontendPreference.useServerGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, frontendPreference.useServerGithubProxyUrl)}
|
||||
className="flex-1"
|
||||
/>
|
||||
</div>
|
||||
@@ -189,8 +190,8 @@ export const ClientID = ({ client }: { client: ClientTableSchema }) => {
|
||||
onClick={() =>
|
||||
navigator.clipboard.writeText(LinuxInstallCommand('client', client, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl))
|
||||
githubProxyUrl: frontendPreference.useServerGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, frontendPreference.useServerGithubProxyUrl))
|
||||
}
|
||||
disabled={!platformInfo}
|
||||
size="sm"
|
||||
@@ -202,8 +203,8 @@ export const ClientID = ({ client }: { client: ClientTableSchema }) => {
|
||||
readOnly
|
||||
value={LinuxInstallCommand('client', client, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl)}
|
||||
githubProxyUrl: frontendPreference.useServerGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, frontendPreference.useServerGithubProxyUrl)}
|
||||
className="flex-1"
|
||||
/>
|
||||
</div>
|
||||
@@ -344,7 +345,6 @@ export const ClientActions: React.FC<ClientItemProps> = ({ client, table }) => {
|
||||
const { t } = useTranslation()
|
||||
const router = useRouter()
|
||||
const platformInfo = useStore($platformInfo)
|
||||
const useGithubProxyUrl = useStore($useServerGithubProxyUrl)
|
||||
const frontendPreference = useStore($frontendPreference)
|
||||
|
||||
const removeClient = useMutation({
|
||||
@@ -433,8 +433,8 @@ export const ClientActions: React.FC<ClientItemProps> = ({ client, table }) => {
|
||||
if (platformInfo) {
|
||||
navigator.clipboard.writeText(LinuxInstallCommand('client', client, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl))
|
||||
githubProxyUrl: frontendPreference.useServerGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, frontendPreference.useServerGithubProxyUrl))
|
||||
toast(t('client.actions_menu.copy_success'))
|
||||
} else {
|
||||
toast(t('client.actions_menu.copy_failed'))
|
||||
|
@@ -20,13 +20,13 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import React, { useState } from 'react'
|
||||
import React from 'react'
|
||||
import { ClientEnvFile, ExecCommandStr, LinuxInstallCommand, WindowsInstallCommand } from '@/lib/consts'
|
||||
import { useMutation, useQuery } from '@tanstack/react-query'
|
||||
import { deleteServer } from '@/api/server'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { $frontendPreference, $platformInfo, $useServerGithubProxyUrl } from '@/store/user'
|
||||
import { $frontendPreference, $platformInfo } from '@/store/user'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
import { getClientsStatus } from '@/api/platform'
|
||||
import { ClientType } from '@/lib/pb/common'
|
||||
@@ -124,7 +124,6 @@ export const columns: ColumnDef<ServerTableSchema>[] = [
|
||||
export const ServerID = ({ server }: { server: ServerTableSchema }) => {
|
||||
const { t } = useTranslation()
|
||||
const platformInfo = useStore($platformInfo)
|
||||
const useGithubProxyUrl = useStore($useServerGithubProxyUrl)
|
||||
const frontendPreference = useStore($frontendPreference)
|
||||
|
||||
if (!platformInfo) {
|
||||
@@ -151,7 +150,9 @@ export const ServerID = ({ server }: { server: ServerTableSchema }) => {
|
||||
<div className="grid gap-2">
|
||||
<div className="grid grid-cols-2 items-center gap-4 justify-items-center">
|
||||
<Label>{t('client.install.use_github_proxy_url')}</Label>
|
||||
<Checkbox onCheckedChange={$useServerGithubProxyUrl.set} defaultChecked={useGithubProxyUrl} />
|
||||
<Checkbox onCheckedChange={(checked) => {
|
||||
$frontendPreference.set({ ...frontendPreference, useServerGithubProxyUrl: checked === 'indeterminate' ? false : checked })
|
||||
}} defaultChecked={frontendPreference.useServerGithubProxyUrl} />
|
||||
</div>
|
||||
<div className="grid grid-cols-2 items-center gap-4 justify-items-center">
|
||||
<Label>{t('client.install.github_proxy_url')}</Label>
|
||||
@@ -162,8 +163,8 @@ export const ServerID = ({ server }: { server: ServerTableSchema }) => {
|
||||
<Button
|
||||
onClick={() => navigator.clipboard.writeText(WindowsInstallCommand('server', server, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl))}
|
||||
githubProxyUrl: frontendPreference.useServerGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, frontendPreference.useServerGithubProxyUrl))}
|
||||
disabled={!platformInfo}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
@@ -174,8 +175,8 @@ export const ServerID = ({ server }: { server: ServerTableSchema }) => {
|
||||
readOnly
|
||||
value={WindowsInstallCommand('server', server, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl)}
|
||||
githubProxyUrl: frontendPreference.useServerGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, frontendPreference.useServerGithubProxyUrl)}
|
||||
className="flex-1"
|
||||
/>
|
||||
</div>
|
||||
@@ -183,8 +184,8 @@ export const ServerID = ({ server }: { server: ServerTableSchema }) => {
|
||||
<Button
|
||||
onClick={() => navigator.clipboard.writeText(LinuxInstallCommand('server', server, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl))}
|
||||
githubProxyUrl: frontendPreference.useServerGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, frontendPreference.useServerGithubProxyUrl))}
|
||||
disabled={!platformInfo}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
@@ -195,8 +196,8 @@ export const ServerID = ({ server }: { server: ServerTableSchema }) => {
|
||||
readOnly
|
||||
value={LinuxInstallCommand('server', server, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl)}
|
||||
githubProxyUrl: frontendPreference.useServerGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, frontendPreference.useServerGithubProxyUrl)}
|
||||
className="flex-1"
|
||||
/>
|
||||
</div>
|
||||
|
@@ -3,6 +3,7 @@
|
||||
import {
|
||||
ChevronsUpDown,
|
||||
LogOut,
|
||||
SettingsIcon,
|
||||
User as UserIcon, // 别名避免和 User 类型冲突
|
||||
} from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
@@ -67,13 +68,18 @@ export function NavUser({ user }: NavUserProps) {
|
||||
</div>
|
||||
</DropdownMenuLabel>
|
||||
|
||||
{/* 使用 next/link 创建 “User Info” 菜单项 */}
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href="/user-info" className="w-full flex items-center space-x-2">
|
||||
<UserIcon className="h-4 w-4" />
|
||||
<span>{t('common.userInfo')}</span>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href="/platform-settings" className="w-full flex items-center space-x-2">
|
||||
<SettingsIcon className="h-4 w-4" />
|
||||
<span>{t('平台设置')}</span>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
|
202
www/components/platform/settings.tsx
Normal file
202
www/components/platform/settings.tsx
Normal file
@@ -0,0 +1,202 @@
|
||||
import { toast } from 'sonner'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import * as z from 'zod'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { $platformInfo } from '@/store/user'
|
||||
|
||||
import { $frontendPreference, FrontendPreference } from '@/store/user'
|
||||
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, FormDescription } from '@/components/ui/form'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Switch } from '@/components/ui/switch'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter as DialogFooterUI,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from '@/components/ui/dialog'
|
||||
|
||||
// 表单校验 Schema
|
||||
const platformSchema = z.object({
|
||||
useServerGithubProxyUrl: z.boolean().default(false),
|
||||
// githubProxyUrl 可选;若为空字符串则忽略,否则需为合法 URL
|
||||
githubProxyUrl: z.union([z.string().trim().url('Invalid URL'), z.literal('')]).optional(),
|
||||
clientApiUrl: z.union([z.string().trim().url('Invalid URL'), z.literal('')]).optional(),
|
||||
clientRpcUrl: z.union([z.string().trim().url('Invalid URL'), z.literal('')]).optional(),
|
||||
})
|
||||
|
||||
type PlatformFormValues = z.infer<typeof platformSchema>
|
||||
|
||||
export function PlatformSettingsForm() {
|
||||
const { t } = useTranslation()
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [initial, setInitial] = useState<PlatformFormValues | null>(null)
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const platformInfo = useStore($platformInfo)
|
||||
|
||||
const form = useForm<PlatformFormValues>({
|
||||
resolver: zodResolver(platformSchema),
|
||||
defaultValues: {
|
||||
useServerGithubProxyUrl: false,
|
||||
githubProxyUrl: '',
|
||||
clientApiUrl: '',
|
||||
clientRpcUrl: '',
|
||||
},
|
||||
})
|
||||
|
||||
// 组件挂载时读取持久化设置
|
||||
useEffect(() => {
|
||||
const pref = ($frontendPreference.get() ?? {}) as FrontendPreference
|
||||
form.reset({
|
||||
useServerGithubProxyUrl: pref.useServerGithubProxyUrl ?? false,
|
||||
githubProxyUrl: pref.githubProxyUrl ?? '',
|
||||
clientApiUrl: pref.clientApiUrl ?? '',
|
||||
clientRpcUrl: pref.clientRpcUrl ?? '',
|
||||
})
|
||||
setInitial(form.getValues())
|
||||
setLoading(false)
|
||||
}, [])
|
||||
|
||||
const onSubmit = (values: PlatformFormValues) => {
|
||||
const pref: FrontendPreference = {
|
||||
useServerGithubProxyUrl: values.useServerGithubProxyUrl,
|
||||
githubProxyUrl: values.githubProxyUrl?.trim() || undefined,
|
||||
clientApiUrl: values.clientApiUrl?.trim() || undefined,
|
||||
clientRpcUrl: values.clientRpcUrl?.trim() || undefined,
|
||||
}
|
||||
$frontendPreference.set(pref)
|
||||
toast.success(t('已更新平台设置'))
|
||||
// 重置 initial 状态 & 清空 dirty
|
||||
form.reset(values)
|
||||
setInitial(values)
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-64">
|
||||
<p className="text-gray-500">{t('正在加载平台设置')}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="max-w-lg mx-auto">
|
||||
<CardHeader className="border-b">
|
||||
<CardTitle>{t('平台设置')}</CardTitle>
|
||||
<CardDescription>{t('修改前端平台设置')}</CardDescription>
|
||||
<p className="text-xs text-muted-foreground mt-1 italic">{t('此配置仅保存在本地')}</p>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6 pt-2">
|
||||
{/* 使用服务器代理开关 */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="useServerGithubProxyUrl"
|
||||
render={({ field }) => (
|
||||
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm">
|
||||
<div className="space-y-0.5">
|
||||
<FormLabel>{t('使用服务器 Github 代理')}</FormLabel>
|
||||
<FormDescription>{t('若开启,则使用后台配置的代理地址下载')}</FormDescription>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Switch checked={field.value} onCheckedChange={field.onChange} />
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* 自定义 GitHub Proxy URL */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="githubProxyUrl"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('自定义 Github 代理地址')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={platformInfo?.githubProxyUrl || t('例如 https://ghproxy.com/')} {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* 自定义 API URL */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="clientApiUrl"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('自定义 API URL')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={platformInfo?.clientApiUrl || t('例如 https://api.example.com/')} {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* 自定义 RPC URL */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="clientRpcUrl"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('自定义 RPC URL')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={platformInfo?.clientRpcUrl || t('例如 https://rpc.example.com/')} {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</form>
|
||||
</Form>
|
||||
</CardContent>
|
||||
<CardFooter className="flex justify-end">
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
disabled={
|
||||
form.formState.isSubmitting ||
|
||||
JSON.stringify(form.getValues()) === JSON.stringify(initial)
|
||||
}
|
||||
onClick={() => setOpen(true)}
|
||||
>
|
||||
{t('保存更改')}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t('确认保存')}</DialogTitle>
|
||||
<DialogDescription>{t('确定保存修改?')}</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogFooterUI>
|
||||
<Button variant={'destructive'} onClick={() => setOpen(false)}>
|
||||
{t('取消')}
|
||||
</Button>
|
||||
<Button
|
||||
variant={'secondary'}
|
||||
onClick={() => {
|
||||
form.handleSubmit(onSubmit)()
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
{t('确认')}
|
||||
</Button>
|
||||
</DialogFooterUI>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)
|
||||
}
|
@@ -37,16 +37,16 @@ export const getNavItems = (t: any) => [
|
||||
url: "/proxies",
|
||||
icon: Cable,
|
||||
},
|
||||
{
|
||||
title: t('nav.editClient'),
|
||||
url: "/clientedit",
|
||||
icon: MonitorCogIcon,
|
||||
},
|
||||
{
|
||||
title: t('nav.editServer'),
|
||||
url: "/serveredit",
|
||||
icon: ServerCogIcon,
|
||||
},
|
||||
// {
|
||||
// title: t('nav.editClient'),
|
||||
// url: "/clientedit",
|
||||
// icon: MonitorCogIcon,
|
||||
// },
|
||||
// {
|
||||
// title: t('nav.editServer'),
|
||||
// url: "/serveredit",
|
||||
// icon: ServerCogIcon,
|
||||
// },
|
||||
{
|
||||
title: t('nav.trafficStats'),
|
||||
url: "/clientstats",
|
||||
|
@@ -625,5 +625,23 @@
|
||||
"save": "Save",
|
||||
"save_success": "Save successful",
|
||||
"save_error": "Save failed"
|
||||
}
|
||||
},
|
||||
"平台设置": "Platform Settings",
|
||||
"修改前端平台设置": "Modify frontend preferences",
|
||||
"已更新平台设置": "Platform settings updated",
|
||||
"使用服务器 Github 代理": "Use server GitHub proxy",
|
||||
"若开启,则使用后台配置的代理地址下载": "If enabled, backend proxy URL will be used for downloads",
|
||||
"自定义 Github 代理地址": "Custom GitHub proxy URL",
|
||||
"例如 https://ghproxy.com/": "e.g. https://ghproxy.com/",
|
||||
"保存更改": "Save changes",
|
||||
"确认保存": "Confirm save",
|
||||
"确定保存修改?": "Are you sure to save changes?",
|
||||
"确认": "Confirm",
|
||||
"正在加载平台设置": "Loading platform settings...",
|
||||
"取消": "Cancel",
|
||||
"自定义 API URL": "Custom API URL",
|
||||
"自定义 RPC URL": "Custom RPC URL",
|
||||
"例如 https://api.example.com/": "e.g. https://api.example.com/",
|
||||
"例如 https://rpc.example.com/": "e.g. https://rpc.example.com/",
|
||||
"此配置仅保存在本地": "These settings are stored only in this browser and will not sync across devices."
|
||||
}
|
@@ -625,5 +625,23 @@
|
||||
"save": "Enregistrer",
|
||||
"save_success": "Enregistrement réussi",
|
||||
"save_error": "Échec de l'enregistrement"
|
||||
}
|
||||
},
|
||||
"平台设置": "Paramètres de la plateforme",
|
||||
"修改前端平台设置": "Modifier les préférences du frontal",
|
||||
"已更新平台设置": "Paramètres de la plateforme mis à jour",
|
||||
"使用服务器 Github 代理": "Utiliser le proxy GitHub du serveur",
|
||||
"若开启,则使用后台配置的代理地址下载": "Si activé, l'URL proxy définie côté serveur sera utilisée pour le téléchargement",
|
||||
"自定义 Github 代理地址": "URL proxy GitHub personnalisée",
|
||||
"例如 https://ghproxy.com/": "ex. https://ghproxy.com/",
|
||||
"保存更改": "Enregistrer les modifications",
|
||||
"确认保存": "Confirmer l'enregistrement",
|
||||
"确定保存修改?": "Êtes-vous sûr de vouloir enregistrer les modifications ?",
|
||||
"确认": "Confirmer",
|
||||
"正在加载平台设置": "Chargement des paramètres de la plateforme...",
|
||||
"取消": "Annuler",
|
||||
"自定义 API URL": "URL API personnalisée",
|
||||
"自定义 RPC URL": "URL RPC personnalisée",
|
||||
"例如 https://api.example.com/": "ex. https://api.example.com/",
|
||||
"例如 https://rpc.example.com/": "ex. https://rpc.example.com/",
|
||||
"此配置仅保存在本地": "Ces paramètres sont enregistrés uniquement dans ce navigateur et ne seront pas synchronisés entre les appareils."
|
||||
}
|
@@ -623,5 +623,23 @@
|
||||
"save": "保存",
|
||||
"save_success": "保存成功",
|
||||
"save_error": "保存失败"
|
||||
}
|
||||
},
|
||||
"平台设置": "平台设置",
|
||||
"修改前端平台设置": "修改前端平台设置",
|
||||
"已更新平台设置": "已更新平台设置",
|
||||
"使用服务器 Github 代理": "使用服务器 Github 代理",
|
||||
"若开启,则使用后台配置的代理地址下载": "若开启,则使用后台配置的代理地址下载",
|
||||
"自定义 Github 代理地址": "自定义 Github 代理地址",
|
||||
"例如 https://ghproxy.com/": "例如 https://ghproxy.com/",
|
||||
"保存更改": "保存更改",
|
||||
"确认保存": "确认保存",
|
||||
"确定保存修改?": "确定保存修改?",
|
||||
"确认": "确认",
|
||||
"正在加载平台设置": "正在加载平台设置",
|
||||
"取消": "取消",
|
||||
"自定义 API URL": "自定义 API URL",
|
||||
"自定义 RPC URL": "自定义 RPC URL",
|
||||
"例如 https://api.example.com/": "例如 https://api.example.com/",
|
||||
"例如 https://rpc.example.com/": "例如 https://rpc.example.com/",
|
||||
"此配置仅保存在本地": "该设置仅保存在当前浏览器,不会同步到其他设备"
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"平台设置": "平台設置",
|
||||
"app": {
|
||||
"title": "FRP-Panel",
|
||||
"subtitle": "FRP 隧道面板",
|
||||
@@ -623,5 +624,22 @@
|
||||
"save": "保存",
|
||||
"save_success": "保存成功",
|
||||
"save_error": "保存失敗"
|
||||
}
|
||||
},
|
||||
"修改前端平台设置": "修改前端平台設定",
|
||||
"已更新平台设置": "平台設定已更新",
|
||||
"使用服务器 Github 代理": "使用伺服器 GitHub 代理",
|
||||
"若开启,则使用后台配置的代理地址下载": "若開啟,將使用後端配置的代理位址下載",
|
||||
"自定义 Github 代理地址": "自訂 GitHub 代理位址",
|
||||
"例如 https://ghproxy.com/": "例如 https://ghproxy.com/",
|
||||
"保存更改": "儲存變更",
|
||||
"确认保存": "確認保存",
|
||||
"确定保存修改?": "確定保存變更?",
|
||||
"确认": "確認",
|
||||
"正在加载平台设置": "正在載入平台設定...",
|
||||
"取消": "取消",
|
||||
"自定义 API URL": "自訂 API URL",
|
||||
"自定义 RPC URL": "自訂 RPC URL",
|
||||
"例如 https://api.example.com/": "例如 https://api.example.com/",
|
||||
"例如 https://rpc.example.com/": "例如 https://rpc.example.com/",
|
||||
"此配置仅保存在本地": "此設定僅儲存在此瀏覽器中,不會在裝置間同步。"
|
||||
}
|
@@ -3,6 +3,28 @@ import { Client, Server } from './pb/common'
|
||||
import { GetPlatformInfoResponse } from './pb/api_user'
|
||||
import { TypedProxyConfig } from '@/types/proxy'
|
||||
|
||||
// 延迟加载前端首选项,避免 SSR 期间引用 window/localStorage
|
||||
type FrontendPreferenceLazy = {
|
||||
githubProxyUrl?: string
|
||||
useServerGithubProxyUrl?: boolean
|
||||
clientApiUrl?: string
|
||||
clientRpcUrl?: string
|
||||
}
|
||||
|
||||
const getFrontendPreference = (): FrontendPreferenceLazy => {
|
||||
if (typeof window !== 'undefined') {
|
||||
try {
|
||||
// 动态引入避免打包时静态依赖
|
||||
// eslint-disable-next-line
|
||||
const { $frontendPreference } = require('@/store/user')
|
||||
return ($frontendPreference.get?.() ?? {}) as FrontendPreferenceLazy
|
||||
} catch {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
export const API_PATH = '/api/v1'
|
||||
export const SET_TOKEN_HEADER = 'x-set-authorization'
|
||||
export const X_CLIENT_REQUEST_ID = 'x-client-request-id'
|
||||
@@ -77,17 +99,44 @@ export const ClientConfigured = (client: Client | undefined): boolean => {
|
||||
|
||||
// .refine((e) => e === "abcd@fg.com", "This email is not in our database")
|
||||
|
||||
// 获取最终 Github 代理 URL
|
||||
const getGithubProxyUrl = (info: GetPlatformInfoResponse, applyPref = true): string => {
|
||||
const pref = getFrontendPreference()
|
||||
if (applyPref && pref.useServerGithubProxyUrl === false && pref.githubProxyUrl) {
|
||||
return pref.githubProxyUrl
|
||||
}
|
||||
// 若前端未指定或选择使用服务器,返回后端
|
||||
return info.githubProxyUrl
|
||||
}
|
||||
|
||||
// 获取最终 API URL
|
||||
const getClientApiUrl = (info: GetPlatformInfoResponse, applyPref = true): string => {
|
||||
const pref = getFrontendPreference()
|
||||
return applyPref && pref.clientApiUrl?.trim() ? pref.clientApiUrl.trim() : info.clientApiUrl
|
||||
}
|
||||
|
||||
// 获取最终 RPC URL
|
||||
const getClientRpcUrl = (info: GetPlatformInfoResponse, applyPref = true): string => {
|
||||
const pref = getFrontendPreference()
|
||||
return applyPref && pref.clientRpcUrl?.trim() ? pref.clientRpcUrl.trim() : info.clientRpcUrl
|
||||
}
|
||||
|
||||
export const ExecCommandStr = <T extends Client | Server>(
|
||||
type: 'client' | 'server',
|
||||
item: T,
|
||||
info: GetPlatformInfoResponse,
|
||||
fileName?: string,
|
||||
applyPref = true,
|
||||
) => {
|
||||
return `${fileName || 'frp-panel'} ${type} -s ${item.secret} -i ${item.id} --api-url ${info.clientApiUrl} --rpc-url ${info.clientRpcUrl}`
|
||||
const apiUrl = getClientApiUrl(info, applyPref)
|
||||
const rpcUrl = getClientRpcUrl(info, applyPref)
|
||||
return `${fileName || 'frp-panel'} ${type} -s ${item.secret} -i ${item.id} --api-url ${apiUrl} --rpc-url ${rpcUrl}`
|
||||
}
|
||||
|
||||
export const JoinCommandStr = (info: GetPlatformInfoResponse, token: string, fileName?: string, clientID?: string) => {
|
||||
return `${fileName || 'frp-panel'} join${clientID ? ` -i ${clientID}` : ''} -j ${token} --api-url ${info.clientApiUrl} --rpc-url ${info.clientRpcUrl}`
|
||||
export const JoinCommandStr = (info: GetPlatformInfoResponse, token: string, fileName?: string, clientID?: string, applyPref = true) => {
|
||||
const apiUrl = getClientApiUrl(info, applyPref)
|
||||
const rpcUrl = getClientRpcUrl(info, applyPref)
|
||||
return `${fileName || 'frp-panel'} join${clientID ? ` -i ${clientID}` : ''} -j ${token} --api-url ${apiUrl} --rpc-url ${rpcUrl}`
|
||||
}
|
||||
|
||||
export const WindowsInstallCommand = <T extends Client | Server>(
|
||||
@@ -95,15 +144,17 @@ export const WindowsInstallCommand = <T extends Client | Server>(
|
||||
item: T,
|
||||
info: GetPlatformInfoResponse,
|
||||
github_proxy?: boolean,
|
||||
applyPref = true,
|
||||
) => {
|
||||
const proxyUrl = getGithubProxyUrl(info, applyPref)
|
||||
return (
|
||||
`[Net.ServicePointManager]::SecurityProtocol = ` +
|
||||
`[Net.SecurityProtocolType]::Ssl3 -bor ` +
|
||||
`[Net.SecurityProtocolType]::Tls -bor ` +
|
||||
`[Net.SecurityProtocolType]::Tls11 -bor ` +
|
||||
`[Net.SecurityProtocolType]::Tls12;set-ExecutionPolicy RemoteSigned;` +
|
||||
`Invoke-WebRequest ${github_proxy ? info.githubProxyUrl : ''}https://raw.githubusercontent.com/VaalaCat/frp-panel/main/install.ps1 ` +
|
||||
`-OutFile C:\install.ps1;powershell.exe C:\install.ps1 ${ExecCommandStr(type, item, info, ' ')}`
|
||||
`Invoke-WebRequest ${github_proxy ? proxyUrl : ''}https://raw.githubusercontent.com/VaalaCat/frp-panel/main/install.ps1 ` +
|
||||
`-OutFile C:\install.ps1;powershell.exe C:\install.ps1 ${ExecCommandStr(type, item, info, ' ', applyPref)}`
|
||||
)
|
||||
}
|
||||
|
||||
@@ -112,16 +163,18 @@ export const LinuxInstallCommand = <T extends Client | Server>(
|
||||
item: T,
|
||||
info: GetPlatformInfoResponse,
|
||||
github_proxy?: boolean,
|
||||
applyPref = true,
|
||||
) => {
|
||||
return `curl -fSL ${github_proxy ? info.githubProxyUrl : ''
|
||||
}https://raw.githubusercontent.com/VaalaCat/frp-panel/main/install.sh | bash -s -- ${
|
||||
github_proxy ? `--github-proxy ${info.githubProxyUrl}` : ''
|
||||
}${ExecCommandStr(type, item, info, ' ')}`
|
||||
const proxyUrl = getGithubProxyUrl(info, applyPref)
|
||||
return `curl -fSL ${github_proxy ? proxyUrl : ''}https://raw.githubusercontent.com/VaalaCat/frp-panel/main/install.sh | bash -s -- ${github_proxy ? `--github-proxy ${proxyUrl}` : ''
|
||||
}${ExecCommandStr(type, item, info, ' ', applyPref)}`
|
||||
}
|
||||
|
||||
export const ClientEnvFile = <T extends Client | Server>(item: T, info: GetPlatformInfoResponse) => {
|
||||
export const ClientEnvFile = <T extends Client | Server>(item: T, info: GetPlatformInfoResponse, applyPref = true) => {
|
||||
const apiUrl = getClientApiUrl(info, applyPref)
|
||||
const rpcUrl = getClientRpcUrl(info, applyPref)
|
||||
return `CLIENT_ID=${item.id}
|
||||
CLIENT_SECRET=${item.secret}
|
||||
CLIENT_API_URL=${info.clientApiUrl}
|
||||
CLIENT_RPC_URL=${info.clientRpcUrl}`
|
||||
CLIENT_API_URL=${apiUrl}
|
||||
CLIENT_RPC_URL=${rpcUrl}`
|
||||
}
|
||||
|
16
www/pages/platform-settings.tsx
Normal file
16
www/pages/platform-settings.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Providers } from '@/components/providers'
|
||||
import { RootLayout } from '@/components/layout'
|
||||
import { Header } from '@/components/header'
|
||||
import { PlatformSettingsForm } from '@/components/platform/settings'
|
||||
|
||||
export default function PlatformSettingsPage() {
|
||||
return (
|
||||
<Providers>
|
||||
<RootLayout mainHeader={<Header />}>
|
||||
<div className="w-full py-4">
|
||||
<PlatformSettingsForm />
|
||||
</div>
|
||||
</RootLayout>
|
||||
</Providers>
|
||||
)
|
||||
}
|
@@ -3,7 +3,7 @@ import { RootLayout } from '@/components/layout'
|
||||
import { Header } from '@/components/header'
|
||||
import { UserProfileForm } from '@/components/user/user-info'
|
||||
|
||||
export default function ClientListPage() {
|
||||
export default function UserInfoPage() {
|
||||
return (
|
||||
<Providers>
|
||||
<RootLayout mainHeader={<Header />}>
|
||||
|
@@ -15,13 +15,11 @@ export const $language = persistentAtom<string>('user-language', 'zh', {
|
||||
decode: JSON.parse,
|
||||
})
|
||||
|
||||
export const $useServerGithubProxyUrl = persistentAtom<boolean>('use_server_github_proxy_url', false, {
|
||||
encode: JSON.stringify,
|
||||
decode: JSON.parse,
|
||||
})
|
||||
|
||||
export type FrontendPreference = {
|
||||
export type FrontendPreference = {
|
||||
githubProxyUrl?: string
|
||||
useServerGithubProxyUrl?: boolean
|
||||
clientApiUrl?: string
|
||||
clientRpcUrl?: string
|
||||
}
|
||||
|
||||
export const $frontendPreference = persistentAtom<FrontendPreference>('frontend_preference', {}, {
|
||||
|
Reference in New Issue
Block a user