diff --git a/JOURNAL.md b/JOURNAL.md index 424ac9b..8a2dd11 100644 --- a/JOURNAL.md +++ b/JOURNAL.md @@ -144,4 +144,28 @@ Lizenzmanagement-System für Social Media Account-Erstellungssoftware mit Docker **Nächster Test:** - Container neu starten - Mehrere Lizenzen mit verschiedenen Ablaufdaten erstellen -- Lizenzübersicht unter /licenses aufrufen \ No newline at end of file +- Lizenzübersicht unter /licenses aufrufen + +### 2025-01-06 - Lizenz bearbeiten/löschen implementiert +- Neue Routen für Bearbeiten und Löschen von Lizenzen +- Bearbeitungsformular mit vorausgefüllten Werten +- Aktiv/Inaktiv-Status kann geändert werden +- Lösch-Bestätigung per JavaScript confirm() +- Kunde kann nicht geändert werden (nur Lizenzdetails) + +**Neue Features:** +- `/license/edit/` - Bearbeitungsformular +- `/license/delete/` - Lizenz löschen (POST) +- Aktionen-Spalte in der Lizenzübersicht +- Buttons für Bearbeiten und Löschen +- Checkbox für Aktiv-Status + +**Geänderte/Neue Dateien:** +- v2_adminpanel/app.py (edit_license und delete_license Routen) +- v2_adminpanel/templates/licenses.html (Aktionen-Spalte hinzugefügt) +- v2_adminpanel/templates/edit_license.html (neu erstellt) + +**Sicherheit:** +- Login-Required für alle Aktionen +- POST-only für Löschvorgänge +- Bestätigungsdialog vor dem Löschen \ No newline at end of file diff --git a/v2_adminpanel/app.py b/v2_adminpanel/app.py index 836a647..9a6deec 100644 --- a/v2_adminpanel/app.py +++ b/v2_adminpanel/app.py @@ -126,5 +126,64 @@ def licenses(): return render_template("licenses.html", licenses=licenses, username=session.get('username')) +@app.route("/license/edit/", methods=["GET", "POST"]) +@login_required +def edit_license(license_id): + conn = get_connection() + cur = conn.cursor() + + if request.method == "POST": + # Update license + license_key = request.form["license_key"] + license_type = request.form["license_type"] + valid_from = request.form["valid_from"] + valid_until = request.form["valid_until"] + is_active = request.form.get("is_active") == "on" + + cur.execute(""" + UPDATE licenses + SET license_key = %s, license_type = %s, valid_from = %s, + valid_until = %s, is_active = %s + WHERE id = %s + """, (license_key, license_type, valid_from, valid_until, is_active, license_id)) + + conn.commit() + cur.close() + conn.close() + + return redirect("/licenses") + + # Get license data + cur.execute(""" + SELECT l.id, l.license_key, c.name, c.email, l.license_type, + l.valid_from, l.valid_until, l.is_active, c.id + FROM licenses l + JOIN customers c ON l.customer_id = c.id + WHERE l.id = %s + """, (license_id,)) + + license = cur.fetchone() + cur.close() + conn.close() + + if not license: + return redirect("/licenses") + + return render_template("edit_license.html", license=license, username=session.get('username')) + +@app.route("/license/delete/", methods=["POST"]) +@login_required +def delete_license(license_id): + conn = get_connection() + cur = conn.cursor() + + cur.execute("DELETE FROM licenses WHERE id = %s", (license_id,)) + + conn.commit() + cur.close() + conn.close() + + return redirect("/licenses") + if __name__ == "__main__": app.run(host="0.0.0.0", port=443, ssl_context='adhoc') diff --git a/v2_adminpanel/templates/edit_license.html b/v2_adminpanel/templates/edit_license.html new file mode 100644 index 0000000..4fe4b7d --- /dev/null +++ b/v2_adminpanel/templates/edit_license.html @@ -0,0 +1,76 @@ + + + + + Lizenz bearbeiten - Admin Panel + + + + + +
+
+

Lizenz bearbeiten

