Files
rsspipe/internal/services/settings_service.go
T
haopengzhan ebcaa5d72a
CI Quality Checks & Tests / Gitleaks Secret Detection (push) Failing after 1m59s
CI Quality Checks & Tests / Go Lint & Unit Tests (push) Failing after 2m26s
CI Quality Checks & Tests / Frontend Typecheck & Build (push) Successful in 2m37s
refactor(core): overhaul state transition architecture, persistent job queue, and API layer
- Implement atomic state transitions: add EntryService.PatchEntry() with EntryDiff computation and EventEntryTransition event emission.
- Add persistent SQLite task queue (JobEngine): add jobs table with crash recovery, exponential backoff retries, concurrency control, and idempotency key deduplication.
- Decompose monolithic HTTP handlers: split handlers.go into domain handlers (auth, feeds, entries, jobs, settings, rules, sse).
- Implement real-time Server-Sent Events (/api/events/stream) for multi-tab/device state synchronization.
- Fix SQLite concurrency: apply busy_timeout(10000) and journal_mode(WAL) via DSN query parameters to resolve SQLITE_BUSY (5).
- Fix AI settings & prompt loading: unmarshal JSON from ai_settings, apply configured prompt templates, and enforce strict enabled/API key validations.
- Fix undo share state: remove omitempty from ShareUUID to serialize null for state resets.
- Fix middle list counter: compute true unread/total stats from DB and clean switcher button labels.
- Adapt mobile UI for iOS: add viewport-fit=cover in index.html and calibrate safe-area insets for the bottom navigation and article reader toolbar.
- Add architectural documentation and worklog in docs/009_worklog.md.
2026-09-01 00:41:05 -07:00

99 lines
2.4 KiB
Go

package services
import (
"context"
"encoding/json"
"fmt"
"git.pengzhan.dev/haopengzhan/rsspipe/internal/db"
"git.pengzhan.dev/haopengzhan/rsspipe/internal/events"
"git.pengzhan.dev/haopengzhan/rsspipe/internal/logger"
"git.pengzhan.dev/haopengzhan/rsspipe/internal/models"
"go.uber.org/zap"
)
type SettingsService struct {
db *db.DB
bus *events.EventBus
log *zap.Logger
}
func NewSettingsService(database *db.DB, bus *events.EventBus) *SettingsService {
return &SettingsService{
db: database,
bus: bus,
log: logger.Named("settings-service"),
}
}
func (s *SettingsService) GetPreferences(ctx context.Context, username string) (map[string]interface{}, error) {
user, err := s.db.GetUserByUsername(username)
if err != nil {
return nil, err
}
if user.Preferences == nil {
return make(map[string]interface{}), nil
}
return user.Preferences, nil
}
func (s *SettingsService) UpdatePreferences(ctx context.Context, username string, newPrefs map[string]interface{}) (map[string]interface{}, error) {
user, err := s.db.GetUserByUsername(username)
if err != nil {
return nil, err
}
if user.Preferences == nil {
user.Preferences = make(map[string]interface{})
}
for k, v := range newPrefs {
user.Preferences[k] = v
}
if err := s.db.UpdateUserPreferences(user.ID, user.Preferences); err != nil {
return nil, err
}
if s.bus != nil {
s.bus.Publish(ctx, events.Event{
Type: events.EventPreferencesSync,
DirectAttrs: user.Preferences,
})
}
return user.Preferences, nil
}
func (s *SettingsService) GetAISettings(ctx context.Context) (*models.AISettings, error) {
raw, err := s.db.GetSetting("ai_settings")
if err != nil || raw == "" {
return &models.AISettings{
Enabled: false,
Provider: "openai",
BaseURL: "https://api.openai.com/v1",
APIKey: "",
Model: "gpt-4o-mini",
Temperature: 0.3,
MaxTokens: 1000,
Prompts: []models.PromptTemplate{},
}, nil
}
var cfg models.AISettings
if err := json.Unmarshal([]byte(raw), &cfg); err != nil {
return nil, fmt.Errorf("failed to parse AI settings: %w", err)
}
return &cfg, nil
}
func (s *SettingsService) UpdateAISettings(ctx context.Context, cfg models.AISettings) (*models.AISettings, error) {
bytes, err := json.Marshal(cfg)
if err != nil {
return nil, fmt.Errorf("failed to marshal AI settings: %w", err)
}
if err := s.db.SetSetting("ai_settings", string(bytes)); err != nil {
return nil, err
}
return &cfg, nil
}