Files
usernode/internal/api/handler.go
T

100 lines
3.0 KiB
Go

// Package api 为 HTTP handler 层(RESTful v1)。
// M1 覆盖认证(管理员/外部用户登录、会话、OTP)与用户管理 CRUD;
// M2 覆盖 SSH 公钥管理(上传/重命名/吊销/列表);
// M3 覆盖申请审批(公开提交 + 管理员审批 + 邮件通知);
// M4 覆盖审计查询/导出与系统设置。
package api
import (
"net/http"
"runtime"
"strconv"
"time"
"github.com/gin-gonic/gin"
"ws_usernode/internal/auth"
"ws_usernode/internal/config"
"ws_usernode/internal/service"
)
// Handler 聚合各模块 handler,作为路由注册的挂载点。
type Handler struct {
Health *HealthHandler
Auth *AuthHandler
User *UserHandler
Key *KeyHandler
Approval *ApprovalHandler
Audit *AuditHandler
Settings *SettingsHandler
authSvc *service.AuthService
auditSvc *service.AuditService
}
// New 创建 handler 集合。
func New(cfg *config.Config, authSvc *service.AuthService, userSvc *service.UserService, keySvc *service.KeyService, approvalSvc *service.ApprovalService, auditSvc *service.AuditService, settingsSvc *service.SettingService) *Handler {
h := &Handler{
Health: &HealthHandler{startedAt: time.Now()},
authSvc: authSvc,
auditSvc: auditSvc,
}
h.Auth = &AuthHandler{svc: authSvc, cfg: cfg}
h.User = &UserHandler{svc: userSvc, cfg: cfg, h: h}
h.Key = &KeyHandler{svc: keySvc, cfg: cfg, h: h}
h.Approval = &ApprovalHandler{svc: approvalSvc, cfg: cfg, h: h}
h.Audit = NewAuditHandler(auditSvc, cfg)
h.Settings = NewSettingsHandler(settingsSvc)
return h
}
// audit 记录管理操作审计(append-only)。actor 来自会话中间件。
func (h *Handler) audit(c *gin.Context, action, resourceType, resourceID string, detail any, result string) {
var actorID uint
var actorName string
if sess := sessionFrom(c); sess != nil {
actorID = sess.RefID
if info, err := h.authSvc.Me(c.Request.Context(), sess.ID); err == nil {
actorName = info.Username
} else {
actorName = sess.UserType + "#" + strconv.FormatUint(uint64(sess.RefID), 10)
}
}
_ = h.auditSvc.Record(c.Request.Context(), actorID, actorName, action, resourceType, resourceID, detail, c.ClientIP(), result)
}
// HealthHandler 健康检查。
type HealthHandler struct {
startedAt time.Time
}
func (h *HealthHandler) Healthz(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": "ok",
"version": "0.3.0-m4",
"uptime": time.Since(h.startedAt).String(),
"go": runtime.Version(),
"timestamp": time.Now().UTC().Format(time.RFC3339),
})
}
// sessionFrom 返回会话中间件注入的会话(未登录时为 nil)。
func sessionFrom(c *gin.Context) *auth.Session {
if v, ok := c.Get(SessionContextKey); ok {
if s, ok := v.(*auth.Session); ok {
return s
}
}
return nil
}
// ok 统一成功响应。
func ok(c *gin.Context, data any) {
c.JSON(http.StatusOK, gin.H{"data": data})
}
// fail 统一错误响应。
func fail(c *gin.Context, status int, msg string) {
c.JSON(status, gin.H{"error": msg})
}