+ ← Zurück zur Übersicht +
+ +
+
+
+
+
+ + + Kunde kann nicht geändert werden +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ +
+ + Abbrechen +
+
+
+
+
+ + \ No newline at end of file diff --git a/v2_adminpanel/templates/licenses.html b/v2_adminpanel/templates/licenses.html index b50bd51..c58575e 100644 --- a/v2_adminpanel/templates/licenses.html +++ b/v2_adminpanel/templates/licenses.html @@ -42,6 +42,7 @@ Gültig bis Status Aktiv + Aktionen @@ -76,6 +77,14 @@ {% endif %} + +
+ ✏️ Bearbeiten +
+ +
+
+ {% endfor %} diff --git a/v2_testing/test_license_edit_delete.py b/v2_testing/test_license_edit_delete.py new file mode 100644 index 0000000..46a6b06 --- /dev/null +++ b/v2_testing/test_license_edit_delete.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 +import requests +import urllib3 +from datetime import datetime, timedelta +import subprocess + +# 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 login(session): + """Login to admin panel""" + 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) + return response.status_code == 302 + +def test_edit_license(): + """Test editing a license""" + session = requests.Session() + + if not login(session): + return "✗ Failed to login" + + # First, get a license ID to edit (let's edit license ID 5 - "Bli-bla-blub") + license_id = 5 + + # Test GET edit page + response = session.get(f"{base_url}/license/edit/{license_id}", verify=False) + if response.status_code != 200: + return f"✗ Failed to access edit page: Status {response.status_code}" + + # Check if edit form is displayed + if "Bli-bla-blub" not in response.text: + return "✗ Edit page doesn't show current license data" + + # Test POST edit with new data + new_license_data = { + "license_key": "UPDATED-KEY-2025", + "license_type": "Enterprise-Lösung", + "valid_from": "2025-01-01", + "valid_until": "2025-12-31", + "is_active": "on" + } + + response = session.post(f"{base_url}/license/edit/{license_id}", + data=new_license_data, + verify=False, + allow_redirects=False) + + if response.status_code == 302 and response.headers.get('Location') == '/licenses': + return "✓ License edited successfully" + else: + return f"✗ Failed to edit license: Status {response.status_code}" + +def test_delete_license(): + """Test deleting a license""" + session = requests.Session() + + if not login(session): + return "✗ Failed to login" + + # Create a test license to delete + test_license = { + "customer_name": "Delete Test Company", + "email": "delete@test.com", + "license_key": "DELETE-TEST-KEY", + "license_type": "Test", + "valid_from": datetime.now().strftime("%Y-%m-%d"), + "valid_until": (datetime.now() + timedelta(days=30)).strftime("%Y-%m-%d") + } + + # Create the license + response = session.post(f"{base_url}/", data=test_license, verify=False, allow_redirects=False) + if response.status_code != 302: + return "✗ Failed to create test license for deletion" + + # Get the ID of the newly created license + result = subprocess.run([ + "docker", "exec", "db", "psql", "-U", "adminuser", "-d", "meinedatenbank", "-t", + "-c", "SELECT id FROM licenses WHERE license_key = 'DELETE-TEST-KEY';" + ], capture_output=True, text=True) + + license_id = result.stdout.strip() + if not license_id: + return "✗ Failed to find test license ID" + + # Delete the license + response = session.post(f"{base_url}/license/delete/{license_id}", + verify=False, + allow_redirects=False) + + if response.status_code == 302 and response.headers.get('Location') == '/licenses': + # Verify it's really deleted + result = subprocess.run([ + "docker", "exec", "db", "psql", "-U", "adminuser", "-d", "meinedatenbank", "-t", + "-c", f"SELECT COUNT(*) FROM licenses WHERE id = {license_id};" + ], capture_output=True, text=True) + + count = int(result.stdout.strip()) + if count == 0: + return "✓ License deleted successfully" + else: + return "✗ License still exists in database" + else: + return f"✗ Failed to delete license: Status {response.status_code}" + +# Rebuild and restart admin panel +print("Rebuilding admin panel with new features...") +subprocess.run(["docker-compose", "build", "admin-panel"], capture_output=True) +subprocess.run(["docker-compose", "up", "-d", "admin-panel"], capture_output=True) +subprocess.run(["sleep", "5"], capture_output=True) + +print("\nTesting License Edit/Delete Functionality") +print("=" * 50) + +# Test edit functionality +print("\n1. Testing License Edit:") +print("-" * 30) +edit_result = test_edit_license() +print(edit_result) + +# Verify the edit worked +print("\nVerifying edited license in database:") +result = subprocess.run([ + "docker", "exec", "db", "psql", "-U", "adminuser", "-d", "meinedatenbank", + "-c", "SELECT license_key, license_type, valid_from, valid_until, is_active FROM licenses WHERE license_key = 'UPDATED-KEY-2025';" +], capture_output=True, text=True) +print(result.stdout) + +# Test delete functionality +print("\n2. Testing License Delete:") +print("-" * 30) +delete_result = test_delete_license() +print(delete_result) + +# Show current licenses +print("\n" + "=" * 50) +print("Current licenses in database:") +print("-" * 50) +result = subprocess.run([ + "docker", "exec", "db", "psql", "-U", "adminuser", "-d", "meinedatenbank", + "-c", "SELECT l.id, l.license_key, c.name, l.license_type FROM licenses l JOIN customers c ON l.customer_id = c.id ORDER BY l.id;" +], capture_output=True, text=True) +print(result.stdout) \ No newline at end of file