diff --git a/Readme.md b/Readme.md index 7f660c2..03b4cad 100644 --- a/Readme.md +++ b/Readme.md @@ -5,6 +5,8 @@ A simple clipboard synchronization tool built with Go. It consists of a server a - Real-time clipboard synchronization - Cross-platform support (Windows, macOS, Linux) - Easy to set up and use +- Lightweight and efficient +- Optional statistics dashboard for monitoring connected clients and message relay counts ## Prerequisites - Go programming language installed (version 1.16 or higher) @@ -27,6 +29,7 @@ const ( // Update these values before building EmbeddedServerIP = "0.0.0.0" EmbeddedServerPort = "8080" + EmbeddedServerStats = true ) ``` @@ -58,6 +61,15 @@ I use this on my home server, which i can connect to with tailscale, so i don't Clients are identified by the `EmbeddedIdentification` value in the client config file. Make sure to set this to a unique value for each USER you want to sync. If you want to sync multiple devices for the same user, just run multiple instances of the client with the same `EmbeddedIdentification`. +## Statistics Dashboard + +If `EmbeddedServerStats` is set, the server will serve a live statistics dashboard at [http://localhost:8080/stats](http://localhost:8080/stats) showing: + +- Number of connected clients +- Number of messages relayed + +The dashboard uses [Tailwind CSS CDN](https://cdn.tailwindcss.com) for styling and updates live every second. + ## License This project is licensed under the MIT License. diff --git a/server/server.go b/server/server.go index ff599e3..a241893 100644 --- a/server/server.go +++ b/server/server.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "log" "net/http" "sync" @@ -8,11 +9,50 @@ import ( "github.com/gorilla/websocket" ) -var upgrader = websocket.Upgrader{ - CheckOrigin: func(r *http.Request) bool { - return true - }, -} +var ( + upgrader = websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + return true + }, + } + + dashboardHTML = ` + + + + + Clipboard On The Go - Stats + + + + +
+

Clipboard On The Go - Server Stats

+
+ Connected Clients: + 0 +
+
+ Messages Relayed: + 0 +
+
+ + + +` +) type Client struct { conn *websocket.Conn @@ -26,6 +66,10 @@ type Server struct { unregister chan *Client broadcast chan *Message mu sync.RWMutex + + // Statistics + statsEnabled bool + messagesRelayed uint64 } type Message struct { @@ -36,10 +80,11 @@ type Message struct { func newServer() *Server { return &Server{ - clients: make(map[*Client]bool), - register: make(chan *Client), - unregister: make(chan *Client), - broadcast: make(chan *Message), + clients: make(map[*Client]bool), + register: make(chan *Client), + unregister: make(chan *Client), + broadcast: make(chan *Message), + statsEnabled: EmbeddedServerStats, } } @@ -74,6 +119,11 @@ func (s *Server) run() { } } s.mu.RUnlock() + if s.statsEnabled { + s.mu.Lock() + s.messagesRelayed++ + s.mu.Unlock() + } } } } @@ -157,7 +207,26 @@ func main() { handleWebSocket(server, w, r) }) + // Serve stats dashboard if enabled + if EmbeddedServerStats { + http.HandleFunc("/stats", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + fmt.Fprint(w, dashboardHTML) + }) + http.HandleFunc("/stats/data", func(w http.ResponseWriter, r *http.Request) { + server.mu.RLock() + clients := len(server.clients) + messages := server.messagesRelayed + server.mu.RUnlock() + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `{"clients":%d,"messages":%d}`, clients, messages) + }) + } + log.Printf("Server starting on port %s", port) + if EmbeddedServerStats { + log.Printf("Stats dashboard enabled at /stats") + } err := http.ListenAndServe(":"+port, nil) if err != nil { log.Fatal("ListenAndServe error:", err)