Files
InfoGenie/infogenie-backend-go/internal/handler/ai_runtime.go
2026-03-28 20:59:52 +08:00

110 lines
3.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package handler
import (
"errors"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"infogenie-backend/config"
"infogenie-backend/internal/database"
"infogenie-backend/internal/model"
)
type AIRuntimeHandler struct{}
func NewAIRuntimeHandler() *AIRuntimeHandler { return &AIRuntimeHandler{} }
func maskAPIKey(k string) (set bool, hint string) {
k = strings.TrimSpace(k)
if k == "" {
return false, ""
}
if len(k) <= 4 {
return true, "****"
}
return true, "****" + k[len(k)-4:]
}
// GetAIRuntime 管理员读取当前 AI 上游配置(密钥脱敏)
func (h *AIRuntimeHandler) GetAIRuntime(c *gin.Context) {
if config.Cfg == nil || strings.TrimSpace(config.Cfg.SiteAdminToken) == "" {
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "admin_not_configured"})
return
}
if !siteAdminTokenOK(c.GetHeader("X-Site-Admin-Token")) {
c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"})
return
}
var row model.SiteAIRuntime
_ = database.DB.First(&row, 1).Error
keySet, keyHint := maskAPIKey(row.APIKey)
c.JSON(http.StatusOK, gin.H{
"api_base": strings.TrimSpace(row.APIBase),
"api_key_set": keySet,
"api_key_hint": keyHint,
"default_model": strings.TrimSpace(row.DefaultModel),
"default_provider": strings.TrimSpace(row.DefaultProv),
})
}
type putAIRuntimeBody struct {
APIBase string `json:"api_base"`
APIKey string `json:"api_key"`
DefaultModel string `json:"default_model"`
DefaultProvider string `json:"default_provider"`
}
// PutAIRuntime 管理员写入api_key 留空或不传则保留原密钥
func (h *AIRuntimeHandler) PutAIRuntime(c *gin.Context) {
if config.Cfg == nil || strings.TrimSpace(config.Cfg.SiteAdminToken) == "" {
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "admin_not_configured"})
return
}
if !siteAdminTokenOK(c.GetHeader("X-Site-Admin-Token")) {
c.JSON(http.StatusForbidden, gin.H{"error": "forbidden"})
return
}
var body putAIRuntimeBody
if err := c.ShouldBindJSON(&body); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid_json"})
return
}
var row model.SiteAIRuntime
err := database.DB.First(&row, 1).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
row = model.SiteAIRuntime{ID: 1}
} else {
c.JSON(http.StatusInternalServerError, gin.H{"error": "db_error"})
return
}
}
row.APIBase = strings.TrimSpace(body.APIBase)
if strings.TrimSpace(body.DefaultModel) != "" {
row.DefaultModel = strings.TrimSpace(body.DefaultModel)
}
if strings.TrimSpace(body.DefaultProvider) != "" {
row.DefaultProv = strings.TrimSpace(body.DefaultProvider)
} else if row.DefaultProv == "" {
row.DefaultProv = "deepseek"
}
newKey := strings.TrimSpace(body.APIKey)
if newKey != "" && !strings.Contains(newKey, "****") {
row.APIKey = newKey
}
if err := database.DB.Save(&row).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "db_error"})
return
}
c.JSON(http.StatusOK, gin.H{"ok": true})
}