khatru: list clients and client details.
This commit is contained in:
@@ -2,6 +2,8 @@ package khatru
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
"iter"
|
"iter"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -9,6 +11,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"fiatjaf.com/lib/channelmutex"
|
"fiatjaf.com/lib/channelmutex"
|
||||||
"fiatjaf.com/nostr"
|
"fiatjaf.com/nostr"
|
||||||
@@ -212,6 +215,89 @@ func (rl *Relay) Stats() (clients, listeners int) {
|
|||||||
return len(rl.clients), listeners
|
return len(rl.clients), listeners
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ClientInfo struct {
|
||||||
|
ID string
|
||||||
|
IP string
|
||||||
|
UserAgent string
|
||||||
|
Origin string
|
||||||
|
Authenticated []nostr.PubKey
|
||||||
|
SubscriptionCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubscriptionInfo struct {
|
||||||
|
ID string
|
||||||
|
Filter nostr.Filter
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientSnapshot struct {
|
||||||
|
ClientInfo
|
||||||
|
Subscriptions []SubscriptionInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rl *Relay) ListClients() []ClientInfo {
|
||||||
|
rl.clientsMutex.Lock()
|
||||||
|
defer rl.clientsMutex.Unlock()
|
||||||
|
|
||||||
|
clients := make([]ClientInfo, 0, len(rl.clients))
|
||||||
|
for ws, specs := range rl.clients {
|
||||||
|
clients = append(clients, ClientInfo{
|
||||||
|
ID: ws.GetID(),
|
||||||
|
IP: GetIPFromRequest(ws.Request),
|
||||||
|
UserAgent: ws.Request.UserAgent(),
|
||||||
|
Origin: ws.Request.Header.Get("Origin"),
|
||||||
|
Authenticated: ws.AuthedPublicKeys,
|
||||||
|
SubscriptionCount: len(specs),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return clients
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rl *Relay) GetClientSnapshot(id string) (ClientSnapshot, bool) {
|
||||||
|
rl.clientsMutex.Lock()
|
||||||
|
defer rl.clientsMutex.Unlock()
|
||||||
|
|
||||||
|
ptrn, err := base64.RawURLEncoding.DecodeString(id)
|
||||||
|
if err != nil {
|
||||||
|
return ClientSnapshot{}, false
|
||||||
|
}
|
||||||
|
ptr := binary.LittleEndian.Uint64(ptrn)
|
||||||
|
|
||||||
|
// DANGEROUS:
|
||||||
|
// don't try to do anything with this `ws` object before we confirm it exists by checking the rl.clients map
|
||||||
|
ws := (*WebSocket)(unsafe.Pointer(uintptr(ptr)))
|
||||||
|
specs, ok := rl.clients[ws]
|
||||||
|
if !ok {
|
||||||
|
return ClientSnapshot{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
details := ClientSnapshot{
|
||||||
|
ClientInfo: ClientInfo{
|
||||||
|
ID: id,
|
||||||
|
IP: GetIPFromRequest(ws.Request),
|
||||||
|
UserAgent: ws.Request.UserAgent(),
|
||||||
|
Origin: ws.Request.Header.Get("Origin"),
|
||||||
|
Authenticated: ws.AuthedPublicKeys,
|
||||||
|
SubscriptionCount: len(specs),
|
||||||
|
},
|
||||||
|
Subscriptions: make([]SubscriptionInfo, 0, len(specs)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, spec := range specs {
|
||||||
|
filter := nostr.Filter{}
|
||||||
|
if sub, ok := rl.dispatcher.subscriptions.Load(spec.ssid); ok {
|
||||||
|
filter = sub.filter
|
||||||
|
}
|
||||||
|
|
||||||
|
details.Subscriptions = append(details.Subscriptions, SubscriptionInfo{
|
||||||
|
ID: spec.sid,
|
||||||
|
Filter: filter,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return details, true
|
||||||
|
}
|
||||||
|
|
||||||
func (rl *Relay) Router() *http.ServeMux {
|
func (rl *Relay) Router() *http.ServeMux {
|
||||||
return rl.serveMux
|
return rl.serveMux
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ package khatru
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"fiatjaf.com/nostr"
|
"fiatjaf.com/nostr"
|
||||||
"github.com/fasthttp/websocket"
|
"github.com/fasthttp/websocket"
|
||||||
@@ -31,6 +34,13 @@ type WebSocket struct {
|
|||||||
negentropySessions *xsync.MapOf[string, *NegentropySession]
|
negentropySessions *xsync.MapOf[string, *NegentropySession]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ws *WebSocket) GetID() string {
|
||||||
|
ptr := uintptr(unsafe.Pointer(ws))
|
||||||
|
var id [8]byte
|
||||||
|
binary.LittleEndian.PutUint64(id[:], uint64(ptr))
|
||||||
|
return base64.RawURLEncoding.EncodeToString(id[:])
|
||||||
|
}
|
||||||
|
|
||||||
func (ws *WebSocket) WriteJSON(any any) error {
|
func (ws *WebSocket) WriteJSON(any any) error {
|
||||||
if ws == nil {
|
if ws == nil {
|
||||||
return fmt.Errorf("connection doesn't exist")
|
return fmt.Errorf("connection doesn't exist")
|
||||||
|
|||||||
Reference in New Issue
Block a user