449 lines
12 KiB
Markdown
449 lines
12 KiB
Markdown
# LinkSyncExtension - Design Documentation
|
|
|
|
## Architecture Overview
|
|
|
|
LinkSyncExtension is a Firefox browser extension that synchronizes bookmarks with LinkSyncServer. It runs as a background service worker with popup and settings interfaces.
|
|
|
|
### Component Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────┐
|
|
│ LinkSyncExtension │
|
|
│ │
|
|
│ ┌─────────────────┐ ┌─────────────────────────┐ │
|
|
│ │ Background │ │ Popup UI │ │
|
|
│ │ Service │ │ (Add/Edit Bookmarks) │ │
|
|
│ │ Worker │ │ │ │
|
|
│ └─────────────────┘ └─────────────────────────┘ │
|
|
│ ┌─────────────────┐ ┌─────────────────────────┐ │
|
|
│ │ Storage │ │ Settings UI │ │
|
|
│ │ Manager │ │ (Configuration) │ │
|
|
│ └─────────────────┘ └─────────────────────────┘ │
|
|
│ ┌─────────────────┐ ┌─────────────────────────┐ │
|
|
│ │ Sync Engine │ │ Query Engine │ │
|
|
│ │ (3 modes) │ │ (Parser + Executor) │ │
|
|
│ └─────────────────┘ └─────────────────────────┘ │
|
|
│ ┌─────────────────┐ ┌─────────────────────────┐ │
|
|
│ │ Bookmark │ │ API Client │ │
|
|
│ │ Manipulator │ │ (REST calls) │ │
|
|
│ └─────────────────┘ └─────────────────────────┘ │
|
|
│ │
|
|
│ ┌───────────────────────────────────────────┐ │
|
|
│ │ Browser Bookmarks API │ │
|
|
│ └───────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
## File Structure
|
|
|
|
```
|
|
LinkSyncExtension/
|
|
├── manifest.json # Extension manifest v2
|
|
├── popup.html # Bookmark add/edit UI
|
|
├── popup.css # Popup styling
|
|
├── popup.js # Popup logic
|
|
├── background.html # Settings page
|
|
├── background.js # Service worker
|
|
├── content/
|
|
│ └── content.js # Content script (optional)
|
|
├── utils/
|
|
│ ├── bookmark.js # Bookmark manipulation
|
|
│ ├── collection.js # Collection management
|
|
│ ├── query-engine.js # Query parsing/execution
|
|
│ └── sync.js # Sync logic
|
|
├── icons/
|
|
│ ├── icon-48.png # 48x48 icon
|
|
│ └── icon-96.png # 96x96 icon
|
|
└── styles/
|
|
├── base.css # Common styles
|
|
└── theme.css # Theme variables
|
|
```
|
|
|
|
## Manifest Design
|
|
|
|
### manifest.json
|
|
|
|
```json
|
|
{
|
|
"manifest_version": 2,
|
|
"name": "LinkSync",
|
|
"version": "1.0.0",
|
|
"description": "Sync bookmarks with LinkSyncServer",
|
|
"permissions": [
|
|
"bookmarks",
|
|
"storage",
|
|
"activeTab",
|
|
"http://*/*",
|
|
"https://*/*"
|
|
],
|
|
"browser_action": {
|
|
"default_icon": {
|
|
"48": "icons/icon-48.png",
|
|
"96": "icons/icon-96.png"
|
|
},
|
|
"default_title": "LinkSync"
|
|
},
|
|
"background": {
|
|
"page": "background.html"
|
|
},
|
|
"browser_specific_settings": {
|
|
"gecko": {
|
|
"id": "{linksync-id}",
|
|
"strict_min_version": "109.0"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Permissions
|
|
|
|
- `bookmarks` - Read/write Firefox bookmarks
|
|
- `storage` - Store settings, API keys, state
|
|
- `activeTab` - Get current page data
|
|
- HTTP/HTTPS - API communication
|
|
|
|
## Background Worker Design
|
|
|
|
### Responsibilities
|
|
|
|
1. **Sync Loop**
|
|
- Check for pending syncs
|
|
- Compare browser vs server bookmarks
|
|
- Apply sync mode rules
|
|
- Handle conflicts
|
|
|
|
2. **Event Handlers**
|
|
- `onMessage` - UI requests
|
|
- `onInstall` - Initialization
|
|
- `onUpdate` - Handle version changes
|
|
|
|
3. **State Management**
|
|
- Store collection mapping
|
|
- Track sync timestamps
|
|
- Monitor pending changes
|
|
|
|
### Code Structure
|
|
|
|
```javascript
|
|
// background.js
|
|
const Background = {
|
|
// Constants
|
|
SYNC_CHECK_INTERVAL: 60000, // 1 minute
|
|
|
|
// Storage keys
|
|
STORAGE: {
|
|
API_KEY: 'linksync_api_key',
|
|
COLLECTION: 'linksync_collection',
|
|
MODE: 'linksync_sync_mode',
|
|
DELETIONS: 'linksync_deletions',
|
|
AUTO_SYNC: 'linksync_auto_sync'
|
|
},
|
|
|
|
// Methods
|
|
init(), // Initialize on install/update
|
|
checkSync(), // Run sync loop
|
|
handleSyncAction(), // Process sync actions
|
|
handleEvent(), // Event handlers
|
|
sendMessage(), // UI communication
|
|
authenticate() // Handle auth
|
|
};
|
|
```
|
|
|
|
### Sync Logic
|
|
|
|
```javascript
|
|
async function handleSync() {
|
|
const config = await loadConfig();
|
|
|
|
// Get browser bookmarks
|
|
const browserBookmarks = await getBrowserBookmarks();
|
|
|
|
// Get server bookmarks via API
|
|
const serverBookmarks = await fetchServerBookmarks();
|
|
|
|
// Apply sync mode
|
|
const actions = applySyncMode(config.mode, browserBookmarks, serverBookmarks);
|
|
|
|
// Process deletions if enabled
|
|
if (config.deletions) {
|
|
actions = applyDeletions(actions);
|
|
}
|
|
|
|
// Apply actions
|
|
await applyActions(actions);
|
|
|
|
// Update sync timestamp
|
|
await saveSyncTimestamp();
|
|
}
|
|
```
|
|
|
|
### Sync Modes
|
|
|
|
| Mode | Browser→Server | Server→Browser |
|
|
|------|---------------|---------------|
|
|
| **Bi-directional** | Push | Push |
|
|
| **Browser Authoritative** | Push | Overwrite |
|
|
| **Server Authoritative** | Download | Overwrite |
|
|
|
|
## Popup Design
|
|
|
|
### Components
|
|
|
|
1. **Add/Edit Form**
|
|
- URL (auto-filled)
|
|
- Title (auto-filled)
|
|
- Description (auto-filled)
|
|
- Notes
|
|
- Tags input
|
|
- Folder path
|
|
- Actions (Add, Edit, Delete)
|
|
|
|
2. **Bookmark List**
|
|
- Paginated list of synced bookmarks
|
|
- Search filter
|
|
- Select for batch operations
|
|
|
|
3. **Collections Panel**
|
|
- View all collections
|
|
- Execute query
|
|
- Create dynamic collection
|
|
|
|
4. **Settings Modal**
|
|
- Server URL
|
|
- API Key
|
|
- Collection name
|
|
- Sync mode
|
|
- Auto-sync toggle
|
|
|
|
### HTML Structure
|
|
|
|
```html
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<link rel="stylesheet" href="popup.css">
|
|
</head>
|
|
<body>
|
|
<!-- Header -->
|
|
<header>
|
|
<h1>LinkSync</h1>
|
|
</header>
|
|
|
|
<!-- Add/Edit Form -->
|
|
<section id="bookmark-form">
|
|
<form id="bookmark-form">
|
|
<input id="url" type="url">
|
|
<input id="title" type="text">
|
|
<textarea id="description"></textarea>
|
|
<textarea id="notes"></textarea>
|
|
<input id="tags" placeholder="comma-separated">
|
|
<input id="folder" placeholder="path">
|
|
<button id="submit">Add Bookmark</button>
|
|
</form>
|
|
</section>
|
|
|
|
<!-- Bookmark List -->
|
|
<section id="bookmark-list">
|
|
<!-- Bookmarks -->
|
|
</section>
|
|
|
|
<!-- Footer with actions -->
|
|
<footer>
|
|
<button id="sync-btn">Sync Now</button>
|
|
<button id="settings-btn">Settings</button>
|
|
</footer>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
## Storage Design
|
|
|
|
### localStorage Keys
|
|
|
|
| Key | Type | Description |
|
|
|-----|------|-------------|
|
|
| `linksync_api_key` | string | JWT API token |
|
|
| `linksync_collection` | string | Collection name |
|
|
| `linksync_sync_mode` | string | Sync mode |
|
|
| `linksync_deletions` | boolean | Enable deletions |
|
|
| `linksync_auto_sync` | boolean | Auto-sync toggle |
|
|
| `linksync_last_sync` | timestamp | Last sync time |
|
|
| `linksync_pending` | number | Pending changes count |
|
|
|
|
### Encrypted Storage
|
|
|
|
API keys should be encrypted before storage:
|
|
|
|
```javascript
|
|
async function saveEncryptedKey(key) {
|
|
const iv = crypto.getRandomValues(new Uint8Array(16));
|
|
const encrypted = await window.crypto.subtle.encrypt(
|
|
{ name: "AES-GCM", length: 256 },
|
|
await window.crypto.subtle.importKey(
|
|
"raw",
|
|
encryptionKey,
|
|
{ name: "AES-GCM" },
|
|
false
|
|
),
|
|
key
|
|
);
|
|
// Store iv + encrypted data
|
|
}
|
|
```
|
|
|
|
## Query Engine Design
|
|
|
|
### Query Syntax
|
|
|
|
```
|
|
('term1', 'term2') OR tagA AND tagB XOR url:example.com
|
|
```
|
|
|
|
### Parser
|
|
|
|
```javascript
|
|
class QueryParser {
|
|
parse(expression) {
|
|
// Tokenize
|
|
const tokens = this.tokenize(expression);
|
|
|
|
// Build AST
|
|
const ast = this.buildAST(tokens);
|
|
|
|
// Validate
|
|
this.validate(ast);
|
|
|
|
return this.serialize(ast);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Executor
|
|
|
|
```javascript
|
|
class QueryExecutor {
|
|
async execute(ast) {
|
|
// Build SQL
|
|
const sql = this.buildSQL(ast);
|
|
|
|
// Execute
|
|
const result = await fetch(`/api/links/?sql=${sql}`);
|
|
|
|
return await result.json();
|
|
}
|
|
}
|
|
```
|
|
|
|
## API Client Design
|
|
|
|
### REST API Integration
|
|
|
|
```javascript
|
|
const API = {
|
|
baseUrl: '',
|
|
headers: {
|
|
'Authorization': 'Token {key}',
|
|
'Content-Type': 'application/json'
|
|
},
|
|
|
|
async login() {
|
|
const response = await fetch(`${this.baseUrl}/api/auth/login/`, {
|
|
method: 'POST',
|
|
body: JSON.stringify({ username, password })
|
|
});
|
|
const data = await response.json();
|
|
this.storeApiKey(data.token);
|
|
return data;
|
|
},
|
|
|
|
async listLinks() {
|
|
return await this.request('/api/links/');
|
|
},
|
|
|
|
async createLink(link) {
|
|
return await this.post('/api/links/', link);
|
|
},
|
|
|
|
async executeQuery(expression) {
|
|
return await this.post('/api/queries/execute/', { expression });
|
|
}
|
|
};
|
|
```
|
|
|
|
## UI/UX Design
|
|
|
|
### Color Scheme
|
|
|
|
```css
|
|
:root {
|
|
--primary: #3b82f6;
|
|
--secondary: #6b7280;
|
|
--success: #10b981;
|
|
--warning: #f59e0b;
|
|
--error: #ef4444;
|
|
--background: #ffffff;
|
|
--surface: #f9fafb;
|
|
--border: #e5e7eb;
|
|
--text: #111827;
|
|
--text-secondary: #6b7280;
|
|
}
|
|
```
|
|
|
|
### Typography
|
|
|
|
- Font family: System UI
|
|
- Base size: 14px
|
|
- Heading: 18px
|
|
- Form labels: 12px
|
|
|
|
### Responsive Design
|
|
|
|
- Mobile-first approach
|
|
- Breakpoint: 480px for landscape
|
|
- Touch-friendly tap targets (44px minimum)
|
|
|
|
## Security Design
|
|
|
|
### API Key Handling
|
|
|
|
1. **Storage**
|
|
- Encrypted in localStorage
|
|
- Never logged or exposed
|
|
|
|
2. **Transmission**
|
|
- HTTPS preferred
|
|
- Token in Authorization header
|
|
- No tokens in URL params
|
|
|
|
3. **Validation**
|
|
- Verify response signatures
|
|
- Check rate limits
|
|
- Handle 401/403 gracefully
|
|
|
|
### Data Privacy
|
|
|
|
- No bookmarks stored locally after sync
|
|
- API keys user-managed
|
|
- No telemetry or analytics
|
|
|
|
## Testing Strategy
|
|
|
|
### Unit Tests
|
|
|
|
- Sync logic modes
|
|
- Conflict detection
|
|
- Query parsing
|
|
- Storage operations
|
|
|
|
### Integration Tests
|
|
|
|
- API endpoint calls
|
|
- Background worker events
|
|
- Popup communication
|
|
|
|
### Manual Testing
|
|
|
|
- Add/edit/delete bookmarks
|
|
- Collection creation
|
|
- Query execution
|
|
- Conflict scenarios |