package api import ( "encoding/base64" "errors" "fmt" "net/http" "strings" "time" "github.com/gin-gonic/gin" "go.uber.org/fx" "github.com/looplj/axonhub/internal/build" "github.com/looplj/axonhub/internal/log" "github.com/looplj/axonhub/internal/server/assets" "github.com/looplj/axonhub/internal/server/biz" ) type SystemHandlersParams struct { fx.In SystemService *biz.SystemService } func NewSystemHandlers(params SystemHandlersParams) *SystemHandlers { return &SystemHandlers{ SystemService: params.SystemService, } } type SystemHandlers struct { SystemService *biz.SystemService } // SystemStatusResponse 系统状态响应. type SystemStatusResponse struct { IsInitialized bool `json:"isInitialized"` } // HealthResponse 健康检查响应. type HealthResponse struct { Status string `json:"status"` Timestamp time.Time `json:"timestamp"` Version string `json:"version"` Build build.Info `json:"build"` Uptime string `json:"uptime"` } // InitializeSystemRequest 系统初始化请求. type InitializeSystemRequest struct { OwnerEmail string `json:"ownerEmail" binding:"required,email"` OwnerPassword string `json:"ownerPassword" binding:"required,min=6"` OwnerFirstName string `json:"ownerFirstName" binding:"required"` OwnerLastName string `json:"ownerLastName" binding:"required"` BrandName string `json:"brandName" binding:"required"` } // InitializeSystemResponse 系统初始化响应. type InitializeSystemResponse struct { Success bool `json:"success"` Message string `json:"message"` } // GetSystemStatus returns the system initialization status. func (h *SystemHandlers) GetSystemStatus(c *gin.Context) { isInitialized, err := h.SystemService.IsInitialized(c.Request.Context()) if err != nil { JSONError(c, http.StatusInternalServerError, errors.New("Failed to check system status")) return } c.JSON(http.StatusOK, SystemStatusResponse{ IsInitialized: isInitialized, }) } // Health returns the application health status and build information. func (h *SystemHandlers) Health(c *gin.Context) { buildInfo := build.GetBuildInfo() c.JSON(http.StatusOK, HealthResponse{ Status: "healthy", Timestamp: time.Now(), Version: build.Version, Build: buildInfo, Uptime: buildInfo.Uptime, }) } // InitializeSystem initializes the system with owner credentials. func (h *SystemHandlers) InitializeSystem(c *gin.Context) { var req InitializeSystemRequest err := c.ShouldBindJSON(&req) if err != nil { c.JSON(http.StatusBadRequest, InitializeSystemResponse{ Success: false, Message: "Invalid request format", }) return } // Check if system is already initialized isInitialized, err := h.SystemService.IsInitialized(c.Request.Context()) if err != nil { JSONError(c, http.StatusInternalServerError, errors.New("Failed to check initialization status")) return } if isInitialized { c.JSON(http.StatusBadRequest, InitializeSystemResponse{ Success: false, Message: "System is already initialized", }) return } // Initialize system err = h.SystemService.Initialize(c.Request.Context(), &biz.InitializeSystemParams{ OwnerEmail: req.OwnerEmail, OwnerPassword: req.OwnerPassword, OwnerFirstName: req.OwnerFirstName, OwnerLastName: req.OwnerLastName, BrandName: req.BrandName, }) if err != nil { c.JSON(http.StatusInternalServerError, InitializeSystemResponse{ Success: false, Message: fmt.Sprintf("Failed to initialize system: %v", err), }) return } c.JSON(http.StatusOK, InitializeSystemResponse{ Success: true, Message: "System initialized successfully", }) } // GetFavicon returns the system brand logo as favicon. func (h *SystemHandlers) GetFavicon(c *gin.Context) { ctx := c.Request.Context() brandLogo, err := h.SystemService.BrandLogo(ctx) if err != nil { log.Error(ctx, "Failed to get brand logo", log.Cause(err)) } // 如果没有设置品牌标识,返回默认 favicon if brandLogo == "" { defaultFaviconData, err := assets.Favicon.ReadFile("favicon.ico") if err != nil { JSONError(c, http.StatusInternalServerError, errors.New("Failed to read default favicon")) return } c.Header("Content-Type", "image/x-icon") c.Header("Cache-Control", "public, max-age=3600") c.Data(http.StatusOK, "image/x-icon", defaultFaviconData) return } // 解析 base64 编码的图片数据 // 假设格式为 "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..." if !strings.HasPrefix(brandLogo, "data:") { JSONError(c, http.StatusBadRequest, errors.New("Invalid brand logo format")) return } // 提取 MIME 类型和 base64 数据 parts := strings.Split(brandLogo, ",") if len(parts) != 2 { JSONError(c, http.StatusBadRequest, errors.New("Invalid brand logo format")) return } // 提取 MIME 类型 headerPart := parts[0] // "data:image/png;base64" mimeStart := strings.Index(headerPart, ":") mimeEnd := strings.Index(headerPart, ";") if mimeStart == -1 || mimeEnd == -1 { JSONError(c, http.StatusBadRequest, errors.New("Invalid brand logo format")) return } mimeType := headerPart[mimeStart+1 : mimeEnd] // 解码 base64 数据 imageData, err := base64.StdEncoding.DecodeString(parts[1]) if err != nil { JSONError(c, http.StatusBadRequest, errors.New("Failed to decode brand logo")) return } // 设置响应头 c.Header("Content-Type", mimeType) c.Header("Cache-Control", "public, max-age=3600") // 缓存 1 小时 c.Header("Content-Length", fmt.Sprintf("%d", len(imageData))) // 返回图片数据 c.Data(http.StatusOK, mimeType, imageData) }