chore: 初始化仓库基线(AGENTS.md、git 规范、敏感文件排除)

This commit is contained in:
weijuesen
2026-08-10 22:30:53 +08:00
commit 84abf4454c
358 changed files with 75993 additions and 0 deletions
+254
View File
@@ -0,0 +1,254 @@
package ws
import (
"encoding/json"
"log/slog"
"net/http"
"strings"
"sync"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"github.com/gorilla/websocket"
"silk-server-go/internal/service"
)
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { return true },
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
// client WebSocket 客户端
type client struct {
conn *websocket.Conn
rooms map[string]bool // 订阅的房间(device:<deviceKey>
send chan []byte
}
// Hub WebSocket 中心,管理客户端和房间
type Hub struct {
jwtSecret string
clients map[*client]bool
mu sync.RWMutex
}
// NewHub 创建 Hub
func NewHub(jwtSecret string) *Hub {
return &Hub{
jwtSecret: jwtSecret,
clients: make(map[*client]bool),
}
}
// HandleWebSocket 处理 WebSocket 连接(Gin handler
func (h *Hub) HandleWebSocket(c *gin.Context) {
// 验证 JWT(从 query.auth.token / query.token / header.Authorization 获取)
tokenStr := ""
if t := c.Query("auth.token"); t != "" {
tokenStr = t
} else if t := c.Query("token"); t != "" {
tokenStr = t
} else if auth := c.GetHeader("Authorization"); strings.HasPrefix(auth, "Bearer ") {
tokenStr = auth[7:]
}
if tokenStr == "" {
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err == nil {
conn.WriteJSON(map[string]interface{}{"event": "auth.fail", "data": map[string]bool{"ok": false}})
conn.Close()
}
return
}
// 验证 token
claims := jwt.MapClaims{}
token, err := jwt.ParseWithClaims(tokenStr, claims, func(t *jwt.Token) (interface{}, error) {
return []byte(h.jwtSecret), nil
}, jwt.WithValidMethods([]string{"HS256"}))
if err != nil || !token.Valid {
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err == nil {
conn.WriteJSON(map[string]interface{}{"event": "auth.fail", "data": map[string]bool{"ok": false}})
conn.Close()
}
return
}
// 升级为 WebSocket
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
slog.Warn("WebSocket 升级失败", "err", err)
return
}
cl := &client{
conn: conn,
rooms: make(map[string]bool),
send: make(chan []byte, 256),
}
h.mu.Lock()
h.clients[cl] = true
h.mu.Unlock()
// 发送 auth.ok
h.sendJSON(cl, "auth.ok", map[string]interface{}{
"ok": true,
"user": claims,
})
go h.readPump(cl)
go h.writePump(cl)
}
// readPump 读取客户端消息
func (h *Hub) readPump(cl *client) {
defer func() {
h.mu.Lock()
delete(h.clients, cl)
h.mu.Unlock()
cl.conn.Close()
}()
for {
_, msg, err := cl.conn.ReadMessage()
if err != nil {
break
}
// 解析消息(兼容 {event: "...", deviceKey: "..."} 格式)
var data struct {
Event string `json:"event"`
DeviceKey string `json:"deviceKey"`
}
if err := json.Unmarshal(msg, &data); err != nil {
// 尝试 Socket.IO 格式 ["event", {deviceKey: "..."}]
var arr []json.RawMessage
if err2 := json.Unmarshal(msg, &arr); err2 == nil && len(arr) >= 2 {
if len(arr[0]) > 0 {
json.Unmarshal(arr[0], &data.Event)
}
if len(arr[1]) > 0 {
var payload struct {
DeviceKey string `json:"deviceKey"`
}
json.Unmarshal(arr[1], &payload)
data.DeviceKey = payload.DeviceKey
}
}
}
switch data.Event {
case "subscribe.device":
room := "device:" + data.DeviceKey
h.mu.Lock()
cl.rooms[room] = true
h.mu.Unlock()
h.sendJSON(cl, "subscribed", map[string]interface{}{"ok": true, "room": room})
case "unsubscribe.device":
room := "device:" + data.DeviceKey
h.mu.Lock()
delete(cl.rooms, room)
h.mu.Unlock()
h.sendJSON(cl, "unsubscribed", map[string]interface{}{"ok": true})
}
}
}
// writePump 向客户端发送消息
func (h *Hub) writePump(cl *client) {
ticker := time.NewTicker(30 * time.Second)
defer func() {
ticker.Stop()
cl.conn.Close()
}()
for {
select {
case msg, ok := <-cl.send:
if !ok {
cl.conn.WriteMessage(websocket.CloseMessage, []byte{})
return
}
if err := cl.conn.WriteMessage(websocket.TextMessage, msg); err != nil {
return
}
case <-ticker.C:
if err := cl.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
return
}
}
}
}
// sendJSON 向客户端发送 JSON 消息
func (h *Hub) sendJSON(cl *client, event string, data interface{}) {
msg := map[string]interface{}{"event": event, "data": data}
body, _ := json.Marshal(msg)
select {
case cl.send <- body:
default:
slog.Warn("WebSocket 客户端发送缓冲区满,丢弃消息")
}
}
// BroadcastTelemetry 广播遥测数据(实现 service.EventHub 接口)
func (h *Hub) BroadcastTelemetry(deviceKey string, data interface{}) {
h.mu.RLock()
defer h.mu.RUnlock()
room := "device:" + deviceKey
for cl := range h.clients {
// 推送到设备房间
if cl.rooms[room] {
h.sendJSON(cl, "telemetry", data)
}
// 全局推送
h.sendJSON(cl, "telemetry.all", data)
}
}
// BroadcastAlarm 广播告警(实现 service.EventHub 接口)
func (h *Hub) BroadcastAlarm(event service.AlarmEvent) {
h.mu.RLock()
defer h.mu.RUnlock()
room := "device:" + event.DeviceKey
isRecovery := event.Code == "recovery"
for cl := range h.clients {
if isRecovery {
// 恢复通知
h.sendJSON(cl, "alarm.recovery", event)
if event.DeviceKey != "" && cl.rooms[room] {
h.sendJSON(cl, "alarm.device.recovery", event)
}
} else {
// 告警触发
h.sendJSON(cl, "alarm", event)
if event.DeviceKey != "" && cl.rooms[room] {
h.sendJSON(cl, "alarm.device", event)
}
}
}
}
// BroadcastDeviceStatus 广播设备状态变更(实现 service.EventHub 接口)
func (h *Hub) BroadcastDeviceStatus(deviceKey string, status string) {
h.mu.RLock()
defer h.mu.RUnlock()
room := "device:" + deviceKey
data := map[string]interface{}{"deviceKey": deviceKey, "status": status}
for cl := range h.clients {
h.sendJSON(cl, "device.status", data)
if cl.rooms[room] {
h.sendJSON(cl, "device.status.device", data)
}
}
}