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
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

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>

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 {

View File

@@ -96,10 +96,10 @@ 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"`
@@ -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,

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()

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;

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

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)
) )
} }