Files
linkvault/README.md
Erik Thiele 604c4ea9c2 Add invite-code registration and admin user management
Open registration let anyone with the URL create an account. Two
changes address that:

- REGISTRATION_CODE (.env, optional): when set, registration requires
  entering it correctly. Empty/unset keeps registration open, so
  existing installs are unaffected until configured.
- is_admin flag on User: the first account ever created on an install
  becomes admin automatically (existing installs get their oldest
  account promoted via the startup migration, so nobody is locked
  out of user management after upgrading).

Admins get a new "Benutzerverwaltung" panel in Einstellungen listing
every account (email, link/prompt counts, join date) with a delete
button per account — deleting cascades to that user's links and
prompts via the existing relationship cascade. Deleting your own
account through this page is blocked (redirects with an error) to
avoid accidental admin lockout. Non-admins get a 403 on the
/settings/users routes.

Also fixes several pre-existing German pluralization bugs found while
writing the new counts ("2 Linke" -> "2 Links", "Kontoen"/"Konton" ->
"Konten") — irregular plurals need a full word swap, not a suffix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-31 11:16:01 +02:00

8.9 KiB
Raw Permalink Blame History

LinkVault

KI-gestützte Sammlung für Links und Prompts: eingeben → automatisch kategorisieren, in Kategorien / Hersteller / Tags einsortieren und schnell wiederfinden. Beide Sammlungen laufen in derselben App und Datenbank, per Menü umschaltbar.

Funktionen

  • 🔗 Link hinzufügen Seite wird geladen und der Textinhalt extrahiert.
  • Prompt hinzufügen eigener Bereich für KI-Prompts, per Kopf-Menü zwischen „Links" und „Prompts" umschaltbar. Prompts werden ebenfalls automatisch kategorisiert und getaggt, lassen sich per Kategorie/Volltext durchsuchen und mit einem Klick in die Zwischenablage kopieren. Der Prompt-Text unterstützt Markdown (Überschriften, Listen, Fett/Kursiv, Codeblöcke, Zitate) in der Karten-Vorschau wird das gerendert, beim Kopieren landet der rohe Markdown-Text (für Struktur in der Ziel-KI) in der Zwischenablage.
  • 🤖 Automatische KI-Analyse (OpenAI) Titel, Zusammenfassung/Kategorie und Tags werden erzeugt. Bestehende Kategorien werden wiederverwendet, damit die Sammlung konsistent bleibt.
  • Bearbeiten alle Felder lassen sich jederzeit manuell anpassen. Alternativ kann die KI Links/Prompts neu beschreiben bzw. neu kategorisieren lassen.
  • 🗂️ Filtern nach Kategorie (Links zusätzlich nach Hersteller) über die Seitenleiste.
  • 🔍 Suche klassische Textsuche, bei Links zusätzlich optionale semantische KI-Suche (findet auch sinnverwandte Treffer via Embeddings).
  • ↕️ Sortieren nach Datum (neueste/älteste zuerst) oder Name (AZ / ZA).
  • 🔖 Merkliste Links lassen sich zur späteren Durchsicht markieren.
  • 🕓 Hinzufügedatum wird pro Eintrag angezeigt.
  • 📤 Export/Import (Einstellungen) Links als CSV oder SQL-Dump exportieren, SQL-Dump auch wieder importieren.
  • 🌗 Dark/Light-Modus über den Umschalter im Kopfbereich.
  • 👥 Mehrbenutzer Registrierung/Login, jeder Nutzer sieht nur seine eigenen Links und Prompts. Registrierung optional per Einladungscode schützbar (REGISTRATION_CODE in der .env); das zuerst angelegte Konto wird automatisch Admin und kann unter Einstellungen → Benutzerverwaltung weitere Konten einsehen und löschen.

Die Fußzeile zeigt Autor, Version und den Hostnamen des Servers. Die Version lässt sich über APP_VERSION in der .env setzen.

Technik

  • Backend: FastAPI (Python)
  • Frontend: HTML + HTMX (kein Node.js nötig)
  • Datenbank: SQLite (Standard, konfigurierbar über DATABASE_URL)
  • KI: OpenAI (gpt-4o-mini + text-embedding-3-small, konfigurierbar)

Schnellstart

./run.sh

Das Skript legt eine virtuelle Umgebung an, installiert die Abhängigkeiten, erstellt bei Bedarf eine .env und startet den Server auf http://127.0.0.1:8000.

Danach in .env den OPENAI_API_KEY eintragen und den Server neu starten.

