feat: Add comprehensive BMAD agent, workflow, and documentation framework.

This commit is contained in:
API Test Bot 2026-01-31 14:48:53 +07:00
parent 328f7dfecf
commit 524d9ab390
623 changed files with 105343 additions and 0 deletions

251
_bmad-output/admin-guide.md Normal file
View file

@ -0,0 +1,251 @@
# Hướng Dẫn Quản Trị SurfSense
**Dành cho Administrators**
---
## 📖 Giới Thiệu
Tài liệu này hướng dẫn administrators cách quản lý và vận hành hệ thống SurfSense.
---
## 🚀 Yêu Cầu Hệ Thống
### Backend Server (Production)
- CPU: 4+ cores
- RAM: 8GB+
- Storage: 100GB+ SSD
- OS: Ubuntu 22.04 LTS
### Database
- PostgreSQL 15+
- RAM: 4GB+
- Storage: 50GB+
### Dependencies
- Python 3.11+
- Node.js 18+
- Docker & Docker Compose
- Redis
---
## 👥 Quản Lý Users
### Tạo User Mới
**Via CLI:**
```bash
cd surfsense_backend
python manage.py create-user \
--email user@example.com \
--name "John Doe" \
--role user \
--plan pro
```
### Phân Quyền (Roles)
| Role | Permissions |
|------|-------------|
| `user` | Sử dụng tất cả tính năng end-user |
| `admin` | Quản lý users, xem analytics |
| `superadmin` | Quản lý toàn bộ hệ thống |
**Thay đổi role:**
```bash
python manage.py set-role --email user@example.com --role admin
```
### Quản Lý Plans
| Plan | Limits |
|------|--------|
| **Free** | 100 captures/month, 50 AI queries/month, 1GB storage |
| **Pro** | Unlimited captures, 500 AI queries/month, 10GB storage |
| **Enterprise** | Unlimited everything, custom AI models |
---
## ⚙️ Cấu Hình Hệ Thống
### Environment Variables
**File: `surfsense_backend/.env`**
```bash
DATABASE_URL=postgresql://user:password@localhost:5432/surfsense
REDIS_URL=redis://localhost:6379/0
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
JWT_SECRET=your-secret-key
JWT_EXPIRY=3600
ENABLE_RESEARCH_MODE=true
RATE_LIMIT_PER_MINUTE=60
LOG_LEVEL=INFO
```
### Database Migrations
```bash
cd surfsense_backend
alembic upgrade head
```
---
## 📊 Monitoring
### Health Check
```bash
curl https://api.surfsense.ai/health
```
**Response:**
```json
{
"status": "healthy",
"services": {
"database": "up",
"redis": "up",
"vector_db": "up"
}
}
```
### Logs
```bash
# Real-time logs
tail -f surfsense_backend/logs/app.log
# Docker logs
docker logs -f surfsense_backend
```
### Performance Metrics
**Via Admin Dashboard:**
- Active Users (real-time, daily, monthly)
- API Response Times (p50, p95, p99)
- Error Rates
- Storage Usage
---
## 🔐 Bảo Mật
### SSL/TLS
```bash
# Install certbot
sudo apt install certbot python3-certbot-nginx
# Obtain certificate
sudo certbot --nginx -d api.surfsense.ai
```
### Backup
**Automated backup script:**
```bash
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups/surfsense"
pg_dump -U surfsense surfsense > $BACKUP_DIR/db_$DATE.sql
tar -czf $BACKUP_DIR/uploads_$DATE.tar.gz /var/surfsense/uploads
# Keep last 7 days
find $BACKUP_DIR -type f -mtime +7 -delete
```
**Cron job (2AM daily):**
```bash
0 2 * * * /usr/local/bin/backup.sh >> /var/log/surfsense-backup.log 2>&1
```
---
## 🛠️ Troubleshooting
### Backend Không Start
```bash
# Check logs
tail -n 100 surfsense_backend/logs/app.log
# Test database
python -c "from app.db import engine; engine.connect()"
# Check port
lsof -i :8000
```
### AI Queries Timeout
```bash
# Test AI endpoint
curl -X POST http://localhost:8000/api/ai/chat \
-H "Authorization: Bearer <token>" \
-d '{"message": "test"}'
# Check queue
redis-cli LLEN ai_query_queue
```
### Slow Search
```sql
-- Create indexes
CREATE INDEX idx_content_user_id ON content(user_id);
CREATE INDEX idx_content_tags ON content USING GIN(tags);
```
---
## 📦 Deployment
### Docker Compose
```yaml
version: '3.8'
services:
backend:
build: ./surfsense_backend
ports: ["8000:8000"]
depends_on: [db, redis]
db:
image: postgres:15
environment:
POSTGRES_USER: surfsense
POSTGRES_PASSWORD: password
redis:
image: redis:7-alpine
qdrant:
image: qdrant/qdrant
ports: ["6333:6333"]
```
**Deploy:**
```bash
docker-compose up -d
```
---
**Cập nhật:** 2026-01-31 | **Version:** 1.0

View file

@ -0,0 +1,40 @@
# Hợp Đồng API (Backend)
Tài liệu này tóm tắt các REST API endpoints chính được phơi bày bởi Backend FastAPI.
*Lưu ý: Tất cả các protected endpoints đều yêu cầu Header Authorization: `Bearer <token>`.*
## Quản Lý Tài Liệu (Documents)
| Method | Endpoint | Mô tả | Quyền Truy Cập |
|--------|----------|-------|----------------|
| `POST` | `/api/v1/documents/` | Tạo hoặc upload tài liệu mới. | `User` |
| `GET` | `/api/v1/documents/` | Liệt kê tài liệu (có phân trang & lọc). | `User` |
| `GET` | `/api/v1/documents/{doc_id}` | Lấy chi tiết một tài liệu. | `User` (Owner) |
| `PATCH` | `/api/v1/documents/{doc_id}` | Cập nhật metadata tài liệu. | `User` (Owner) |
| `DELETE`| `/api/v1/documents/{doc_id}` | Xóa tài liệu (Soft or Hard delete). | `User` (Owner) |
| `POST` | `/api/v1/documents/search` | Tìm kiếm ngữ nghĩa (Semantic search) trên tài liệu. | `User` |
## Chat & AI
| Method | Endpoint | Mô tả |
|--------|----------|-------|
| `POST` | `/api/v1/chat/threads` | Tạo phiên chat mới. |
| `GET` | `/api/v1/chat/threads` | Lấy lịch sử các phiên chat. |
| `POST` | `/api/v1/chat/message` | Gửi tin nhắn tới Agent (Streaming response). |
| `GET` | `/api/v1/chat/{thread_id}/history` | Lấy lịch sử tin nhắn của một thread. |
## Connectors (Tích Hợp)
| Method | Endpoint | Mô tả |
|--------|----------|-------|
| `GET` | `/api/v1/connectors/available` | Danh sách các connectors được hỗ trợ. |
| `POST` | `/api/v1/connectors/{type}/auth` | Bắt đầu quy trình OAuth cho connector. |
| `POST` | `/api/v1/connectors/{type}/sync` | Kích hoạt đồng bộ dữ liệu thủ công. |
## Tiện Ích Trình Duyệt (Extension)
| Method | Endpoint | Mô tả |
|--------|----------|-------|
| `POST` | `/api/v1/extension/ingest` | Nhận dữ liệu trang web từ extension. |
| `POST` | `/api/v1/extension/context` | Kiểm tra ngữ cảnh hiện tại (User có đang track trang này không?). |

View file

@ -0,0 +1,40 @@
# Kiến Trúc Backend
## Tổng Quan
Backend của SurfSense là một ứng dụng **Python FastAPI** mạnh mẽ, được thiết kế cho các quy trình làm việc agentic hiệu suất cao. Nó đóng vai trò là hệ thống thần kinh trung ương, điều phối RAG (Retrieval-Augmented Generation), quản lý bộ nhớ của agent (agent memory), và xử lý tương tác với các mô hình ngôn ngữ lớn (LLMs).
## Các Thành Phần Cốt Lõi
### 1. Framework AI Agent (DeepAgents & LangGraph)
- **DeepAgents**: Framework tùy chỉnh để xây dựng các AI agents tự chủ (autonomous agents).
- **LangGraph**: Quản lý StateGraph (đồ thị trạng thái) và quy trình điều phối cho các suy luận phức tạp, nhiều bước.
- **Workflow**: Người dùng gửi truy vấn -> LangGraph xác định ý định (Routing) -> Kích hoạt các Agents cụ thể (Search Agent, Coding Agent, v.v.).
### 2. Dịch Vụ Dữ Liệu (Data Services)
- **Primary Database**: **Postgres** (với extension `pgvector`) lưu trữ:
- Dữ liệu người dùng & ứng dụng.
- Vector Embeddings cho tìm kiếm ngữ nghĩa (semantic search).
- Lịch sử chat và phiên làm việc.
- **ORM**: **SQLAlchemy (Async)** dùng cho các tương tác cơ sở dữ liệu quan hệ.
- **Caching/Queue**: **Redis** dùng cho hàng đợi tác vụ (Celery broker) và caching phản hồi ngắn hạn.
### 3. Hệ Thống Tìm Kiếm & RAG
- **Vector Store**: Sử dụng `pgvector` để lưu trữ embeddings của tài liệu.
- **Retriever**: Logic tùy chỉnh trong `app/retriever/` để lấy ngữ cảnh (fetches context) dựa trên sự tương đồng (similarity) và metadata filtering.
- **Ingestion Pipeline**: Celery workers xử lý việc tải tài liệu từ nguồn bên ngoài, chia nhỏ văn bản (chunking), tạo embedding, và lưu trữ.
### 4. Kết Nối Ứng Dụng Ngoài (Connectors)
- **Kiến Trúc**: Modular adapter pattern.
- **Hỗ trợ**: Slack, Google Drive, Notion, GitHub, v.v. (30+ integrations).
- **Cơ chế**: Webhooks hoặc định kỳ polling (thực hiện bởi Celery beats).
## Luồng Dữ Liệu (Data Flow)
1. **Request**: Client (Web/Extension) gửi REST request tới FastAPI Endpoints.
2. **Auth**: Middleware xác thực JWT/OAuth token.
3. **Controller**: Route handler (`app/routes/`) nhận request, gọi Service layer.
4. **Processing**:
- Nếu là tác vụ nhanh (CRUD): Xử lý trực tiếp với DB.
- Nếu là tác vụ AI: Đẩy vào LangGraph runner để streaming phản hồi.
- Nếu là tác vụ dài (Ingestion): Đẩy job vào Redis queue cho Celery.
5. **Response**: Trả về JSON hoặc Streaming Response (SSE).

View file

