Initial LinkLog implementation

This commit is contained in:
Olaf
2026-08-24 14:30:30 +02:00
commit 1c827956c4
50 changed files with 3436 additions and 0 deletions
+216
View File
@@ -0,0 +1,216 @@
# 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.
## 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>
- 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.
On the profile page, the authenticated username is displayed as read-only. The profile form prepopulates the default avatar URL when it has not been customized. 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_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://localhost/>
- Direct application port: <http://localhost:8000/>
- Traefik dashboard: <http://localhost:8080/dashboard/>
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`.
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
```
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.