diff --git a/handlers/json.go b/handlers/json.go deleted file mode 100644 index aa3f3e7..0000000 --- a/handlers/json.go +++ /dev/null @@ -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 -} diff --git a/models/jsonData.go b/models/jsonData.go new file mode 100644 index 0000000..649d513 --- /dev/null +++ b/models/jsonData.go @@ -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"` +} diff --git a/server/clients.go b/server/clients.go new file mode 100644 index 0000000..e5221c3 --- /dev/null +++ b/server/clients.go @@ -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) + } +} diff --git a/server/routes.go b/server/routes.go new file mode 100644 index 0000000..e194760 --- /dev/null +++ b/server/routes.go @@ -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!") + }) +} diff --git a/server/server.go b/server/server.go index 525540d..eae5e15 100644 --- a/server/server.go +++ b/server/server.go @@ -1,15 +1,8 @@ package server import ( - "context" "fmt" - "log" - "net/http" - "sync" - "time" - "github.com/coder/websocket" - "github.com/coder/websocket/wsjson" "github.com/gin-gonic/gin" ) @@ -17,28 +10,11 @@ type Server struct { engine *gin.Engine } -type Client struct { - conn *websocket.Conn - ctx context.Context -} - -var ( - clients = make(map[*Client]bool) - clientsMu sync.Mutex -) - func NewServer() *Server { - s := Server{} - - s.engine = gin.Default() - - 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!") - }) + s := Server{ + engine: gin.Default(), + } + s.AddRoutes() return &s } @@ -48,68 +24,3 @@ func (s *Server) Serve(port uint) error { } 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) - } -} diff --git a/server/webSocket.go b/server/webSocket.go new file mode 100644 index 0000000..f35d1f4 --- /dev/null +++ b/server/webSocket.go @@ -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") +}