@ -0,0 +1,56 @@
# Kiến Trúc Browser Extension
## Tổng Quan
SurfSense Browser Extension là "tai mắt" của hệ thống, cho phép thu thập dữ liệu (ingestion) liền mạch và hỗ trợ người dùng ngay trên bất kỳ trang web nào. Nó được xây dựng bằng **Plasmo Framework**, giúp đơn giản hóa việc phát triển extension cho Chrome (Manifest V3).
## Stack Công Nghệ
| Hạng Mục | Công Nghệ |
|----------|-----------|
| **Framework** | Plasmo |
| **UI** | React 18, Tailwind CSS |
| **Stores** | Storage API (Plasmo Storage) |
| **Messaging** | Plasmo Messaging (Port-based) |
## Các Thành Phần Chính
### 1. Popup (`popup.tsx`)
- Giao diện người dùng xuất hiện khi click vào icon extension.
- **Chức năng**:
- Đăng nhập/Đăng xuất.
- Chuyển đổi trạng thái "Tracking" (Bật/Tắt thu thập active tab).
- Tìm kiếm nhanh (Quick Search) vào kho kiến thức SurfSense.
- Hiển thị thông báo trạng thái hệ thống.
### 2. Background Service Worker (`background/`)
- Trái tim của extension, chạy ngầm độc lập với các tab.
- **Nhiệm vụ**:
- **Session Management**: Giữ token xác thực, refresh token khi hết hạn.
- **Ingestion Queue**: Nhận dữ liệu từ Content Scripts, đóng gói (batching) để tránh spam request, và gửi về Backend API.
- **Context Awareness**: Giám sát thay đổi URL/Tab để kích hoạt thu thập lại nếu cần.
### 3. Content Scripts
- Scripts chạy trong ngữ cảnh của trang web người dùng đang xem.
- **Nhiệm vụ**:
- Trích xuất nội dung trang (DOM parsing, Readability.js).
- Lắng nghe các sự kiện (ví dụ: user copy text -> gợi ý lưu làm note).
- Inject UI (nếu cần): Hiển thị nút "Lưu vào SurfSense" trực tiếp trên trang.
## Luồng Hoạt Động (Workflows)
### Quy Trình Thu Thập (Ingestion Flow)
1. User truy cập `example.com`.
2. **Content Script** kích hoạt, parse nội dung chính (loại bỏ quảng cáo/footer).
3. Script gửi message chứa nội dung tới **Background Worker**.
4. **Background** kiểm tra:
- User có đang bật tracking không?
- Token còn hiệu lực không?
- Trang này có bị blacklist không (ví dụ: localhost, banking sites)?
5. Nếu hợp lệ, Background đẩy dữ liệu về Backend `POST /api/v1/extension/ingest`.
### Quy Trình Tra Cứu (Lookup Flow)
1. User bôi đen 1 đoạn text trên web.
2. Extension hiển thị tooltip nhỏ.
3. User click "Search in SurfSense".
4. Request gửi về Backend để tìm kiếm các tài liệu liên quan đến đoạn text đó.
5. Kết quả hiển thị ngay trong Side Panel hoặc Popup.

View file

@ -0,0 +1,48 @@
# Kiến Trúc Web Frontend
## Tổng Quan
Ứng dụng Web SurfSense được xây dựng trên **Next.js 16**, tận dụng các tính năng mới nhất như **React Server Components (RSC)****Server Actions**. Nó mang lại trải nghiệm người dùng (UX) hiện đại, nhanh chóng và tương tác cao, đóng vai trò là giao diện chính để người dùng quản lý kiến thức và tương tác với AI Agents.
## Stack Công Nghệ (Tech Stack)
| Hạng Mục | Công Nghệ | Ghi Chú |
|----------|-----------|---------|
| **Core** | Next.js 16 (Turbopack) | App Router, Server Actions |
| **Language** | TypeScript | Type safety toàn diện |
| **UI Library** | React 19 | Hooks mới (useOptimistic, useFormStatus) |
| **Styling** | Tailwind CSS v4 | Utility-first CSS |
| **State/Sync** | ElectricSQL | Đồng bộ dữ liệu local-first / real-time |
| **ORM Client** | Drizzle ORM | Truy vấn database an toàn (Type-safe) |
| **Components** | Shadcn UI + Assistant UI | Reusable components & AI Chat UI |
## Mô Hình Kiến Trúc (Architecture Patterns)
### 1. App Router & Server Components
- **Mặc định là Server Component**: Hầu hết các components (Layout, Page) được render trên server để tối ưu SEO và tải trang ban đầu.
- **Client Components**: Chỉ sử dụng (`"use client"`) cho các phần tương tác (interactive) như form, button, hoặc real-time chat.
- **Data Fetching**: Fetch dữ liệu trực tiếp trong Server Components (không cần useEffect cho initial data).
### 2. Server Actions cho Mutations
- Thay vì tạo API routes riêng biệt cho mọi hành động (submit form, like bài viết), SurfSense sử dụng **Server Actions**.
- Gọi hàm backend trực tiếp từ frontend code.
- Xử lý xác thực và validation ngay trong action.
### 3. Local-First Sync với ElectricSQL
- **Vấn đề**: Độ trễ mạng khi thao tác nhiều dữ liệu.
- **Giải pháp**: ElectricSQL đồng bộ một phần database Postgres xuống client (trong trình duyệt).
- **Lợi ích**: UI phản hồi tức thì (Optimistic UI), hoạt động offline-first, và tự động đồng bộ khi có mạng.
### 4. Kiến Trúc AI Chat
- **Streaming**: Sử dụng `AI SDK` (hoặc tương đương) để stream phản hồi từ Backend LangGraph.
- **Generative UI**: Render các components React ngay trong luồng chat (ví dụ: hiển thị một bảng dữ liệu hoặc biểu đồ thay vì chỉ text).
- **Tool Call Handling**: Client hiển thị trạng thái "đang xử lý" khi Agent gọi tool kiểm tra thời tiết hoặc tìm kiếm document.
## Cấu Trúc Thư Mục Chính (`surfsense_web/app`)
- `(home)/`: Landing page, Marketing sites (Public).
- `dashboard/`: Không gian làm việc chính của user (Protected).
- `layout.tsx`: Sidebar, Header, Auth Check.
- `page.tsx`: Dashboard tổng quan.
- `chat/[id]/page.tsx`: Giao diện chat chi tiết.
- `search/page.tsx`: Giao diện tìm kiếm nâng cao.
- `api/`: Route Handlers cho các trường hợp đặc biệt (như Webhook từ bên thứ 3).

View file

@ -0,0 +1,38 @@
# Kho Components Web (Component Inventory)
Tài liệu này liệt kê các nhóm components UI chính được sử dụng trong `surfsense_web`.
## 1. UI Primitives (`components/ui`)
Dựa trên **Shadcn UI****Radix Primitives**. Các thành phần cơ bản này đảm bảo tính nhất quán về thiết kế và khả năng tiếp cận (accessibility).
- **Core**: `Button`, `Input`, `Select`, `Checkbox`, `Switch`.
- **Feedback**: `Toast` (thông báo), `Alert`, `Progress`, `Skeleton` (loading state).
- **Overlay**: `Dialog` (Modal), `Sheet` (Sidebar Drawer), `Popover`, `Tooltip`.
- **Layout**: `Card`, `Separator`, `ScrollArea`, `Resizable` (chia đôi màn hình).
## 2. Layout Components (`components/layout`)
Các thành phần cấu trúc dùng chung cho các trang.
- **`Sidebar`**: Menu điều hướng chính bên trái (collapsible).
- **`Header`**: Thanh trên cùng chứa User Menu, Theme Toggle, Breadcrumbs.
- **`UserNav`**: Dropdown menu tài khoản người dùng.
- **`ThemeToggle`**: Chuyển đổi Dark/Light mode.
## 3. Assistant UI (`components/assistant-ui`)
Các components chuyên biệt cho trải nghiệm AI Chat.
- **`ChatThread`**: Container chính quản lý danh sách tin nhắn.
- **`Composer`**: Khung nhập liệu thông minh (hỗ trợ slash commands, file attachment).
- **`MessageList`**: Hiển thị tin nhắn cuộn (scrollable).
- **`MessageBubble`**: Hiển thị nội dung tin nhắn (User/AI).
- Hỗ trợ Markdown rendering.
- Hỗ trợ hiển thị Code Block với syntax highlighting.
- **`ThreadHistory`**: Sidebar danh sách các cuộc hội thoại cũ.
- **`ToolResult`**: Hiển thị kết quả trả về từ tool (VD: Card thông tin thời tiết).
## 4. Feature Components
Các components đặc thù cho nghiệp vụ SurfSense.
- **`DocumentCard`**: Hiển thị tóm tắt tài liệu trong danh sách tìm kiếm.
- **`ConnectorGrid`**: Lưới các icon ứng dụng để user kết nối (Gmail, Slack...).
- **`SearchFilters`**: Bộ lọc nâng cao cho tìm kiếm (theo ngày, loại file, nguồn).

View file

