diff --git a/conf/helper.go b/conf/helper.go index 44b630a..3f51830 100644 --- a/conf/helper.go +++ b/conf/helper.go @@ -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") } diff --git a/conf/settings.go b/conf/settings.go index fdda36c..fc4bcba 100644 --- a/conf/settings.go +++ b/conf/settings.go @@ -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"` diff --git a/docs/deploy-server.md b/docs/deploy-server.md index 17fd4bb..2445abd 100644 --- a/docs/deploy-server.md +++ b/docs/deploy-server.md @@ -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 上部署 diff --git a/docs/en/deploy-server.md b/docs/en/deploy-server.md index 93dfb2a..5434250 100644 --- a/docs/en/deploy-server.md +++ b/docs/en/deploy-server.md @@ -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 diff --git a/www/components/frpc/client_item.tsx b/www/components/frpc/client_item.tsx index 89bd07f..e5e56f0 100644 --- a/www/components/frpc/client_item.tsx +++ b/www/components/frpc/client_item.tsx @@ -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[] = [ 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 }) => {
- + { + $frontendPreference.set({ ...frontendPreference, useServerGithubProxyUrl: checked === 'indeterminate' ? false : checked }) + }} defaultChecked={frontendPreference.useServerGithubProxyUrl} />
@@ -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" />
@@ -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" />
@@ -344,7 +345,6 @@ export const ClientActions: React.FC = ({ 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 = ({ 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')) diff --git a/www/components/frps/server_item.tsx b/www/components/frps/server_item.tsx index 7fb65c7..c43247f 100644 --- a/www/components/frps/server_item.tsx +++ b/www/components/frps/server_item.tsx @@ -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[] = [ 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 }) => {
- + { + $frontendPreference.set({ ...frontendPreference, useServerGithubProxyUrl: checked === 'indeterminate' ? false : checked }) + }} defaultChecked={frontendPreference.useServerGithubProxyUrl} />
@@ -162,8 +163,8 @@ export const ServerID = ({ server }: { server: ServerTableSchema }) => {
@@ -183,8 +184,8 @@ export const ServerID = ({ server }: { server: ServerTableSchema }) => {
diff --git a/www/components/nav-user.tsx b/www/components/nav-user.tsx index aec0f91..71229eb 100644 --- a/www/components/nav-user.tsx +++ b/www/components/nav-user.tsx @@ -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) { - {/* 使用 next/link 创建 “User Info” 菜单项 */} {t('common.userInfo')} + + + + {t('平台设置')} + + diff --git a/www/components/platform/settings.tsx b/www/components/platform/settings.tsx new file mode 100644 index 0000000..62196ca --- /dev/null +++ b/www/components/platform/settings.tsx @@ -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 + +export function PlatformSettingsForm() { + const { t } = useTranslation() + const [loading, setLoading] = useState(true) + const [initial, setInitial] = useState(null) + const [open, setOpen] = useState(false) + + const platformInfo = useStore($platformInfo) + + const form = useForm({ + 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 ( +
+

{t('正在加载平台设置')}

+
+ ) + } + + return ( + + + {t('平台设置')} + {t('修改前端平台设置')} +

{t('此配置仅保存在本地')}

+
+ +
+ + {/* 使用服务器代理开关 */} + ( + +
+ {t('使用服务器 Github 代理')} + {t('若开启,则使用后台配置的代理地址下载')} +
+ + + +
+ )} + /> + + {/* 自定义 GitHub Proxy URL */} + ( + + {t('自定义 Github 代理地址')} + + + + + + )} + /> + + {/* 自定义 API URL */} + ( + + {t('自定义 API URL')} + + + + + + )} + /> + + {/* 自定义 RPC URL */} + ( + + {t('自定义 RPC URL')} + + + + + + )} + /> + + +
+ + + + + + + + {t('确认保存')} + {t('确定保存修改?')} + + + + + + + + +
+ ) +} diff --git a/www/config/nav.ts b/www/config/nav.ts index 9490d08..5a42f90 100644 --- a/www/config/nav.ts +++ b/www/config/nav.ts @@ -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", diff --git a/www/i18n/locales/en.json b/www/i18n/locales/en.json index fd429a5..f6bec50 100644 --- a/www/i18n/locales/en.json +++ b/www/i18n/locales/en.json @@ -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." +} \ No newline at end of file diff --git a/www/i18n/locales/fr.json b/www/i18n/locales/fr.json index a9f7bae..8bf65f8 100644 --- a/www/i18n/locales/fr.json +++ b/www/i18n/locales/fr.json @@ -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." +} \ No newline at end of file diff --git a/www/i18n/locales/zh.json b/www/i18n/locales/zh.json index 6c28be3..6d617cb 100644 --- a/www/i18n/locales/zh.json +++ b/www/i18n/locales/zh.json @@ -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/", + "此配置仅保存在本地": "该设置仅保存在当前浏览器,不会同步到其他设备" +} \ No newline at end of file diff --git a/www/i18n/locales/zhtw.json b/www/i18n/locales/zhtw.json index c331bcf..08411b6 100644 --- a/www/i18n/locales/zhtw.json +++ b/www/i18n/locales/zhtw.json @@ -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/", + "此配置仅保存在本地": "此設定僅儲存在此瀏覽器中,不會在裝置間同步。" +} \ No newline at end of file diff --git a/www/lib/consts.ts b/www/lib/consts.ts index 0dd481e..4865d0f 100644 --- a/www/lib/consts.ts +++ b/www/lib/consts.ts @@ -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 = ( 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 = ( @@ -95,15 +144,17 @@ export const WindowsInstallCommand = ( 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 = ( 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 = (item: T, info: GetPlatformInfoResponse) => { +export const ClientEnvFile = (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}` } diff --git a/www/pages/platform-settings.tsx b/www/pages/platform-settings.tsx new file mode 100644 index 0000000..3193ee9 --- /dev/null +++ b/www/pages/platform-settings.tsx @@ -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 ( + + }> +
+ +
+
+
+ ) +} diff --git a/www/pages/user-info.tsx b/www/pages/user-info.tsx index 81fa33b..4ca68cd 100644 --- a/www/pages/user-info.tsx +++ b/www/pages/user-info.tsx @@ -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 ( }> diff --git a/www/store/user.ts b/www/store/user.ts index 70f5165..ef94c39 100644 --- a/www/store/user.ts +++ b/www/store/user.ts @@ -15,13 +15,11 @@ export const $language = persistentAtom('user-language', 'zh', { decode: JSON.parse, }) -export const $useServerGithubProxyUrl = persistentAtom('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('frontend_preference', {}, {