SurfSense/_bmad-output/implementation-artifacts/1-6-settings-sync.md
API Test Bot f60ba3d01f update
2026-03-11 19:32:08 +07:00

10 KiB

Story 1.6: Đồng bộ Cài đặt (Settings Sync) với Frontend

Status: ready-for-dev

Story

Là một SurfSense user, Tôi muốn extension sử dụng cùng model và search space như web dashboard, Để tôi không phải cấu hình lại.

Dependencies

  • REQUIRES: Story 1.0 (Authentication System) - Must be completed first
  • Extension must have valid JWT token to call backend APIs

Acceptance Criteria

AC 1.6.1: Hiển thị Dropdown Cài đặt

  • Given user đã đăng nhập
  • When user click icon ⚙️ trong header
  • Then settings dropdown mở ra
  • And dropdown hiển thị:
    • Current model: "GPT-4 Turbo" (chỉ xem, bị mờ)
    • Current search space: "Crypto Research" (chỉ xem, bị mờ)
    • Links đến web dashboard:
      • "🔗 Manage Connectors"
      • "💬 View All Chats"
      • "⚙️ Full Settings"
    • Nút "🚪 Logout"

AC 1.6.2: Đồng bộ Cài đặt khi Đăng nhập

  • Given user hoàn tất đăng nhập
  • When nhận được JWT token
  • Then extension gọi GET /api/v1/searchspaces để lấy danh sách search spaces
  • And extension gọi GET /api/v1/search-spaces/{id}/llm-preferences để lấy LLM config
  • And settings được lưu trong Plasmo Storage
  • And settings hiển thị trong dropdown

Response Format (từ backend):

// GET /api/v1/searchspaces
[
  {
    "id": 1,
    "name": "Crypto Research",
    "description": "...",
    "agent_llm_id": 0,
    "document_summary_llm_id": 0
  }
]

// GET /api/v1/search-spaces/{id}/llm-preferences
{
  "agent_llm_id": 0,
  "document_summary_llm_id": 0,
  "agent_llm": {
    "id": 0,
    "name": "Auto (Load Balanced)",
    "provider": "AUTO",
    "model_name": "auto"
  }
}

AC 1.6.3: Tự động cập nhật Cài đặt

  • Given user thay đổi model trên web dashboard
  • When extension phát hiện thay đổi (qua polling)
  • Then extension lấy settings đã cập nhật
  • And dropdown phản ánh model mới
  • And các cuộc chat tiếp theo sử dụng model mới

Polling:

  • Given extension đang hoạt động
  • When mỗi 5 phút
  • Then extension polls GET /api/v1/searchspaces và LLM preferences

AC 1.6.4: Search Space Selector

  • Given user có nhiều search spaces
  • When user click vào search space selector trong header
  • Then dropdown hiển thị tất cả search spaces của user
  • And user có thể chọn search space khác
  • And extension lưu selection vào Plasmo Storage
  • And các API calls tiếp theo sử dụng search_space_id mới

AC 1.6.5: Offline Handling

  • Given user đã đăng nhập và có settings đã cache
  • When user mất kết nối internet
  • Then extension sử dụng settings đã cache
  • And hiển thị indicator "Using cached settings"
  • When kết nối được khôi phục
  • Then extension sync settings mới từ backend

Tasks / Subtasks

  • Task 1: Settings Service (AC: 1.6.2, 1.6.3)

    • 1.1 Tạo lib/settings/settings-service.ts - API calls cho settings
    • 1.2 Implement fetchSearchSpaces() - GET /api/v1/searchspaces
    • 1.3 Implement fetchLLMPreferences(spaceId) - GET LLM config
    • 1.4 Implement polling mechanism (5 phút interval)
    • 1.5 Implement settings caching trong Plasmo Storage
  • Task 2: Settings State Management (AC: 1.6.1, 1.6.4)

    • 2.1 Tạo lib/settings/settings-store.ts - Zustand/Context store
    • 2.2 Define settings types và interfaces
    • 2.3 Implement search space selection logic
    • 2.4 Implement settings sync on login
  • Task 3: Settings UI Components (AC: 1.6.1, 1.6.4)

    • 3.1 Update sidepanel/chat/ChatHeader.tsx - Integrate real settings
    • 3.2 Tạo sidepanel/settings/SettingsDropdown.tsx - Enhanced dropdown
    • 3.3 Tạo sidepanel/settings/SearchSpaceSelector.tsx - Space picker
    • 3.4 Tạo sidepanel/settings/ModelDisplay.tsx - Read-only model info
  • Task 4: Integration với Auth (AC: 1.6.2, 1.6.5)

    • 4.1 Hook settings fetch vào auth flow (sau login thành công)
    • 4.2 Implement offline detection và fallback
    • 4.3 Clear settings on logout
    • 4.4 Handle 401 errors (redirect to login)
  • Task 5: Testing

    • 5.1 Test settings sync sau login
    • 5.2 Test polling mechanism
    • 5.3 Test search space switching
    • 5.4 Test offline mode với cached settings

Dev Notes

Backend APIs (ALREADY EXISTS)

Backend đã có đầy đủ APIs cho settings:

Search Spaces:

GET  /api/v1/searchspaces                           - List all user's search spaces
POST /api/v1/searchspaces                           - Create new search space
GET  /api/v1/searchspaces/{id}                      - Get single search space
PUT  /api/v1/searchspaces/{id}                      - Update search space
DELETE /api/v1/searchspaces/{id}                    - Delete search space