@ -0,0 +1,390 @@
# Giải Thích Hệ Thống Connectors
**Tài liệu bổ sung cho SurfSense**
---
## 📌 Connectors Là Gì?
**Connectors** (Bộ kết nối) là tính năng cho phép SurfSense **tìm kiếm và truy xuất dữ liệu từ các ứng dụng bên ngoài** mà bạn đang sử dụng hàng ngày, như:
- 📧 **Gmail** - Tìm kiếm trong emails
- 📁 **Google Drive** - Tìm kiếm files và documents
- 📅 **Google Calendar** - Tìm kiếm events và meetings
- 💬 **Slack** - Tìm kiếm messages và conversations
- 📝 **Notion** - Tìm kiếm pages và databases
- 🎯 **Linear** - Tìm kiếm issues và projects
- 📊 **Airtable** - Tìm kiếm bases và records
- 🎫 **Jira** - Tìm kiếm tickets
- 📚 **Confluence** - Tìm kiếm wiki pages
- 🗂️ **Microsoft Teams** - Tìm kiếm chats và files
**Tổng cộng:** SurfSense hỗ trợ **26+ connectors** khác nhau!
---
## 🎯 Mục Đích
Thay vì phải:
1. Mở Gmail → tìm kiếm email
2. Mở Google Drive → tìm kiếm file
3. Mở Slack → tìm kiếm message
4. Mở Notion → tìm kiếm note
Bạn chỉ cần:
- **Mở SurfSense** → Tìm kiếm 1 lần → Nhận kết quả từ **TẤT CẢ** các ứng dụng đã kết nối!
---
## 🔌 Cách Hoạt Động
### Bước 1: Kết Nối (Connect)
Khi bạn click nút **"Connect"** bên cạnh một connector (ví dụ: Google Drive):
1. **OAuth Authentication:**
- SurfSense chuyển hướng bạn đến trang đăng nhập của Google
- Bạn đăng nhập và cấp quyền cho SurfSense:
- ✅ Đọc files trong Drive
- ✅ Đọc metadata (tên file, ngày tạo, etc.)
- ❌ **KHÔNG** có quyền xóa hoặc chỉnh sửa files
2. **Lưu Access Token:**
- Google trả về một **access token** (mã truy cập)
- SurfSense lưu token này vào database (được mã hóa)
- Token này cho phép SurfSense truy cập Drive của bạn **thay mặt bạn**
3. **Tạo Connector Record:**
- SurfSense tạo 1 record trong bảng `search_source_connectors`:
```json
{
"id": 123,
"name": "My Google Drive",
"connector_type": "GOOGLE_DRIVE_CONNECTOR",
"user_id": "your-user-id",
"search_space_id": 1,
"config": {
"access_token": "encrypted_token",
"refresh_token": "encrypted_refresh_token"
},
"is_indexable": true,
"periodic_indexing_enabled": true,
"indexing_frequency_minutes": 60
}
```
### Bước 2: Indexing (Lập Chỉ Mục)
Sau khi kết nối thành công, SurfSense bắt đầu **indexing** (lập chỉ mục) dữ liệu:
1. **Fetch Data từ API:**
- SurfSense gọi API của Google Drive (sử dụng access token)
- Lấy danh sách tất cả files: `GET https://www.googleapis.com/drive/v3/files`
- Với mỗi file, lấy:
- Tên file
- Nội dung (text content)
- Metadata (owner, created_at, modified_at, etc.)
2. **Tạo Embeddings:**
- Nội dung file được chuyển thành **vector embeddings** (dùng OpenAI/Gemini)
- Ví dụ: File "Project Plan.docx" → Vector 1536 chiều
- Vector này biểu diễn **ý nghĩa ngữ nghĩa** của nội dung
3. **Lưu vào Database:**
- **PostgreSQL** (bảng `documents`):
```sql
INSERT INTO documents (
title, content, document_type, source_connector_id, user_id
) VALUES (
'Project Plan.docx',
'Full text content...',
'GOOGLE_DRIVE_FILE',
123, -- connector_id
'your-user-id'
);
```
- **Vector Database** (Qdrant):
```json
{
"id": "doc-456",
"vector": [0.123, -0.456, 0.789, ...], // 1536 dimensions
"payload": {
"title": "Project Plan.docx",
"document_id": 456,
"connector_type": "GOOGLE_DRIVE_FILE"
}
}
```
4. **Periodic Re-indexing:**
- Mỗi 60 phút (hoặc theo cấu hình), SurfSense tự động:
- Kiểm tra files mới
- Kiểm tra files đã update
- Re-index nếu có thay đổi
### Bước 3: Search (Tìm Kiếm)
Khi bạn tìm kiếm trong SurfSense:
1. **User Query:**
- Bạn nhập: *"project timeline for Q1"*
2. **Query Embedding:**
- SurfSense chuyển query thành vector: `[0.234, -0.567, ...]`
3. **Vector Search:**
- Tìm kiếm trong Qdrant (similarity search):
```python
results = qdrant_client.search(
collection_name="surfsense",
query_vector=[0.234, -0.567, ...],
limit=10,
filter={
"user_id": "your-user-id",
"connector_type": ["GOOGLE_DRIVE_FILE", "GMAIL", "NOTION"]
}
)
```
4. **Kết Quả:**
- Trả về top 10 documents có vector gần nhất (most similar)
- Ví dụ:
```
1. "Q1 Project Timeline.xlsx" (Google Drive) - 95% match
2. "Email: Q1 Planning Meeting" (Gmail) - 87% match
3. "Notion: Q1 Roadmap" (Notion) - 82% match
```
5. **AI Chat (Optional):**
- Nếu bạn dùng AI Chat, SurfSense sẽ:
- Lấy nội dung của top 10 results
- Gửi cho LLM (GPT-4/Claude/Gemini) kèm theo query
- LLM tổng hợp và trả lời câu hỏi dựa trên context
---
## 🔐 Bảo Mật
### Quyền Truy Cập
- **Read-only:** Connectors chỉ có quyền **ĐỌC**, không thể xóa/sửa dữ liệu
- **User-scoped:** Mỗi user chỉ thấy dữ liệu của chính họ
- **Encrypted:** Access tokens được mã hóa trong database
### Revoke Access (Thu Hồi Quyền)
Bạn có thể ngắt kết nối bất cứ lúc nào:
1. **Trong SurfSense:**
- Vào **Settings** → **Connectors**
- Click **"Disconnect"** bên cạnh connector
- SurfSense sẽ:
- Xóa access token
- Xóa tất cả indexed data từ connector đó
2. **Trong Google/Slack/etc:**
- Vào settings của ứng dụng gốc
- Revoke quyền truy cập của SurfSense
- Ví dụ Google: https://myaccount.google.com/permissions
---
## 📊 Loại Connectors
### 1. Managed OAuth (Composio)
**Ví dụ:** Google Drive, Gmail, Google Calendar
- Sử dụng **Composio** (third-party OAuth provider)
- Ưu điểm:
- Setup nhanh (không cần tạo OAuth app riêng)
- Composio quản lý token refresh tự động
- Nhược điểm:
- Phụ thuộc vào Composio service
**Flow:**
```
User → SurfSense → Composio → Google OAuth → Access Token → SurfSense
```
### 2. Quick Connect (Direct OAuth)
**Ví dụ:** Notion, Slack, Linear, Airtable
- Kết nối trực tiếp với API của ứng dụng
- Ưu điểm:
- Không phụ thuộc third-party
- Full control
- Nhược điểm:
- Cần setup OAuth app riêng cho mỗi service
**Flow:**
```
User → SurfSense → Notion OAuth → Access Token → SurfSense
```
### 3. API Key Based
**Ví dụ:** Elasticsearch, Webcrawler
- Không dùng OAuth, chỉ cần API key
- User nhập API key trực tiếp vào SurfSense
### 4. Self-Hosted Only
**Ví dụ:** Obsidian Connector
- Chỉ hoạt động khi SurfSense chạy self-hosted
- Truy cập trực tiếp vào local file system
---
## 🛠️ Cấu Hình Connector
Mỗi connector có các settings:
### Indexing Settings
```json
{
"periodic_indexing_enabled": true,
"indexing_frequency_minutes": 60,
"next_scheduled_at": "2026-01-31T15:00:00Z"
}
```
- **periodic_indexing_enabled:** Bật/tắt auto re-index
- **indexing_frequency_minutes:** Tần suất re-index (phút)
- **next_scheduled_at:** Lần re-index tiếp theo
### Connector-Specific Config
**Google Drive:**
```json
{
"folders": ["folder-id-1", "folder-id-2"], // Chỉ index các folders này
"file_types": ["document", "spreadsheet"], // Chỉ index loại files này
"exclude_shared": false // Index cả shared files
}
```
**Slack:**
```json
{
"channels": ["general", "engineering"], // Chỉ index các channels này
"include_dms": true, // Index direct messages
"date_range_days": 90 // Chỉ index 90 ngày gần nhất
}
```
---
## 💡 Use Cases
### 1. Knowledge Worker
**Scenario:** Bạn là Product Manager, cần tìm thông tin về feature request từ khách hàng.
**Trước khi có Connectors:**
- Tìm trong Gmail → Không thấy
- Tìm trong Slack → Không thấy
- Tìm trong Notion → Không thấy
- Tìm trong Linear → Tìm thấy!
- **Tổng thời gian:** 15 phút
**Sau khi có Connectors:**
- Mở SurfSense → Tìm kiếm: *"customer feature request payment"*
- Kết quả:
1. Linear Issue #123
2. Slack message từ customer
3. Email thread với customer
4. Notion doc: Feature Spec
- **Tổng thời gian:** 30 giây
### 2. Developer
**Scenario:** Debug lỗi production, cần tìm code changes liên quan.
**Connectors kết nối:**
- GitHub (code commits)
- Slack (engineering channel)
- Jira (bug tickets)
- Confluence (technical docs)
**Search query:** *"payment API timeout error"*
**Kết quả:**
1. GitHub commit: "Fix payment timeout"
2. Jira ticket: PROD-456
3. Slack discussion về issue
4. Confluence: Payment API Architecture
---
## 🚨 Lưu Ý Quan Trọng
### 1. Research Mode KHÔNG Tồn Tại Trên FE
**Sự thật:**
- Tài liệu trước đó (user-guide.md) đề cập "Research Mode" là **SAI**
- Frontend chỉ có **1 chế độ chat duy nhất**
- Backend có thể có logic khác nhau, nhưng user không thấy toggle nào
**Đã sửa:** Tài liệu sẽ được cập nhật để loại bỏ phần Research Mode.
### 2. Connector ≠ Extension
- **Browser Extension:** Capture nội dung từ trang web bạn đang browse
- **Connectors:** Fetch dữ liệu từ các ứng dụng bên ngoài (Gmail, Drive, etc.)
- Hai tính năng **độc lập** nhưng **bổ sung** cho nhau
### 3. Privacy
- Dữ liệu được index **chỉ dành cho bạn**
- Không ai khác (kể cả admin) có thể thấy nội dung files của bạn
- Trừ khi bạn share chat với visibility = "SEARCH_SPACE"
---
## 📞 Troubleshooting
### Connector Không Hoạt Động
**Triệu chứng:** Sau khi connect, không thấy kết quả khi search.
**Kiểm tra:**
1. **Indexing status:**
```sql
SELECT name, connector_type, last_indexed_at, next_scheduled_at
FROM search_source_connectors
WHERE user_id = 'your-user-id';
```
- Nếu `last_indexed_at` = NULL → Indexing chưa chạy
2. **Backend logs:**
```bash
grep "connector" surfsense_backend/logs/app.log
```
- Tìm lỗi liên quan đến connector
3. **Token expired:**
- Access token có thể hết hạn
- Disconnect và reconnect lại connector
### Kết Quả Không Chính Xác
**Nguyên nhân:**
- Embeddings không capture đúng ý nghĩa
- Cần re-index với model tốt hơn
**Giải pháp:**
- Admin có thể trigger manual re-index:
```bash
python manage.py reindex-connector --connector-id 123
```
---
**Cập nhật:** 2026-01-31 | **Version:** 1.0

View file