Ohne API-Key läuft die App trotzdem Links werden dann nur ohne KI-Analyse gespeichert (Kategorie „Sonstiges", keine Zusammenfassung/semantische Suche).

Manuell

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env        # OPENAI_API_KEY eintragen
uvicorn app.main:app --reload --port 8000

Projektstruktur

app/
  main.py        FastAPI-App, Routen (Links & Prompts), Auth-Sessions
  config.py      Konfiguration aus .env
  database.py    SQLAlchemy Engine/Session
  models.py      User-, Link- und Prompt-Modelle
  security.py    Passwort-Hashing (bcrypt)
  scraper.py     Seiteninhalt laden & extrahieren (nur Links)
  ai.py          OpenAI: Kategorisierung (Links & Prompts), Embeddings
  search.py      Semantische Suche (Kosinus-Ähnlichkeit, nur Links)
  backup.py      CSV-/SQL-Export und sicherer SQL-Import (nur Links)
  mdrender.py    Sicheres Markdown-Rendering für Prompt-Inhalte
  templates/     Jinja2 + HTMX Oberfläche

Proxmox

Für den Betrieb in einem Proxmox-VE-LXC-Container gibt es ein Helper-Script (scripts/proxmox/install-linkvault.sh). Es legt automatisch einen Debian-12-Container an und richtet LinkVault darin als systemd-Service ein.

Installation

Auf dem Proxmox-Host (als root) ausführen:

bash <(curl -fsSL https://gitea.teamthiele.de/ethiele/linkvault/raw/branch/main/scripts/proxmox/install-linkvault.sh)

Falls das Repo (wieder) auf privat gestellt wird, zusätzlich ein Gitea Access Token verwenden (Einstellungen → Anwendungen → Neuen Token erzeugen, Scope read:repository genügt):

export GIT_TOKEN=<dein-token>
bash <(curl -fsSL -H "Authorization: token ${GIT_TOKEN}" \
  https://gitea.teamthiele.de/ethiele/linkvault/raw/branch/main/scripts/proxmox/install-linkvault.sh)

GIT_TOKEN wird automatisch an das Script weitergereicht und auch beim git clone innerhalb des LXC-Containers verwendet.

Alternativ, falls das Repo bereits lokal ausgecheckt ist:

bash scripts/proxmox/install-linkvault.sh

Das Script:

  1. lädt bei Bedarf das Debian-12-LXC-Template herunter,
  2. erstellt einen unprivilegierten LXC-Container,
  3. installiert darin Python, klont das Repo nach /opt/linkvault,
  4. legt ein venv an und installiert die Abhängigkeiten,
  5. erzeugt eine .env mit zufälligem SECRET_KEY,
  6. richtet LinkVault als systemd-Service (linkvault.service) ein und startet ihn.

Am Ende gibt das Script die URL des Containers sowie das generierte root-Passwort aus.

Konfiguration

Alle Einstellungen lassen sich per Umgebungsvariable vor dem Aufruf setzen (Defaults siehe Kopf des Scripts):

Variable Bedeutung Default
CTID Container-ID nächste freie ID
CT_HOSTNAME Hostname des Containers linkvault
CT_DISK_GB Größe der Root-Disk in GB 8
CT_MEMORY_MB RAM in MB 1024
CT_SWAP_MB Swap in MB 512
CT_CORES CPU-Kerne 2
CT_BRIDGE Netzwerk-Bridge vmbr0
CT_IP Statische IP (192.168.1.50/24) oder dhcp dhcp
CT_GW Gateway (nur bei statischer IP nötig)
CT_STORAGE Storage für die Root-Disk local-lvm
TEMPLATE_STORAGE Storage für das LXC-Template local
CT_PASSWORD root-Passwort des Containers zufällig generiert
REPO_URL Git-Repo, das geklont wird .../ethiele/linkvault.git
APP_PORT Port, auf dem uvicorn lauscht 8000
GIT_TOKEN Gitea Access Token (nur bei privatem Repo)
TIMEZONE Zeitzone des Containers (timedatectl) Europe/Berlin

Beispiel mit statischer IP und fester Container-ID:

CTID=150 CT_HOSTNAME=linkvault CT_IP=192.168.1.50/24 CT_GW=192.168.1.1 \
  bash <(curl -fsSL https://gitea.teamthiele.de/ethiele/linkvault/raw/branch/main/scripts/proxmox/install-linkvault.sh)

Nach der Installation

In /opt/linkvault/.env innerhalb des Containers den OPENAI_API_KEY eintragen (siehe Schnellstart für weitere .env-Optionen), danach den Dienst neu starten:

pct exec <CTID> -- systemctl restart linkvault

Nützliche Befehle:

pct exec <CTID> -- systemctl status linkvault   # Status prüfen
pct exec <CTID> -- journalctl -u linkvault -f    # Logs verfolgen

Updaten

Wenn sich im Git-Repo etwas geändert hat, den Container mit scripts/proxmox/update-linkvault.sh auf den neuesten Stand bringen. Das Script zieht per git pull, aktualisiert die Python-Abhängigkeiten und startet den systemd-Service neu. Auf dem Proxmox-Host ausführen:

CTID=113 bash <(curl -fsSL https://gitea.teamthiele.de/ethiele/linkvault/raw/branch/main/scripts/proxmox/update-linkvault.sh)

(GIT_TOKEN nur nötig, falls das Repo privat ist siehe oben.)

Alternativ, falls das Repo bereits auf dem Proxmox-Host selbst per git clone ausgecheckt wurde (z.B. unter ~/linkvault) und du dich in diesem Verzeichnis befindest:

CTID=113 bash scripts/proxmox/update-linkvault.sh

Hinweise für den Produktivbetrieb

  • Einen zufälligen SECRET_KEY in .env setzen.
  • Hinter einem HTTPS-Reverse-Proxy betreiben (Session-Cookies).
  • Für mehr Last statt SQLite z.B. PostgreSQL via DATABASE_URL verwenden.