mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-10 11:17:41 +08:00
feat: Implement DeepSeek integration, refactor model adapters for streaming and tool calls, enhance admin and account management, and introduce new UI features for settings, API testing, and Vercel sync.
This commit is contained in:
119
internal/admin/handler_settings_write.go
Normal file
119
internal/admin/handler_settings_write.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
authn "ds2api/internal/auth"
|
||||
"ds2api/internal/config"
|
||||
)
|
||||
|
||||
func (h *Handler) updateSettings(w http.ResponseWriter, r *http.Request) {
|
||||
var req map[string]any
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]any{"detail": "invalid json"})
|
||||
return
|
||||
}
|
||||
|
||||
adminCfg, runtimeCfg, toolcallCfg, responsesCfg, embeddingsCfg, claudeMap, aliasMap, err := parseSettingsUpdateRequest(req)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]any{"detail": err.Error()})
|
||||
return
|
||||
}
|
||||
if runtimeCfg != nil {
|
||||
if err := validateMergedRuntimeSettings(h.Store.Snapshot().Runtime, runtimeCfg); err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]any{"detail": err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := h.Store.Update(func(c *config.Config) error {
|
||||
if adminCfg != nil {
|
||||
if adminCfg.JWTExpireHours > 0 {
|
||||
c.Admin.JWTExpireHours = adminCfg.JWTExpireHours
|
||||
}
|
||||
}
|
||||
if runtimeCfg != nil {
|
||||
if runtimeCfg.AccountMaxInflight > 0 {
|
||||
c.Runtime.AccountMaxInflight = runtimeCfg.AccountMaxInflight
|
||||
}
|
||||
if runtimeCfg.AccountMaxQueue > 0 {
|
||||
c.Runtime.AccountMaxQueue = runtimeCfg.AccountMaxQueue
|
||||
}
|
||||
if runtimeCfg.GlobalMaxInflight > 0 {
|
||||
c.Runtime.GlobalMaxInflight = runtimeCfg.GlobalMaxInflight
|
||||
}
|
||||
}
|
||||
if toolcallCfg != nil {
|
||||
if strings.TrimSpace(toolcallCfg.Mode) != "" {
|
||||
c.Toolcall.Mode = strings.TrimSpace(toolcallCfg.Mode)
|
||||
}
|
||||
if strings.TrimSpace(toolcallCfg.EarlyEmitConfidence) != "" {
|
||||
c.Toolcall.EarlyEmitConfidence = strings.TrimSpace(toolcallCfg.EarlyEmitConfidence)
|
||||
}
|
||||
}
|
||||
if responsesCfg != nil && responsesCfg.StoreTTLSeconds > 0 {
|
||||
c.Responses.StoreTTLSeconds = responsesCfg.StoreTTLSeconds
|
||||
}
|
||||
if embeddingsCfg != nil && strings.TrimSpace(embeddingsCfg.Provider) != "" {
|
||||
c.Embeddings.Provider = strings.TrimSpace(embeddingsCfg.Provider)
|
||||
}
|
||||
if claudeMap != nil {
|
||||
c.ClaudeMapping = claudeMap
|
||||
c.ClaudeModelMap = nil
|
||||
}
|
||||
if aliasMap != nil {
|
||||
c.ModelAliases = aliasMap
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]any{"detail": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
h.applyRuntimeSettings()
|
||||
needsSync := config.IsVercel() || h.Store.IsEnvBacked()
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"success": true,
|
||||
"message": "settings updated and hot reloaded",
|
||||
"env_backed": h.Store.IsEnvBacked(),
|
||||
"needs_vercel_sync": needsSync,
|
||||
"manual_sync_message": "配置已保存。Vercel 部署请在 Vercel Sync 页面手动同步。",
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handler) updateSettingsPassword(w http.ResponseWriter, r *http.Request) {
|
||||
var req map[string]any
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]any{"detail": "invalid json"})
|
||||
return
|
||||
}
|
||||
newPassword := strings.TrimSpace(fieldString(req, "new_password"))
|
||||
if newPassword == "" {
|
||||
newPassword = strings.TrimSpace(fieldString(req, "password"))
|
||||
}
|
||||
if len(newPassword) < 4 {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]any{"detail": "new password must be at least 4 characters"})
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
hash := authn.HashAdminPassword(newPassword)
|
||||
if err := h.Store.Update(func(c *config.Config) error {
|
||||
c.Admin.PasswordHash = hash
|
||||
c.Admin.JWTValidAfterUnix = now
|
||||
return nil
|
||||
}); err != nil {
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]any{"detail": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"success": true,
|
||||
"message": "password updated",
|
||||
"force_relogin": true,
|
||||
"jwt_valid_after_unix": now,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user