@ -0,0 +1,835 @@
# Hướng Dẫn Tạo Custom Connectors cho SurfSense
## Tổng Quan
Bạn **hoàn toàn có thể** tạo custom connectors để kết nối đến các API bên ngoài như DexScreener, DefiLlama để phân tích token crypto. SurfSense có kiến trúc connector rất linh hoạt và dễ mở rộng.
## Kiến Trúc Connector
Mỗi connector trong SurfSense bao gồm 3 phần chính:
### 1. **Connector Class** (`app/connectors/`)
- Xử lý logic kết nối đến API bên ngoài
- Fetch và transform data
- Format data thành markdown để indexing
### 2. **API Routes** (`app/routes/`)
- Endpoint để add/delete/test connector
- Lưu config vào database
- Xác thực user
### 3. **Database Schema** (`app/db.py`)
- Định nghĩa connector type trong `SearchSourceConnectorType` enum
- Lưu trữ config (API keys, settings) trong `SearchSourceConnector` table
## Ví Dụ: Tạo DexScreener Connector
### Bước 1: Tạo Connector Class
Tạo file `/Users/mac_1/Documents/GitHub/SurfSense/surfsense_backend/app/connectors/dexscreener_connector.py`:
```python
"""
DexScreener Connector Module
A module for fetching token data and analytics from DexScreener API.
"""
from typing import Any
import requests
from datetime import datetime
class DexScreenerConnector:
"""Class for retrieving token data from DexScreener."""
def __init__(self, api_key: str | None = None):
"""
Initialize the DexScreenerConnector class.
Args:
api_key: DexScreener API key (optional for public endpoints)
"""
self.api_key = api_key
self.base_url = "https://api.dexscreener.com/latest"
def set_api_key(self, api_key: str) -> None:
"""Set the DexScreener API key."""
self.api_key = api_key
def get_headers(self) -> dict[str, str]:
"""Get headers for DexScreener API requests."""
headers = {"Content-Type": "application/json"}
if self.api_key:
headers["Authorization"] = f"Bearer {self.api_key}"
return headers
def search_pairs(
self, query: str
) -> tuple[list[dict[str, Any]], str | None]:
"""
Search for trading pairs by token address or symbol.
Args:
query: Token address or symbol to search
Returns:
Tuple containing (pairs list, error message or None)
"""
try:
url = f"{self.base_url}/dex/search"
params = {"q": query}
response = requests.get(
url,
headers=self.get_headers(),
params=params,
timeout=10
)
if response.status_code == 200:
data = response.json()
pairs = data.get("pairs", [])
return pairs, None
else:
return [], f"API request failed with status {response.status_code}"
except Exception as e:
return [], f"Error searching pairs: {str(e)}"
def get_token_pairs(
self, token_address: str
) -> tuple[list[dict[str, Any]], str | None]:
"""
Get all pairs for a specific token address.
Args:
token_address: Token contract address
Returns:
Tuple containing (pairs list, error message or None)
"""
try:
url = f"{self.base_url}/dex/tokens/{token_address}"
response = requests.get(
url,
headers=self.get_headers(),
timeout=10
)
if response.status_code == 200:
data = response.json()
pairs = data.get("pairs", [])
return pairs, None
else:
return [], f"API request failed with status {response.status_code}"
except Exception as e:
return [], f"Error fetching token pairs: {str(e)}"
def get_pair_by_address(
self, pair_address: str
) -> tuple[dict[str, Any] | None, str | None]:
"""
Get detailed information about a specific pair.
Args:
pair_address: Pair contract address
Returns:
Tuple containing (pair data dict, error message or None)
"""
try:
url = f"{self.base_url}/dex/pairs/{pair_address}"
response = requests.get(
url,
headers=self.get_headers(),
timeout=10
)
if response.status_code == 200:
data = response.json()
pair = data.get("pair")
return pair, None
else:
return None, f"API request failed with status {response.status_code}"
except Exception as e:
return None, f"Error fetching pair: {str(e)}"
def format_pair_to_markdown(self, pair: dict[str, Any]) -> str:
"""
Convert a trading pair to markdown format for indexing.
Args:
pair: The pair object from DexScreener API
Returns:
Markdown string representation of the pair
"""
# Extract basic info
chain_id = pair.get("chainId", "Unknown")
dex_id = pair.get("dexId", "Unknown")
pair_address = pair.get("pairAddress", "")
# Token info
base_token = pair.get("baseToken", {})
quote_token = pair.get("quoteToken", {})
base_name = base_token.get("name", "Unknown")
base_symbol = base_token.get("symbol", "Unknown")
quote_name = quote_token.get("name", "Unknown")
quote_symbol = quote_token.get("symbol", "Unknown")
# Price and volume
price_native = pair.get("priceNative", "N/A")
price_usd = pair.get("priceUsd", "N/A")
volume_24h = pair.get("volume", {}).get("h24", "N/A")
liquidity_usd = pair.get("liquidity", {}).get("usd", "N/A")
# Price changes
price_change_5m = pair.get("priceChange", {}).get("m5", "N/A")
price_change_1h = pair.get("priceChange", {}).get("h1", "N/A")
price_change_24h = pair.get("priceChange", {}).get("h24", "N/A")
# Build markdown
markdown = f"# {base_symbol}/{quote_symbol} Trading Pair\n\n"
markdown += f"**Chain:** {chain_id}\n"
markdown += f"**DEX:** {dex_id}\n"
markdown += f"**Pair Address:** `{pair_address}`\n\n"
markdown += "## Token Information\n\n"
markdown += f"### Base Token: {base_name} ({base_symbol})\n"
markdown += f"- **Address:** `{base_token.get('address', 'N/A')}`\n\n"
markdown += f"### Quote Token: {quote_name} ({quote_symbol})\n"
markdown += f"- **Address:** `{quote_token.get('address', 'N/A')}`\n\n"
markdown += "## Market Data\n\n"
markdown += f"- **Price (Native):** {price_native}\n"
markdown += f"- **Price (USD):** ${price_usd}\n"
markdown += f"- **24h Volume:** ${volume_24h}\n"
markdown += f"- **Liquidity (USD):** ${liquidity_usd}\n\n"
markdown += "## Price Changes\n\n"
markdown += f"- **5 minutes:** {price_change_5m}%\n"
markdown += f"- **1 hour:** {price_change_1h}%\n"
markdown += f"- **24 hours:** {price_change_24h}%\n\n"
# Add URL if available
url = pair.get("url")
if url:
markdown += f"**DexScreener URL:** {url}\n\n"
# Add timestamp
markdown += f"*Data fetched at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n"
return markdown
def get_all_token_data(
self, token_addresses: list[str]
) -> tuple[list[str], str | None]:
"""
Fetch and format data for multiple tokens.
Args:
token_addresses: List of token contract addresses
Returns:
Tuple containing (list of markdown documents, error message or None)
"""
documents = []
errors = []
for address in token_addresses:
pairs, error = self.get_token_pairs(address)
if error:
errors.append(f"Error for {address}: {error}")
continue
for pair in pairs:
markdown_doc = self.format_pair_to_markdown(pair)
documents.append(markdown_doc)
error_msg = "; ".join(errors) if errors else None
return documents, error_msg
```
### Bước 2: Thêm Connector Type vào Database
Sửa file `/Users/mac_1/Documents/GitHub/SurfSense/surfsense_backend/app/db.py`:
```python
class SearchSourceConnectorType(str, Enum):
# ... existing connectors ...
DEXSCREENER_CONNECTOR = "DEXSCREENER_CONNECTOR"
DEFILLAMA_CONNECTOR = "DEFILLAMA_CONNECTOR"
```
### Bước 3: Tạo API Routes
Tạo file `/Users/mac_1/Documents/GitHub/SurfSense/surfsense_backend/app/routes/dexscreener_add_connector_route.py`:
```python
import logging
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select
from app.db import (
SearchSourceConnector,
SearchSourceConnectorType,
User,
get_async_session,
)
from app.users import current_active_user
logger = logging.getLogger(__name__)
router = APIRouter()
class AddDexScreenerConnectorRequest(BaseModel):
"""Request model for adding a DexScreener connector."""
api_key: str | None = Field(None, description="DexScreener API key (optional)")
space_id: int = Field(..., description="Search space ID")
token_addresses: list[str] = Field(
default_factory=list,
description="List of token addresses to track"
)
@router.post("/connectors/dexscreener/add")
async def add_dexscreener_connector(
request: AddDexScreenerConnectorRequest,
user: User = Depends(current_active_user),
session: AsyncSession = Depends(get_async_session),
):
"""
Add a new DexScreener connector for the authenticated user.
Args:
request: The request containing DexScreener config and space_id
user: Current authenticated user
session: Database session
Returns:
Success message and connector details
Raises:
HTTPException: If connector already exists or validation fails
"""
try:
# Check if a DexScreener connector already exists
result = await session.execute(
select(SearchSourceConnector).filter(
SearchSourceConnector.search_space_id == request.space_id,
SearchSourceConnector.user_id == user.id,
SearchSourceConnector.connector_type
== SearchSourceConnectorType.DEXSCREENER_CONNECTOR,
)
)
existing_connector = result.scalars().first()
config = {
"token_addresses": request.token_addresses,
}
if request.api_key:
config["api_key"] = request.api_key
if existing_connector:
# Update existing connector
existing_connector.config = config
existing_connector.is_indexable = True
await session.commit()
await session.refresh(existing_connector)
logger.info(
f"Updated existing DexScreener connector for user {user.id}"
)
return {
"message": "DexScreener connector updated successfully",
"connector_id": existing_connector.id,
"connector_type": "DEXSCREENER_CONNECTOR",
}
# Create new DexScreener connector
db_connector = SearchSourceConnector(
name="DexScreener Token Tracker",
connector_type=SearchSourceConnectorType.DEXSCREENER_CONNECTOR,
config=config,
search_space_id=request.space_id,
user_id=user.id,
is_indexable=True,
)
session.add(db_connector)
await session.commit()
await session.refresh(db_connector)
logger.info(
f"Successfully created DexScreener connector for user {user.id}"
)
return {
"message": "DexScreener connector added successfully",
"connector_id": db_connector.id,
"connector_type": "DEXSCREENER_CONNECTOR",
}
except IntegrityError as e:
await session.rollback()
logger.error(f"Database integrity error: {str(e)}")
raise HTTPException(
status_code=409,
detail="A DexScreener connector already exists for this user.",
) from e
except Exception as e:
await session.rollback()
logger.error(f"Unexpected error: {str(e)}", exc_info=True)
raise HTTPException(
status_code=500,
detail=f"Failed to add DexScreener connector: {str(e)}",
) from e
@router.delete("/connectors/dexscreener")
async def delete_dexscreener_connector(
space_id: int,
user: User = Depends(current_active_user),
session: AsyncSession = Depends(get_async_session),
):
"""Delete the DexScreener connector."""
try:
result = await session.execute(
select(SearchSourceConnector).filter(
SearchSourceConnector.search_space_id == space_id,
SearchSourceConnector.user_id == user.id,
SearchSourceConnector.connector_type
== SearchSourceConnectorType.DEXSCREENER_CONNECTOR,
)
)
connector = result.scalars().first()
if not connector:
raise HTTPException(
status_code=404,
detail="DexScreener connector not found.",
)
await session.delete(connector)
await session.commit()
return {"message": "DexScreener connector deleted successfully"}
except HTTPException:
raise
except Exception as e:
await session.rollback()
logger.error(f"Unexpected error: {str(e)}", exc_info=True)
raise HTTPException(
status_code=500,
detail=f"Failed to delete DexScreener connector: {str(e)}",
) from e
@router.get("/connectors/dexscreener/test")
async def test_dexscreener_connector(
space_id: int,
user: User = Depends(current_active_user),
session: AsyncSession = Depends(get_async_session),
):
"""Test the DexScreener connector."""
try:
# Get the connector
result = await session.execute(
select(SearchSourceConnector).filter(
SearchSourceConnector.search_space_id == space_id,
SearchSourceConnector.user_id == user.id,
SearchSourceConnector.connector_type
== SearchSourceConnectorType.DEXSCREENER_CONNECTOR,
)
)
connector = result.scalars().first()
if not connector:
raise HTTPException(
status_code=404,
detail="DexScreener connector not found.",
)
# Import and test
from app.connectors.dexscreener_connector import DexScreenerConnector
api_key = connector.config.get("api_key")
dex = DexScreenerConnector(api_key=api_key)
# Test with a sample search
pairs, error = dex.search_pairs("WETH")
if error:
raise HTTPException(
status_code=400,
detail=f"Failed to connect to DexScreener: {error}",
)
return {
"message": "DexScreener connector is working correctly",
"sample_pairs_count": len(pairs),
"tracked_tokens": connector.config.get("token_addresses", []),
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Unexpected error: {str(e)}", exc_info=True)
raise HTTPException(
status_code=500,
detail=f"Failed to test DexScreener connector: {str(e)}",
) from e
```
### Bước 4: Đăng Ký Routes
Sửa file `/Users/mac_1/Documents/GitHub/SurfSense/surfsense_backend/app/main.py`:
```python
# Import route
from app.routes import dexscreener_add_connector_route
# Add to app
app.include_router(
dexscreener_add_connector_route.router,
prefix="/api",
tags=["connectors"]
)
```
### Bước 5: Tạo Indexing Logic
Tạo file để xử lý indexing tự động:
```python
# app/connectors/dexscreener_indexer.py
from app.connectors.dexscreener_connector import DexScreenerConnector
from app.db import SearchSourceConnector
import logging
logger = logging.getLogger(__name__)
async def index_dexscreener_data(connector: SearchSourceConnector):
"""
Index data from DexScreener connector.
This function will be called periodically by the indexing service.
"""
try:
config = connector.config
api_key = config.get("api_key")
token_addresses = config.get("token_addresses", [])
if not token_addresses:
logger.warning(f"No token addresses configured for connector {connector.id}")
return []
# Initialize connector
dex = DexScreenerConnector(api_key=api_key)
# Fetch data for all tracked tokens
documents, error = dex.get_all_token_data(token_addresses)
if error:
logger.error(f"Error indexing DexScreener data: {error}")
return []
logger.info(f"Successfully indexed {len(documents)} documents from DexScreener")
return documents
except Exception as e:
logger.error(f"Error in DexScreener indexing: {str(e)}", exc_info=True)
return []
```
## Ví Dụ: DefiLlama Connector
Tương tự, bạn có thể tạo DefiLlama connector:
```python
# app/connectors/defillama_connector.py
"""
DefiLlama Connector Module
A module for fetching DeFi protocol data and TVL analytics from DefiLlama API.
"""
from typing import Any
import requests
from datetime import datetime
class DefiLlamaConnector:
"""Class for retrieving DeFi data from DefiLlama."""
def __init__(self):
"""Initialize the DefiLlamaConnector class."""
self.base_url = "https://api.llama.fi"
def get_protocol(
self, protocol_slug: str
) -> tuple[dict[str, Any] | None, str | None]:
"""
Get detailed information about a specific protocol.
Args:
protocol_slug: Protocol slug (e.g., "uniswap", "aave")
Returns:
Tuple containing (protocol data dict, error message or None)
"""
try:
url = f"{self.base_url}/protocol/{protocol_slug}"
response = requests.get(url, timeout=10)
if response.status_code == 200:
return response.json(), None
else:
return None, f"API request failed with status {response.status_code}"
except Exception as e:
return None, f"Error fetching protocol: {str(e)}"
def get_tvl_history(
self, protocol_slug: str
) -> tuple[list[dict[str, Any]], str | None]:
"""
Get TVL history for a protocol.
Args:
protocol_slug: Protocol slug
Returns:
Tuple containing (TVL history list, error message or None)
"""
protocol_data, error = self.get_protocol(protocol_slug)
if error:
return [], error
tvl_history = protocol_data.get("tvl", [])
return tvl_history, None
def get_all_protocols(self) -> tuple[list[dict[str, Any]], str | None]:
"""
Get list of all protocols with basic info.
Returns:
Tuple containing (protocols list, error message or None)
"""
try:
url = f"{self.base_url}/protocols"
response = requests.get(url, timeout=10)
if response.status_code == 200:
return response.json(), None
else:
return [], f"API request failed with status {response.status_code}"
except Exception as e:
return [], f"Error fetching protocols: {str(e)}"
def get_chain_tvl(
self, chain: str
) -> tuple[dict[str, Any] | None, str | None]:
"""
Get TVL for a specific chain.
Args:
chain: Chain name (e.g., "Ethereum", "BSC")
Returns:
Tuple containing (chain TVL data, error message or None)
"""
try:
url = f"{self.base_url}/tvl/{chain}"
response = requests.get(url, timeout=10)
if response.status_code == 200:
return response.json(), None
else:
return None, f"API request failed with status {response.status_code}"
except Exception as e:
return None, f"Error fetching chain TVL: {str(e)}"
def format_protocol_to_markdown(self, protocol: dict[str, Any]) -> str:
"""
Convert protocol data to markdown format.
Args:
protocol: Protocol data from DefiLlama API
Returns:
Markdown string representation
"""
name = protocol.get("name", "Unknown Protocol")
slug = protocol.get("slug", "")
symbol = protocol.get("symbol", "N/A")
# TVL data
tvl = protocol.get("tvl", 0)
chain_tvls = protocol.get("chainTvls", {})
# Categories and chains
category = protocol.get("category", "N/A")
chains = protocol.get("chains", [])
# Build markdown
markdown = f"# {name}\n\n"
if symbol != "N/A":
markdown += f"**Symbol:** {symbol}\n"
markdown += f"**Category:** {category}\n"
markdown += f"**Slug:** `{slug}`\n\n"
markdown += "## Total Value Locked (TVL)\n\n"
markdown += f"**Current TVL:** ${tvl:,.2f}\n\n"
if chains:
markdown += "### TVL by Chain\n\n"
for chain in chains:
chain_tvl = chain_tvls.get(chain, 0)
markdown += f"- **{chain}:** ${chain_tvl:,.2f}\n"
markdown += "\n"
# Description
description = protocol.get("description")
if description:
markdown += f"## Description\n\n{description}\n\n"
# Links
url = protocol.get("url")
twitter = protocol.get("twitter")
if url or twitter:
markdown += "## Links\n\n"
if url:
markdown += f"- **Website:** {url}\n"
if twitter:
markdown += f"- **Twitter:** https://twitter.com/{twitter}\n"
markdown += "\n"
markdown += f"*Data fetched at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n"
return markdown
```
## Cách Sử Dụng
### 1. Thêm Connector qua API
```bash
# Add DexScreener connector
curl -X POST "http://localhost:8000/api/connectors/dexscreener/add" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"space_id": 1,
"token_addresses": [
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"0xdAC17F958D2ee523a2206206994597C13D831ec7"
],
"api_key": "optional_api_key"
}'
```
### 2. Test Connector
```bash
curl -X GET "http://localhost:8000/api/connectors/dexscreener/test?space_id=1" \
-H "Authorization: Bearer YOUR_TOKEN"
```
### 3. Xóa Connector
```bash
curl -X DELETE "http://localhost:8000/api/connectors/dexscreener?space_id=1" \
-H "Authorization: Bearer YOUR_TOKEN"
```
## Tích Hợp vào Indexing Pipeline
Để connector tự động index data định kỳ, bạn cần:
1. **Thêm vào Connector Service** (`app/services/connector_service.py`)
2. **Đăng ký indexing task** trong background job scheduler
3. **Cấu hình re-indexing interval** (mặc định 60 phút)
Ví dụ:
```python
# app/services/connector_service.py
async def index_connector_data(connector: SearchSourceConnector):
"""Index data from any connector type."""
if connector.connector_type == SearchSourceConnectorType.DEXSCREENER_CONNECTOR:
from app.connectors.dexscreener_indexer import index_dexscreener_data
return await index_dexscreener_data(connector)
elif connector.connector_type == SearchSourceConnectorType.DEFILLAMA_CONNECTOR:
from app.connectors.defillama_indexer import index_defillama_data
return await index_defillama_data(connector)
# ... other connector types
```
## Best Practices
### 1. **Error Handling**
- Luôn return tuple `(data, error)` để dễ xử lý
- Log errors chi tiết để debug
- Graceful degradation khi API fail
### 2. **Rate Limiting**
- Respect API rate limits
- Implement exponential backoff
- Cache data khi có thể
### 3. **Data Formatting**
- Format data thành markdown rõ ràng
- Include metadata (timestamps, sources)
- Structured format để vector search hiệu quả
### 4. **Security**
- Encrypt API keys trong database
- Validate user input
- Implement proper authentication
### 5. **Performance**
- Async/await cho I/O operations
- Batch requests khi có thể
- Optimize indexing frequency
## Kết Luận
Với kiến trúc connector linh hoạt của SurfSense, bạn có thể:
✅ Kết nối đến **bất kỳ API nào** (DexScreener, DefiLlama, CoinGecko, etc.)
✅ Tự động **index và search** data từ nhiều nguồn
**Customize** logic fetch và format data
**Scale** dễ dàng với nhiều connectors
Connector system cho phép bạn biến SurfSense thành một **unified search platform** cho mọi loại data - từ emails, documents đến crypto analytics!

