From da03acd80532c151be2701abe9e4f1925613e5ae Mon Sep 17 00:00:00 2001 From: UserIsMH Date: Sat, 7 Jun 2025 13:26:52 +0200 Subject: [PATCH] =?UTF-8?q?Lizenz=C3=BCbersichtsseite?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 3 +- JOURNAL.md | 26 ++++++- v2_adminpanel/app.py | 26 +++++++ v2_adminpanel/templates/index.html | 5 +- v2_adminpanel/templates/licenses.html | 95 +++++++++++++++++++++++ v2_testing/test_license_overview.py | 104 ++++++++++++++++++++++++++ 6 files changed, 256 insertions(+), 3 deletions(-) create mode 100644 v2_adminpanel/templates/licenses.html create mode 100644 v2_testing/test_license_overview.py diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 28fd183..7674241 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -10,7 +10,8 @@ "Bash(docker logs:*)", "Bash(docker exec:*)", "Bash(python3:*)", - "Bash(docker-compose restart:*)" + "Bash(docker-compose restart:*)", + "Bash(docker-compose build:*)" ], "deny": [] } diff --git a/JOURNAL.md b/JOURNAL.md index 3f88bf2..424ac9b 100644 --- a/JOURNAL.md +++ b/JOURNAL.md @@ -120,4 +120,28 @@ Lizenzmanagement-System für Social Media Account-Erstellungssoftware mit Docker **Nächster Test:** - Container neu bauen und starten - Kundennamen mit Umlauten testen (z.B. "Müller GmbH", "Björn Schäfer") -- Email mit Umlauten testen \ No newline at end of file +- Email mit Umlauten testen + +### 2025-01-06 - Lizenzübersicht implementiert +- Neue Route `/licenses` für Lizenzübersicht +- SQL-Query mit JOIN zwischen licenses und customers +- Status-Berechnung (aktiv, läuft bald ab, abgelaufen) +- Farbcodierung für verschiedene Status +- Navigation zwischen Lizenz erstellen und Übersicht + +**Neue Features:** +- Anzeige aller Lizenzen mit Kundeninformationen +- Status-Anzeige basierend auf Ablaufdatum +- Unterscheidung zwischen Voll- und Testversion +- Responsive Tabelle mit Bootstrap +- Link von Dashboard zur Übersicht und zurück + +**Geänderte/Neue Dateien:** +- v2_adminpanel/app.py (neue Route hinzugefügt) +- v2_adminpanel/templates/licenses.html (neu erstellt) +- v2_adminpanel/templates/index.html (Navigation ergänzt) + +**Nächster Test:** +- Container neu starten +- Mehrere Lizenzen mit verschiedenen Ablaufdaten erstellen +- Lizenzübersicht unter /licenses aufrufen \ No newline at end of file diff --git a/v2_adminpanel/app.py b/v2_adminpanel/app.py index f4e801a..836a647 100644 --- a/v2_adminpanel/app.py +++ b/v2_adminpanel/app.py @@ -100,5 +100,31 @@ def dashboard(): return render_template("index.html", username=session.get('username')) +@app.route("/licenses") +@login_required +def licenses(): + conn = get_connection() + cur = conn.cursor() + + # Alle Lizenzen mit Kundeninformationen abrufen + cur.execute(""" + SELECT l.id, l.license_key, c.name, c.email, l.license_type, + l.valid_from, l.valid_until, l.is_active, + CASE + WHEN l.valid_until < CURRENT_DATE THEN 'abgelaufen' + WHEN l.valid_until < CURRENT_DATE + INTERVAL '30 days' THEN 'läuft bald ab' + ELSE 'aktiv' + END as status + FROM licenses l + JOIN customers c ON l.customer_id = c.id + ORDER BY l.valid_until DESC + """) + + licenses = cur.fetchall() + cur.close() + conn.close() + + return render_template("licenses.html", licenses=licenses, username=session.get('username')) + if __name__ == "__main__": app.run(host="0.0.0.0", port=443, ssl_context='adhoc') diff --git a/v2_adminpanel/templates/index.html b/v2_adminpanel/templates/index.html index 7727bc2..eee5257 100644 --- a/v2_adminpanel/templates/index.html +++ b/v2_adminpanel/templates/index.html @@ -17,7 +17,10 @@
-

Neue Lizenz erstellen

+
+

Neue Lizenz erstellen

+ 📋 Lizenzübersicht +
diff --git a/v2_adminpanel/templates/licenses.html b/v2_adminpanel/templates/licenses.html new file mode 100644 index 0000000..b50bd51 --- /dev/null +++ b/v2_adminpanel/templates/licenses.html @@ -0,0 +1,95 @@ + + + + + Lizenzübersicht - Admin Panel + + + + + + +
+
+

