diff --git a/.env.example b/.env.example index 585e4c7..1166c65 100644 --- a/.env.example +++ b/.env.example @@ -65,8 +65,9 @@ USER_TEMPLATE_IMAGE=user-template-01:latest # Beispiele für Anpassungen: # - Nur Nginx: "user-template-01:latest" # - Mit Next.js: "user-template-01:latest;user-template-next:latest" -# - Alle: "user-template-01:latest;user-template-02:latest;user-template-next:latest" -USER_TEMPLATE_IMAGES="user-template-01:latest;user-template-02:latest;user-template-next:latest" +# - Mit Wörterbuch: "user-template-01:latest;user-template-dictionary:latest" +# - Alle: "user-template-01:latest;user-template-02:latest;user-template-next:latest;user-template-dictionary:latest" +USER_TEMPLATE_IMAGES="user-template-01:latest;user-template-02:latest;user-template-next:latest;user-template-dictionary:latest" # ============================================================ # RESSOURCEN - Container-Limits diff --git a/docs/templates/DICTIONARY_TEMPLATE.md b/docs/templates/DICTIONARY_TEMPLATE.md new file mode 100644 index 0000000..934c3ad --- /dev/null +++ b/docs/templates/DICTIONARY_TEMPLATE.md @@ -0,0 +1,589 @@ +# 📚 Wörterbuch Template - Vollständige Dokumentation + +## Übersicht + +Das **Wörterbuch Template** (`user-template-dictionary`) ist eine Flask-basierte Web-Anwendung, die es Benutzern ermöglicht, persönliche Wörterbuch-Einträge zu speichern und zu verwalten. Jeder Eintrag besteht aus einem **Wort** und seiner **Bedeutung**, die in einer **SQLite-Datenbank** persistiert werden. + +### Features +- ✅ **Persönliche Wörterbuch-Datenbank** pro Benutzer +- ✅ **SQLite-Persistierung** - Daten bleiben erhalten nach Container-Neustart +- ✅ **REST API** für Verwaltung (GET, POST, PUT, DELETE) +- ✅ **Moderne HTML/CSS/JS Frontend** mit Fehlerbehandlung +- ✅ **Health Checks** für Monitoring +- ✅ **Vollständige Fehlerbehandlung** und Logging +- ✅ **Docker Volume Support** für Datenpersistierung + +--- + +## Architektur + +### High-Level Diagramm + +``` +Browser Request + ↓ +Flask Backend (Port 8080) + ↓ +SQLite Database (/data/app.db) + ↓ +Docker Volume (/volumes/{user-id}) + ↓ +Persistente Speicherung +``` + +### Komponenten + +**Frontend (HTML/CSS/JavaScript):** +- `templates/index.html` - Single Page Application mit React-ähnlichem State Management +- Responsive Design (Mobile-freundlich) +- Real-time UI Updates +- Benutzerfreundliche Fehlerbehandlung + +**Backend (Flask + SQLite):** +- `app.py` - Python Flask Anwendung +- SQLite Datenbank in `/data/app.db` +- REST API Endpoints +- Logging und Health Checks + +**Containerisierung:** +- `Dockerfile` - Python 3.11 slim Image +- `requirements.txt` - Python Dependencies (Flask, Werkzeug) +- Unprivileged User (Port 8080) +- Health Check Endpoint + +--- + +## Installation & Setup + +### Schritt 1: Template in `.env` registrieren + +Bearbeite `.env` und füge das Dictionary Template hinzu: + +```bash +# .env +USER_TEMPLATE_IMAGES="user-template-01:latest;user-template-02:latest;user-template-next:latest;user-template-dictionary:latest" +``` + +**Wichtig:** Nur hier definierte Templates werden von `bash install.sh` gebaut! + +### Schritt 2: Metadaten in `templates.json` aktualisieren + +Das Template ist bereits in `templates.json` registriert: + +```json +{ + "type": "dictionary", + "image": "user-template-dictionary:latest", + "display_name": "📚 Wörterbuch", + "description": "Persönliches Wörterbuch mit Datenbank - Speichern Sie Wörter und Bedeutungen" +} +``` + +### Schritt 3: Build & Deploy + +```bash +# Alle Templates bauen (inkl. dictionary) +bash install.sh + +# Docker Compose neu starten +docker-compose up -d --build +``` + +--- + +## REST API Referenz + +### Base URL +``` +http://localhost:8080 +``` + +### Endpoints + +#### 1. Frontend abrufen +```http +GET / +``` +**Response:** HTML-Seite mit Interface + +--- + +#### 2. Alle Wörter abrufen +```http +GET /api/words +``` + +**Response (200 OK):** +```json +{ + "words": [ + { + "id": 1, + "word": "Serendipität", + "meaning": "Das glückliche Finden von etwas Ungesucht", + "created_at": "2026-03-18T10:30:45" + }, + { + "id": 2, + "word": "Wanderlust", + "meaning": "Starkes Verlangen zu reisen und die Welt zu erkunden", + "created_at": "2026-03-18T11:15:20" + } + ], + "count": 2 +} +``` + +--- + +#### 3. Neues Wort hinzufügen +```http +POST /api/words +Content-Type: application/json + +{ + "word": "Schadenfreude", + "meaning": "Freude über das Unglück anderer" +} +``` + +**Response (201 Created):** +```json +{ + "id": 3, + "word": "Schadenfreude", + "meaning": "Freude über das Unglück anderer", + "created_at": "2026-03-18T12:00:00" +} +``` + +**Error Response (409 Conflict):** +```json +{ + "error": "Das Wort \"Schadenfreude\" existiert bereits" +} +``` + +--- + +#### 4. Wort aktualisieren +```http +PUT /api/words/{id} +Content-Type: application/json + +{ + "word": "Schadenfreude", + "meaning": "Böse Freude über Missgeschick eines anderen" +} +``` + +**Response (200 OK):** +```json +{ + "id": 3, + "word": "Schadenfreude", + "meaning": "Böse Freude über Missgeschick eines anderen", + "created_at": "2026-03-18T12:00:00" +} +``` + +--- + +#### 5. Wort löschen +```http +DELETE /api/words/{id} +``` + +**Response (204 No Content):** +(Leerer Body, nur Status 204) + +--- + +#### 6. Statistiken abrufen +```http +GET /api/stats +``` + +**Response (200 OK):** +```json +{ + "total_words": 2, + "last_added": "2026-03-18T11:15:20", + "database": "sqlite3", + "storage": "/data/app.db" +} +``` + +--- + +#### 7. Health Check +```http +GET /health +``` + +**Response (200 OK):** +```json +{ + "status": "ok", + "database": "connected" +} +``` + +**Error Response (500):** +```json +{ + "status": "error", + "message": "database connection failed" +} +``` + +--- + +## Datapersistierung + +### Docker Volumes + +Die Datenbank wird in einem **Docker Volume** gespeichert, damit Daten bei Container-Neustarts erhalten bleiben. + +### Automatische Konfiguration (via `container_manager.py`) + +Das Spawner Backend sollte automatisch Volumes mounten: + +```python +volumes = { + f"/volumes/{user_id}": { + "bind": "/data", + "mode": "rw" + } +} +``` + +**Ergebnis:** +- Jeder User erhält ein eigenes Verzeichnis `/volumes/{user_id}` +- Die SQLite DB wird gespeichert in `/volumes/{user_id}/app.db` +- Der Container sieht dies als `/data/app.db` +- Beim Container-Restart bleiben Daten erhalten + +### Manuelles Testen + +```bash +# Container starten +docker run -v /volumes/user-123:/data -p 8080:8080 user-template-dictionary:latest + +# Datenbank inspizieren +sqlite3 /volumes/user-123/app.db "SELECT * FROM words;" + +# Container stoppen und neustart +docker stop +docker start + +# Daten sollten noch da sein! +sqlite3 /volumes/user-123/app.db "SELECT * FROM words;" +``` + +--- + +## Datenbankschema + +### Tabelle: `words` + +```sql +CREATE TABLE words ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + word TEXT NOT NULL UNIQUE, + meaning TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) +``` + +**Spalten:** +- `id` - Eindeutige ID (Auto-Increment) +- `word` - Das Wort (UNIQUE - keine Duplikate!) +- `meaning` - Bedeutung/Definition +- `created_at` - Erstellungsdatum +- `updated_at` - Letztes Update + +**Constraints:** +- `word` ist UNIQUE - kann nicht doppelt vorkommen +- Maximale Länge: 255 Zeichen für Wort, 2000 für Bedeutung + +--- + +## Sicherheit + +### Input Validation + +- ✅ Wort und Bedeutung sind erforderlich +- ✅ Maximale Längen: 255 Zeichen (Wort), 2000 Zeichen (Bedeutung) +- ✅ HTML-Escaping in Frontend (XSS-Schutz) +- ✅ SQL-Injection-Schutz via Prepared Statements + +### Error Handling + +- ✅ Konsistente JSON-Error-Responses +- ✅ Aussagekräftige Error-Messages +- ✅ HTTP Status Codes korrekt gesetzt +- ✅ Logging aller Fehler + +### Docker Security + +- ✅ Unprivileged User (nicht Root) +- ✅ Port 8080 (nicht privilegierter Port) +- ✅ SQLite für Single-User (keine Multi-Client Issues) + +--- + +## Monitoring & Debugging + +### Logs anschauen + +```bash +# Live Logs des Containers +docker logs -f user-dictionary-abc123 + +# Beispiel Log Output: +# [DICTIONARY] Database path: /data/app.db +# [DICTIONARY] Table 'words' already exists +# [DICTIONARY] Retrieved 2 words +# [DICTIONARY] Word added: 'Serendipität' +``` + +### Health Check + +```bash +# In Chrome DevTools oder terminal: +curl http://localhost:8080/health + +# Antwort: +# {"status": "ok", "database": "connected"} +``` + +### Database Debugging + +```bash +# Mit der Python Shell in den Container gehen +docker exec -it python + +# Dann: +import sqlite3 +conn = sqlite3.connect('/data/app.db') +cursor = conn.cursor() +cursor.execute('SELECT * FROM words') +print(cursor.fetchall()) +``` + +### Statistiken + +```bash +curl http://localhost:8080/api/stats + +# Antwort: +# {"total_words": 5, "last_added": "2026-03-18T...", "database": "sqlite3", "storage": "/data/app.db"} +``` + +--- + +## Performance & Limits + +### Empfehlungen + +- **SQLite Limit:** ~1 Million Zeilen problemlos +- **Typical Use:** Bis zu 10.000 Wörter problemlos +- **Query Time:** < 10ms für typische Abfragen +- **Database Size:** ~1KB pro Wort + +### Resource Limits (via `.env`) + +```bash +# In .env definieren: +DEFAULT_MEMORY_LIMIT=512m # RAM pro Container +DEFAULT_CPU_QUOTA=50000 # 0.5 CPU +``` + +### Skalierung + +Falls die Anwendung wächst: +1. **PostgreSQL verwenden** statt SQLite (für Multi-User) +2. **Redis Caching** für häufige Abfragen +3. **Elasticsearch** für Full-Text Suche in Bedeutungen + +--- + +## Troubleshooting + +### Problem: "Datenbankfehler beim Abrufen" + +``` +Mögliche Ursachen: +1. Volume nicht gemountet +2. /data Verzeichnis nicht vorhanden +3. Datenbank-Datei korrupt +4. Permissionen falsch + +Lösung: +docker exec container-id ls -la /data +docker exec container-id sqlite3 /data/app.db "SELECT 1" +``` + +### Problem: "Wort existiert bereits" beim Hinzufügen + +``` +Das ist normal! Die Anwendung verhindert Duplikate. +Wörter sind eindeutig (UNIQUE Constraint). + +Wenn das Problem ist: Bearbeiten statt Hinzufügen nutzen. +Oder: Wort löschen und neu hinzufügen. +``` + +### Problem: Daten nach Restart weg + +``` +Ursache: Volume nicht korrekt gemountet. + +Prüfen: +docker inspect | grep -A10 Mounts + +Sollte zeigen: +"Mounts": [ + { + "Type": "bind", + "Source": "/volumes/user-123", + "Destination": "/data" + } +] +``` + +### Problem: Container startet nicht + +```bash +# Logs prüfen +docker logs 2>&1 | tail -50 + +# Python Syntax prüfen +docker build -t test . --no-cache + +# Dockerfile validieren +docker run --rm -it user-template-dictionary:latest python app.py +``` + +--- + +## Wartung & Updates + +### Backup der Datenbank + +```bash +# Einzelnen User-Backup +tar -czf backup-user-123.tar.gz /volumes/user-123/ + +# Alle User-Datenbanken +tar -czf backup-all-users.tar.gz /volumes/ +``` + +### Datenbank Upgrade + +Falls die Tabelle erweitert werden soll: + +```python +# In app.py - init_db() Methode: +cursor.execute(''' + ALTER TABLE words ADD COLUMN + category TEXT DEFAULT 'general' +''') +``` + +### Logs archivieren + +```bash +# Logs rotieren +docker logs --timestamps user-dictionary-abc > logs.txt +``` + +--- + +## Integration mit Spawner + +### Automatischer Container-Spawn + +Wenn ein Benutzer dieses Template wählt: + +1. Spawner erstellt Container mit Template `user-template-dictionary:latest` +2. Mountet Volume: `/volumes/{user-id}:/data` +3. Traefik routet Request zu Container unter `https://coder.domain.com/{user-slug}` +4. Benutzer sieht Wörterbuch-Interface +5. Datenbank wird in `/volumes/{user-id}/app.db` erstellt +6. Bei nächstem Login: Gleicher Container + Gleiche Datenbank = Gleiche Wörter! + +### Admin-Dashboard Integration + +Im Admin-Dashboard können Admins: +- ✅ Container starten/stoppen +- ✅ Container löschen (löscht auch Datenbank!) +- ✅ Logs ansehen +- ✅ Container-Status prüfen + +--- + +## Weitere Verbesserungen (Optional) + +### Mögliche Features für Zukunft + +1. **Kategorien** - Wörter in Kategorien organisieren +2. **Export/Import** - CSV/JSON Download +3. **Suche** - Volltext-Suche in Wörtern/Bedeutungen +4. **Tags** - Flexible Kategorisierung +5. **Statistiken** - Graphen, Lernfortschritt +6. **Multi-Language** - Übersetzungen hinzufügen +7. **Phonetik** - Audio-Aussprache +8. **Spaced Repetition** - Lern-Algorithmus + +--- + +## Datenschutz & DSGVO + +- ✅ Daten werden lokal in Containern gespeichert +- ✅ Keine Daten an Dritte übertragen +- ✅ Benutzer hat vollständige Kontrolle +- ✅ Einfaches Löschen möglich (Container löschen) + +--- + +## Support & Issues + +Bei Problemen: + +1. Logs prüfen: `docker logs container-id` +2. Health Check testen: `curl http://localhost:8080/health` +3. Datenbank prüfen: `sqlite3 /data/app.db ".tables"` +4. API testen: `curl http://localhost:8080/api/words` + +--- + +## Version & Changelog + +**Version:** 1.0.0 (2026-03-18) + +### Features +- ✅ Wort hinzufügen/löschen/bearbeiten +- ✅ SQLite Persistierung +- ✅ REST API +- ✅ Modern HTML/CSS/JS Frontend +- ✅ Health Checks +- ✅ Fehlerbehandlung + +--- + +## Lizenz & Attribution + +**Template:** Container Spawner +**Autor:** Rainer Wieland +**Lizenz:** MIT oder ähnlich + +--- + +## Letzte Aktualisierung + +- **Datum:** 2026-03-18 +- **Version:** 1.0.0 +- **Status:** Production Ready ✅ diff --git a/templates.json b/templates.json index 2c4e567..d71f079 100644 --- a/templates.json +++ b/templates.json @@ -17,6 +17,12 @@ "image": "user-template-next:latest", "display_name": "Next.js Production", "description": "React-Anwendung mit Shadcn/UI, TypeScript und modernem Build-Setup" + }, + { + "type": "dictionary", + "image": "user-template-dictionary:latest", + "display_name": "📚 Wörterbuch", + "description": "Persönliches Wörterbuch mit Datenbank - Speichern Sie Wörter und Bedeutungen" } ] } diff --git a/user-template-dictionary/Dockerfile b/user-template-dictionary/Dockerfile new file mode 100644 index 0000000..85bf872 --- /dev/null +++ b/user-template-dictionary/Dockerfile @@ -0,0 +1,24 @@ +FROM python:3.11-slim + +WORKDIR /app + +# Abhängigkeiten installieren +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# App-Code +COPY app.py . +COPY templates/ templates/ + +# Daten-Verzeichnis für SQLite Datenbank (wird als Volume gemountet) +RUN mkdir -p /data && chmod 755 /data + +# Port 8080 exponieren (unprivileged) +EXPOSE 8080 + +# Health Check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD python -c "import requests; requests.get('http://localhost:8080/health')" || exit 1 + +# Starten +CMD ["python", "app.py"] diff --git a/user-template-dictionary/app.py b/user-template-dictionary/app.py new file mode 100644 index 0000000..0048868 --- /dev/null +++ b/user-template-dictionary/app.py @@ -0,0 +1,267 @@ +""" +Persönliches Wörterbuch - Flask Backend mit SQLite +Speichert Wörter und Bedeutungen in einer persistenten Datenbank pro Benutzer +""" + +from flask import Flask, render_template, request, jsonify +from datetime import datetime +import sqlite3 +import os +import logging + +app = Flask(__name__) + +# Logging konfigurieren +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +# Pfad zur persistenten Datenbank (wird als Docker Volume gemountet) +DB_PATH = "/data/app.db" +os.makedirs("/data", exist_ok=True) + +logger.info(f"[DICTIONARY] Database path: {DB_PATH}") + + +def get_db(): + """Verbindung zur SQLite-Datenbank""" + conn = sqlite3.connect(DB_PATH) + conn.row_factory = sqlite3.Row + return conn + + +def init_db(): + """Erstelle Tabelle beim Start (falls noch nicht vorhanden)""" + conn = get_db() + cursor = conn.cursor() + + # Prüfe ob Tabelle bereits existiert + cursor.execute(""" + SELECT name FROM sqlite_master + WHERE type='table' AND name='words' + """) + + if not cursor.fetchone(): + logger.info("[DICTIONARY] Creating 'words' table...") + cursor.execute(''' + CREATE TABLE words ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + word TEXT NOT NULL UNIQUE, + meaning TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + ''') + conn.commit() + logger.info("[DICTIONARY] Table 'words' created successfully") + else: + logger.info("[DICTIONARY] Table 'words' already exists") + + conn.close() + + +@app.route('/') +def index(): + """Hauptseite mit HTML-Interface""" + return render_template('index.html') + + +@app.route('/health') +def health(): + """Health Check Endpoint für Docker""" + try: + conn = get_db() + cursor = conn.cursor() + cursor.execute("SELECT 1") + conn.close() + return {'status': 'ok', 'database': 'connected'}, 200 + except Exception as e: + logger.error(f"[DICTIONARY] Health check failed: {e}") + return {'status': 'error', 'message': str(e)}, 500 + + +@app.route('/api/words', methods=['GET']) +def get_words(): + """Alle gespeicherten Wörter abrufen""" + try: + conn = get_db() + cursor = conn.cursor() + cursor.execute('SELECT id, word, meaning, created_at FROM words ORDER BY created_at DESC') + words = [dict(row) for row in cursor.fetchall()] + conn.close() + + logger.info(f"[DICTIONARY] Retrieved {len(words)} words") + return jsonify({ + 'words': words, + 'count': len(words) + }), 200 + except Exception as e: + logger.error(f"[DICTIONARY] Error retrieving words: {e}") + return jsonify({'error': 'Fehler beim Abrufen der Wörter'}), 500 + + +@app.route('/api/words', methods=['POST']) +def add_word(): + """Neues Wort hinzufügen""" + try: + data = request.get_json() + + if not data: + return jsonify({'error': 'Keine Daten empfangen'}), 400 + + word = data.get('word', '').strip() + meaning = data.get('meaning', '').strip() + + # Validierung + if not word or not meaning: + return jsonify({'error': 'Wort und Bedeutung sind erforderlich'}), 400 + + if len(word) > 255: + return jsonify({'error': 'Wort ist zu lang (max. 255 Zeichen)'}), 400 + + if len(meaning) > 2000: + return jsonify({'error': 'Bedeutung ist zu lang (max. 2000 Zeichen)'}), 400 + + conn = get_db() + cursor = conn.cursor() + + try: + cursor.execute( + 'INSERT INTO words (word, meaning) VALUES (?, ?)', + (word, meaning) + ) + conn.commit() + word_id = cursor.lastrowid + + # Neuen Eintrag zurück + cursor.execute('SELECT id, word, meaning, created_at FROM words WHERE id = ?', (word_id,)) + new_word = dict(cursor.fetchone()) + conn.close() + + logger.info(f"[DICTIONARY] Word added: '{word}'") + return jsonify(new_word), 201 + + except sqlite3.IntegrityError: + conn.close() + logger.warning(f"[DICTIONARY] Duplicate word: '{word}'") + return jsonify({'error': f'Das Wort "{word}" existiert bereits'}), 409 + + except Exception as e: + logger.error(f"[DICTIONARY] Error adding word: {e}") + return jsonify({'error': 'Fehler beim Speichern des Wortes'}), 500 + + +@app.route('/api/words/', methods=['PUT']) +def update_word(word_id): + """Wort aktualisieren""" + try: + data = request.get_json() + + if not data: + return jsonify({'error': 'Keine Daten empfangen'}), 400 + + word = data.get('word', '').strip() + meaning = data.get('meaning', '').strip() + + # Validierung + if not word or not meaning: + return jsonify({'error': 'Wort und Bedeutung sind erforderlich'}), 400 + + conn = get_db() + cursor = conn.cursor() + + try: + cursor.execute( + 'UPDATE words SET word = ?, meaning = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?', + (word, meaning, word_id) + ) + conn.commit() + + if cursor.rowcount == 0: + conn.close() + return jsonify({'error': 'Wort nicht gefunden'}), 404 + + # Aktualisiertes Wort zurück + cursor.execute('SELECT id, word, meaning, created_at FROM words WHERE id = ?', (word_id,)) + updated_word = dict(cursor.fetchone()) + conn.close() + + logger.info(f"[DICTIONARY] Word updated: ID {word_id}") + return jsonify(updated_word), 200 + + except sqlite3.IntegrityError: + conn.close() + logger.warning(f"[DICTIONARY] Duplicate word on update: '{word}'") + return jsonify({'error': f'Das Wort "{word}" existiert bereits'}), 409 + + except Exception as e: + logger.error(f"[DICTIONARY] Error updating word: {e}") + return jsonify({'error': 'Fehler beim Aktualisieren des Wortes'}), 500 + + +@app.route('/api/words/', methods=['DELETE']) +def delete_word(word_id): + """Wort löschen""" + try: + conn = get_db() + cursor = conn.cursor() + cursor.execute('DELETE FROM words WHERE id = ?', (word_id,)) + conn.commit() + + if cursor.rowcount == 0: + conn.close() + return jsonify({'error': 'Wort nicht gefunden'}), 404 + + conn.close() + logger.info(f"[DICTIONARY] Word deleted: ID {word_id}") + return '', 204 + + except Exception as e: + logger.error(f"[DICTIONARY] Error deleting word: {e}") + return jsonify({'error': 'Fehler beim Löschen des Wortes'}), 500 + + +@app.route('/api/stats') +def get_stats(): + """Statistiken über die Wörterbuch-Datenbank""" + try: + conn = get_db() + cursor = conn.cursor() + + cursor.execute('SELECT COUNT(*) as total FROM words') + total = cursor.fetchone()['total'] + + cursor.execute('SELECT MAX(created_at) as last_added FROM words') + last_added = cursor.fetchone()['last_added'] + + conn.close() + + return jsonify({ + 'total_words': total, + 'last_added': last_added, + 'database': 'sqlite3', + 'storage': '/data/app.db' + }), 200 + + except Exception as e: + logger.error(f"[DICTIONARY] Error getting stats: {e}") + return jsonify({'error': 'Fehler beim Abrufen der Statistiken'}), 500 + + +@app.errorhandler(404) +def not_found(error): + """404 Handler""" + return jsonify({'error': 'Endpoint nicht gefunden'}), 404 + + +@app.errorhandler(500) +def server_error(error): + """500 Handler""" + logger.error(f"[DICTIONARY] Server error: {error}") + return jsonify({'error': 'Interner Fehler'}), 500 + + +if __name__ == '__main__': + logger.info("[DICTIONARY] Starting Flask application...") + init_db() + logger.info("[DICTIONARY] Database initialized") + app.run(host='0.0.0.0', port=8080, debug=False) diff --git a/user-template-dictionary/requirements.txt b/user-template-dictionary/requirements.txt new file mode 100644 index 0000000..fd07738 --- /dev/null +++ b/user-template-dictionary/requirements.txt @@ -0,0 +1,2 @@ +Flask==3.0.0 +Werkzeug==3.0.0 diff --git a/user-template-dictionary/templates/index.html b/user-template-dictionary/templates/index.html new file mode 100644 index 0000000..6f2ef65 --- /dev/null +++ b/user-template-dictionary/templates/index.html @@ -0,0 +1,593 @@ + + + + + + 📚 Mein Wörterbuch + + + +
+
+

📚 Mein Wörterbuch

+

Ihre persönliche Wörter-Sammlung

+
+ 0 Wörter gespeichert +
+
+ +
+
+
+ +
+

✍️ Neues Wort hinzufügen

+
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ +
+

📖 Ihre Wörter 0

+
+
+
+
+ + + +