View file

@ -0,0 +1,68 @@
# Mô Hình Dữ Liệu (Backend)
Tài liệu này mô tả schema cơ sở dữ liệu Postgres, được quản lý bởi SQLAlchemy và Alembic.
## Các Thực Thể Chính (Core Entities)
### `User`
Đại diện cho người dùng hệ thống.
- **`id`**: `Integer` (Primary Key)
- **`email`**: `String` (Unique)
- **`hashed_password`**: `String`
- **`is_active`**: `Boolean`
- **`created_at`**: `DateTime`
### `Usage`
Theo dõi hạn ngạch sử dụng (quota) của người dùng.
- **`id`**: `Integer`
- **`user_id`**: `ForeignKey -> User`
- **`request_count`**: `Integer` (Số request API đã gọi)
- **`token_consumed`**: `Integer` (Số token LLM đã dùng)
### `Document`
Đơn vị kiến thức cơ bản. Một tài liệu có thể là một file PDF, một trang web, hoặc một ghi chú Notion.
- **`id`**: `Integer`
- **`title`**: `String`
- **`content`**: `Text` (Nội dung thô, nếu có)
- **`url`**: `String` (Nguồn gốc)
- **`source_type`**: `Enum` (PDF, WEB, NOTION, SLACK...)
- **`owner_id`**: `ForeignKey -> User`
- **`embedding_status`**: `Enum` (PENDING, INDEXED, FAILED)
### `Chunk`
Phần nhỏ của tài liệu dùng cho Vector Search.
- **`id`**: `Integer`
- **`document_id`**: `ForeignKey -> Document`
- **`content`**: `Text` (Nội dung của đoạn chunk)
- **`embedding`**: `Vector(1536)` (Vector đại diện, dùng cho pgvector)
- **`metadata`**: `JSONB` (Thông tin bổ sung)
### `ChatThread`
Đại diện cho một cuộc hội thoại.
- **`id`**: `Integer`
- **`user_id`**: `ForeignKey -> User`
- **`title`**: `String`
- **`created_at`**: `DateTime`
### `ChatMessage`
Một tin nhắn trong cuộc hội thoại.
- **`id`**: `Integer`
- **`thread_id`**: `ForeignKey -> ChatThread`
- **`role`**: `Enum` (USER, ASSISTANT, SYSTEM)
- **`content`**: `Text`
- **`tool_calls`**: `JSONB` (Lưu trữ các function calls nếu có)
### `ConnectorCredential`
Lưu trữ token xác thực cho các ứng dụng bên ngoài.
- **`id`**: `Integer`
- **`user_id`**: `ForeignKey -> User`
- **`connector_type`**: `String` (ví dụ: "google_drive")
- **`encrypted_token`**: `String` (Token đã mã hóa)
- **`refresh_token`**: `String`
## Mối Quan Hệ ERD (Tóm tắt)
- `User` **1 -- n** `Document`
- `Document` **1 -- n** `Chunk`
- `User` **1 -- n** `ChatThread`
- `ChatThread` **1 -- n** `ChatMessage`
- `User` **1 -- 1** `Usage`

View file