Lizenzübersicht

+ ➕ Neue Lizenz erstellen +
+ +
+
+
+ + + + + + + + + + + + + + + + {% for license in licenses %} + + + + + + + + + + + + {% endfor %} + +
IDLizenzschlüsselKundeE-MailTypGültig vonGültig bisStatusAktiv
{{ license[0] }}{{ license[1] }}{{ license[2] }}{{ license[3] or '-' }} + {% if license[4] == 'full' %} + Vollversion + {% else %} + Testversion + {% endif %} + {{ license[5].strftime('%d.%m.%Y') }}{{ license[6].strftime('%d.%m.%Y') }} + {% if license[8] == 'abgelaufen' %} + ⚠️ Abgelaufen + {% elif license[8] == 'läuft bald ab' %} + ⏰ Läuft bald ab + {% else %} + ✅ Aktiv + {% endif %} + + {% if license[7] %} + + {% else %} + + {% endif %} +
+ + {% if not licenses %} +
+

Noch keine Lizenzen vorhanden.

+ Erste Lizenz erstellen +
+ {% endif %} +
+
+
+
+ + \ No newline at end of file diff --git a/v2_testing/test_license_overview.py b/v2_testing/test_license_overview.py new file mode 100644 index 0000000..3f54a69 --- /dev/null +++ b/v2_testing/test_license_overview.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +import requests +import urllib3 + +# Disable SSL warnings for self-signed certificate +urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + +# Test configuration +base_url = "https://localhost:443" +admin_user = {"username": "rac00n", "password": "1248163264"} + +def test_license_overview(): + """Test the new license overview page""" + session = requests.Session() + + # Login first + login_data = { + "username": admin_user["username"], + "password": admin_user["password"] + } + + response = session.post(f"{base_url}/login", data=login_data, verify=False, allow_redirects=False) + if response.status_code != 302: + return "Failed to login" + + # Access the licenses page + response = session.get(f"{base_url}/licenses", verify=False) + + if response.status_code == 200: + content = response.text + + # Check if we have the expected licenses + test_results = [] + + # Check for previously created licenses + if "Müller GmbH & Co. KG" in content: + test_results.append("✓ Found: Müller GmbH & Co. KG") + else: + test_results.append("✗ Missing: Müller GmbH & Co. KG") + + if "Schröder Süßwaren AG" in content: + test_results.append("✓ Found: Schröder Süßwaren AG") + else: + test_results.append("✗ Missing: Schröder Süßwaren AG") + + if "Björn Köhler Einzelunternehmen" in content: + test_results.append("✓ Found: Björn Köhler Einzelunternehmen") + else: + test_results.append("✗ Missing: Björn Köhler Einzelunternehmen") + + # Check for license status indicators + if "aktiv" in content or "abgelaufen" in content or "läuft bald ab" in content: + test_results.append("✓ Status indicators found") + else: + test_results.append("✗ No status indicators found") + + # Check for UTF-8 characters + if "ä" in content or "ö" in content or "ü" in content or "ß" in content: + test_results.append("✓ UTF-8 characters displayed correctly") + else: + test_results.append("✗ No UTF-8 characters found") + + return test_results, response.status_code + else: + return [f"✗ Failed to access licenses page: Status {response.status_code}"], response.status_code + +print("Testing License Overview Page") +print("=" * 50) + +# First restart admin panel to get the new code +print("Restarting admin panel container...") +import subprocess +subprocess.run(["docker", "restart", "admin-panel"], capture_output=True) +print("Waiting for container to start...") +subprocess.run(["sleep", "5"], capture_output=True) + +results, status_code = test_license_overview() + +print(f"\nAccessing /licenses endpoint - Status Code: {status_code}") +print("-" * 50) + +for result in results: + print(result) + +print("\n" + "=" * 50) +print("Database content check:") +print("-" * 50) + +# Check what's actually in the database +result = subprocess.run([ + "docker", "exec", "db", "psql", "-U", "adminuser", "-d", "meinedatenbank", + "-c", """SELECT l.id, l.license_key, c.name, l.license_type, + l.valid_until, + CASE + WHEN l.valid_until < CURRENT_DATE THEN 'abgelaufen' + WHEN l.valid_until < CURRENT_DATE + INTERVAL '30 days' THEN 'läuft bald ab' + ELSE 'aktiv' + END as status + FROM licenses l + JOIN customers c ON l.customer_id = c.id + ORDER BY l.id DESC LIMIT 5;""" +], capture_output=True, text=True) + +print(result.stdout) \ No newline at end of file