make json abstraction for server

This commit is contained in:
Adrian Zuercher
2025-04-15 11:04:14 +02:00
parent 3d1dee25f1
commit a1f947e24a
6 changed files with 149 additions and 130 deletions

View File

@@ -1,37 +0,0 @@
package handlers
import (
"encoding/json"
"os"
)
func SaveJson(file string, data any) error {
f, err := os.OpenFile(file, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0666)
if err != nil {
return err
}
defer f.Close()
b, err := json.Marshal(data)
if err != nil {
return err
}
_, err = f.Write(b)
return err
}
func OpenJson(file string, data any) (err error) {
b, err := os.ReadFile(file)
if err != nil {
return
}
err = json.Unmarshal(b, &data)
if err != nil {
return
}
return
}

21
models/jsonData.go Normal file
View File

@@ -0,0 +1,21 @@
package models
type JsonData struct {
Get *[]Get `json:"get,omitempty"`
Set *[]Set `json:"set,omitempty"`
}
type Get struct {
Path string `json:"path"`
Query *Query `json:"query,omitempty"`
}
type Set struct {
Path string `json:"path"`
Value any `json:"value"`
}
type Query struct {
Depth int `json:"depth,omitempty"`
RegExp string `json:"regExp,omitempty"`
}

49
server/clients.go Normal file
View File

@@ -0,0 +1,49 @@
package server
import (
"context"
"log"
"sync"
"github.com/coder/websocket"
"github.com/coder/websocket/wsjson"
"github.com/zuadi/tecamino-dbm.git/models"
)
var (
clients = make(map[*Client]bool)
clientsMu sync.Mutex
)
type Client struct {
conn *websocket.Conn
ctx context.Context
}
func registerClient(c *Client) {
clientsMu.Lock()
defer clientsMu.Unlock()
clients[c] = true
log.Printf("Client connected (%d total)", len(clients))
}
func unregisterClient(c *Client) {
clientsMu.Lock()
defer clientsMu.Unlock()
delete(clients, c)
log.Printf("Client disconnected (%d total)", len(clients))
}
func broadcast(data models.JsonData) {
clientsMu.Lock()
defer clientsMu.Unlock()
for c := range clients {
go func(client *Client) {
err := wsjson.Write(client.ctx, client.conn, data)
if err != nil {
log.Printf("Broadcast error: %v", err)
}
}(c)
}
}

13
server/routes.go Normal file
View File

@@ -0,0 +1,13 @@
package server
import "github.com/gin-gonic/gin"
func (s *Server) AddRoutes() {
s.engine.GET("/json_data", func(c *gin.Context) {
handleWebSocket(c.Writer, c.Request)
})
s.engine.GET("/", func(c *gin.Context) {
c.String(200, "WebSocket Broadcast Server is running!")
})
}

View File

@@ -1,15 +1,8 @@
package server package server
import ( import (
"context"
"fmt" "fmt"
"log"
"net/http"
"sync"
"time"
"github.com/coder/websocket"
"github.com/coder/websocket/wsjson"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@@ -17,28 +10,11 @@ type Server struct {
engine *gin.Engine engine *gin.Engine
} }
type Client struct {
conn *websocket.Conn
ctx context.Context
}
var (
clients = make(map[*Client]bool)
clientsMu sync.Mutex
)
func NewServer() *Server { func NewServer() *Server {
s := Server{} s := Server{
engine: gin.Default(),
s.engine = gin.Default() }
s.AddRoutes()
s.engine.GET("/ws", func(c *gin.Context) {
handleWebSocket(c.Writer, c.Request)
})
s.engine.GET("/", func(c *gin.Context) {
c.String(200, "WebSocket Broadcast Server is running!")
})
return &s return &s
} }
@@ -48,68 +24,3 @@ func (s *Server) Serve(port uint) error {
} }
return nil return nil
} }
func handleWebSocket(w http.ResponseWriter, r *http.Request) {
conn, err := websocket.Accept(w, r, &websocket.AcceptOptions{
OriginPatterns: []string{"*"},
})
if err != nil {
log.Println("WebSocket accept error:", err)
return
}
defer conn.Close(websocket.StatusInternalError, "Internal error")
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Minute)
defer cancel()
client := &Client{conn: conn, ctx: ctx}
// Register client
registerClient(client)
defer unregisterClient(client)
// Read loop
for {
var msg string
err := wsjson.Read(ctx, conn, &msg)
if err != nil {
log.Println("Read error:", err)
break
}
log.Printf("Received: %s", msg)
// Broadcast to all
broadcast("Broadcast: " + msg)
}
conn.Close(websocket.StatusNormalClosure, "Normal closure")
}
func registerClient(c *Client) {
clientsMu.Lock()
defer clientsMu.Unlock()
clients[c] = true
log.Printf("Client connected (%d total)", len(clients))
}
func unregisterClient(c *Client) {
clientsMu.Lock()
defer clientsMu.Unlock()
delete(clients, c)
log.Printf("Client disconnected (%d total)", len(clients))
}
func broadcast(message string) {
clientsMu.Lock()
defer clientsMu.Unlock()
for c := range clients {
go func(client *Client) {
err := wsjson.Write(client.ctx, client.conn, message)
if err != nil {
log.Printf("Broadcast error: %v", err)
}
}(c)
}
}

62
server/webSocket.go Normal file
View File

@@ -0,0 +1,62 @@
package server
import (
"context"
"log"
"net/http"
"time"
"github.com/coder/websocket"
"github.com/coder/websocket/wsjson"
"github.com/zuadi/tecamino-dbm.git/models"
)
func handleWebSocket(w http.ResponseWriter, r *http.Request) {
conn, err := websocket.Accept(w, r, &websocket.AcceptOptions{
OriginPatterns: []string{"*"},
})
if err != nil {
log.Println("WebSocket accept error:", err)
return
}
defer conn.Close(websocket.StatusInternalError, "Internal error")
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Minute)
defer cancel()
client := &Client{conn: conn, ctx: ctx}
// Register client
registerClient(client)
defer unregisterClient(client)
// Read loop
for {
var data models.JsonData
err := wsjson.Read(ctx, conn, &data)
if err != nil {
log.Println("Read error:", err)
var response struct {
Code int `json:"errorCode"`
Message string `json:"message"`
Error bool `json:"error"`
}
response.Code = 404
response.Error = true
response.Message = err.Error()
err = wsjson.Write(ctx, conn, response)
if err != nil {
log.Println("Read error:", err)
}
break
}
log.Printf("Received: %v", data)
// Broadcast to all
broadcast(data)
}
conn.Close(websocket.StatusNormalClosure, "Normal closure")
}