功能:完善通用云管工作台、审计与诊断能力

This commit is contained in:
songyiming committed 2026-11-11 18:46:16 +08:00
1 parent 5ac692724d
commit 9f05aa20ea
225 files changed
+14266 -141

No files matched your search

+23 -1
View File
@@ -69,14 +69,23 @@ func New(cfg config.Config, db *sqlx.DB, pve *proxmox.Client) *Server {
t.POST("/connect-ticket", s.createTicket)
t.POST("/connect-ticket/consume", s.consumeTicket)
t.POST("/session", s.deviceSession)
t.POST("/desktop-prepare", s.prepareGPUDesktop)
t.GET("/desktop-prepare/:id", s.gpuPrepareStatus)
a := r.Group("/api/v1", s.adminAuth, s.refreshComputeRequest)
a.GET("/auth/me", func(c *gin.Context) { c.JSON(200, c.MustGet("admin")) })
a.POST("/auth/logout", s.logout)
a.POST("/auth/password", s.changeOwnPassword)
a.GET("/auth/mfa", s.mfaStatus)
a.POST("/auth/mfa/setup", s.beginMFA)
a.POST("/auth/mfa/confirm", s.confirmMFA)
a.POST("/auth/mfa/recovery", func(c *gin.Context) { s.changeMFA(c, false) })
a.DELETE("/auth/mfa", func(c *gin.Context) { s.changeMFA(c, true) })
a.GET("/administrators", s.allow("super_admin"), s.administratorList)
a.POST("/administrators", s.allow("super_admin"), s.createAdministrator)
a.PUT("/administrators/:id", s.allow("super_admin"), s.updateAdministrator)
a.POST("/administrators/:id/password", s.allow("super_admin"), s.resetAdministratorPassword)
a.GET("/administrators/:id/sessions", s.allow("super_admin"), s.administratorSessions)
a.DELETE("/administrators/:id/sessions/:sessionKey", s.allow("super_admin"), s.revokeAdministratorSession)
a.GET("/overview", s.overview)
a.GET("/sessions/history", s.sessionHistory)
a.GET("/monitor/series", s.resourceSeries)
@@ -96,6 +105,7 @@ func New(cfg config.Config, db *sqlx.DB, pve *proxmox.Client) *Server {
a.POST("/desktops/:id/snapshots/check", s.allow("super_admin"), s.checkSnapshot)
a.POST("/desktops/:id/snapshots/actions", s.allow("super_admin"), s.queueSnapshot)
a.POST("/tasks/:id/snapshot-reconcile", s.allow("super_admin"), s.reconcileSnapshot)
a.POST("/tasks/:id/gpu-reconcile", s.allow("super_admin", "operator"), s.reconcileGPUPrepare)
a.POST("/desktops/:id/hardware/check", s.allow("super_admin"), s.checkHardware)
a.POST("/desktops/:id/hardware", s.allow("super_admin"), s.queueHardware)
a.GET("/resources", s.resources)
@@ -143,11 +153,15 @@ func New(cfg config.Config, db *sqlx.DB, pve *proxmox.Client) *Server {
a.POST("/users/batch-bindings", s.allow("super_admin"), s.batchBindings)
a.PUT("/users/:id/bindings", s.allow("super_admin"), s.bindings)
a.GET("/tasks", s.tasks)
a.GET("/tasks/export", s.exportTasks)
a.GET("/tasks/:id", s.task)
a.POST("/tasks/:id/hardware-reconcile", s.allow("super_admin"), s.reconcileHardware)
a.POST("/tasks/:id/reconcile", s.allow("super_admin"), s.reconcileProvision)
a.GET("/tasks/:id/diagnostics", s.allow("super_admin", "operator"), s.taskDiagnostics)
a.GET("/diagnostics", s.allow("super_admin", "operator"), s.diagnostics)
a.GET("/diagnostics/export", s.allow("super_admin", "operator"), s.exportDiagnostics)
a.GET("/audit-logs", s.audits)
a.GET("/audit-logs/export", s.exportAudits)
r.NoRoute(func(c *gin.Context) { s.fail(c, 404, "not_found", "接口不存在") })
return s
}
@@ -216,6 +230,7 @@ func (s *Server) login(c *gin.Context) {
var input struct {
Username string `json:"username" binding:"required,max=80"`
Password string `json:"password" binding:"required,max=200"`
Code string `json:"code" binding:"max=80"`
}
if !s.decode(c, &input) {
return
@@ -231,11 +246,14 @@ func (s *Server) login(c *gin.Context) {
}
defer tx.Rollback()
// Serialize session creation against account disable, role changes and password reset.
err = tx.GetContext(c.Request.Context(), &a, "SELECT * FROM administrators WHERE username=$1 AND enabled FOR SHARE", input.Username)
err = tx.GetContext(c.Request.Context(), &a, "SELECT * FROM administrators WHERE username=$1 AND enabled FOR UPDATE", input.Username)
if err != nil || bcrypt.CompareHashAndPassword([]byte(a.PasswordHash), []byte(input.Password)) != nil {
s.fail(c, 401, "invalid_credentials", "用户名或密码错误")
return
}
if !s.loginMFA(c, tx, a.ID, input.Code) {
return
}
now := time.Now()
id := store.ID("sess_")
expires := now.Add(time.Hour)
@@ -248,6 +266,10 @@ func (s *Server) login(c *gin.Context) {
s.dbError(c, err)
return
}
if _, err = tx.ExecContext(c.Request.Context(), "INSERT INTO admin_session_details(session_id,client_ip,user_agent) VALUES($1,$2,$3)", id, c.ClientIP(), sessionUserAgent(c.GetHeader("User-Agent"))); err != nil {
s.dbError(c, err)
return
}
if err = tx.Commit(); err != nil {
s.dbError(c, err)
return