mirror of
https://github.com/fatedier/frp.git
synced 2026-01-10 10:13:16 +00:00
server: replace client metadata with IP address in registry (#5118)
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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>
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user