mirror of
https://github.com/VaalaCat/frp-panel.git
synced 2025-10-05 15:27:12 +08:00
feat: advance mode for proxy mutate form
This commit is contained in:
@@ -352,35 +352,35 @@ func patchConfig(appInstance app.Application, commonArgs CommonArgs) conf.Config
|
|||||||
c := context.Background()
|
c := context.Background()
|
||||||
tmpCfg := appInstance.GetConfig()
|
tmpCfg := appInstance.GetConfig()
|
||||||
|
|
||||||
if commonArgs.RpcHost != nil {
|
if commonArgs.RpcHost != nil && len(*commonArgs.RpcHost) > 0 {
|
||||||
tmpCfg.Master.RPCHost = *commonArgs.RpcHost
|
tmpCfg.Master.RPCHost = *commonArgs.RpcHost
|
||||||
tmpCfg.Master.APIHost = *commonArgs.RpcHost
|
tmpCfg.Master.APIHost = *commonArgs.RpcHost
|
||||||
}
|
}
|
||||||
|
|
||||||
if commonArgs.ApiHost != nil {
|
if commonArgs.ApiHost != nil && len(*commonArgs.RpcHost) > 0 {
|
||||||
tmpCfg.Master.APIHost = *commonArgs.ApiHost
|
tmpCfg.Master.APIHost = *commonArgs.ApiHost
|
||||||
}
|
}
|
||||||
|
|
||||||
if commonArgs.RpcPort != nil {
|
if commonArgs.RpcPort != nil && *commonArgs.RpcPort > 0 {
|
||||||
tmpCfg.Master.RPCPort = *commonArgs.RpcPort
|
tmpCfg.Master.RPCPort = *commonArgs.RpcPort
|
||||||
}
|
}
|
||||||
if commonArgs.ApiPort != nil {
|
if commonArgs.ApiPort != nil && *commonArgs.ApiPort > 0 {
|
||||||
tmpCfg.Master.APIPort = *commonArgs.ApiPort
|
tmpCfg.Master.APIPort = *commonArgs.ApiPort
|
||||||
}
|
}
|
||||||
if commonArgs.ApiScheme != nil {
|
if commonArgs.ApiScheme != nil && len(*commonArgs.ApiScheme) > 0 {
|
||||||
tmpCfg.Master.APIScheme = *commonArgs.ApiScheme
|
tmpCfg.Master.APIScheme = *commonArgs.ApiScheme
|
||||||
}
|
}
|
||||||
if commonArgs.ClientID != nil {
|
if commonArgs.ClientID != nil && len(*commonArgs.ClientID) > 0 {
|
||||||
tmpCfg.Client.ID = *commonArgs.ClientID
|
tmpCfg.Client.ID = *commonArgs.ClientID
|
||||||
}
|
}
|
||||||
if commonArgs.ClientSecret != nil {
|
if commonArgs.ClientSecret != nil && len(*commonArgs.ClientSecret) > 0 {
|
||||||
tmpCfg.Client.Secret = *commonArgs.ClientSecret
|
tmpCfg.Client.Secret = *commonArgs.ClientSecret
|
||||||
}
|
}
|
||||||
|
|
||||||
if commonArgs.ApiUrl != nil {
|
if commonArgs.ApiUrl != nil && len(*commonArgs.ApiUrl) > 0 {
|
||||||
tmpCfg.Client.APIUrl = *commonArgs.ApiUrl
|
tmpCfg.Client.APIUrl = *commonArgs.ApiUrl
|
||||||
}
|
}
|
||||||
if commonArgs.RpcUrl != nil {
|
if commonArgs.RpcUrl != nil && len(*commonArgs.RpcUrl) > 0 {
|
||||||
tmpCfg.Client.RPCUrl = *commonArgs.RpcUrl
|
tmpCfg.Client.RPCUrl = *commonArgs.RpcUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -27,6 +27,8 @@ import { ProxyConfig, Server } from '@/lib/pb/common'
|
|||||||
import { TypedProxyConfigValid } from '@/lib/consts'
|
import { TypedProxyConfigValid } from '@/lib/consts'
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
import { $proxyTableRefetchTrigger } from '@/store/refetch-trigger'
|
import { $proxyTableRefetchTrigger } from '@/store/refetch-trigger'
|
||||||
|
import { Switch } from '../ui/switch'
|
||||||
|
import { Textarea } from '../ui/textarea'
|
||||||
|
|
||||||
export type ProxyConfigMutateDialogProps = {
|
export type ProxyConfigMutateDialogProps = {
|
||||||
overwrite?: boolean
|
overwrite?: boolean
|
||||||
@@ -72,6 +74,9 @@ export const ProxyConfigMutateForm = ({
|
|||||||
const [proxyType, setProxyType] = useState<ProxyType>('http')
|
const [proxyType, setProxyType] = useState<ProxyType>('http')
|
||||||
const [selectedServer, setSelectedServer] = useState<Server | undefined>()
|
const [selectedServer, setSelectedServer] = useState<Server | undefined>()
|
||||||
const supportedProxyTypes: ProxyType[] = ['http', 'tcp', 'udp']
|
const supportedProxyTypes: ProxyType[] = ['http', 'tcp', 'udp']
|
||||||
|
// advanced mode toggle
|
||||||
|
const [advancedMode, setAdvancedMode] = useState<boolean>(false)
|
||||||
|
const [rawConfig, setRawConfig] = useState<string>('{}')
|
||||||
|
|
||||||
const createProxyConfigMutation = useMutation({
|
const createProxyConfigMutation = useMutation({
|
||||||
mutationKey: ['createProxyConfig', newClientID, newServerID],
|
mutationKey: ['createProxyConfig', newClientID, newServerID],
|
||||||
@@ -99,10 +104,16 @@ export const ProxyConfigMutateForm = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (proxyName && proxyType) {
|
if (proxyName && proxyType) {
|
||||||
setProxyConfigs([{...defaultProxyConfig, name: proxyName, type: proxyType }])
|
setProxyConfigs([{ ...defaultProxyConfig, name: proxyName, type: proxyType }])
|
||||||
}
|
}
|
||||||
}, [proxyName, proxyType])
|
}, [proxyName, proxyType])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (proxyConfigs) {
|
||||||
|
setRawConfig(JSON.stringify(proxyConfigs, null, 2))
|
||||||
|
}
|
||||||
|
}, [proxyConfigs, setRawConfig])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (defaultProxyConfig && defaultOriginalProxyConfig) {
|
if (defaultProxyConfig && defaultOriginalProxyConfig) {
|
||||||
setProxyConfigs([defaultProxyConfig])
|
setProxyConfigs([defaultProxyConfig])
|
||||||
@@ -110,6 +121,7 @@ export const ProxyConfigMutateForm = ({
|
|||||||
setProxyName(defaultProxyConfig.name)
|
setProxyName(defaultProxyConfig.name)
|
||||||
setNewClientID(defaultOriginalProxyConfig.originClientId)
|
setNewClientID(defaultOriginalProxyConfig.originClientId)
|
||||||
setNewServerID(defaultOriginalProxyConfig.serverId)
|
setNewServerID(defaultOriginalProxyConfig.serverId)
|
||||||
|
setRawConfig(JSON.stringify([defaultProxyConfig], null, 2))
|
||||||
}
|
}
|
||||||
}, [defaultProxyConfig, defaultOriginalProxyConfig])
|
}, [defaultProxyConfig, defaultOriginalProxyConfig])
|
||||||
|
|
||||||
@@ -119,45 +131,76 @@ export const ProxyConfigMutateForm = ({
|
|||||||
<ServerSelector setServerID={setNewServerID} serverID={newServerID} setServer={setSelectedServer} />
|
<ServerSelector setServerID={setNewServerID} serverID={newServerID} setServer={setSelectedServer} />
|
||||||
<Label>{t('proxy.config.select_client')} </Label>
|
<Label>{t('proxy.config.select_client')} </Label>
|
||||||
<ClientSelector setClientID={setNewClientID} clientID={newClientID} />
|
<ClientSelector setClientID={setNewClientID} clientID={newClientID} />
|
||||||
<Label>{t('proxy.config.select_proxy_type')} </Label>
|
<div className="flex items-center space-x-2 my-2">
|
||||||
<BaseSelector
|
<Label>{t('proxy.config.advanced_mode')}</Label>
|
||||||
dataList={supportedProxyTypes.map((type) => ({ value: type, label: type }))}
|
<Switch onCheckedChange={setAdvancedMode} />
|
||||||
value={proxyType}
|
</div>
|
||||||
setValue={(value) => {
|
{!advancedMode && (
|
||||||
setProxyType(value as ProxyType)
|
<>
|
||||||
}}
|
<Label>{t('proxy.config.select_proxy_type')} </Label>
|
||||||
/>
|
<BaseSelector
|
||||||
{proxyConfigs &&
|
dataList={supportedProxyTypes.map((type) => ({ value: type, label: type }))}
|
||||||
selectedServer &&
|
value={proxyType}
|
||||||
proxyConfigs.length > 0 &&
|
setValue={(value) => {
|
||||||
proxyConfigs[0] &&
|
setProxyType(value as ProxyType)
|
||||||
TypedProxyConfigValid(proxyConfigs[0]) && (
|
}}
|
||||||
<div className="flex flex-row w-full overflow-auto">
|
/>
|
||||||
<div className="flex flex-col">
|
{proxyConfigs &&
|
||||||
<VisitPreview server={selectedServer} typedProxyConfig={proxyConfigs[0]} />
|
selectedServer &&
|
||||||
</div>
|
proxyConfigs.length > 0 &&
|
||||||
</div>
|
proxyConfigs[0] &&
|
||||||
)}
|
TypedProxyConfigValid(proxyConfigs[0]) && (
|
||||||
<Label>{t('proxy.config.proxy_name')} </Label>
|
<div className="flex flex-row w-full overflow-auto">
|
||||||
<Input
|
<div className="flex flex-col">
|
||||||
className="text-sm"
|
<VisitPreview server={selectedServer} typedProxyConfig={proxyConfigs[0]} />
|
||||||
defaultValue={proxyName}
|
</div>
|
||||||
onChange={(e) => setProxyName(e.target.value)}
|
</div>
|
||||||
disabled={disableChangeProxyName}
|
)}
|
||||||
/>
|
<Label>{t('proxy.config.proxy_name')} </Label>
|
||||||
{proxyName && newClientID && newServerID && (
|
<Input
|
||||||
<TypedProxyForm
|
className="text-sm"
|
||||||
serverID={newServerID}
|
defaultValue={proxyName}
|
||||||
clientID={newClientID}
|
onChange={(e) => setProxyName(e.target.value)}
|
||||||
proxyName={proxyName}
|
disabled={disableChangeProxyName}
|
||||||
defaultProxyConfig={proxyConfigs && proxyConfigs.length > 0 ? proxyConfigs[0] : undefined}
|
/>
|
||||||
clientProxyConfigs={proxyConfigs}
|
{proxyName && newClientID && newServerID && (
|
||||||
setClientProxyConfigs={setProxyConfigs}
|
<TypedProxyForm
|
||||||
enablePreview={false}
|
serverID={newServerID}
|
||||||
/>
|
clientID={newClientID}
|
||||||
|
proxyName={proxyName}
|
||||||
|
defaultProxyConfig={proxyConfigs && proxyConfigs.length > 0 ? proxyConfigs[0] : undefined}
|
||||||
|
clientProxyConfigs={proxyConfigs}
|
||||||
|
setClientProxyConfigs={setProxyConfigs}
|
||||||
|
enablePreview={false}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{advancedMode && (
|
||||||
|
<>
|
||||||
|
<Label>{t('proxy.config.raw_json')}</Label>
|
||||||
|
<Textarea
|
||||||
|
className="w-full h-64 font-mono text-sm p-2 border"
|
||||||
|
value={rawConfig}
|
||||||
|
onChange={(e) => setRawConfig(e.target.value)}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(rawConfig) as TypedProxyConfig[]
|
||||||
|
setProxyConfigs(parsed)
|
||||||
|
} catch {
|
||||||
|
toast(t('proxy.config.invalid_json'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('proxy.config.draft')}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
disabled={!TypedProxyConfigValid(proxyConfigs[0])}
|
disabled={advancedMode ? proxyConfigs.length === 0 : !TypedProxyConfigValid(proxyConfigs[0])}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!TypedProxyConfigValid(proxyConfigs[0])) {
|
if (!TypedProxyConfigValid(proxyConfigs[0])) {
|
||||||
toast(t('proxy.config.invalid_config'))
|
toast(t('proxy.config.invalid_config'))
|
||||||
|
@@ -59,7 +59,7 @@ export const CreateWorkerDialog: React.FC<CreateWorkerDialogProps> = ({ refetchT
|
|||||||
refetchTrigger(new Date().toISOString())
|
refetchTrigger(new Date().toISOString())
|
||||||
},
|
},
|
||||||
onError: (err: any) => {
|
onError: (err: any) => {
|
||||||
toast(err?.message || t('worker.create'))
|
toast(err?.message || t('worker.create.error'))
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -230,7 +230,7 @@ export function WorkerStatus({ workerId, clients = [], compact = false }: Worker
|
|||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1">
|
||||||
<span className="w-2 h-2 rounded-full bg-red-500" />
|
<span className="w-2 h-2 rounded-full bg-red-500" />
|
||||||
<span>
|
<span>
|
||||||
{t('worker.status')}: {Clients}/{totalClients}
|
{t('worker.status_text')}: {Clients}/{totalClients}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1">
|
||||||
@@ -242,7 +242,7 @@ export function WorkerStatus({ workerId, clients = [], compact = false }: Worker
|
|||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1">
|
||||||
<span className="w-2 h-2 rounded-full bg-red-500" />
|
<span className="w-2 h-2 rounded-full bg-red-500" />
|
||||||
<span>
|
<span>
|
||||||
{t('worker.status')}: {Ingresses}/{totalIngresses}
|
{t('worker.status_text')}: {Ingresses}/{totalIngresses}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -287,8 +287,12 @@
|
|||||||
"create_failed": "Create failed",
|
"create_failed": "Create failed",
|
||||||
"select_server": "Select Server",
|
"select_server": "Select Server",
|
||||||
"select_client": "Select Client",
|
"select_client": "Select Client",
|
||||||
|
"advanced_mode": "Advanced Mode",
|
||||||
"select_proxy_type": "Select Tunnel Type",
|
"select_proxy_type": "Select Tunnel Type",
|
||||||
"proxy_name": "Tunnel Name",
|
"proxy_name": "Tunnel Name",
|
||||||
|
"raw_json": "Original Config",
|
||||||
|
"invalid_json": "Json Invalid",
|
||||||
|
"draft": "Draft",
|
||||||
"invalid_config": "Invalid configuration",
|
"invalid_config": "Invalid configuration",
|
||||||
"submit": "Submit"
|
"submit": "Submit"
|
||||||
},
|
},
|
||||||
@@ -542,6 +546,8 @@
|
|||||||
"delete_description": "Ingress deleted successfully"
|
"delete_description": "Ingress deleted successfully"
|
||||||
},
|
},
|
||||||
"create": {
|
"create": {
|
||||||
|
"success": "Create Success",
|
||||||
|
"error": "Create Error",
|
||||||
"button": "Create Worker",
|
"button": "Create Worker",
|
||||||
"title": "Create Worker",
|
"title": "Create Worker",
|
||||||
"description": "Create a new Worker",
|
"description": "Create a new Worker",
|
||||||
@@ -574,9 +580,10 @@
|
|||||||
"healthy": "Healthy",
|
"healthy": "Healthy",
|
||||||
"degraded": "Degraded",
|
"degraded": "Degraded",
|
||||||
"clients": "Deployments",
|
"clients": "Deployments",
|
||||||
"running": "Running",
|
"ingresses": "Ingresses",
|
||||||
"ingresses": "Ingresses"
|
"running": "Running"
|
||||||
}
|
},
|
||||||
|
"status_text": "Status"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"new": "New",
|
"new": "New",
|
||||||
|
@@ -286,8 +286,12 @@
|
|||||||
"create_failed": "创建失败",
|
"create_failed": "创建失败",
|
||||||
"select_server": "选择服务器",
|
"select_server": "选择服务器",
|
||||||
"select_client": "选择客户端",
|
"select_client": "选择客户端",
|
||||||
|
"advanced_mode": "高级模式",
|
||||||
"select_proxy_type": "选择隧道类型",
|
"select_proxy_type": "选择隧道类型",
|
||||||
"proxy_name": "隧道名称",
|
"proxy_name": "隧道名称",
|
||||||
|
"raw_json": "原始配置文件",
|
||||||
|
"invalid_json": "JSON非法",
|
||||||
|
"draft": "暂存",
|
||||||
"invalid_config": "配置不正确",
|
"invalid_config": "配置不正确",
|
||||||
"submit": "提交"
|
"submit": "提交"
|
||||||
},
|
},
|
||||||
@@ -540,6 +544,8 @@
|
|||||||
"delete_description": "入口已成功删除"
|
"delete_description": "入口已成功删除"
|
||||||
},
|
},
|
||||||
"create": {
|
"create": {
|
||||||
|
"success": "创建成功",
|
||||||
|
"error": "创建失败",
|
||||||
"button": "创建",
|
"button": "创建",
|
||||||
"title": "创建Worker",
|
"title": "创建Worker",
|
||||||
"description": "创建一个新的Worker",
|
"description": "创建一个新的Worker",
|
||||||
@@ -572,9 +578,10 @@
|
|||||||
"healthy": "健康",
|
"healthy": "健康",
|
||||||
"degraded": "降级",
|
"degraded": "降级",
|
||||||
"clients": "部署",
|
"clients": "部署",
|
||||||
"running": "运行中",
|
"ingresses": "入口",
|
||||||
"ingresses": "入口"
|
"running": "运行中"
|
||||||
}
|
},
|
||||||
|
"status_text": "状态"
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"new": "新建",
|
"new": "新建",
|
||||||
|
Reference in New Issue
Block a user