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
9.2 KiB
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 identifierurl- Bookmark URL (duplicates allowed)title- Display titledescription- Optional descriptionnotes- User notestags- Array of tag namesfavicon_url- Icon URLpath- Folder structurecreated_at,updated_at- Timestampsvisit_count- Number of visitsis_bookmarked- Bookmarked statussource_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
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
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
Dockerfiledefines exactly how your app is packaged
Rebuilding after code changes:
# 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
# Windows
.\deploy.ps1 C:\deploy\linksync
# 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
# 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
# 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.