Debian LXC templates default to UTC, which made the "hinzugefügt am" timestamps and export filenames look wrong. Add a TIMEZONE variable (default Europe/Berlin) applied via timedatectl during install, and document it in the README. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
207 lines
8.7 KiB
Markdown
207 lines
8.7 KiB
Markdown
# 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 (A–Z / Z–A).
|
||
- 🔖 **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.
|
||
|
||
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
|
||
|
||
```bash
|
||
./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
|
||
|
||
```bash
|
||
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
|
||
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):
|
||
> ```bash
|
||
> 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
|
||
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:
|
||
|
||
```bash
|
||
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](#schnellstart) für weitere `.env`-Optionen),
|
||
danach den Dienst neu starten:
|
||
|
||
```bash
|
||
pct exec <CTID> -- systemctl restart linkvault
|
||
```
|
||
|
||
Nützliche Befehle:
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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:
|
||
|
||
```bash
|
||
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.
|