server: replace client metadata with IP address in registry (#5118)

This commit is contained in:
fatedier
2026-01-09 11:07:19 +08:00
committed by GitHub
Unverified
parent 479e9f50c2
commit 1245f8804e
9 changed files with 48 additions and 85 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -4,8 +4,8 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>frp server</title> <title>frp server</title>
<script type="module" crossorigin src="./index-BUrDiw1t.js"></script> <script type="module" crossorigin src="./index-r9B2t7lx.js"></script>
<link rel="stylesheet" crossorigin href="./index-D4KRVvIu.css"> <link rel="stylesheet" crossorigin href="./index-Cl4R6mJh.css">
</head> </head>
<body> <body>
+5 -10
View File
@@ -2,7 +2,6 @@ package server
import ( import (
"fmt" "fmt"
"maps"
"sync" "sync"
"time" "time"
) )
@@ -14,7 +13,7 @@ type ClientInfo struct {
ClientID string ClientID string
RunID string RunID string
Hostname string Hostname string
Metas map[string]string IP string
FirstConnectedAt time.Time FirstConnectedAt time.Time
LastConnectedAt time.Time LastConnectedAt time.Time
DisconnectedAt time.Time DisconnectedAt time.Time
@@ -37,7 +36,7 @@ func NewClientRegistry() *ClientRegistry {
} }
// Register stores/updates metadata for a client and returns the registry key plus whether it conflicts with an online client. // Register stores/updates metadata for a client and returns the registry key plus whether it conflicts with an online client.
func (cr *ClientRegistry) Register(user, clientID, runID, hostname string, metas map[string]string) (key string, conflict bool) { func (cr *ClientRegistry) Register(user, clientID, runID, hostname, remoteAddr string) (key string, conflict bool) {
if runID == "" { if runID == "" {
return "", false return "", false
} }
@@ -72,7 +71,7 @@ func (cr *ClientRegistry) Register(user, clientID, runID, hostname string, metas
info.RunID = runID info.RunID = runID
info.Hostname = hostname info.Hostname = hostname
info.Metas = metas info.IP = remoteAddr
if info.FirstConnectedAt.IsZero() { if info.FirstConnectedAt.IsZero() {
info.FirstConnectedAt = now info.FirstConnectedAt = now
} }
@@ -113,9 +112,7 @@ func (cr *ClientRegistry) List() []ClientInfo {
result := make([]ClientInfo, 0, len(cr.clients)) result := make([]ClientInfo, 0, len(cr.clients))
for _, info := range cr.clients { for _, info := range cr.clients {
cp := *info result = append(result, *info)
cp.Metas = maps.Clone(info.Metas)
result = append(result, cp)
} }
return result return result
} }
@@ -129,9 +126,7 @@ func (cr *ClientRegistry) GetByKey(key string) (ClientInfo, bool) {
if !ok { if !ok {
return ClientInfo{}, false return ClientInfo{}, false
} }
cp := *info return *info, true
cp.Metas = maps.Clone(info.Metas)
return cp, true
} }
func (cr *ClientRegistry) composeClientKey(user, id string) string { func (cr *ClientRegistry) composeClientKey(user, id string) string {
+11 -11
View File
@@ -94,16 +94,16 @@ type serverInfoResp struct {
} }
type clientInfoResp struct { type clientInfoResp struct {
Key string `json:"key"` Key string `json:"key"`
User string `json:"user"` User string `json:"user"`
ClientID string `json:"clientId"` ClientID string `json:"clientID"`
RunID string `json:"runId"` RunID string `json:"runID"`
Hostname string `json:"hostname"` Hostname string `json:"hostname"`
Metas map[string]string `json:"metas,omitempty"` ClientIP string `json:"clientIP,omitempty"`
FirstConnectedAt int64 `json:"firstConnectedAt"` FirstConnectedAt int64 `json:"firstConnectedAt"`
LastConnectedAt int64 `json:"lastConnectedAt"` LastConnectedAt int64 `json:"lastConnectedAt"`
DisconnectedAt int64 `json:"disconnectedAt,omitempty"` DisconnectedAt int64 `json:"disconnectedAt,omitempty"`
Online bool `json:"online"` Online bool `json:"online"`
} }
// /healthz // /healthz
@@ -531,7 +531,7 @@ func buildClientInfoResp(info ClientInfo) clientInfoResp {
ClientID: info.ClientID, ClientID: info.ClientID,
RunID: info.RunID, RunID: info.RunID,
Hostname: info.Hostname, Hostname: info.Hostname,
Metas: info.Metas, ClientIP: info.IP,
FirstConnectedAt: toUnix(info.FirstConnectedAt), FirstConnectedAt: toUnix(info.FirstConnectedAt),
LastConnectedAt: toUnix(info.LastConnectedAt), LastConnectedAt: toUnix(info.LastConnectedAt),
Online: info.Online, Online: info.Online,
+5 -1
View File
@@ -615,7 +615,11 @@ func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login, inter
oldCtl.WaitClosed() oldCtl.WaitClosed()
} }
_, conflict := svr.clientRegistry.Register(loginMsg.User, loginMsg.ClientID, loginMsg.RunID, loginMsg.Hostname, loginMsg.Metas) remoteAddr := ctlConn.RemoteAddr().String()
if host, _, err := net.SplitHostPort(remoteAddr); err == nil {
remoteAddr = host
}
_, conflict := svr.clientRegistry.Register(loginMsg.User, loginMsg.ClientID, loginMsg.RunID, loginMsg.Hostname, remoteAddr)
if conflict { if conflict {
svr.ctlManager.Del(loginMsg.RunID, ctl) svr.ctlManager.Del(loginMsg.RunID, ctl)
ctl.Close() ctl.Close()
+8 -47
View File
@@ -17,6 +17,12 @@
<span class="info-value">{{ client.hostname || 'N/A' }}</span> <span class="info-value">{{ client.hostname || 'N/A' }}</span>
</div> </div>
<div class="info-row" v-if="client.ip">
<el-icon class="info-icon"><Connection /></el-icon>
<span class="info-label">IP:</span>
<span class="info-value monospace">{{ client.ip }}</span>
</div>
<div class="info-row" v-if="client.user"> <div class="info-row" v-if="client.user">
<el-icon class="info-icon"><User /></el-icon> <el-icon class="info-icon"><User /></el-icon>
<span class="info-label">User:</span> <span class="info-label">User:</span>
@@ -26,7 +32,7 @@
<div class="info-row"> <div class="info-row">
<el-icon class="info-icon"><Key /></el-icon> <el-icon class="info-icon"><Key /></el-icon>
<span class="info-label">Run ID:</span> <span class="info-label">Run ID:</span>
<span class="info-value monospace">{{ client.runId }}</span> <span class="info-value monospace">{{ client.runID }}</span>
</div> </div>
<div class="info-row" v-if="client.firstConnectedAt"> <div class="info-row" v-if="client.firstConnectedAt">
@@ -48,26 +54,12 @@
</div> </div>
</div> </div>
<div class="client-metas" v-if="client.metasArray.length > 0">
<div class="metas-label">Metadata:</div>
<div class="metas-tags">
<el-tag
v-for="meta in client.metasArray"
:key="meta.key"
size="small"
type="info"
class="meta-tag"
>
{{ meta.key }}: {{ meta.value }}
</el-tag>
</div>
</div>
</el-card> </el-card>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
import { Monitor, User, Key, Clock, CircleClose } from '@element-plus/icons-vue' import { Monitor, User, Key, Clock, CircleClose, Connection } from '@element-plus/icons-vue'
import type { Client } from '../utils/client' import type { Client } from '../utils/client'
interface Props { interface Props {
@@ -190,37 +182,6 @@ html.dark .info-value {
color: #d1d5db; color: #d1d5db;
} }
.client-metas {
margin-bottom: 16px;
padding-top: 12px;
border-top: 1px solid #e4e7ed;
}
html.dark .client-metas {
border-top-color: #3a3d5c;
}
.metas-label {
font-size: 13px;
color: #909399;
font-weight: 500;
margin-bottom: 8px;
}
html.dark .metas-label {
color: #9ca3af;
}
.metas-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.meta-tag {
font-size: 12px;
}
.monospace { .monospace {
font-family: 'Courier New', Courier, monospace; font-family: 'Courier New', Courier, monospace;
font-size: 12px; font-size: 12px;
+3 -2
View File
@@ -1,9 +1,10 @@
export interface ClientInfoData { export interface ClientInfoData {
key: string key: string
user: string user: string
clientId: string clientID: string
runId: string runID: string
hostname: string hostname: string
clientIP?: string
metas?: Record<string, string> metas?: Record<string, string>
firstConnectedAt: number firstConnectedAt: number
lastConnectedAt: number lastConnectedAt: number
+12 -10
View File
@@ -4,9 +4,10 @@ import type { ClientInfoData } from '../types/client'
export class Client { export class Client {
key: string key: string
user: string user: string
clientId: string clientID: string
runId: string runID: string
hostname: string hostname: string
ip: string
metas: Map<string, string> metas: Map<string, string>
firstConnectedAt: Date firstConnectedAt: Date
lastConnectedAt: Date lastConnectedAt: Date
@@ -16,9 +17,10 @@ export class Client {
constructor(data: ClientInfoData) { constructor(data: ClientInfoData) {
this.key = data.key this.key = data.key
this.user = data.user this.user = data.user
this.clientId = data.clientId this.clientID = data.clientID
this.runId = data.runId this.runID = data.runID
this.hostname = data.hostname this.hostname = data.hostname
this.ip = data.clientIP || ''
this.metas = new Map<string, string>() this.metas = new Map<string, string>()
if (data.metas) { if (data.metas) {
for (const [key, value] of Object.entries(data.metas)) { for (const [key, value] of Object.entries(data.metas)) {
@@ -34,14 +36,14 @@ export class Client {
} }
get displayName(): string { get displayName(): string {
if (this.clientId) { if (this.clientID) {
return this.user ? `${this.user}.${this.clientId}` : this.clientId return this.user ? `${this.user}.${this.clientID}` : this.clientID
} }
return this.runId return this.runID
} }
get shortRunId(): string { get shortRunId(): string {
return this.runId.substring(0, 8) return this.runID.substring(0, 8)
} }
get firstConnectedAgo(): string { get firstConnectedAgo(): string {
@@ -74,8 +76,8 @@ export class Client {
return ( return (
this.key.toLowerCase().includes(search) || this.key.toLowerCase().includes(search) ||
this.user.toLowerCase().includes(search) || this.user.toLowerCase().includes(search) ||
this.clientId.toLowerCase().includes(search) || this.clientID.toLowerCase().includes(search) ||
this.runId.toLowerCase().includes(search) || this.runID.toLowerCase().includes(search) ||
this.hostname.toLowerCase().includes(search) this.hostname.toLowerCase().includes(search)
) )
} }