From 0313821f591116ac0b42724935b04197f205a145 Mon Sep 17 00:00:00 2001 From: ValueOn AG Date: Fri, 6 Feb 2026 02:42:39 +0100 Subject: [PATCH] protected app --- app.py | 57 +++- setupserver.md | 687 +++++++++++++++++++++---------------------- templates/index.html | 15 +- templates/login.html | 224 ++++++++++++++ 4 files changed, 618 insertions(+), 365 deletions(-) create mode 100644 templates/login.html diff --git a/app.py b/app.py index 36d5170..f24d6ce 100644 --- a/app.py +++ b/app.py @@ -3,13 +3,15 @@ Belegscanner - KI-Dokumentenanalyse Python Flask Web App mit CORS-Unterstützung und Poweron Design """ -from flask import Flask, render_template, request, jsonify +from flask import Flask, render_template, request, jsonify, session, redirect, url_for from flask_cors import CORS +from functools import wraps import requests import base64 import json import re import io +import os # PDF Support try: @@ -21,7 +23,29 @@ except ImportError: print("Installieren mit: pip install pymupdf") app = Flask(__name__) -CORS(app) # CORS für alle Routen aktivieren +app.secret_key = os.environ.get('SECRET_KEY', 'poweron-secret-key-change-in-production') +CORS(app, supports_credentials=True) # CORS für alle Routen aktivieren + +# ============================================================================ +# Authentication +# ============================================================================ + +# Einfache Credentials (für minimalen Schutz) +AUTH_USERNAME = os.environ.get('AUTH_USERNAME', 'poweron') +AUTH_PASSWORD = os.environ.get('AUTH_PASSWORD', 'poweron') + + +def _loginRequired(f): + """Decorator für geschützte Routen""" + @wraps(f) + def decorated_function(*args, **kwargs): + if not session.get('logged_in'): + # Bei API-Calls JSON zurückgeben, sonst redirect + if request.path.startswith('/api/'): + return jsonify({'error': 'Nicht autorisiert', 'login_required': True}), 401 + return redirect(url_for('_login')) + return f(*args, **kwargs) + return decorated_function # ============================================================================ @@ -120,13 +144,40 @@ def _isVisionModel(modelName): # Routes # ============================================================================ +@app.route('/login', methods=['GET', 'POST']) +def _login(): + """Login-Seite""" + error = None + if request.method == 'POST': + username = request.form.get('username', '') + password = request.form.get('password', '') + + if username == AUTH_USERNAME and password == AUTH_PASSWORD: + session['logged_in'] = True + session['username'] = username + return redirect(url_for('_index')) + else: + error = 'Ungültige Anmeldedaten' + + return render_template('login.html', error=error) + + +@app.route('/logout') +def _logout(): + """Logout""" + session.clear() + return redirect(url_for('_login')) + + @app.route('/') +@_loginRequired def _index(): """Hauptseite mit dem Belegscanner UI""" return render_template('index.html') @app.route('/api/analyze', methods=['POST']) +@_loginRequired def _analyzeDocument(): """ Analysiert ein Dokument mit Ollama Vision API oder verarbeitet Text mit Non-Vision Modellen @@ -221,6 +272,7 @@ def _healthCheck(): @app.route('/api/pdf/extract', methods=['POST']) +@_loginRequired def _extractPdfImages(): """ Extrahiert Bilder aus einem PDF. @@ -263,6 +315,7 @@ def _extractPdfImages(): @app.route('/api/ollama/status', methods=['GET']) +@_loginRequired def _ollamaStatus(): """Prüft ob Ollama erreichbar ist und listet verfügbare Modelle""" ollamaUrl = request.args.get('url', 'http://localhost:11434') diff --git a/setupserver.md b/setupserver.md index f5249e9..30d1913 100644 --- a/setupserver.md +++ b/setupserver.md @@ -31,7 +31,7 @@ Connect: ssh -i "C:\Users\pmots\Downloads\ollama-deploy-key.pem" ubuntu@83.228.2 ▼ ┌─────────────────┐ │ Infomaniak GPU │ - │ Server │ + │ 83.228.200.109 │ │ ┌───────────┐ │ │ │ Ollama │ │ │ │ + Flask │ │ @@ -43,43 +43,52 @@ Connect: ssh -i "C:\Users\pmots\Downloads\ollama-deploy-key.pem" ubuntu@83.228.2 --- +## Finale Konfiguration + +| Komponente | Wert | +|------------|------| +| **Server IP** | `83.228.200.109` | +| **GPU** | NVIDIA L4 (24GB VRAM) | +| **OS** | Ubuntu 24.04 LTS | +| **SSH User** | `ubuntu` | +| **App Port** | `5000` | +| **Ollama Port** | `11434` | +| **GitHub Repo** | `https://github.com/valueonag/private-llm` | + +### Installierte Modelle + +| Modell | Verwendung | +|--------|------------| +| `granite3.2-vision` | Rechnungen, Belege, Dokumente | +| `qwen2.5vl:7b` | Handschrift | +| `deepseek-ocr` | OCR / Text-Extraktion | + +### URLs + +| Service | URL | +|---------|-----| +| **App** | http://83.228.200.109:5000 | +| **Health Check** | http://83.228.200.109:5000/api/health | +| **Ollama Status** | http://83.228.200.109:5000/api/ollama/status | + +--- + # Teil A: GitHub Repository Setup ## A.1 Repository klonen in Cursor -Öffne ein Terminal in Cursor oder lokal: - ```bash -# In deinen Projektordner wechseln -cd ~/Projects # oder wo du deine Projekte speicherst - -# Repository klonen +cd ~/Projects git clone https://github.com/valueonag/private-llm.git - -# In den Ordner wechseln cd private-llm -``` - -## A.2 Cursor mit Repo verbinden - -**Option 1: Ordner in Cursor öffnen** -1. Cursor öffnen -2. **File → Open Folder** -3. Wähle den `private-llm` Ordner - -**Option 2: Über Terminal** -```bash -cd ~/Projects/private-llm cursor . ``` -## A.3 Projektstruktur erstellen - -Erstelle folgende Struktur: +## A.2 Projektstruktur ``` private-llm/ -├── app.py # Deine Flask App +├── app.py # Flask App ├── requirements.txt # Python Dependencies ├── templates/ │ └── index.html # Frontend Template @@ -91,9 +100,9 @@ private-llm/ └── README.md ``` -### A.3.1 requirements.txt +## A.3 Wichtige Dateien -Erstelle `requirements.txt`: +### requirements.txt ```txt flask>=3.0.0 @@ -103,9 +112,7 @@ pymupdf>=1.24.0 gunicorn>=21.0.0 ``` -### A.3.2 .gitignore - -Erstelle `.gitignore`: +### .gitignore ```gitignore # Python @@ -143,40 +150,20 @@ logs/ htmlcov/ ``` -### A.3.3 README.md - -Erstelle `README.md`: - -```markdown -# Private LLM - Belegscanner - -KI-Dokumentenanalyse mit lokalen Ollama Vision-Modellen. - -## Features - -- Rechnungen, Belege, Bankauszüge analysieren -- Handschrift erkennen -- PDF-Support -- 100% lokal - keine Cloud-APIs - -## Tech Stack - -- **Backend:** Python Flask -- **AI:** Ollama Vision Models -- **Server:** Infomaniak Swiss Cloud (GPU) - -## Deployment - -Automatisches Deployment via GitHub Actions bei Push zu `main`. -``` - --- # Teil B: GitHub Actions Deploy Workflow -## B.1 Workflow-Datei erstellen +## B.1 GitHub Secret einrichten -Erstelle `.github/workflows/deploy.yml`: +1. Gehe zu: `https://github.com/valueonag/private-llm/settings/secrets/actions` +2. **New repository secret** +3. Name: `SSH_PRIVATE_KEY` +4. Value: Inhalt der `ollama-deploy-key.pem` Datei + +**Nur dieses eine Secret wird benötigt!** + +## B.2 Deploy Workflow (.github/workflows/deploy.yml) ```yaml name: Deploy to Infomaniak @@ -185,30 +172,29 @@ on: push: branches: - main - workflow_dispatch: # Manueller Trigger möglich + workflow_dispatch: env: APP_DIR: /opt/ollama-webapp SERVICE_NAME: ollama-webapp + SERVER_HOST: 83.228.200.109 + SERVER_USER: ubuntu jobs: deploy: runs-on: ubuntu-latest steps: - # 1. Code auschecken - name: Checkout code uses: actions/checkout@v4 - # 2. SSH Setup - name: Setup SSH run: | mkdir -p ~/.ssh echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/deploy_key chmod 600 ~/.ssh/deploy_key - ssh-keyscan -H ${{ secrets.SERVER_HOST }} >> ~/.ssh/known_hosts + ssh-keyscan -H ${{ env.SERVER_HOST }} >> ~/.ssh/known_hosts - # 3. Dateien zum Server kopieren - name: Deploy files to server run: | rsync -avz --delete \ @@ -220,76 +206,60 @@ jobs: --exclude 'venv' \ --exclude '.env' \ --exclude 'logs' \ - ./ ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }}:${{ env.APP_DIR }}/app/ + ./ ${{ env.SERVER_USER }}@${{ env.SERVER_HOST }}:${{ env.APP_DIR }}/app/ - # 4. Dependencies installieren und Service neu starten - name: Install dependencies and restart service run: | ssh -i ~/.ssh/deploy_key -o StrictHostKeyChecking=no \ - ${{ secrets.SERVER_USER }}@${{ secrets.SERVER_HOST }} << 'ENDSSH' + ${{ env.SERVER_USER }}@${{ env.SERVER_HOST }} << 'ENDSSH' - echo "📦 Installing dependencies..." + echo "Installing dependencies..." cd /opt/ollama-webapp ./venv/bin/pip install -r app/requirements.txt --quiet --upgrade - echo "🔄 Restarting service..." + echo "Restarting service..." sudo systemctl restart ollama-webapp - echo "⏳ Waiting for service to start..." + echo "Waiting for service to start..." sleep 5 - echo "📊 Service status:" + echo "Service status:" sudo systemctl status ollama-webapp --no-pager -l - echo "✅ Deployment complete!" + echo "Deployment complete!" ENDSSH - # 5. Health Check - name: Health Check run: | - echo "🏥 Running health check..." + echo "Running health check..." sleep 3 HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \ - http://${{ secrets.SERVER_HOST }}:5000/api/health || echo "000") + http://${{ env.SERVER_HOST }}:5000/api/health || echo "000") if [ "$HTTP_STATUS" = "200" ]; then - echo "✅ Health check passed! (HTTP $HTTP_STATUS)" + echo "Health check passed! (HTTP $HTTP_STATUS)" else - echo "❌ Health check failed! (HTTP $HTTP_STATUS)" + echo "Health check failed! (HTTP $HTTP_STATUS)" exit 1 fi - # 6. Deployment Summary - name: Deployment Summary if: success() run: | - echo "🎉 Deployment successful!" + echo "Deployment successful!" echo "" - echo "📍 App URL: http://${{ secrets.SERVER_HOST }}:5000" - echo "📍 Health: http://${{ secrets.SERVER_HOST }}:5000/api/health" - echo "📍 Ollama: http://${{ secrets.SERVER_HOST }}:5000/api/ollama/status" + echo "App URL: http://${{ env.SERVER_HOST }}:5000" + echo "Health: http://${{ env.SERVER_HOST }}:5000/api/health" ``` -## B.2 GitHub Secrets einrichten - -1. Gehe zu: `https://github.com/valueonag/private-llm/settings/secrets/actions` -2. Klicke **New repository secret** -3. Erstelle diese 3 Secrets: - -| Secret Name | Wert | Beschreibung | -|-------------|------|--------------| -| `SERVER_HOST` | `185.xxx.xxx.xxx` | Deine Server-IP | -| `SERVER_USER` | `ubuntu` | SSH Benutzer | -| `SSH_PRIVATE_KEY` | `-----BEGIN OPENSSH...` | Der Private Key (ganzer Inhalt) | - --- # Teil C: Infomaniak Server Setup ## C.1 Horizon Dashboard Login -1. Öffne: https://api.pub1.infomaniak.cloud/horizon +1. Oeffne: https://api.pub1.infomaniak.cloud/horizon 2. Login-Daten: | Feld | Wert | @@ -304,7 +274,7 @@ jobs: 1. Gehe zu: **Compute → Key Pairs** 2. Klicke: **Create Key Pair** -3. Fülle aus: +3. Fuelle aus: | Feld | Wert | |------|------| @@ -312,9 +282,7 @@ jobs: | **Key Type** | `SSH Key` | 4. Klicke **Create Key Pair** -5. ⚠️ **WICHTIG:** Die `.pem` Datei wird automatisch heruntergeladen - - Speichere sie sicher ab (z.B. `~/Downloads/ollama-deploy-key.pem`) - - Du brauchst sie später für SSH-Zugang! +5. **WICHTIG:** Die `.pem` Datei wird automatisch heruntergeladen - sicher aufbewahren! --- @@ -322,12 +290,12 @@ jobs: 1. Gehe zu: **Network → Security Groups** 2. Klicke: **Create Security Group** -3. Fülle aus: +3. Fuelle aus: | Feld | Wert | |------|------| | **Name** | `ollama-webapp` | -| **Description** | `Ports für Ollama und Flask App` | +| **Description** | `Ports fuer Ollama und Flask App` | 4. Klicke **Create Security Group** 5. In der Liste: Klicke **Manage Rules** bei `ollama-webapp` @@ -341,17 +309,65 @@ jobs: | 4 | Ingress | IPv4 | TCP | 5000 | `0.0.0.0/0` | | 5 | Ingress | IPv4 | TCP | 11434 | `0.0.0.0/0` | -**Für jede Regel:** +**Fuer jede Regel:** - Klicke **Add Rule** - Direction: `Ingress` -- Wähle bei "Rule": `Custom TCP Rule` +- Waehle bei "Rule": `Custom TCP Rule` - Port: Jeweilige Portnummer eingeben - CIDR: `0.0.0.0/0` - Klicke **Add** --- -## C.4 GPU-Instanz erstellen +## C.4 Privates Netzwerk erstellen + +Da Infomaniak nur externe Netzwerke hat, muss ein privates Netzwerk erstellt werden. + +### Schritt 1: Netzwerk erstellen + +1. **Network → Networks → Create Network** + +| Tab | Feld | Wert | +|-----|------|------| +| **Network** | Network Name | `private-network` | +| | Enable Admin State | Ja | +| | Create Subnet | Ja | +| **Subnet** | Subnet Name | `private-subnet` | +| | Network Address | `192.168.100.0/24` | +| | IP Version | `IPv4` | +| | Gateway IP | `192.168.100.1` | +| **Subnet Details** | Enable DHCP | Ja | +| | DNS Name Servers | `8.8.8.8` | + +2. Klicke **Create** + +### Schritt 2: Router erstellen + +1. **Network → Routers → Create Router** + +| Feld | Wert | +|------|------| +| **Router Name** | `main-router` | +| **Enable Admin State** | Ja | +| **External Network** | `ext-floating1` | + +2. Klicke **Create Router** + +### Schritt 3: Router mit Subnet verbinden + +1. Klicke auf `main-router` +2. Gehe zu Tab: **Interfaces** +3. Klicke: **Add Interface** + +| Feld | Wert | +|------|------| +| **Subnet** | `private-subnet` | + +4. Klicke **Submit** + +--- + +## C.5 GPU-Instanz erstellen 1. Gehe zu: **Compute → Instances** 2. Klicke: **Launch Instance** @@ -372,32 +388,28 @@ jobs: | Feld | Wert | |------|------| | **Select Boot Source** | `Image` | -| **Create New Volume** | `Yes` ✓ | +| **Create New Volume** | `Yes` | | **Volume Size (GB)** | `150` | -| **Delete Volume on Instance Delete** | `No` ✗ | +| **Delete Volume on Instance Delete** | `No` | -**Image auswählen:** +**Image auswaehlen:** 1. In der unteren Liste "Available" suche: `Ubuntu 24.04 LTS Noble Numbat` -2. Klicke den **↑ Pfeil** rechts davon +2. Klicke den **Pfeil nach oben** rechts davon 3. Das Image erscheint oben unter "Allocated" → Klicke **Next** ### Tab 3: Flavor -**GPU-Flavor auswählen:** +**GPU-Flavor auswaehlen:** -In der Liste "Available" suche nach GPU-Flavors (beginnen mit `nvl4-`, `t4-`, oder `a2-`): +| Flavor | GPU | VRAM | vCPUs | RAM | +|--------|-----|------|-------|-----| +| `nvl4-a8-ram16-disk0` | L4 | 24GB | 8 | 16GB | -| Flavor | GPU | VRAM | vCPUs | RAM | Empfehlung | -|--------|-----|------|-------|-----|------------| -| `nvl4-12-46-0` | L4 | 24GB | 12 | 46GB | ✓ Beste Wahl | -| `t4-8-32-0` | T4 | 16GB | 8 | 32GB | Budget | -| `a2-8-32-0` | A2 | 16GB | 8 | 32GB | Alternative | - -1. Finde den gewünschten Flavor (z.B. mit "L4" oder "nvl4" im Namen) -2. Klicke den **↑ Pfeil** rechts davon -3. Der Flavor erscheint oben unter "Allocated" +1. Suche nach `nvl4` in der Liste +2. Waehle `nvl4-a8-ram16-disk0` (oder aehnlich) +3. Klicke den **Pfeil nach oben** → Klicke **Next** @@ -405,25 +417,26 @@ In der Liste "Available" suche nach GPU-Flavors (beginnen mit `nvl4-`, `t4-`, od | Feld | Wert | |------|------| -| **Network** | `ext-net1` | +| **Network** | `private-network` | -1. In der Liste "Available" finde: `ext-net1` -2. Klicke den **↑ Pfeil** -3. `ext-net1` erscheint unter "Allocated" +**WICHTIG:** Waehle `private-network`, NICHT `ext-net1`! + +1. In der Liste "Available" finde: `private-network` +2. Klicke den **Pfeil nach oben** → Klicke **Next** ### Tab 5: Network Ports -Überspringe diesen Tab (leer lassen). +Ueberspringe diesen Tab (leer lassen). → Klicke **Next** ### Tab 6: Security Groups -1. Falls `default` unter "Allocated" steht: Klicke den **↓ Pfeil** um es zu entfernen +1. Falls `default` unter "Allocated" steht: Klicke den **Pfeil nach unten** um es zu entfernen 2. In "Available" finde: `ollama-webapp` -3. Klicke den **↑ Pfeil** +3. Klicke den **Pfeil nach oben** 4. Unter "Allocated" sollte nur `ollama-webapp` stehen → Klicke **Next** @@ -431,127 +444,114 @@ In der Liste "Available" suche nach GPU-Flavors (beginnen mit `nvl4-`, `t4-`, od ### Tab 7: Key Pair 1. In "Available" finde: `ollama-deploy-key` -2. Klicke den **↑ Pfeil** +2. Klicke den **Pfeil nach oben** 3. Unter "Allocated" steht: `ollama-deploy-key` → Klicke **Next** -### Tab 8: Configuration (optional) +### Tab 8-11: Optional -Überspringe diesen Tab (leer lassen). - -→ Klicke **Next** - -### Tab 9: Server Groups (optional) - -Überspringe diesen Tab (leer lassen). - -→ Klicke **Next** - -### Tab 10: Scheduler Hints (optional) - -Überspringe diesen Tab (leer lassen). - -→ Klicke **Next** - -### Tab 11: Metadata (optional) - -Überspringe diesen Tab (leer lassen). - ---- +Ueberspringe diese Tabs (leer lassen). ### Instanz starten Klicke: **Launch Instance** -⏳ Warte bis der Status von `Build` zu `Active` wechselt (ca. 1-3 Minuten). +Warte bis der Status von `Build` zu `Active` wechselt (ca. 1-3 Minuten). --- -## C.5 Floating IP zuweisen - -Die Instanz braucht eine öffentliche IP-Adresse: +## C.6 Floating IP zuweisen 1. Gehe zu: **Network → Floating IPs** 2. Klicke: **Allocate IP to Project** -3. Wähle: +3. Waehle: | Feld | Wert | |------|------| -| **Pool** | `ext-net1` | -| **Description** | `IP für Ollama Server` | +| **Pool** | `ext-floating1` | +| **Description** | `IP fuer Local LLM Server` | 4. Klicke **Allocate IP** 5. In der Liste: Klicke **Associate** bei der neuen IP -6. Wähle: - -| Feld | Wert | -|------|------| -| **Port to be associated** | `local-llm` (deine Instanz) | - +6. Waehle deine Instanz: `local-llm` 7. Klicke **Associate** -📝 **Notiere dir die IP-Adresse** (z.B. `185.132.xxx.xxx`) - du brauchst sie für: -- SSH-Zugang -- GitHub Secrets -- Browser-Zugriff auf die App +**Notiere die IP-Adresse** (z.B. `83.228.200.109`) --- -## C.6 Zusammenfassung deiner Instanz - -Nach erfolgreichem Setup hast du: +## C.7 Zusammenfassung Instanz | Komponente | Wert | |------------|------| | **Instance Name** | `local-llm` | | **Description** | `Local LLM Server` | | **Image** | Ubuntu 24.04 LTS | -| **Flavor** | GPU mit L4/T4/A2 | +| **Flavor** | `nvl4-a8-ram16-disk0` (NVIDIA L4) | | **Disk** | 150 GB | -| **Network** | `ext-net1` | +| **Network** | `private-network` | | **Security Group** | `ollama-webapp` | | **Key Pair** | `ollama-deploy-key` | -| **Floating IP** | `185.xxx.xxx.xxx` | +| **Floating IP** | `83.228.200.109` | -## C.2 Server Basis-Setup +--- -SSH zum Server: +# Teil D: Server Einrichtung -```bash -ssh -i ~/Downloads/ollama-deploy-key.pem ubuntu@185.xxx.xxx.xxx +## D.1 SSH-Verbindung + +**Windows PowerShell:** + +```powershell +ssh -i "C:\Users\pmots\Downloads\ollama-deploy-key.pem" ubuntu@83.228.200.109 ``` -### System aktualisieren +**Mac/Linux:** ```bash -sudo apt update && sudo apt upgrade -y +chmod 400 ~/Downloads/ollama-deploy-key.pem +ssh -i ~/Downloads/ollama-deploy-key.pem ubuntu@83.228.200.109 ``` -### NVIDIA-Treiber installieren +--- + +## D.2 System vorbereiten ```bash -sudo apt install -y nvidia-driver-550 -sudo reboot +# Falls dpkg Fehler auftreten +sudo dpkg --configure -a + +# System aktualisieren +sudo apt update +sudo apt upgrade -y ``` -Nach Neustart wieder verbinden und prüfen: +--- + +## D.3 GPU pruefen ```bash nvidia-smi ``` -### Ollama installieren +Sollte NVIDIA L4 mit 24GB VRAM zeigen (Treiber sind vorinstalliert). + +--- + +## D.4 Ollama installieren ```bash -# Installieren curl -fsSL https://ollama.com/install.sh | sh +``` -# Konfigurieren +### Ollama fuer Netzwerkzugriff konfigurieren + +```bash sudo systemctl edit ollama ``` -Füge ein: +Fuege ein (zwischen den Kommentaren): ```ini [Service] @@ -561,24 +561,41 @@ Environment="OLLAMA_NUM_PARALLEL=4" Environment="OLLAMA_MAX_LOADED_MODELS=2" ``` +Speichern: `Ctrl+X`, dann `Y`, dann `Enter` + ```bash sudo systemctl restart ollama sudo systemctl enable ollama ``` -### Modelle herunterladen +--- + +## D.5 Modelle herunterladen ```bash +# Fuer Dokumente (Rechnungen, Belege) ollama pull granite3.2-vision + +# Fuer Handschrift ollama pull qwen2.5vl:7b + +# OCR-Spezialist ollama pull deepseek-ocr ``` -## C.3 Python-Umgebung vorbereiten +### Modelle pruefen + +```bash +ollama list +``` + +--- + +## D.6 Python-Umgebung einrichten ```bash # Pakete installieren -sudo apt install -y python3-pip python3-venv git +sudo apt install -y python3-pip python3-venv python3.12-venv git # App-Verzeichnis erstellen sudo mkdir -p /opt/ollama-webapp/{app,venv,logs} @@ -592,28 +609,9 @@ python3 -m venv /opt/ollama-webapp/venv /opt/ollama-webapp/venv/bin/pip install flask flask-cors requests pymupdf gunicorn ``` -## C.4 Deploy SSH-Key erstellen (für GitHub Actions) +--- -```bash -# Key erstellen -ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/github_deploy_key -N "" - -# Zu authorized_keys hinzufügen -cat ~/.ssh/github_deploy_key.pub >> ~/.ssh/authorized_keys - -# Private Key anzeigen - DIESEN IN GITHUB SECRETS KOPIEREN! -echo "" -echo "==========================================" -echo "DIESEN KEY ALS 'SSH_PRIVATE_KEY' IN GITHUB SPEICHERN:" -echo "==========================================" -cat ~/.ssh/github_deploy_key -echo "" -echo "==========================================" -``` - -**Kopiere den kompletten Private Key** (inkl. `-----BEGIN...` und `-----END...`) und speichere ihn als GitHub Secret `SSH_PRIVATE_KEY`. - -## C.5 Systemd Service erstellen +## D.7 Systemd Service erstellen ```bash sudo nano /etc/systemd/system/ollama-webapp.service @@ -648,20 +646,22 @@ RestartSec=5 WantedBy=multi-user.target ``` -Aktivieren: +Speichern: `Ctrl+X`, dann `Y`, dann `Enter` ```bash sudo systemctl daemon-reload sudo systemctl enable ollama-webapp ``` -## C.6 Sudo-Rechte für GitHub Actions +--- + +## D.8 Sudo-Rechte fuer GitHub Actions ```bash sudo visudo ``` -Füge am Ende hinzu: +Fuege am Ende hinzu: ``` ubuntu ALL=(ALL) NOPASSWD: /bin/systemctl restart ollama-webapp @@ -670,67 +670,19 @@ ubuntu ALL=(ALL) NOPASSWD: /bin/systemctl stop ollama-webapp ubuntu ALL=(ALL) NOPASSWD: /bin/systemctl start ollama-webapp ``` -## C.7 Templates-Ordner erstellen - -Falls deine Flask-App Templates verwendet: - -```bash -mkdir -p /opt/ollama-webapp/app/templates -mkdir -p /opt/ollama-webapp/app/static -``` - ---- - -# Teil D: Erster Commit und Deploy - -## D.1 In Cursor: Code committen - -Öffne Terminal in Cursor (im `private-llm` Ordner): - -```bash -# Status prüfen -git status - -# Alle Dateien hinzufügen -git add . - -# Commit erstellen -git commit -m "Initial setup: Flask app with CI/CD" - -# Zu GitHub pushen -git push origin main -``` - -## D.2 GitHub Actions beobachten - -1. Gehe zu: `https://github.com/valueonag/private-llm/actions` -2. Du solltest einen laufenden Workflow sehen -3. Klicke drauf um den Fortschritt zu sehen - -## D.3 App testen - -Nach erfolgreichem Deploy: - -```bash -# Health Check -curl http://185.xxx.xxx.xxx:5000/api/health - -# Ollama Status -curl http://185.xxx.xxx.xxx:5000/api/ollama/status - -# Im Browser öffnen -open http://185.xxx.xxx.xxx:5000 -``` +Speichern: `Ctrl+X`, dann `Y`, dann `Enter` --- # Teil E: Entwicklungs-Workflow -## E.1 Lokale Entwicklung in Cursor +## E.1 Lokale Entwicklung ```bash -# Virtual Environment erstellen (einmalig) +# In Cursor: Terminal oeffnen cd ~/Projects/private-llm + +# Virtual Environment (einmalig) python3 -m venv venv source venv/bin/activate # Mac/Linux # oder: .\venv\Scripts\activate # Windows @@ -738,156 +690,177 @@ source venv/bin/activate # Mac/Linux # Dependencies installieren pip install -r requirements.txt -# Lokal starten (mit lokalem Ollama) +# Lokal starten python app.py ``` -## E.2 Änderungen deployen +## E.2 Deployment (automatisch) ```bash -# Änderungen speichern +# Aenderungen speichern git add . - -# Commit mit Beschreibung -git commit -m "Feature: Neue Funktion XYZ" - -# Push zu GitHub → Automatischer Deploy! +git commit -m "Beschreibung der Aenderung" git push origin main + +# GitHub Actions deployed automatisch! ``` -## E.3 Cursor Git-Integration +## E.3 Deployment Status pruefen -In Cursor kannst du auch die GUI nutzen: - -1. **Source Control** Tab (linke Sidebar) -2. Änderungen sehen -3. **+** um Dateien zu stagen -4. Commit Message eingeben -5. **✓** zum Committen -6. **...** → **Push** zum Pushen +1. Gehe zu: https://github.com/valueonag/private-llm/actions +2. Klicke auf den neuesten Workflow Run +3. Gruen = Erfolgreich, Rot = Fehler --- -# Teil F: Nützliche Befehle +# Teil F: Server-Befehle -## Server-Befehle +## SSH Verbindung ```bash -# SSH zum Server -ssh -i ~/Downloads/ollama-deploy-key.pem ubuntu@185.xxx.xxx.xxx +ssh -i "C:\Users\pmots\Downloads\ollama-deploy-key.pem" ubuntu@83.228.200.109 +``` -# Service Status +## Service-Verwaltung + +```bash +# Status pruefen sudo systemctl status ollama-webapp sudo systemctl status ollama +# Neu starten +sudo systemctl restart ollama-webapp +sudo systemctl restart ollama + # Logs anschauen tail -f /opt/ollama-webapp/logs/access.log tail -f /opt/ollama-webapp/logs/error.log sudo journalctl -u ollama-webapp -f - -# Service neu starten -sudo systemctl restart ollama-webapp - -# GPU Status -nvidia-smi - -# Ollama Modelle -ollama list -ollama pull +sudo journalctl -u ollama -f ``` -## Git-Befehle +## GPU Status ```bash -# Status -git status +nvidia-smi +``` -# Änderungen sehen -git diff +## Ollama Modelle -# Commit und Push -git add . -git commit -m "Beschreibung" -git push origin main +```bash +# Liste +ollama list -# Vom Server holen (falls jemand anders gepusht hat) -git pull origin main +# Neues Modell hinzufuegen +ollama pull + +# Modell entfernen +ollama rm + +# Modell testen +ollama run granite3.2-vision "Beschreibe dieses Bild" ``` --- # Teil G: Troubleshooting -## Deploy schlägt fehl - -1. **GitHub Actions Tab prüfen** - Fehlermeldung lesen -2. **SSH testen:** - ```bash - ssh -i ~/.ssh/github_deploy_key ubuntu@185.xxx.xxx.xxx - ``` -3. **Secrets prüfen** - Sind alle 3 Secrets korrekt? - -## App startet nicht +## App nicht erreichbar ```bash -# Auf dem Server: -sudo systemctl status ollama-webapp -l -cat /opt/ollama-webapp/logs/error.log +# Service Status pruefen +sudo systemctl status ollama-webapp + +# Logs pruefen +tail -50 /opt/ollama-webapp/logs/error.log + +# Port pruefen +sudo netstat -tlnp | grep 5000 ``` ## Ollama nicht erreichbar ```bash -# Status prüfen +# Status pruefen sudo systemctl status ollama # Neu starten sudo systemctl restart ollama -# Logs +# Logs pruefen sudo journalctl -u ollama -f ``` +## GitHub Actions fehlgeschlagen + +1. Gehe zu: https://github.com/valueonag/private-llm/actions +2. Klicke auf den fehlgeschlagenen Run +3. Klicke auf den fehlgeschlagenen Step +4. Lies die Fehlermeldung + +**Haeufige Probleme:** +- SSH Key falsch: Secret `SSH_PRIVATE_KEY` pruefen +- Server nicht erreichbar: Floating IP und Security Group pruefen +- Syntax-Fehler in Code: Lokal testen vor Push + +## GPU nicht erkannt + +```bash +# Treiber pruefen +nvidia-smi + +# Falls nicht vorhanden +sudo apt install -y nvidia-driver-550 +sudo reboot +``` + --- # Checkliste -## GitHub Setup -- [ ] Repository geklont -- [ ] Cursor mit Repo verbunden -- [ ] `requirements.txt` erstellt -- [ ] `.gitignore` erstellt -- [ ] `.github/workflows/deploy.yml` erstellt -- [ ] GitHub Secrets konfiguriert: - - [ ] `SERVER_HOST` - - [ ] `SERVER_USER` - - [ ] `SSH_PRIVATE_KEY` +## Einmalige Einrichtung -## Server Setup -- [ ] GPU-Instanz erstellt -- [ ] Floating IP zugewiesen -- [ ] NVIDIA-Treiber installiert -- [ ] Ollama installiert und konfiguriert -- [ ] Modelle heruntergeladen -- [ ] Python venv erstellt -- [ ] Deploy SSH-Key erstellt -- [ ] Systemd Service erstellt -- [ ] Sudo-Rechte konfiguriert +- [x] Infomaniak Public Cloud Account +- [x] GPU-Quota aktiviert +- [x] SSH Key Pair erstellt (`ollama-deploy-key`) +- [x] Security Group erstellt (`ollama-webapp`) +- [x] Privates Netzwerk erstellt (`private-network`) +- [x] Router erstellt (`main-router`) +- [x] GPU-Instanz erstellt (`local-llm`) +- [x] Floating IP zugewiesen (`83.228.200.109`) +- [x] Ollama installiert und konfiguriert +- [x] Modelle heruntergeladen +- [x] Python-Umgebung eingerichtet +- [x] Systemd Service erstellt +- [x] GitHub Secret konfiguriert (`SSH_PRIVATE_KEY`) +- [x] GitHub Actions Workflow erstellt -## Erster Deploy -- [ ] Code committed und gepusht -- [ ] GitHub Actions erfolgreich -- [ ] Health Check funktioniert -- [ ] App im Browser erreichbar +## Bei jedem Deploy + +- [ ] Code aendern +- [ ] `git add .` +- [ ] `git commit -m "Beschreibung"` +- [ ] `git push origin main` +- [ ] GitHub Actions pruefen +- [ ] App testen --- -# URLs +# Kosten -| Service | URL | -|---------|-----| -| App | `http://DEINE-IP:5000` | -| Health Check | `http://DEINE-IP:5000/api/health` | -| Ollama Status | `http://DEINE-IP:5000/api/ollama/status` | -| GitHub Repo | `https://github.com/valueonag/private-llm` | -| GitHub Actions | `https://github.com/valueonag/private-llm/actions` | \ No newline at end of file +## Infomaniak Public Cloud (geschaetzt) + +| Komponente | Preis ca. | +|------------|-----------| +| GPU L4 Instanz (24/7) | ~CHF 580/Monat | +| GPU L4 Instanz (8h/Tag, Mo-Fr) | ~CHF 140/Monat | +| Block Storage 150GB | ~CHF 15/Monat | +| Floating IP | ~CHF 3/Monat | + +**Tipp:** Instanz stoppen wenn nicht benoetigt! + +```bash +# Im Horizon Dashboard: Compute → Instances → Shut Off Instance +# Oder per CLI +openstack server stop local-llm +``` \ No newline at end of file diff --git a/templates/index.html b/templates/index.html index 2abfbcd..fbeacf8 100644 --- a/templates/index.html +++ b/templates/index.html @@ -630,14 +630,17 @@
-
-
diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..aae67ac --- /dev/null +++ b/templates/login.html @@ -0,0 +1,224 @@ + + + + + + Login – Belegscanner + + + + + + +