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/searchspacesvà 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
- 1.1 Tạo
-
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
- 2.1 Tạo
-
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
- 3.1 Update
-
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 datasidepanel/index.tsx- Add SettingsProviderlib/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
- Settings API calls require valid JWT (handled by api-client)
- Cache settings locally for offline access
- Clear cached settings on logout
- 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)