package api import ( "encoding/json" "net/http" "strconv" "strings" "time" "git.pengzhan.dev/haopengzhan/rsspipe/internal/ai" "git.pengzhan.dev/haopengzhan/rsspipe/internal/auth" "git.pengzhan.dev/haopengzhan/rsspipe/internal/models" ) // User Preferences Handlers func (h *Handler) GetUserPreferences(w http.ResponseWriter, r *http.Request) { claims, ok := r.Context().Value(auth.UserContextKey).(*auth.Claims) if !ok { h.writeError(w, http.StatusUnauthorized, "unauthorized") return } prefs, err := h.settingsService.GetPreferences(r.Context(), claims.Username) if err != nil { h.writeError(w, http.StatusInternalServerError, err.Error()) return } h.writeJSON(w, http.StatusOK, prefs) } func (h *Handler) UpdateUserPreferences(w http.ResponseWriter, r *http.Request) { claims, ok := r.Context().Value(auth.UserContextKey).(*auth.Claims) if !ok { h.writeError(w, http.StatusUnauthorized, "unauthorized") return } var newPrefs map[string]interface{} if err := json.NewDecoder(r.Body).Decode(&newPrefs); err != nil { h.writeError(w, http.StatusBadRequest, "invalid request payload") return } updated, err := h.settingsService.UpdatePreferences(r.Context(), claims.Username, newPrefs) if err != nil { h.writeError(w, http.StatusInternalServerError, err.Error()) return } h.writeJSON(w, http.StatusOK, updated) } // AI Settings Handlers func (h *Handler) GetAISettings(w http.ResponseWriter, r *http.Request) { cfg, err := h.settingsService.GetAISettings(r.Context()) if err != nil { h.writeError(w, http.StatusInternalServerError, err.Error()) return } h.writeJSON(w, http.StatusOK, cfg) } func (h *Handler) UpdateAISettings(w http.ResponseWriter, r *http.Request) { var body models.AISettings if err := json.NewDecoder(r.Body).Decode(&body); err != nil { h.writeError(w, http.StatusBadRequest, "invalid body") return } updated, err := h.settingsService.UpdateAISettings(r.Context(), body) if err != nil { h.writeError(w, http.StatusInternalServerError, err.Error()) return } h.writeJSON(w, http.StatusOK, updated) } func (h *Handler) TestAIConnection(w http.ResponseWriter, r *http.Request) { var body models.AISettings _ = json.NewDecoder(r.Body).Decode(&body) cfg := ai.Config{ Enabled: body.Enabled, Provider: body.Provider, BaseURL: body.BaseURL, APIKey: body.APIKey, Model: body.Model, } if cfg.Provider == "" { cfg.Provider = "openai" } if cfg.BaseURL == "" { cfg.BaseURL = "https://api.openai.com/v1" } start := time.Now() testEntry := models.FeedEntry{ Title: "RSSPipe Connection Test", Content: "This is a quick test to verify connection to your AI provider.", } res, err := h.aiClient.GenerateEnrichment(r.Context(), cfg, "Respond with 'Connection successful' only.", testEntry) latency := time.Since(start).Milliseconds() if err != nil { h.writeJSON(w, http.StatusOK, map[string]interface{}{ "success": false, "message": err.Error(), "latency_ms": latency, "model": cfg.Model, }) return } h.writeJSON(w, http.StatusOK, map[string]interface{}{ "success": true, "message": strings.TrimSpace(res), "latency_ms": latency, "model": cfg.Model, }) } // Search History Handlers func (h *Handler) GetSearchHistory(w http.ResponseWriter, r *http.Request) { q := r.URL.Query().Get("q") limit, _ := strconv.Atoi(r.URL.Query().Get("limit")) items, err := h.db.GetSearchHistory(q, limit) if err != nil { h.writeError(w, http.StatusInternalServerError, err.Error()) return } if items == nil { items = []models.SearchHistoryItem{} } h.writeJSON(w, http.StatusOK, items) } func (h *Handler) RecordSearch(w http.ResponseWriter, r *http.Request) { var body struct { Keyword string `json:"keyword"` } if err := json.NewDecoder(r.Body).Decode(&body); err != nil || strings.TrimSpace(body.Keyword) == "" { h.writeError(w, http.StatusBadRequest, "keyword is required") return } if err := h.db.RecordSearch(body.Keyword); err != nil { h.writeError(w, http.StatusInternalServerError, err.Error()) return } h.writeJSON(w, http.StatusOK, map[string]string{"message": "recorded"}) } func (h *Handler) DeleteSearchHistory(w http.ResponseWriter, r *http.Request) { kw := r.URL.Query().Get("keyword") if kw != "" { _ = h.db.DeleteSearchHistory(kw) } else { _ = h.db.ClearSearchHistory() } h.writeJSON(w, http.StatusOK, map[string]string{"message": "deleted"}) }