mirror of
https://gitcode.com/JianFeeeee/webui4frpc.git
synced 2026-09-21 09:27:57 +00:00
feat: M2 HTTP/HTTPS 完善
- store: Local 增加 customDomains/subdomain/locations/hostHeaderRewrite/httpHeaders/basicAuth;Remote 增加 vhostHttpPort;migrate() 补列;CRUD 读写(JSOm 编解码) - render: http/https proxy 输出 customDomains/subdomain/locations/hostHeaderRewrite/httpHeaders/basicAuth - main: renderRemote 透传 M2 字段(customDomains 逗号拆分 splitCSV) - web: LocalNode 折叠 HTTP 路由高级区;RemoteNode vhostHTTPPort;StatusView 显示 HTTP 访问端口 - 测试: TestRenderHTTPAdvanced / TestRenderHTTPSUsesCustomDomains;端到端 PUT canvas→config 验证通过
This commit is contained in:
@ -46,6 +46,15 @@
|
||||
<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 class="adv-sep">HTTP/HTTPS 路由</div>
|
||||
<label>域名(逗号分隔) <input v-model="customDomainsField" placeholder="app.example.com" /></label>
|
||||
<label>子域 <input v-model="subdomainField" placeholder="app (需 frps subdomain_host)" /></label>
|
||||
<label>路径路由(JSON) <input v-model="locationsField" placeholder='["/api","/admin"]' /></label>
|
||||
<label>Host 改写 <input v-model="hostHeaderRewriteField" placeholder="backend.internal" /></label>
|
||||
<label>请求头(JSON) <input v-model="httpHeadersField" placeholder='{"X-Custom":"val"}' /></label>
|
||||
<label>BasicAuth 用户 <input v-model="basicAuthUserField" placeholder="user" /></label>
|
||||
<label>BasicAuth 密码 <input v-model="basicAuthPasswordField" type="password" placeholder="pass" /></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -114,6 +123,44 @@ const annotationsField = computed({
|
||||
emit('update:data', { ...props.data, annotations: parsed })
|
||||
},
|
||||
})
|
||||
const customDomainsField = field('customDomains')
|
||||
const subdomainField = field('subdomain')
|
||||
const hostHeaderRewriteField = field('hostHeaderRewrite')
|
||||
const basicAuthUserField = field('basicAuthUser')
|
||||
const basicAuthPasswordField = field('basicAuthPassword')
|
||||
|
||||
// locations is an array shown as JSON in the UI
|
||||
const locationsField = computed({
|
||||
get: () => JSON.stringify(props.data.locations ?? []),
|
||||
set: (v: string) => {
|
||||
let parsed: string[] = []
|
||||
if (v.trim()) {
|
||||
try {
|
||||
const arr = JSON.parse(v)
|
||||
if (Array.isArray(arr)) parsed = arr.map(String)
|
||||
else return
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
}
|
||||
emit('update:data', { ...props.data, locations: parsed })
|
||||
},
|
||||
})
|
||||
// httpHeaders is a map shown as JSON
|
||||
const httpHeadersField = computed({
|
||||
get: () => JSON.stringify(props.data.httpHeaders ?? {}),
|
||||
set: (v: string) => {
|
||||
let parsed: Record<string, string> = {}
|
||||
if (v.trim()) {
|
||||
try {
|
||||
parsed = JSON.parse(v)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
}
|
||||
emit('update:data', { ...props.data, httpHeaders: parsed })
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@ -45,6 +45,8 @@
|
||||
</label>
|
||||
<label>TLS SNI <input v-model="transportTlsServerNameField" placeholder="frps.local" /></label>
|
||||
<label>连接池 <input v-model.number="transportPoolField" type="number" min="0" /></label>
|
||||
<label class="adv-sep">HTTP 访问端口 (frps vhostHTTPPort)</label>
|
||||
<label>vhostHTTPPort <input v-model.number="vhostHttpPortField" type="number" min="0" /></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -84,6 +86,7 @@ const transportProtocolField = field('transportProtocol')
|
||||
const transportTlsField = field('transportTls')
|
||||
const transportTlsServerNameField = field('transportTlsServerName')
|
||||
const transportPoolField = field('transportPool')
|
||||
const vhostHttpPortField = field('vhostHttpPort')
|
||||
const showAdv = ref(false)
|
||||
</script>
|
||||
|
||||
|
||||
140
web/src/types.ts
140
web/src/types.ts
@ -2,109 +2,121 @@
|
||||
|
||||
// CanvasLocal / CanvasRemote are aliases kept for node components migrated
|
||||
// from the original prototype; they equal Local / Remote.
|
||||
export type CanvasLocal = Local
|
||||
export type CanvasRemote = Remote
|
||||
export type CanvasLocal = Local;
|
||||
export type CanvasRemote = Remote;
|
||||
|
||||
// CanvasLink mirrors the store.Link JSON shape used by the canvas editor.
|
||||
export type CanvasLink = Link
|
||||
export type CanvasLink = Link;
|
||||
|
||||
export interface Local {
|
||||
name: string
|
||||
ip: string
|
||||
port: number
|
||||
protocol: string // tcp | udp | http | https
|
||||
name: string;
|
||||
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>
|
||||
useEncryption?: boolean;
|
||||
useCompression?: boolean;
|
||||
bandwidthLimit?: string;
|
||||
poolCount?: number;
|
||||
metadatas?: Record<string, string>;
|
||||
annotations?: Record<string, string>;
|
||||
|
||||
// M2 HTTP/HTTPS routing (http/https only)
|
||||
customDomains?: string; // comma-separated
|
||||
subdomain?: string;
|
||||
locations?: string[];
|
||||
hostHeaderRewrite?: string;
|
||||
httpHeaders?: Record<string, string>;
|
||||
basicAuthUser?: string;
|
||||
basicAuthPassword?: string;
|
||||
}
|
||||
|
||||
export interface Remote {
|
||||
name: string
|
||||
ip: string
|
||||
port: number // frps connect port
|
||||
token?: string
|
||||
url?: string
|
||||
enabled: boolean
|
||||
name: string;
|
||||
ip: string;
|
||||
port: number; // frps connect port
|
||||
token?: string;
|
||||
url?: string;
|
||||
enabled: boolean;
|
||||
|
||||
// M1 transport section
|
||||
transportProtocol?: string // tcp | quic | kcp | websocket
|
||||
transportTls?: boolean
|
||||
transportPool?: number
|
||||
transportTlsServerName?: string
|
||||
transportProtocol?: string; // tcp | quic | kcp | websocket
|
||||
transportTls?: boolean;
|
||||
transportPool?: number;
|
||||
transportTlsServerName?: string;
|
||||
|
||||
// M2: frps vhostHTTPPort
|
||||
vhostHttpPort?: number;
|
||||
}
|
||||
|
||||
export interface Link {
|
||||
id?: number
|
||||
local: string
|
||||
remote: string
|
||||
remotePort: number
|
||||
offsetX?: number
|
||||
offsetY?: number
|
||||
id?: number;
|
||||
local: string;
|
||||
remote: string;
|
||||
remotePort: number;
|
||||
offsetX?: number;
|
||||
offsetY?: number;
|
||||
}
|
||||
|
||||
export interface CanvasData {
|
||||
locals: Local[]
|
||||
remotes: Remote[]
|
||||
links: Link[]
|
||||
locals: Local[];
|
||||
remotes: Remote[];
|
||||
links: Link[];
|
||||
}
|
||||
|
||||
export interface Settings {
|
||||
autoStartProfiles: boolean
|
||||
restartOnExit: boolean
|
||||
restartIntervalSeconds: number
|
||||
binaryPath?: string
|
||||
autoStartProfiles: boolean;
|
||||
restartOnExit: boolean;
|
||||
restartIntervalSeconds: number;
|
||||
binaryPath?: string;
|
||||
}
|
||||
|
||||
export interface ProcessStatus {
|
||||
state: string
|
||||
pid?: number
|
||||
startTime?: number
|
||||
restartCount: number
|
||||
exitCode?: number
|
||||
err?: string
|
||||
state: string;
|
||||
pid?: number;
|
||||
startTime?: number;
|
||||
restartCount: number;
|
||||
exitCode?: number;
|
||||
err?: string;
|
||||
}
|
||||
|
||||
export interface StatusProfile {
|
||||
name: string
|
||||
enabled: boolean
|
||||
process: ProcessStatus
|
||||
hasProcess: boolean
|
||||
forwards: { service: string; remotePort: number; localPort?: number }[]
|
||||
name: string;
|
||||
enabled: boolean;
|
||||
process: ProcessStatus;
|
||||
hasProcess: boolean;
|
||||
forwards: { service: string; remotePort: number; localPort?: number }[];
|
||||
}
|
||||
|
||||
export interface StatusResp {
|
||||
version: string
|
||||
workDir: string
|
||||
settings: Settings
|
||||
services: Local[]
|
||||
remotes: Remote[]
|
||||
binaryPath: string
|
||||
profiles: StatusProfile[]
|
||||
localStatus: LocalStatus[]
|
||||
version: string;
|
||||
workDir: string;
|
||||
settings: Settings;
|
||||
services: Local[];
|
||||
remotes: Remote[];
|
||||
binaryPath: string;
|
||||
profiles: StatusProfile[];
|
||||
localStatus: LocalStatus[];
|
||||
}
|
||||
|
||||
export interface LocalTargetStatus {
|
||||
remote: string
|
||||
remotePort: number
|
||||
workerState: string
|
||||
remote: string;
|
||||
remotePort: number;
|
||||
workerState: string;
|
||||
}
|
||||
|
||||
export interface LocalStatus {
|
||||
local: Local
|
||||
targets: LocalTargetStatus[]
|
||||
local: Local;
|
||||
targets: LocalTargetStatus[];
|
||||
}
|
||||
|
||||
export interface BinaryStatus {
|
||||
binaryPath: string
|
||||
version?: string
|
||||
binaryPath: string;
|
||||
version?: string;
|
||||
}
|
||||
|
||||
export interface InstallResult {
|
||||
path: string
|
||||
version: string
|
||||
path: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
@ -33,6 +33,9 @@
|
||||
<div class="ns-meta">
|
||||
<span v-if="p.process.pid">pid {{ p.process.pid }}</span>
|
||||
<span class="ns-err" v-else-if="p.process.err">{{ p.process.err }}</span>
|
||||
<span v-if="remoteVhostPort(p.name)" class="ns-vhost">
|
||||
HTTP 访问端口 :{{ remoteVhostPort(p.name) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="ns-forwards" v-if="p.forwards.length">
|
||||
<span v-for="f in p.forwards" :key="f.service" class="ns-fwd">
|
||||
@ -218,6 +221,10 @@ const workerStateLabel = (s?: string) => {
|
||||
|
||||
const workerHealthy = (s?: string) => s === 'running'
|
||||
|
||||
// remoteVhostPort returns the frps vhostHTTPPort for a remote (0 if unset).
|
||||
const remoteVhostPort = (name: string): number =>
|
||||
status.value?.remotes.find((r) => r.name === name)?.vhostHttpPort || 0
|
||||
|
||||
onMounted(() => {
|
||||
loadStatus()
|
||||
timer = window.setInterval(loadStatus, 5000)
|
||||
@ -402,6 +409,15 @@ onBeforeUnmount(() => {
|
||||
color: $color-danger;
|
||||
}
|
||||
|
||||
.ns-vhost {
|
||||
margin-left: 8px;
|
||||
background: #ecf5ff;
|
||||
color: #409eff;
|
||||
border-radius: 6px;
|
||||
padding: 1px 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ns-forwards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
Reference in New Issue
Block a user