mirror of
https://gitcode.com/JianFeeeee/webui4frpc.git
synced 2026-09-21 01:17:56 +00:00
feat: M1 高级传输参数(P0)
- store: Local 增加 useEncryption/useCompression/bandwidthLimit/poolCount/metadatas/annotations;Remote 增加 transportProtocol/transportTls/transportPool/transportTlsServerName;新增 migrate() ALTER TABLE 迁移旧库 - render: 输出 transport 段(protocol/tls/poolCount)与 proxy 高级字段;补 TestRenderTransportAdvanced/OmittedWhenEmpty - main: renderRemote 闭包透传 M1 字段到 render.Proxy - web: LocalNode/RemoteNode 折叠高级区表单,types.ts 接口扩展 - 验证:go test 全绿、vue-tsc/build 通过、端到端 PUT canvas→frpc JSON 输出完整
This commit is contained in:
@ -28,12 +28,32 @@
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="adv">
|
||||
<button class="adv-toggle" @click.stop="showAdv = !showAdv">
|
||||
{{ showAdv ? '▾ 收起高级' : '▸ 高级' }}
|
||||
</button>
|
||||
<div v-if="showAdv" class="adv-body">
|
||||
<label class="adv-check">
|
||||
<input v-model="useEncryptionField" type="checkbox" /> 加密
|
||||
(useEncryption)
|
||||
</label>
|
||||
<label class="adv-check">
|
||||
<input v-model="useCompressionField" type="checkbox" /> 压缩
|
||||
(useCompression)
|
||||
</label>
|
||||
<label>限速 <input v-model="bandwidthLimitField" placeholder="如 1MB" /></label>
|
||||
<label>连接池 <input v-model.number="poolCountField" type="number" min="0" /></label>
|
||||
<label>元数据(JSON) <input v-model="metadatasField" placeholder='{"env":"prod"}' /></label>
|
||||
<label>注解(JSON) <input v-model="annotationsField" placeholder='{"owner":"ops"}' /></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Handle, Position } from '@vue-flow/core'
|
||||
import { computed } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import type { CanvasLocal } from '../types'
|
||||
|
||||
const props = defineProps<{
|
||||
@ -59,6 +79,41 @@ const nameField = field('name')
|
||||
const ipField = field('ip')
|
||||
const portField = field('port')
|
||||
const protocolField = field('protocol')
|
||||
const useEncryptionField = field('useEncryption')
|
||||
const useCompressionField = field('useCompression')
|
||||
const bandwidthLimitField = field('bandwidthLimit')
|
||||
const poolCountField = field('poolCount')
|
||||
|
||||
// map fields shown as JSON strings in the UI
|
||||
const showAdv = ref(false)
|
||||
const metadatasField = computed({
|
||||
get: () => JSON.stringify(props.data.metadatas ?? {}),
|
||||
set: (v: string) => {
|
||||
let parsed: Record<string, string> = {}
|
||||
if (v.trim()) {
|
||||
try {
|
||||
parsed = JSON.parse(v)
|
||||
} catch {
|
||||
return // keep old value on invalid JSON
|
||||
}
|
||||
}
|
||||
emit('update:data', { ...props.data, metadatas: parsed })
|
||||
},
|
||||
})
|
||||
const annotationsField = computed({
|
||||
get: () => JSON.stringify(props.data.annotations ?? {}),
|
||||
set: (v: string) => {
|
||||
let parsed: Record<string, string> = {}
|
||||
if (v.trim()) {
|
||||
try {
|
||||
parsed = JSON.parse(v)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
}
|
||||
emit('update:data', { ...props.data, annotations: parsed })
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@ -162,5 +217,54 @@ const protocolField = field('protocol')
|
||||
flex: 0 0 64px;
|
||||
}
|
||||
}
|
||||
|
||||
.adv {
|
||||
margin-top: 6px;
|
||||
.adv-toggle {
|
||||
border: 1px dashed #a5d6a7;
|
||||
background: transparent;
|
||||
color: #558b2f;
|
||||
font-size: 11px;
|
||||
border-radius: 6px;
|
||||
padding: 2px 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.adv-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
margin-top: 6px;
|
||||
padding: 6px;
|
||||
background: rgba(76, 175, 80, 0.06);
|
||||
border-radius: 8px;
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 11px;
|
||||
color: #558b2f;
|
||||
|
||||
input,
|
||||
select {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
border: 1px solid #a5d6a7;
|
||||
border-radius: 6px;
|
||||
padding: 2px 6px;
|
||||
font-size: 12px;
|
||||
background: #fff;
|
||||
color: #333;
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: #4caf50;
|
||||
}
|
||||
}
|
||||
}
|
||||
.adv-check {
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -25,12 +25,34 @@
|
||||
<label>令牌 <input v-model="tokenField" /></label>
|
||||
<label>URL <input v-model="urlField" /></label>
|
||||
</div>
|
||||
|
||||
<div class="adv">
|
||||
<button class="adv-toggle" @click.stop="showAdv = !showAdv">
|
||||
{{ showAdv ? '▾ 收起高级' : '▸ 高级' }}
|
||||
</button>
|
||||
<div v-if="showAdv" class="adv-body">
|
||||
<label
|
||||
>传输协议
|
||||
<select v-model="transportProtocolField">
|
||||
<option value="">tcp(默认)</option>
|
||||
<option value="quic">quic</option>
|
||||
<option value="kcp">kcp</option>
|
||||
<option value="websocket">websocket</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="adv-check">
|
||||
<input v-model="transportTlsField" type="checkbox" /> TLS 传输
|
||||
</label>
|
||||
<label>TLS SNI <input v-model="transportTlsServerNameField" placeholder="frps.local" /></label>
|
||||
<label>连接池 <input v-model.number="transportPoolField" type="number" min="0" /></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Handle, Position } from '@vue-flow/core'
|
||||
import { computed } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import type { CanvasRemote } from '../types'
|
||||
|
||||
const props = defineProps<{
|
||||
@ -58,6 +80,11 @@ const portField = field('port')
|
||||
const tokenField = field('token')
|
||||
const urlField = field('url')
|
||||
const enabledField = field('enabled')
|
||||
const transportProtocolField = field('transportProtocol')
|
||||
const transportTlsField = field('transportTls')
|
||||
const transportTlsServerNameField = field('transportTlsServerName')
|
||||
const transportPoolField = field('transportPool')
|
||||
const showAdv = ref(false)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@ -168,5 +195,54 @@ const enabledField = field('enabled')
|
||||
flex: 0 0 64px;
|
||||
}
|
||||
}
|
||||
|
||||
.adv {
|
||||
margin-top: 6px;
|
||||
.adv-toggle {
|
||||
border: 1px dashed #ffcc80;
|
||||
background: transparent;
|
||||
color: #ef6c00;
|
||||
font-size: 11px;
|
||||
border-radius: 6px;
|
||||
padding: 2px 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.adv-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
margin-top: 6px;
|
||||
padding: 6px;
|
||||
background: rgba(255, 152, 0, 0.06);
|
||||
border-radius: 8px;
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 11px;
|
||||
color: #ef6c00;
|
||||
|
||||
input,
|
||||
select {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
border: 1px solid #ffcc80;
|
||||
border-radius: 6px;
|
||||
padding: 2px 6px;
|
||||
font-size: 12px;
|
||||
background: #fff;
|
||||
color: #333;
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: #ff9800;
|
||||
}
|
||||
}
|
||||
}
|
||||
.adv-check {
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -13,6 +13,14 @@ export interface Local {
|
||||
ip: string
|
||||
port: number
|
||||
protocol: string // tcp | udp | http | https
|
||||
|
||||
// M1 advanced transport knobs (optional; empty/zero = frpc defaults)
|
||||
useEncryption?: boolean
|
||||
useCompression?: boolean
|
||||
bandwidthLimit?: string
|
||||
poolCount?: number
|
||||
metadatas?: Record<string, string>
|
||||
annotations?: Record<string, string>
|
||||
}
|
||||
|
||||
export interface Remote {
|
||||
@ -22,6 +30,12 @@ export interface Remote {
|
||||
token?: string
|
||||
url?: string
|
||||
enabled: boolean
|
||||
|
||||
// M1 transport section
|
||||
transportProtocol?: string // tcp | quic | kcp | websocket
|
||||
transportTls?: boolean
|
||||
transportPool?: number
|
||||
transportTlsServerName?: string
|
||||
}
|
||||
|
||||
export interface Link {
|
||||
|
||||
Reference in New Issue
Block a user