Files
myworkspace/LinkSyncServer/README.md
DavidSaylor 09d30427f4 Complete LinkSyncServer and LinkSyncExtension implementation
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
2026-05-19 13:21:26 -05:00

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.