LinkSyncServer: - Fix app.py imports, add CORS middleware, lifespan events - Create api/routes.py router aggregator - Create config/settings.py for centralized configuration - Rewrite models/base.py with proper relationships and serialization - Rewrite all API endpoints with real DB integration (auth, links, collections, sync, queries, tags) - Add admin endpoints (user management, stats, audit log) - Complete query parser with recursive descent and proper precedence - Complete query executor with set operations and field filters - Set up Alembic migrations with initial schema - Create web interface (templates, CSS, JS) - Add 42 passing tests (auth, links, collections, queries) - Add deploy.ps1 and deploy.sh scripts - Update README with deployment workflow LinkSyncExtension: - Create utils/api.js (REST client with retries, auth, error handling) - Create utils/sync.js (3 sync modes + conflict detection) - Create utils/collection.js (collection management) - Create utils/query-engine.js (client-side query parser) - Rewrite background.js (sync loop, bookmark events, message routing) - Rewrite popup.js (tabs, settings modal, notifications, CRUD) - Update popup.html (tabbed interface, query builder, modal) - Update popup.css (full redesign) - Create content/content.js (page metadata extraction) - Create options.html/js (dedicated settings page) - Generate icons (48x48, 96x96) - Update manifest.json (host permissions, content scripts, options) - Create AGENTS.md
339 lines
9.2 KiB
Markdown
339 lines
9.2 KiB
Markdown
# LinkSyncServer
|
|
|
|
A self-hosted bookmark server with advanced collection and query capabilities, designed to work with browser extensions for bookmark synchronization.
|
|
|
|
## Overview
|
|
|
|
LinkSyncServer replaces the need for workarounds in existing bookmark sync solutions. It provides:
|
|
|
|
- **True Collections** - First-class collection objects with saved queries
|
|
- **Advanced Query Engine** - Supports AND, OR, XOR set operations
|
|
- **Firefox-Compatible Fields** - All bookmark attributes natively supported
|
|
- **Multi-User Support** - Authentication with admin and regular user roles
|
|
- **RESTful API** - Full CRUD operations for links and collections
|
|
- **Web Interface** - Modern UI for browsing, searching, and managing collections
|
|
- **Docker-Ready** - Easy deployment with Docker Compose
|
|
|
|
## Features
|
|
|
|
### Collections
|
|
|
|
Two types of collections:
|
|
|
|
| Type | Description |
|
|
|------|-------------|
|
|
| **Static** | Explicit set of link IDs |
|
|
| **Dynamic** | Query expression evaluated on each access |
|
|
|
|
#### Dynamic Collection Query Syntax
|
|
|
|
```
|
|
('term1', 'term2', 'term3') OR tagA AND tagB XOR url:example.com
|
|
```
|
|
|
|
- Parentheses evaluated first (innermost to outermost)
|
|
- Left-to-right evaluation otherwise
|
|
- Precedence: `()` > XOR > AND > OR
|
|
|
|
### Set Operations
|
|
|
|
Query builder supports visual set operations:
|
|
|
|
```
|
|
Set1 AND Set2 XOR Set3 OR Set4
|
|
```
|
|
|
|
This evaluates as: `(((Set1 AND Set2) XOR Set3) OR Set4)`
|
|
|
|
### Synchronization Modes
|
|
|
|
| Mode | Browser → Server | Server → Browser |
|
|
|------|------------------|------------------|
|
|
| **Bi-directional** | Add/update | Add/update |
|
|
| **Browser Authoritative** | Add/update | Overwrite |
|
|
| **Server Authoritative** | Download only | Overwrite |
|
|
|
|
Optional: Enable deletions for all modes.
|
|
|
|
### Bookmarks (Links)
|
|
|
|
All Firefox bookmark attributes supported:
|
|
|
|
- `id` - Unique identifier
|
|
- `url` - Bookmark URL (duplicates allowed)
|
|
- `title` - Display title
|
|
- `description` - Optional description
|
|
- `notes` - User notes
|
|
- `tags` - Array of tag names
|
|
- `favicon_url` - Icon URL
|
|
- `path` - Folder structure
|
|
- `created_at`, `updated_at` - Timestamps
|
|
- `visit_count` - Number of visits
|
|
- `is_bookmarked` - Bookmarked status
|
|
- `source_set_id` - Collection that added this link
|
|
|
|
## Architecture
|
|
|
|
```
|
|
┌─────────────────────────────────────┐
|
|
│ LinkSyncServer │
|
|
│ │
|
|
│ ┌──────────────┐ ┌─────────────┐ │
|
|
│ │ API Layer │ │ Auth │ │
|
|
│ └──────────────┘ └─────────────┘ │
|
|
│ ┌──────────────┐ ┌─────────────┐ │
|
|
│ │ Query │ │ Models │ │
|
|
│ │ Engine │ │ (SQLAlchemy)│ │
|
|
│ └──────────────┘ └─────────────┘ │
|
|
│ ┌──────────────┐ ┌─────────────┐ │
|
|
│ │ Templates │ │ Static │ │
|
|
│ └──────────────┘ │ Files │ │
|
|
│ ┌──────────────┐ └─────────────┘ │
|
|
│ │ PostgreSQL │ │ │
|
|
│ │ │ │ │
|
|
│ └──────────────┘ │ │
|
|
└─────────────────────────────────────┘
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
### Prerequisites
|
|
|
|
- Docker and Docker Compose
|
|
- Port 5000 available (or configurable)
|
|
|
|
### Docker Compose
|
|
|
|
```yaml
|
|
version: '3.8'
|
|
|
|
services:
|
|
web:
|
|
build: .
|
|
ports:
|
|
- "5000:5000"
|
|
environment:
|
|
- DATABASE_URL=postgresql://user:password@db:5432/linksync
|
|
- SECRET_KEY=your-secret-key-here
|
|
- ADMIN_USERNAME=admin
|
|
- ADMIN_PASSWORD=admin123
|
|
depends_on:
|
|
- db
|
|
|
|
db:
|
|
image: postgres:15-alpine
|
|
environment:
|
|
- POSTGRES_DB=linksync
|
|
- POSTGRES_USER=user
|
|
- POSTGRES_PASSWORD=password
|
|
volumes:
|
|
- linkdata:/var/lib/postgresql/data
|
|
|
|
volumes:
|
|
linkdata:
|
|
```
|
|
|
|
### Build and Run
|
|
|
|
```bash
|
|
docker-compose up -d --build
|
|
```
|
|
|
|
### How `build: .` Works
|
|
|
|
In `docker-compose.yml`, the `web` service uses `build: .` instead of `image:`. This is a key distinction:
|
|
|
|
| Key | Behavior |
|
|
|-----|----------|
|
|
| `image: postgres:15-alpine` | Pulls a pre-built image from Docker Hub |
|
|
| `build: .` | Builds a custom image from a `Dockerfile` in the current directory (`.`) |
|
|
|
|
**The build process works like this:**
|
|
|
|
```
|
|
docker-compose up --build
|
|
│
|
|
▼
|
|
Reads docker-compose.yml
|
|
│
|
|
▼
|
|
Finds build: . → looks for Dockerfile in current directory
|
|
│
|
|
▼
|
|
Executes each instruction in the Dockerfile:
|
|
1. FROM python:3.12-slim ← Base image
|
|
2. RUN apt-get install curl ← Install system deps
|
|
3. COPY requirements.txt . ← Copy dependency list
|
|
4. RUN pip install -r ... ← Install Python packages
|
|
5. COPY . . ← Copy all project files
|
|
6. EXPOSE 5000 ← Declare port
|
|
7. CMD ["uvicorn", ...] ← Set startup command
|
|
│
|
|
▼
|
|
Tags the built image as linksyncserver-web (auto-generated name)
|
|
│
|
|
▼
|
|
Starts the container from the built image
|
|
```
|
|
|
|
**Why build instead of pull?**
|
|
|
|
- You're running your own application code, not a third-party image
|
|
- Every code change requires a rebuild to take effect
|
|
- The `Dockerfile` defines exactly how your app is packaged
|
|
|
|
**Rebuilding after code changes:**
|
|
|
|
```bash
|
|
# Rebuild and restart (picks up all code changes)
|
|
docker-compose up -d --build
|
|
|
|
# Rebuild without cache (forces fresh pip install)
|
|
docker-compose build --no-cache && docker-compose up -d
|
|
|
|
# Just restart without rebuilding (uses existing image)
|
|
docker-compose restart
|
|
```
|
|
|
|
**The `--build` flag:** Forces Docker Compose to rebuild images before starting containers. Without it, Compose reuses any previously built image, meaning your code changes won't be reflected.
|
|
|
|
### Initial Login
|
|
|
|
- URL: `http://localhost:5000`
|
|
- Admin credentials from environment variables
|
|
- Create first admin account
|
|
- Admin can create regular users and admin users
|
|
|
|
## API Documentation
|
|
|
|
See `/api/docs` or `/api/openapi.json` for complete API specification.
|
|
|
|
## Configuration
|
|
|
|
Environment variables:
|
|
|
|
| Variable | Description | Default |
|
|
|----------|-------------|---------|
|
|
| `DATABASE_URL` | PostgreSQL connection string | Required |
|
|
| `SECRET_KEY` | JWT secret key | Required |
|
|
| `ADMIN_USERNAME` | Initial admin username | - |
|
|
| `ADMIN_PASSWORD` | Initial admin password | - |
|
|
| `DEBUG` | Debug mode | False |
|
|
| `HOST` | Bind address | 0.0.0.0 |
|
|
| `PORT` | Port | 5000 |
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
LinkSyncServer/
|
|
├── README.md
|
|
├── TODOs.txt
|
|
├── design.md
|
|
├── tasks.md
|
|
├── AGENTS.md
|
|
├── docker-compose.yml
|
|
├── Dockerfile
|
|
├── requirements.txt
|
|
├── app.py
|
|
├── config/
|
|
├── api/
|
|
├── models/
|
|
├── queries/
|
|
├── templates/
|
|
└── static/
|
|
```
|
|
|
|
## Deployment
|
|
|
|
### Deploy Script
|
|
|
|
The project includes `deploy.ps1` (Windows) and `deploy.sh` (Linux/macOS) to prepare a clean deployment package. These scripts copy only production files, exclude development artifacts (`tests/`, `__pycache__/`, `.git/`, etc.), and create a starter `.env` file.
|
|
|
|
#### Usage
|
|
|
|
```powershell
|
|
# Windows
|
|
.\deploy.ps1 C:\deploy\linksync
|
|
```
|
|
|
|
```bash
|
|
# Linux/macOS
|
|
chmod +x deploy.sh
|
|
./deploy.sh /opt/deploy/linksync
|
|
```
|
|
|
|
#### What Gets Deployed
|
|
|
|
```
|
|
linksync-deploy/
|
|
├── .env ← starter file (edit with production secrets)
|
|
├── .env.example
|
|
├── docker-compose.yml
|
|
├── Dockerfile
|
|
├── requirements.txt
|
|
├── app.py
|
|
├── api/
|
|
├── models/
|
|
├── queries/
|
|
├── config/
|
|
├── templates/
|
|
├── static/
|
|
├── alembic/
|
|
├── pyproject.toml
|
|
├── README.md
|
|
├── AGENTS.md
|
|
├── design.md
|
|
├── tasks.md
|
|
└── TODOs.txt
|
|
```
|
|
|
|
#### What Is Excluded
|
|
|
|
`tests/`, `__pycache__/`, `.pytest_cache/`, `.git/`, `.vscode/`, `*.pyc`, `*.db`, `*.sqlite3`, `node_modules/`, `dist/`, `build/`, and the deploy scripts themselves.
|
|
|
|
#### Full Deployment Workflow
|
|
|
|
```bash
|
|
# 1. Clone the repository to a temporary location
|
|
git clone <repo-url> /tmp/linksync-src
|
|
cd /tmp/linksync-src
|
|
|
|
# 2. Run the deploy script to prepare the package
|
|
./deploy.sh /opt/linksync
|
|
|
|
# 3. Configure production secrets
|
|
cd /opt/linksync
|
|
nano .env
|
|
# Set these values:
|
|
# DATABASE_URL=postgresql://user:pass@db:5432/linksync
|
|
# SECRET_KEY=<generate with: openssl rand -base64 32>
|
|
# ADMIN_PASSWORD=<strong password>
|
|
|
|
# 4. Build and start
|
|
docker-compose up -d --build
|
|
|
|
# 5. Verify
|
|
curl http://localhost:5000/health
|
|
|
|
# 6. Clean up the source clone
|
|
rm -rf /tmp/linksync-src
|
|
```
|
|
|
|
#### Updating an Existing Deployment
|
|
|
|
```bash
|
|
# On the server, pull latest code and redeploy
|
|
cd /tmp/linksync-src && git pull
|
|
./deploy.sh /opt/linksync
|
|
cd /opt/linksync
|
|
docker-compose up -d --build
|
|
rm -rf /tmp/linksync-src
|
|
```
|
|
|
|
## License
|
|
|
|
MIT License
|
|
|
|
## Support
|
|
|
|
For issues and feature requests, see the GitHub repository. |