110 lines
3.1 KiB
Go
110 lines
3.1 KiB
Go
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})
|
||
}
|