Files
aiops/backend/internal/api/handlers/webshell.go
T
attacker 00d5510366 feat: 完善告警规则配置页面 — 增删改查弹窗 + 修复白屏bug + UpdateRule notify_channels序列化修复
- AlertManager: 新建/编辑告警规则弹窗(规则名称/描述/类型/数据源/表达式/级别/持续时间/通知渠道Switch)
- AlertManager: 修复白屏bug — getRules返回{list,total}未解包导致rules.map崩溃(Frontend Pitfall #1)
- AlertManager: 修复规则列表字段显示 — 原引用不存在的rule.metric/operator/threshold字段,改为rule.source/expression
- AlertManager: 新增规则删除按钮(带确认)
- alert.go: 修复UpdateRule notify_channels序列化bug — 原直接赋值[]string到text列导致500,改用toJSON()
- 同步提交前序未提交改动(WebShell/K8s/Server/Login等组件改进)
2026-06-14 03:07:44 +08:00

124 lines
3.0 KiB
Go

package handlers
import (
"encoding/json"
"log"
"net/http"
"sync"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"gorm.io/gorm"
"ezops/internal/models"
)
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true // allow all origins in dev
},
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
// WSMessage WebSocket消息结构
type WSMessage struct {
Status int `json:"status"` // 0=data, 1=resize
Data string `json:"data"`
Cols int `json:"cols"`
Rows int `json:"rows"`
}
// WebShellHandler WebShell处理器
type WebShellHandler struct {
db *gorm.DB
clients sync.Map // serverID -> []*websocket.Conn
}
// NewWebShellHandler 创建WebShell处理器
func NewWebShellHandler(db *gorm.DB) *WebShellHandler {
return &WebShellHandler{db: db}
}
// Terminal WebSocket终端连接
// @Summary WebShell终端
// @Description 通过WebSocket连接到服务器的SSH终端
// @Tags WebShell
// @Param id query int true "服务器ID"
// @Param cols query int false "终端列数"
// @Param rows query int false "终端行数"
// @Router /ws/terminal [get]
func (h *WebShellHandler) Terminal(c *gin.Context) {
serverID := c.Query("id")
if serverID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "missing server id"})
return
}
// Verify server exists
var server models.Server
if err := h.db.First(&server, serverID).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "server not found"})
return
}
// Upgrade to WebSocket
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
log.Printf("[WebShell] upgrade error: %v", err)
return
}
defer conn.Close()
log.Printf("[WebShell] connected to server %s (%s:%d)", server.Hostname, server.IP, server.Port)
// Send welcome message
welcome := WSMessage{
Status: 0,
Data: "\r\n\x1b[32mEZOps WebShell\x1b[0m - Connected to " + server.Hostname + " (" + server.IP + ")\r\n",
}
conn.WriteJSON(welcome)
// Demo mode: echo input back with a prompt
// In production this would connect to actual SSH via crypto/ssh or a PTY bridge
for {
msgType, data, err := conn.ReadMessage()
if err != nil {
log.Printf("[WebShell] read error: %v", err)
break
}
if msgType == websocket.TextMessage {
var msg WSMessage
if err := json.Unmarshal(data, &msg); err != nil {
continue
}
switch msg.Status {
case 0: // data
// Process command input
resp := WSMessage{Status: 0}
switch msg.Data {
case "\r", "\n":
resp.Data = "\r\n[" + server.Hostname + " ~]$ "
case "\u0003": // Ctrl+C
resp.Data = "^C\r\n[" + server.Hostname + " ~]$ "
case "\u0004": // Ctrl+D
resp.Data = "\r\n\x1b[33mConnection closed.\x1b[0m\r\n"
conn.WriteJSON(resp)
goto done
default:
resp.Data = msg.Data // echo back
}
conn.WriteJSON(resp)
case 1: // resize
log.Printf("[WebShell] resize to cols=%d rows=%d", msg.Cols, msg.Rows)
}
}
}
done:
log.Printf("[WebShell] disconnected from server %s", server.Hostname)
}