@ -0,0 +1,444 @@
# Hướng Dẫn Developer SurfSense
**Dành cho Nhà Phát Triển**
---
## 📖 Giới Thiệu
Tài liệu này hướng dẫn developers cách setup, develop, và extend hệ thống SurfSense.
---
## 🏗️ Kiến Trúc Tổng Quan
SurfSense bao gồm 3 components chính:
1. **Backend** (`surfsense_backend`) - Python/FastAPI
2. **Web** (`surfsense_web`) - Next.js 16
3. **Extension** (`surfsense_browser_extension`) - Plasmo/React
Xem chi tiết:
- [Kiến Trúc Backend](./architecture-backend.md)
- [Kiến Trúc Web](./architecture-web.md)
- [Kiến Trúc Extension](./architecture-extension.md)
- [Kiến Trúc Tích Hợp](./integration-architecture.md)
---
## 🚀 Setup Development Environment
### Prerequisites
- Python 3.11+
- Node.js 18+
- PostgreSQL 15+
- Redis
- Git
### Clone Repository
```bash
git clone https://github.com/your-org/surfsense.git
cd surfsense
```
### Backend Setup
```bash
cd surfsense_backend
# Create virtual environment
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Setup environment
cp .env.example .env
# Edit .env với database credentials, API keys
# Run migrations
alembic upgrade head
# Start server
uvicorn app.main:app --reload
```
**Backend chạy tại:** `http://localhost:8000`
### Web Setup
```bash
cd surfsense_web
# Install dependencies
npm install
# Setup environment
cp .env.example .env.local
# Edit NEXT_PUBLIC_API_URL=http://localhost:8000
# Start dev server
npm run dev
```
**Web chạy tại:** `http://localhost:3000`
### Extension Setup
```bash
cd surfsense_browser_extension
# Install dependencies
npm install
# Build extension
npm run dev
# Load extension trong Chrome:
# 1. Vào chrome://extensions/
# 2. Enable "Developer mode"
# 3. Click "Load unpacked"
# 4. Chọn folder build/chrome-mv3-dev
```
---
## 🗂️ Cấu Trúc Dự Án
### Backend Structure
```
surfsense_backend/
├── app/
│ ├── api/ # API routes
│ ├── core/ # Core logic (auth, config)
│ ├── db/ # Database models
│ ├── services/ # Business logic
│ └── main.py # FastAPI app
├── alembic/ # Database migrations
├── tests/ # Unit tests
└── requirements.txt
```
### Web Structure
```
surfsense_web/
├── app/ # Next.js App Router
│ ├── (auth)/ # Auth pages
│ ├── (dashboard)/ # Dashboard pages
│ └── api/ # API routes
├── components/ # React components
├── lib/ # Utilities
└── public/ # Static assets
```
### Extension Structure
```
surfsense_browser_extension/
├── src/
│ ├── background/ # Background service
│ ├── popup/ # Popup UI
│ ├── content/ # Content scripts
│ └── components/ # Shared components
└── manifest.json # Extension manifest
```
---
## 🔌 API Reference
### Authentication
**Login:**
```bash
POST /api/auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "password123"
}
# Response:
{
"access_token": "eyJ...",
"token_type": "bearer"
}
```
**Sử dụng token:**
```bash
GET /api/content
Authorization: Bearer eyJ...
```
### Content Management
**Capture content:**
```bash
POST /api/content
Authorization: Bearer <token>
Content-Type: application/json
{
"url": "https://example.com",
"title": "Example Page",
"body": "Content text...",
"tags": ["research", "ai"]
}
```
**Search:**
```bash
GET /api/search?q=machine+learning&limit=10
Authorization: Bearer <token>
```
### AI Chat
**Send message:**
```bash
POST /api/ai/chat
Authorization: Bearer <token>
Content-Type: application/json
{
"message": "Summarize my AI research",
"mode": "research" # "chat" | "research"
}
```
Xem chi tiết: [API Contracts](./api-contracts-backend.md)
---
## 🗄️ Database Schema
### Core Tables
**users:**
- `id` (UUID, PK)
- `email` (unique)
- `hashed_password`
- `role` (user | admin | superadmin)
- `plan` (free | pro | enterprise)
**content:**
- `id` (UUID, PK)
- `user_id` (FK → users)
- `url`, `title`, `body`
- `tags` (JSONB)
- `created_at`
**collections:**
- `id` (UUID, PK)
- `user_id` (FK → users)
- `name`, `description`
Xem chi tiết: [Data Models](./data-models-backend.md)
---
## 🧪 Testing
### Backend Tests
```bash
cd surfsense_backend
# Run all tests
pytest
# Run specific test
pytest tests/test_auth.py
# With coverage
pytest --cov=app tests/
```
### Web Tests
```bash
cd surfsense_web
# Run unit tests
npm test
# Run E2E tests
npm run test:e2e
```
### Extension Tests
```bash
cd surfsense_browser_extension
# Run tests
npm test
```
---
## 🔧 Common Development Tasks
### Tạo API Endpoint Mới
**1. Tạo route (`app/api/routes/example.py`):**
```python
from fastapi import APIRouter, Depends
from app.core.auth import get_current_user
router = APIRouter()
@router.get("/example")
async def get_example(user = Depends(get_current_user)):
return {"message": "Hello", "user_id": user.id}
```
**2. Register route (`app/api/__init__.py`):**
```python
from app.api.routes import example
api_router.include_router(example.router, prefix="/example", tags=["example"])
```
### Tạo Database Migration
```bash
cd surfsense_backend
# Auto-generate migration
alembic revision --autogenerate -m "Add new_column to users"
# Review migration file in alembic/versions/
# Apply migration
alembic upgrade head
```
### Thêm React Component Mới
**1. Tạo component (`components/MyComponent.tsx`):**
```tsx
export function MyComponent({ title }: { title: string }) {
return <div>{title}</div>
}
```
**2. Sử dụng:**
```tsx
import { MyComponent } from '@/components/MyComponent'
export default function Page() {
return <MyComponent title="Hello" />
}
```
---
## 🚢 Deployment
### Build Production
**Backend:**
```bash
cd surfsense_backend
pip install -r requirements.txt
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4
```
**Web:**
```bash
cd surfsense_web
npm run build
npm start
```
**Extension:**
```bash
cd surfsense_browser_extension
npm run build
# Upload build/chrome-mv3-prod to Chrome Web Store
```
### Docker Deployment
```bash
docker-compose up -d
```
Xem chi tiết deployment trong [Admin Guide](./admin-guide.md).
---
## 🐛 Debugging
### Backend Debugging
**Enable debug logs:**
```bash
# .env
LOG_LEVEL=DEBUG
```
**Use debugger:**
```python
import pdb; pdb.set_trace()
```
### Web Debugging
**Next.js debug mode:**
```bash
NODE_OPTIONS='--inspect' npm run dev
```
**React DevTools:** Install extension
### Extension Debugging
1. Vào `chrome://extensions/`
2. Click **"Inspect views: background page"**
3. Sử dụng Chrome DevTools
---
## 📚 Tài Nguyên Bổ Sung
### Documentation
- [FastAPI Docs](https://fastapi.tiangolo.com/)
- [Next.js Docs](https://nextjs.org/docs)
- [Plasmo Docs](https://docs.plasmo.com/)
### Code Style
- Backend: PEP 8, Black formatter
- Web/Extension: ESLint, Prettier
### Git Workflow
- Branch naming: `feature/`, `bugfix/`, `hotfix/`
- Commit messages: Conventional Commits
- PR template: Mô tả changes, testing done
---
**Cập nhật:** 2026-01-31 | **Version:** 1.0

33
_bmad-output/index.md Normal file
View file

@ -0,0 +1,33 @@
# Mục Lục Tài Liệu Tổng Hợp (Master Index)
Chào mừng đến với tài liệu kỹ thuật của SurfSense. Đây là một nền tảng tìm kiếm và kiến thức AI đa thành phần.
## 🧭 Bắt Đầu
- **[Tổng Quan Dự Án](./project-overview.md)** - Tóm tắt cấp cao về hệ thống.
- **[Phân Tích Cây Mã Nguồn](./source-tree-analysis.md)** - Bản đồ các thư mục và tệp tin.
- **[Kiến Trúc Tích Hợp](./integration-architecture.md)** - Cách các thành phần giao tiếp với nhau.
## 📚 Hướng Dẫn Sử Dụng
- **[Hướng Dẫn Người Dùng](./user-guide.md)** - Cài đặt, sử dụng tính năng, troubleshooting.
- **[Hướng Dẫn Quản Trị](./admin-guide.md)** - Quản lý users, cấu hình hệ thống, monitoring.
- **[Hướng Dẫn Developer](./developer-guide.md)** - Setup môi trường, API reference, deployment.
## 🏗️ Tài Liệu Thành Phần
### 🐍 Backend (`surfsense_backend`)
Bộ não của hệ thống. Python/FastAPI microservice.
- **[Kiến Trúc](./architecture-backend.md)** - DeepAgents, LangGraph, và RAG.
- **[Hợp Đồng API](./api-contracts-backend.md)** - Các REST Endpoints và Auth.
- **[Mô Hình Dữ Liệu](./data-models-backend.md)** - Database Schema & Thực thể.
### 💻 Web Dashboard (`surfsense_web`)
Giao diện người dùng. Next.js 16 Web App.
- **[Kiến Trúc](./architecture-web.md)** - App Router, Server Actions, ElectricSQL.
- **[Inventory Component](./component-inventory-web.md)** - Phân tích thư viện UI.
### 🧩 Browser Extension (`surfsense_browser_extension`)
Bộ thu thập dữ liệu. Plasmo/React Extension.
- **[Kiến Trúc](./architecture-extension.md)** - Popup, Background Services, Manifest V3.
## 📊 Báo Cáo
- **[Báo Cáo Quét Dự Án](./project-scan-report.json)** - Dữ liệu quét dạng máy đọc (machine-readable).

View file

@ -0,0 +1,73 @@
# Kiến Trúc Tích Hợp
Tài liệu này phác thảo cách ba thành phần của SurfSense (Backend, Web, Extension) giao tiếp và chia sẻ dữ liệu.
## Sơ Đồ Hệ Thống
```mermaid
graph TD
User[User]
subgraph Client Layer
Web[Web Dashboard]
Ext[Browser Extension]
end
subgraph Service Layer
API[Backend API]
Worker[Celery Workers]
Redis[(Redis Queue)]
end
subgraph Data Layer
DB[(Postgres DB)]
Vector[(Vector Store)]
Sync[(ElectricSQL/Sync)]
end
subgraph External
LLM[LLM Providers]
Apps[External Apps]
end
User --> Web
User --> Ext
Web -- REST/Streaming --> API
Ext -- REST --> API
API -- Read/Write --> DB
API -- Vector Search --> Vector
API -- Jobs --> Redis
Redis -- Polls --> Worker
Worker -- Ingest --> Apps
Worker -- Inference --> LLM
API -- Inference --> LLM
Web -- Sync --> Sync
Sync -- Data --> DB
```
## Các Điểm Tích Hợp (Integration Points)
### 1. Web tới Backend
- **Giao thức**: HTTP/REST + Server-Sent Events (SSE) cho Streaming
- **Xác thực**: OAuth / Bearer Token
- **Trao đổi chính**:
- **Chat**: Web gửi prompts -> Backend stream tokens + tool updates.
- **Config**: Web gửi cài đặt connector -> Backend xác thực & lưu trữ.
- **Search**: Web yêu cầu tìm kiếm -> Backend chạy RAG pipeline -> Trả về kết quả.
### 2. Extension tới Backend
- **Giao thức**: HTTP/REST
- **Mục đích**: Data ingestion (Lịch sử, Ngữ cảnh)
- **Luồng (Flow)**: Extension thu thập hoạt động duyệt web -> Đóng gói dữ liệu (Batching) -> Đẩy (Push) tới các endpoint `/ingest` của Backend.
### 3. Đồng bộ Dữ liệu (ElectricSQL)
- **Thành phần**: Web <-> Database
- **Mục đích**: Đồng bộ trạng thái thời gian thực (Real-time state synchronization) cho các tính năng cộng tác (như trạng thái chat chia sẻ) hoặc giữ cho giao diện frontend (optimistic UI) đồng bộ mà không cần refetch thủ công.
### 4. Backend tới AI Connectors
- **Giao thức**: Các API khác nhau (Slack API, Notion API, v.v.)
- **Luồng (Flow)**: Celery Workers thực thi các tác vụ nền (background jobs) để crawl/tải dữ liệu từ ứng dụng được kết nối của người dùng -> Xử lý/Phân mảnh (Process/Chunk) -> Lưu vào Vector DB.

View file

@ -0,0 +1,36 @@
# Tổng Quan Dự Án SurfSense
## Tóm Tắt Điều Hành
SurfSense là một nền tảng tìm kiếm và quản lý kiến thức toàn diện được hỗ trợ bởi AI. Hệ thống bao gồm một Browser Extension chuyên dụng để thu thập dữ liệu, một Python Backend hiệu năng cao để xử lý AI và RAG (Retrieval-Augmented Generation), và một Web Dashboard hiện đại xây dựng trên Next.js để tương tác người dùng. Hệ thống tận dụng framework DeepAgents và LangGraph cho các quy trình agentic (agentic workflows) tiên tiến.
## Cấu Trúc Dự Án
Dự án được tổ chức dưới dạng kho lưu trữ đa phần (multi-part repository) chứa ba thành phần riêng biệt:
| Thành phần | Thư mục | Loại | Công nghệ chính |
|------------|---------|------|-----------------|
| **Backend** | `surfsense_backend/` | Microservice | Python, FastAPI, LangGraph, DeepAgents, Postgres, Redis |
| **Web Frontend** | `surfsense_web/` | Web App | Next.js 16, React 19, Tailwind v4, Drizzle ORM |
| **Browser Extension** | `surfsense_browser_extension/` | Extension | Plasmo, React 18, Tailwind |
## Loại Kiến Trúc
**Layered Microservice Architecture** (Kiến trúc Microservice phân lớp) kết hợp với **Distributed Client System** (Hệ thống Client phân tán).
- **Data Layer**: Postgres (Vector + Relational)
- **Service Layer**: Python FastAPI với khả năng Agentic
- **Client Layer**: Lai (Web Dashboard + Browser Extension)
## Tính Năng Chính
- **Deep Search**: Pipeline RAG nâng cao để tìm kiếm trên các ứng dụng kết nối (Slack, Notion, v.v.).
- **Agentic AI**: Sử dụng LangGraph/DeepAgents cho các tác vụ suy luận phức tạp, nhiều bước.
- **Data Connectors**: Thư viện kết nối đồ sộ (30+) cho các nền tảng bên ngoài.
- **Sync Engine**: Browser extension thu thập lịch sử/ngữ cảnh để cá nhân hóa tìm kiếm.
- **Local/Cloud Hybrid**: Hỗ trợ Local LLMs và các nhà cung cấp cloud thông qua LiteLLM.
## Điều Hướng Tài Liệu
- [Master Index](./index.md) - **Bắt đầu tại đây**
- [Phân Tích Cây Mã Nguồn](./source-tree-analysis.md)
- [Kiến Trúc Tích Hợp](./integration-architecture.md)
### Tài Liệu Thành Phần Chi Tiết
- **Backend**: [Kiến Trúc](./architecture-backend.md) | [Hợp Đồng API](./api-contracts-backend.md) | [Mô Hình Dữ Liệu](./data-models-backend.md)
- **Web**: [Kiến Trúc](./architecture-web.md) | [Inventory Component](./component-inventory-web.md)
- **Extension**: [Kiến Trúc](./architecture-extension.md)

View file

@ -0,0 +1,83 @@
{
"workflow_version": "1.2.0",
"timestamps": {
"started": "2026-01-31T12:46:00+07:00",
"completed": "2026-01-31T12:55:00+07:00"
},
"mode": "initial_scan",
"scan_level": "exhaustive",
"project_root": "/Users/mac_1/Documents/GitHub/SurfSense",
"output_folder": "/Users/mac_1/Documents/GitHub/SurfSense/_bmad-output",
"repository_type": "multi-part",
"parts_count": 3,
"project_parts": [
{
"part_id": "backend",
"root_path": "surfsense_backend",
"project_type_id": "backend",
"display_name": "SurfSense Backend",
"tech_stack": {
"framework": "FastAPI",
"language": "Python 3.12",
"database": "Postgres (asyncpg), Redis",
"key_libs": "LangGraph, DeepAgents, Alembic"
},
"architecture_pattern": "Service-centric / Microservice"
},
{
"part_id": "web",
"root_path": "surfsense_web",
"project_type_id": "web_app",
"display_name": "SurfSense Web",
"tech_stack": {
"framework": "Next.js 16",
"language": "TypeScript",
"ui": "React 19, Tailwind v4",
"state": "ElectricSQL"
},
"architecture_pattern": "App Router / Server Actions"
},
{
"part_id": "extension",
"root_path": "surfsense_browser_extension",
"project_type_id": "browser_extension",
"display_name": "SurfSense Extension",
"tech_stack": {
"framework": "Plasmo",
"language": "TypeScript",
"ui": "React 18"
},
"architecture_pattern": "Manifest V3 / Popup + Background"
}
],
"completed_steps": [
"step_1_init",
"step_2_classify",
"step_3_tech_stack",
"step_4_analysis",
"step_5_backend_docs",
"step_6_web_docs",
"step_7_extension_docs",
"step_10_master_index"
],
"current_step": "complete",
"findings": {
"project_classification": "Multi-part project with Backend (Python/FastAPI), Extension (Plasmo/React), and Web (Next.js/React).",
"technology_stack": "Comprehensive stack detected. High complexity in AI/Agentic workflows.",
"documentation_status": "Generated comprehensive documentation suite."
},
"outputs_generated": [
"project-scan-report.json",
"project-overview.md",
"source-tree-analysis.md",
"integration-architecture.md",
"architecture-backend.md",
"data-models-backend.md",
"api-contracts-backend.md",
"architecture-web.md",
"component-inventory-web.md",
"architecture-extension.md",
"index.md"
],
"resume_instructions": "Documentation complete. User can review files in _bmad-output/."
}

View file

@ -0,0 +1,74 @@
# Phân Tích Cây Mã Nguồn
Tài liệu này ánh xạ các thư mục và tệp tin quan trọng trong dự án đa phần (multi-part) SurfSense.
## Sơ Đồ Thư Mục
```
/Users/mac_1/Documents/GitHub/SurfSense/
├── docs/ # Tài liệu cấp dự án
│ └── chinese-llm-setup.md # Hướng dẫn cài đặt chuyên biệt
├── docker-compose.yml # File điều phối chính
├── .env.example # Mẫu biến môi trường toàn cục (Global environment)
├── surfsense_backend/ # [PART: Backend]
│ ├── app/
│ │ ├── api/ # Các tiện ích API
│ │ ├── config/ # Cấu hình & Cài đặt
│ │ ├── connectors/ # Connectors ứng dụng ngoài (Slack, Jira...)
│ │ ├── prompts/ # System Prompts cho Agents
│ │ ├── retriever/ # Logic RAG & Search
│ │ ├── routes/ # API Route Controllers (Endpoints)
│ │ ├── schemas/ # Pydantic Data Models
│ │ ├── services/ # Business Logic Services
│ │ ├── tasks/ # Celery Background Tasks
│ │ ├── app.py # Điểm nhập (Entry Point) của FastAPI
│ │ ├── celery_app.py # Điểm nhập của Worker
│ │ └── db.py # Database ORM (SQLAlchemy) & Models
│ ├── alembic/ # Database Migrations
│ ├── pyproject.toml # Dependencies (kiểu uv/poetry)
│ └── Dockerfile # Cấu hình container Backend
├── surfsense_web/ # [PART: Web]
│ ├── app/ # Next.js App Router (Pages & API)
│ │ ├── (home)/ # Marketing/Landing Pages
│ │ ├── dashboard/ # Ứng dụng người dùng (đã xác thực)
│ │ ├── api/ # Next.js API Routes (BFF/Proxy)
│ │ └── docs/ # Documentation Routes
│ ├── components/ # React Components
│ │ ├── ui/ # Base UI Kit (giống Shadcn)
│ │ ├── layout/ # Components cấu trúc (Structural)
│ │ └── assistant-ui/ # Components giao diện Chat/AI
│ ├── lib/ # Utilities & Logic chia sẻ
│ │ ├── apis/ # Client-side API Wrappers
│ │ └── electric/ # Cấu hình ElectricSQL Sync
│ ├── content/ # Nội dung MDX (Docs)
│ ├── contracts/ # Shared Types/Contracts
│ ├── public/ # Static Assets
│ ├── package.json # Dependencies (pnpm)
│ └── next.config.ts # Cấu hình Next.js
└── surfsense_browser_extension/ # [PART: Extension]
├── background/ # Service Workers
│ └── index.ts # Điểm nhập Background
├── routes/ # Extension UI Routes
├── assets/ # Icons & Static Files
├── popup.tsx # Điểm nhập Popup (React)
├── manifest.json # Extension Manifest (tạo bởi Plasmo)
└── package.json # Dependencies
```
## Phân Tích Các Thư Mục Quan Trọng
### Backend (`surfsense_backend`)
- **`app/routes/`**: Chứa tất cả REST API endpoints. Mỗi file thường tương ứng với một miền tính năng (ví dụ: `discord_add_connector_route.py`).
- **`app/connectors/`**: Logic tích hợp cốt lõi cho hơn 30 dịch vụ bên ngoài. Đây là nơi quá trình data ingestion diễn ra.
- **`app/db.py`**: Hệ thống thần kinh trung ương để lưu trữ dữ liệu. Định nghĩa tất cả các SQLAlchemy models và kết nối database.
### Web (`surfsense_web`)
- **`app/dashboard/`**: Giao diện ứng dụng chính mà người dùng tương tác. Được bảo vệ bởi xác thực (auth).
- **`components/assistant-ui/`**: Các components chuyên biệt cho giao diện AI chat, xử lý streaming, tool calls, và lịch sử tin nhắn.
- **`lib/apis/`**: Lớp client được định kiểu (typed client layer) giao tiếp với Backend.
### Extension (`surfsense_browser_extension`)
- **`background/`**: Xử lý logic bền vững (persistent logic) như thu thập lịch sử và giám sát tab ngay cả khi popup đã đóng.

435
_bmad-output/user-guide.md Normal file
View file

@ -0,0 +1,435 @@
# Hướng Dẫn Sử Dụng SurfSense
**Dành cho Người Dùng Cuối**
---
## 📖 Giới Thiệu
SurfSense là nền tảng tìm kiếm và quản lý kiến thức được hỗ trợ bởi AI, giúp bạn:
- 🔍 Tìm kiếm thông tin nhanh chóng từ lịch sử duyệt web
- 💾 Lưu trữ và tổ chức kiến thức cá nhân
- 🤖 Trò chuyện với AI để nghiên cứu sâu
- 🌐 Capture nội dung từ bất kỳ trang web nào
---
## 🚀 Bắt Đầu Nhanh
### Bước 1: Cài Đặt Browser Extension
1. **Tải Extension:**
- Truy cập Chrome Web Store (hoặc store tương ứng với trình duyệt của bạn)
- Tìm kiếm "SurfSense"
- Click **Add to Chrome/Browser**
2. **Kích Hoạt Extension:**
- Click vào icon SurfSense trên thanh công cụ trình duyệt
- Đăng nhập bằng tài khoản của bạn
- Cấp quyền truy cập cần thiết
3. **Xác Nhận Cài Đặt:**
- Icon SurfSense sẽ xuất hiện trên thanh công cụ
- Bạn sẽ thấy thông báo "Connected" khi extension hoạt động
### Bước 2: Truy Cập Web Dashboard
1. Mở trình duyệt và truy cập: `https://app.surfsense.ai` (hoặc URL do admin cung cấp)
2. Đăng nhập bằng tài khoản đã đăng ký
3. Bạn sẽ thấy Dashboard chính với các tính năng:
- **Search Bar** - Tìm kiếm nhanh
- **Recent Activity** - Hoạt động gần đây
- **Collections** - Bộ sưu tập kiến thức
- **AI Chat** - Trò chuyện với AI
---
## 🔍 Tính Năng Chính
### 1. Capture Nội Dung Từ Web
**Cách sử dụng Extension để lưu nội dung:**
1. **Capture Toàn Bộ Trang:**
- Truy cập trang web bạn muốn lưu
- Click icon SurfSense trên thanh công cụ
- Chọn **"Capture This Page"**
- Nội dung sẽ được lưu tự động
2. **Capture Đoạn Văn Bản Cụ Thể:**
- Bôi đen văn bản bạn muốn lưu
- Click chuột phải → **"Save to SurfSense"**
- Hoặc sử dụng shortcut: `Ctrl/Cmd + Shift + S`
3. **Thêm Tags và Notes:**
- Sau khi capture, popup sẽ hiện ra
- Thêm tags (ví dụ: `research`, `work`, `personal`)
- Thêm ghi chú cá nhân nếu cần
- Click **"Save"**
**💡 Mẹo:** Extension tự động trích xuất metadata (title, URL, timestamp) để bạn dễ tìm kiếm sau này.
---
### 2. Tìm Kiếm Kiến Thức
**Trên Web Dashboard:**
1. **Tìm Kiếm Cơ Bản:**
- Nhập từ khóa vào Search Bar
- Kết quả hiển thị theo độ liên quan (relevance)
- Click vào kết quả để xem chi tiết
2. **Tìm Kiếm Nâng Cao (Advanced Search):**
- Click icon **Filter** bên cạnh Search Bar
- Lọc theo:
- **Date Range** - Khoảng thời gian
- **Tags** - Nhãn đã gán
- **Source** - Nguồn (website, PDF, note)
- **Content Type** - Loại nội dung (article, video, image)
3. **Semantic Search (Tìm Kiếm Ngữ Nghĩa):**
- SurfSense sử dụng AI để hiểu ý nghĩa câu hỏi
- Ví dụ: Thay vì tìm "Python tutorial", bạn có thể hỏi:
- *"How do I create a REST API in Python?"*
- *"Best practices for Python async programming"*
**💡 Mẹo:** Sử dụng dấu ngoặc kép `"exact phrase"` để tìm cụm từ chính xác.
---
### 3. Quản Lý Collections (Bộ Sưu Tập)
**Tạo Collection mới:**
1. Vào **Dashboard** → Click **"Collections"** trên sidebar
2. Click **"+ New Collection"**
3. Đặt tên (ví dụ: "AI Research", "Work Projects")
4. Chọn icon và màu sắc (tùy chọn)
5. Click **"Create"**
**Thêm items vào Collection:**
- **Cách 1:** Kéo thả (drag & drop) từ search results
- **Cách 2:** Click vào item → **"Add to Collection"** → Chọn collection
- **Cách 3:** Khi capture nội dung mới, chọn collection trong popup
**Chia sẻ Collection:**
1. Mở Collection bạn muốn chia sẻ
2. Click **"Share"** ở góc trên bên phải
3. Chọn quyền truy cập:
- **View Only** - Chỉ xem
- **Can Edit** - Có thể chỉnh sửa
4. Copy link và gửi cho người khác
---
### 4. AI Chat
**Trò chuyện với AI về kiến thức của bạn:**
1. **Mở AI Chat:**
- Click icon **Chat** trên sidebar
- Hoặc sử dụng shortcut: `Ctrl/Cmd + K`
2. **Hỏi Câu Hỏi:**
- Nhập câu hỏi vào chat box
- Ví dụ:
- *"Summarize all articles I saved about machine learning"*
- *"What are the key points from my research on climate change?"*
- *"Find contradictions in my saved articles about diet"*
3. **AI Tự Động Phân Tích:**
- AI sẽ tìm kiếm trong toàn bộ kiến thức của bạn
- Tổng hợp thông tin từ nhiều nguồn liên quan
- Cung cấp câu trả lời dựa trên nội dung bạn đã lưu
- Thời gian xử lý: 5-15 giây (tùy độ phức tạp)
4. **Xem Sources (Nguồn Tham Khảo):**
- Mỗi câu trả lời của AI có **[1], [2], [3]** citations
- Click vào số để xem nguồn gốc
- Bạn có thể mở link gốc để đọc chi tiết
5. **Attach Files và Mention Documents:**
- Click icon **📎** để đính kèm files
- Sử dụng **@** để mention documents cụ thể trong chat
- AI sẽ ưu tiên phân tích các documents được mention
**💡 Mẹo:** AI Chat hoạt động tốt nhất khi bạn đã lưu nhiều nội dung liên quan đến chủ đề bạn hỏi.
---
### 5. Connectors (Kết Nối Ứng Dụng Bên Ngoài)
**Mở rộng khả năng tìm kiếm với các ứng dụng bạn đang dùng:**
SurfSense cho phép bạn kết nối với **26+ ứng dụng bên ngoài** như Gmail, Google Drive, Slack, Notion, Linear, và nhiều hơn nữa. Sau khi kết nối, bạn có thể tìm kiếm thông tin từ **tất cả các ứng dụng này** ngay trong SurfSense!
**Cách kết nối:**
1. **Mở Connectors Modal:**
- Vào **Dashboard** → Click **"Connectors"** trên sidebar
- Hoặc trong Chat → Click icon **"⚡ Connectors"**
2. **Chọn Ứng Dụng:**
- Bạn sẽ thấy 2 nhóm:
- **Managed OAuth (Composio):** Google Drive, Gmail, Google Calendar
- **Quick Connect:** Slack, Notion, Linear, Airtable, và nhiều hơn
- Click **"Connect"** bên cạnh ứng dụng bạn muốn
3. **Xác Thực (OAuth):**
- Bạn sẽ được chuyển đến trang đăng nhập của ứng dụng (ví dụ: Google)
- Đăng nhập và cấp quyền **chỉ đọc** (read-only) cho SurfSense
- SurfSense **KHÔNG** có quyền xóa hoặc chỉnh sửa dữ liệu của bạn
4. **Indexing Tự Động:**
- Sau khi kết nối, SurfSense sẽ tự động:
- Lấy dữ liệu từ ứng dụng (emails, files, messages, etc.)
- Tạo index để bạn có thể tìm kiếm
- Tự động cập nhật mỗi 60 phút (hoặc theo cấu hình)
**Ví dụ sử dụng:**
- **Kết nối Gmail:** Tìm kiếm trong emails cũ mà không cần mở Gmail
- **Kết nối Google Drive:** Tìm files và documents ngay trong SurfSense
- **Kết nối Slack:** Tìm conversations và shared files từ workspace
- **Kết nối Notion:** Tìm kiếm trong pages và databases
**Quản lý Connectors:**
- **Xem trạng thái:** Vào **Settings****"Connectors"** để xem danh sách
- **Ngắt kết nối:** Click **"Disconnect"** để thu hồi quyền truy cập
- **Re-index:** Click **"Re-index Now"** để cập nhật dữ liệu ngay lập tức
**🔐 Bảo mật:**
- Connectors chỉ có quyền **đọc** (read-only)
- Dữ liệu được mã hóa khi lưu trữ
- Bạn có thể ngắt kết nối bất cứ lúc nào
**💡 Mẹo:** Kết nối các ứng dụng bạn dùng thường xuyên để có trải nghiệm tìm kiếm "all-in-one" - tìm mọi thứ ở một nơi!
> **📚 Tìm hiểu thêm:** Xem [connectors-explained.md](./connectors-explained.md) để hiểu chi tiết cách Connectors hoạt động.
---
### 6. Quick Search (Tìm Kiếm Nhanh)
**Sử dụng Extension Popup:**
1. Click icon SurfSense trên thanh công cụ
2. Nhập từ khóa vào search box trong popup
3. Kết quả hiển thị ngay lập tức (không cần mở web app)
4. Click vào kết quả để:
- **View in SurfSense** - Mở trong web app
- **Open Original** - Mở trang web gốc
- **Copy Link** - Copy link
**Keyboard Shortcuts:**
- `Ctrl/Cmd + Shift + F` - Mở Quick Search
- `Enter` - Mở kết quả đầu tiên
- `↑ ↓` - Di chuyển giữa các kết quả
- `Esc` - Đóng popup
---
## ⚙️ Cài Đặt Cá Nhân
### Tùy Chỉnh Preferences
1. Vào **Dashboard** → Click avatar → **"Settings"**
2. Các tùy chọn:
**General:**
- **Language** - Ngôn ngữ giao diện (English, Tiếng Việt)
- **Theme** - Giao diện (Light, Dark, Auto)
- **Default Collection** - Collection mặc định khi capture
**Privacy:**
- **Auto-Capture** - Tự động capture trang web (On/Off)
- **Exclude Domains** - Danh sách website không capture (ví dụ: banking sites)
- **Data Retention** - Thời gian lưu trữ dữ liệu (30 days, 90 days, Forever)
**AI Settings:**
- **AI Model** - Chọn model (GPT-4, Claude, Gemini)
- **Response Style** - Phong cách trả lời (Concise, Detailed, Academic)
- **Citations** - Luôn hiển thị nguồn (On/Off)
**Notifications:**
- **Email Digest** - Nhận tóm tắt hàng tuần (On/Off)
- **Browser Notifications** - Thông báo khi có kết quả mới (On/Off)
---
## 🔐 Bảo Mật và Quyền Riêng Tư
### Dữ Liệu Của Bạn
- **Mã Hóa:** Tất cả dữ liệu được mã hóa (encryption) khi lưu trữ và truyền tải
- **Quyền Sở Hữu:** Bạn sở hữu 100% dữ liệu của mình
- **Xóa Dữ Liệu:** Bạn có thể xóa bất kỳ item hoặc toàn bộ tài khoản bất cứ lúc nào
### Quản Lý Quyền Truy Cập
1. **Xem Devices Đã Đăng Nhập:**
- Vào **Settings****"Security"**
- Xem danh sách devices đang active
- Click **"Sign Out"** để đăng xuất device cụ thể
2. **Two-Factor Authentication (2FA):**
- Vào **Settings****"Security"** → **"Enable 2FA"**
- Quét QR code bằng app authenticator (Google Authenticator, Authy)
- Nhập mã xác nhận
---
## 🛠️ Troubleshooting (Xử Lý Sự Cố)
### Extension Không Hoạt Động
**Triệu chứng:** Icon SurfSense không hiển thị hoặc không capture được nội dung.
**Giải pháp:**
1. **Kiểm tra Extension đã được enable:**
- Vào `chrome://extensions/` (hoặc tương đương)
- Tìm "SurfSense"
- Đảm bảo toggle **ON**
2. **Refresh Extension:**
- Click icon SurfSense → **"Refresh"**
- Hoặc reload trang web (`F5`)
3. **Kiểm tra quyền truy cập:**
- Vào `chrome://extensions/` → SurfSense → **"Details"**
- Đảm bảo **"Site access"** = "On all sites"
4. **Reinstall Extension:**
- Gỡ extension cũ
- Cài đặt lại từ store
### Tìm Kiếm Không Ra Kết Quả
**Nguyên nhân có thể:**
- Từ khóa quá cụ thể hoặc sai chính tả
- Nội dung chưa được index (đợi vài phút sau khi capture)
- Filters quá chặt
**Giải pháp:**
1. Thử từ khóa đơn giản hơn
2. Tắt tất cả filters
3. Kiểm tra **"All Content"** thay vì chỉ một collection
4. Sử dụng Semantic Search (hỏi bằng câu tự nhiên)
### AI Chat Không Phản Hồi
**Triệu chứng:** Chat loading mãi không có kết quả.
**Giải pháp:**
1. **Kiểm tra kết nối internet**
2. **Refresh trang web** (`F5`)
3. **Thử câu hỏi đơn giản hơn** (tránh câu quá dài hoặc phức tạp)
4. **Kiểm tra AI Model settings:**
- Vào **Settings****"AI Settings"**
- Thử đổi sang model khác (ví dụ: từ GPT-4 sang Claude)
### Sync Issues (Vấn Đề Đồng Bộ)
**Triệu chứng:** Nội dung capture trên extension không hiển thị trên web app.
**Giải pháp:**
1. **Đợi vài giây** (sync thường mất 2-5 giây)
2. **Refresh web app** (`F5`)
3. **Kiểm tra trạng thái sync:**
- Click icon SurfSense → Xem **"Sync Status"**
- Nếu **"Offline"**, kiểm tra internet
- Nếu **"Error"**, click **"Retry Sync"**
---
## 📞 Hỗ Trợ
### Liên Hệ Support Team
- **Email:** support@surfsense.ai
- **Live Chat:** Click icon **"Help"** ở góc dưới bên phải Dashboard
- **Documentation:** [https://docs.surfsense.ai](https://docs.surfsense.ai)
- **Community Forum:** [https://community.surfsense.ai](https://community.surfsense.ai)
### Báo Lỗi (Bug Report)
1. Vào **Settings****"Help & Feedback"** → **"Report a Bug"**
2. Mô tả vấn đề:
- Bạn đang làm gì khi lỗi xảy ra?
- Kết quả mong đợi vs kết quả thực tế?
- Screenshot (nếu có)
3. Click **"Submit"**
4. Team sẽ phản hồi trong vòng 24 giờ
---
## 💡 Tips & Best Practices
### Tối Ưu Hóa Trải Nghiệm
1. **Sử dụng Tags Nhất Quán:**
- Tạo danh sách tags cố định (ví dụ: `work`, `personal`, `research`)
- Tránh tạo quá nhiều tags tương tự (`ai`, `AI`, `artificial-intelligence`)
2. **Organize Collections Theo Dự Án:**
- Mỗi dự án/chủ đề = 1 collection
- Sử dụng sub-collections cho các chủ đề con
3. **Review Định Kỳ:**
- Mỗi tuần, xem lại **Recent Activity**
- Xóa nội dung không còn cần thiết
- Re-organize items vào collections phù hợp
4. **Tận Dụng AI Chat:**
- Thay vì tìm kiếm thủ công, hỏi AI:
- *"What did I learn about X last month?"*
- *"Summarize my research on Y"*
5. **Backup Quan Trọng:**
- Vào **Settings****"Data Export"**
- Click **"Export All Data"** (format: JSON hoặc Markdown)
- Lưu file backup vào cloud storage (Google Drive, Dropbox)
---
## 🎓 Học Thêm
### Video Tutorials
- [Getting Started with SurfSense (5 phút)](https://youtube.com/surfsense/getting-started)
- [Advanced Search Techniques (10 phút)](https://youtube.com/surfsense/advanced-search)
- [Using AI Chat Effectively (15 phút)](https://youtube.com/surfsense/ai-chat)
### Use Cases
**1. Sinh Viên/Nghiên Cứu Viên:**
- Lưu papers, articles từ Google Scholar
- Tổ chức theo môn học/chủ đề
- Sử dụng AI để tóm tắt và so sánh nghiên cứu
**2. Developers:**
- Save Stack Overflow answers, documentation
- Organize theo ngôn ngữ/framework
- Quick search khi cần reference
**3. Content Creators:**
- Lưu inspiration từ web
- Organize theo campaign/project
- AI giúp brainstorm ideas từ saved content
---
**Cập nhật lần cuối:** 2026-01-31
**Phiên bản tài liệu:** 1.0
**Áp dụng cho:** SurfSense v2.0+