235 lines
11 KiB
Markdown
235 lines
11 KiB
Markdown
# LinkLog
|
|
|
|
LinkLog is a Firefox extension and Python web service for saving links with a title, comment, timestamp, and tracking parameters removed. The service stores links in SQLite and can publish them through plugins, including Mastodon.
|
|
|
|
## Project Layout
|
|
|
|
```text
|
|
backend/ FastAPI application, services, database, and tests
|
|
frontend/ Jinja templates and browser-side assets
|
|
webextension/ Firefox Manifest V3 extension
|
|
Dockerfile Backend container image
|
|
docker-compose.yml App plus Traefik for local proxying
|
|
REQUIREMENTS.md Product requirements
|
|
VIBE/ Conversation and prompt logs
|
|
```
|
|
|
|
## Requirements
|
|
|
|
For local development:
|
|
|
|
- macOS, Linux, or Windows with Python 3.11+
|
|
- Firefox for installing the extension
|
|
- Docker Desktop and Docker Compose v2 for the container workflow
|
|
|
|
The backend currently uses FastAPI, uvicorn, SQLite, and Pydantic. `httpx2` is included for the Starlette-compatible test client.
|
|
Jinja2 is included for server-rendered HTML templates.
|
|
|
|
## Local Installation
|
|
|
|
From the repository root:
|
|
|
|
```sh
|
|
python3.11 -m venv .venv
|
|
. .venv/bin/activate
|
|
python -m pip install --upgrade pip
|
|
python -m pip install -r backend/requirements.txt
|
|
```
|
|
|
|
On Windows PowerShell, activate the environment with:
|
|
|
|
```powershell
|
|
.venv\Scripts\Activate.ps1
|
|
```
|
|
|
|
The SQLite database is created automatically at `backend/data/linklog.db` when the application starts or when the auth router is imported. The starter accounts are:
|
|
|
|
|
|
|
|
| Username | Password | Role |
|
|
| --- | --- | --- |
|
|
| `alice` | `secret123` | administrator |
|
|
| `bob` | `secret123` | standard user |
|
|
|
|
|
|
These credentials are for development only. Change the authentication and seeding design before deploying publicly.
|
|
|
|
The database schema is versioned with SQLite `PRAGMA user_version`. Application startup applies all pending migrations in order, so updating the application does not require deleting an existing database. New schema changes should be added as a new numbered migration in `backend/app/database.py`; existing migration entries must remain unchanged.
|
|
## Run The Backend
|
|
|
|
Activate the virtual environment, then run uvicorn from the repository root:
|
|
|
|
```sh
|
|
. .venv/bin/activate
|
|
PYTHONPATH=. uvicorn backend.app.main:app --reload --host 127.0.0.1 --port 8000
|
|
```
|
|
|
|
Open these URLs:
|
|
|
|
- Public feed: <http://localhost:8000/>
|
|
- User feed: <http://localhost:8000/alice>
|
|
- Profile settings: <http://localhost:8000/profile>
|
|
- Labels: <http://localhost:8000/labels>
|
|
- Admin page: <http://localhost:8000/admin>
|
|
- Web login: <http://localhost:8000/login>
|
|
- Health check: <http://localhost:8000/health>
|
|
- OpenAPI documentation: <http://localhost:8000/docs>
|
|
|
|
The browser extension defaults to `http://localhost:8000`.
|
|
|
|
Appending a username to the root URL, such as `/alice`, opens that user's public feed and profile information.
|
|
|
|
Configuration APIs require a bearer token returned by the login endpoint. User configuration uses the identity in that token. Plugin administration additionally requires an administrator account; the development `alice` account is seeded as an administrator, while `bob` is a standard user.
|
|
|
|
Users can change their password from the profile page. The current password is required, new passwords must contain at least 8 characters, and the endpoint is `PUT /api/user/password`.
|
|
|
|
On the profile page, the authenticated username is displayed as read-only. Users can upload a PNG, JPEG, GIF, or WebP avatar up to 2 MB; uploaded files are stored in the persistent data volume and served by the application. Bio and email fields remain empty until the user provides values. Mastodon settings default to the `mastodon.social` instance and the `From my #LinkLog: "` post prefix.
|
|
|
|
## Run Tests
|
|
|
|
```sh
|
|
. .venv/bin/activate
|
|
PYTHONPATH=. pytest backend/tests -q
|
|
```
|
|
|
|
To ensure warnings are clean as well:
|
|
|
|
```sh
|
|
PYTHONPATH=. PYTHONWARNINGS=error pytest backend/tests -q
|
|
```
|
|
|
|
## Install The Firefox Extension For Development
|
|
|
|
The extension is an unpacked Firefox extension. No build step is required.
|
|
|
|
1. Start the backend locally.
|
|
2. Open Firefox and visit `about:debugging#/runtime/this-firefox`.
|
|
3. Select **Load Temporary Add-on**.
|
|
4. Choose `webextension/manifest.json`.
|
|
5. Open the LinkLog extension options and enter:
|
|
- Backend URL: `http://localhost:8000`
|
|
- Username: `alice`
|
|
- Password: `secret123`
|
|
6. Save the settings and login.
|
|
7. Open a webpage, select the LinkLog toolbar button, review the title and URL, add a comment, and submit it.
|
|
|
|
Temporary extensions are removed when Firefox restarts. Reload the extension from `about:debugging` after changing its files.
|
|
|
|
## Docker And Traefik
|
|
|
|
Create the Docker environment file before starting the stack:
|
|
|
|
```sh
|
|
cp .env.example .env
|
|
```
|
|
|
|
Edit `.env` and replace `LINKLOG_SECRET_KEY` with a long random value. Docker Compose automatically reads `.env` from the repository root. The committed `.env.example` contains safe defaults and placeholders; the real `.env` is ignored by Git.
|
|
|
|
The main configurable values are:
|
|
|
|
| Variable | Purpose | Default |
|
|
| --- | --- | --- |
|
|
| `LINKLOG_SECRET_KEY` | token signing/security secret | required in Docker |
|
|
| `LINKLOG_DATABASE_PATH` | SQLite file path inside the container | `/app/backend/data/linklog.db` |
|
|
| `LINKLOG_TOKEN_EXPIRY_DAYS` | access-token lifetime | `30` |
|
|
| `LINKLOG_TRACKING_PARAMS` | comma-separated tracking parameters | built-in list |
|
|
| `TRAEFIK_HOST` | hostname routed by Traefik | `localhost` |
|
|
| `TRAEFIK_HTTP_PORT` | host port for the application proxy | `80` |
|
|
| `TRAEFIK_DASHBOARD_PORT` | host port for the dashboard | `8080` |
|
|
| `TRAEFIK_DASHBOARD_BIND_ADDRESS` | host address for the dashboard | `127.0.0.1` |
|
|
| `TRAEFIK_API_INSECURE` | enable the local dashboard API | `true` |
|
|
| `APP_PORT` | direct host port for FastAPI | `8000` |
|
|
|
|
The full set of supported variables is listed in `.env.example`. Application variables are passed into the container by Compose; Docker and Traefik variables are used by Compose itself.
|
|
|
|
Build and start the application and local Traefik proxy:
|
|
|
|
```sh
|
|
docker compose up --build
|
|
```
|
|
|
|
The services are available at:
|
|
|
|
- LinkLog through Traefik: <http://example.com/>
|
|
- Direct application port: <http://localhost:8000/>
|
|
|
|
|
|
The Compose configuration routes the hostname `localhost` through Traefik. The SQLite database is stored in the named Docker volume `linklog_data`, mounted at `/app/backend/data`.
|
|
The application runs as a non-root user and reports container health through `/health`; Traefik waits for the application health check before starting.
|
|
|
|
Stop the stack without deleting its database:
|
|
|
|
```sh
|
|
docker compose down
|
|
```
|
|
|
|
Stop the stack and delete the named database volume:
|
|
|
|
```sh
|
|
docker compose down -v
|
|
```
|
|
|
|
For a real deployment, replace the `localhost` router rule, configure TLS, protect the Traefik dashboard, avoid exposing the direct application port, and provide production secrets and authentication. The included Compose file is a local/prototype deployment scaffold, not a production security configuration.
|
|
|
|
## Mastodon Configuration
|
|
|
|
After logging in, open <http://localhost:8000/profile> and save the Mastodon settings:
|
|
|
|
- **Instance**: hostname or URL such as `mastodon.social` or `https://mastodon.social`
|
|
- **Access token**: a Mastodon API token with permission to create statuses
|
|
- **Post prefix**: text placed immediately before the link; defaults to `From my #LinkLog: "`
|
|
|
|
Enable the plugin from the admin API or the admin page. New links are saved first and then posted to the configured instance at `/api/v1/statuses`. A Mastodon network failure does not undo the saved link.
|
|
|
|
## Useful API Calls
|
|
|
|
Login:
|
|
|
|
```sh
|
|
curl -X POST http://localhost:8000/api/auth/login \\
|
|
-H 'Content-Type: application/json' \\
|
|
-d '{"username":"alice","password":"secret123"}'
|
|
```
|
|
|
|
Submit a link using the returned access token:
|
|
|
|
```sh
|
|
curl -X POST http://localhost:8000/api/links \\
|
|
-H 'Content-Type: application/json' \\
|
|
-H 'Authorization: Bearer YOUR_ACCESS_TOKEN' \\
|
|
-d '{"title":"Example","url":"https://example.com","comment":"Worth reading"}'
|
|
```
|
|
|
|
The public feed is available at:
|
|
|
|
```text
|
|
GET http://localhost:8000/api/public/feed
|
|
```
|
|
|
|
When a request includes a valid bearer token, entries owned by that authenticated user include edit permission and show an inline **Edit** action in the feed. The update endpoint is `PUT /api/links/{link_id}` and rejects edits from other users.
|
|
Feed items also expose `is_owner`; it is `true` only for entries owned by the authenticated user and `false` for anonymous viewers or other users.
|
|
|
|
Links support zero to ten tags. Tags are trimmed, deduplicated case-insensitively, and retain their original casing for display. The Firefox capture popup shows existing server tags as checkboxes and accepts new comma-separated tags. The home page displays tags and provides a case-insensitive tag filter; editing a link replaces its complete tag set.
|
|
The inline link editor also allows multiple existing tags to be selected and new tags to be entered. New tag values receive a leading `#` automatically, and the interface prevents saving more than ten tags.
|
|
The installation seeds these available tags: `#Internet`, `#Cybersecurity`, `#Fediverse`, `#Food`, `#Photography`, `#Music`, and `#AI`.
|
|
Users can create, edit, and delete their own labels from `/labels`. Administrators can delete any label from `/admin`; system-seeded labels have no user owner.
|
|
|
|
Admin plugin requests must include the administrator's token:
|
|
|
|
```sh
|
|
curl http://localhost:8000/api/admin/plugins \\
|
|
-H 'Authorization: Bearer YOUR_ADMIN_ACCESS_TOKEN'
|
|
```
|
|
|
|
Administrators can manage user privileges from the admin page. The `Administrator` checkbox sends `PUT /api/admin/users/{user_id}` with `{"is_admin": true}` or `{"is_admin": false}`. The API rejects any change that would leave the system without an administrator.
|
|
|
|
## Troubleshooting
|
|
|
|
- **`ModuleNotFoundError`**: activate `.venv` and rerun `python -m pip install -r backend/requirements.txt`.
|
|
- **Jinja2 import error in Docker**: rebuild the image with `docker compose up --build`; the runtime dependency is declared in `backend/requirements.txt`.
|
|
- **Extension cannot login**: confirm the backend is running, use the exact backend origin without a trailing API path, and check the browser console for blocked requests.
|
|
- **Port 8000 is occupied**: run uvicorn with another port and update the extension backend URL, for example `--port 8001`.
|
|
- **Port 80 or 8080 is occupied**: change the host-side ports in `docker-compose.yml`.
|
|
- **Stale development data**: stop the backend and remove `backend/data/linklog.db`, or run `docker compose down -v` for the container volume.
|
|
- **Schema migration issue**: inspect the database version with `sqlite3 backend/data/linklog.db 'PRAGMA user_version;'` and restart the application to apply pending migrations.
|