LLM Preferences:

GET  /api/v1/search-spaces/{id}/llm-preferences     - Get LLM config for space
PUT  /api/v1/search-spaces/{id}/llm-preferences     - Update LLM config

Global LLM Configs:

GET  /api/v1/global-new-llm-configs                 - List available LLM models

Existing Extension Code

ChatHeader.tsx (đã có UI cơ bản):

  • Search space selector dropdown (hardcoded data)
  • Settings dropdown với menu items
  • User avatar với logout
  • Token search bar

Cần update:

  • Replace hardcoded search spaces với real data từ API
  • Add model display trong settings dropdown
  • Connect logout button với auth service

Data Types

SearchSpace (từ backend):

interface SearchSpace {
  id: number;
  name: string;
  description?: string;
  user_id: string;
  agent_llm_id: number;
  document_summary_llm_id: number;
  created_at: string;
}

LLMPreferences (từ backend):

interface LLMPreferences {
  agent_llm_id: number;
  document_summary_llm_id: number;
  agent_llm?: LLMConfig;
  document_summary_llm?: LLMConfig;
}

interface LLMConfig {
  id: number;
  name: string;
  provider: string;
  model_name: string;
  is_global?: boolean;
  is_auto_mode?: boolean;
}

Project Structure Notes

Files cần tạo mới:

surfsense_browser_extension/
├── lib/
│   └── settings/
│       ├── settings-service.ts    # API calls
│       ├── settings-store.ts      # State management
│       └── types.ts               # TypeScript interfaces
└── sidepanel/
    └── settings/
        ├── SettingsDropdown.tsx   # Enhanced dropdown
        ├── SearchSpaceSelector.tsx # Space picker
        └── ModelDisplay.tsx       # Read-only model info

Files cần modify:

  • sidepanel/chat/ChatHeader.tsx - Integrate real settings data
  • sidepanel/index.tsx - Add SettingsProvider
  • lib/auth/api-client.ts - Add settings endpoints (từ Story 1.0)

Implementation Pattern

Settings Service:

// lib/settings/settings-service.ts
import { apiClient } from '../auth/api-client';

export const settingsService = {
  async fetchSearchSpaces(): Promise<SearchSpace[]> {
    return apiClient.get('/api/v1/searchspaces');
  },

  async fetchLLMPreferences(spaceId: number): Promise<LLMPreferences> {
    return apiClient.get(`/api/v1/search-spaces/${spaceId}/llm-preferences`);
  },

  async fetchGlobalLLMConfigs(): Promise<LLMConfig[]> {
    return apiClient.get('/api/v1/global-new-llm-configs');
  }
};

Settings Store (Plasmo Storage):

// lib/settings/settings-store.ts
import { Storage } from "@plasmohq/storage";

const storage = new Storage({ area: "local" });

export const settingsStore = {
  async saveSearchSpaces(spaces: SearchSpace[]) {
    await storage.set('searchSpaces', spaces);
  },

  async getSearchSpaces(): Promise<SearchSpace[] | null> {
    return storage.get('searchSpaces');
  },

  async saveSelectedSpaceId(id: number) {
    await storage.set('selectedSearchSpaceId', id);
  },

  async getSelectedSpaceId(): Promise<number | null> {
    return storage.get('selectedSearchSpaceId');
  }
};

Polling Implementation

// Polling every 5 minutes
const POLLING_INTERVAL = 5 * 60 * 1000; // 5 minutes

useEffect(() => {
  const pollSettings = async () => {
    try {
      const spaces = await settingsService.fetchSearchSpaces();
      await settingsStore.saveSearchSpaces(spaces);

      const selectedId = await settingsStore.getSelectedSpaceId();
      if (selectedId) {
        const prefs = await settingsService.fetchLLMPreferences(selectedId);
        await settingsStore.saveLLMPreferences(prefs);
      }
    } catch (error) {
      console.error('Settings poll failed:', error);
    }
  };

  const interval = setInterval(pollSettings, POLLING_INTERVAL);
  return () => clearInterval(interval);
}, []);

Security Considerations

  1. Settings API calls require valid JWT (handled by api-client)
  2. Cache settings locally for offline access
  3. Clear cached settings on logout
  4. Handle 401 errors gracefully (redirect to login)

References

  • [Source: surfsense_backend/app/routes/search_spaces_routes.py] - Search space APIs
  • [Source: surfsense_backend/app/routes/new_llm_config_routes.py] - LLM config APIs
  • [Source: surfsense_browser_extension/sidepanel/chat/ChatHeader.tsx] - Existing UI
  • [Source: _bmad-epics/epic-1-ai-powered-crypto-assistant.md#Story-1.6] - Full requirements
  • [Source: _bmad-output/implementation-artifacts/1-0-authentication-system.md] - Auth dependency

Dev Agent Record

Agent Model Used

{{agent_model_name_version}}

Completion Notes List

  • Story created: 2026-02-04
  • Backend APIs already exist and are fully functional
  • Extension UI partially exists (ChatHeader.tsx has basic structure)
  • DEPENDS ON Story 1.0 (Authentication) - must complete auth first
  • Settings are read-only in extension (changes made via web dashboard)

Debug Log References

(To be filled during implementation)

File List

(To be filled during implementation)