diff --git a/v2/cookies.txt b/v2/cookies.txt index c31d989..32e94c9 100644 --- a/v2/cookies.txt +++ b/v2/cookies.txt @@ -2,3 +2,4 @@ # https://curl.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. +#HttpOnly_admin-panel-undso.z5m7q9dk3ah2v1plx6ju.com FALSE / FALSE 1750193106 admin_session AwO_9xkBcSaqhYwpkjUTL1bNPOMWUZ5qMXGUAwdTpNM diff --git a/v2_adminpanel/FEHLERSUCHE.md b/v2_adminpanel/FEHLERSUCHE.md new file mode 100644 index 0000000..6bee7fa --- /dev/null +++ b/v2_adminpanel/FEHLERSUCHE.md @@ -0,0 +1,111 @@ +# Fehlersuche - v2_adminpanel Refactoring + +## Aktueller Stand (17.06.2025) + +### Erfolgreiches Refactoring +- Die ursprüngliche 5000+ Zeilen große app.py wurde erfolgreich in Module aufgeteilt: + - 9 Blueprint-Module in `routes/` + - Separate Module für auth/, utils/, config.py, db.py, models.py + - Hauptdatei app.py nur noch 178 Zeilen + +### Funktionierende Teile +- ✅ Routing-System funktioniert (alle Routen sind registriert) +- ✅ Login-System funktioniert +- ✅ Einfache Test-Routen funktionieren (/simple-test) +- ✅ Blueprint-Registrierung funktioniert korrekt + +### Aktuelle Probleme + +#### 1. **404 bei /test-db Route** +**Problem**: Die Route wird nicht gefunden, obwohl sie in app.py definiert ist +**Ursache**: Docker Container hat die Code-Änderungen noch nicht übernommen +**Lösung**: +```bash +docker-compose down +docker-compose build --no-cache admin-panel +docker-compose up -d +``` + +#### 2. **Redirect zu /login bei /customers-licenses** +**Problem**: Beim Aufruf von /customers-licenses wird man zum Login umgeleitet +**Ursache**: Die Route ist mit `@login_required` geschützt +**Status**: Das ist korrektes Verhalten - man muss eingeloggt sein + +#### 3. **"dict object has no element 5" Fehler** +**Problem**: Nach erfolgreichem Login und Zugriff auf /customers-licenses kommt dieser Fehler +**Ursache**: Die Datenbankabfrage gibt ein Dictionary zurück, aber der Code erwartet ein Tuple +**Bereits implementierte Lösung**: +- customers_licenses() verwendet jetzt direkte psycopg2 Verbindung ohne Helper +- Expliziter normaler Cursor statt möglicherweise Dictionary-Cursor + +#### 4. **Fehlende Templates** +**Problem**: 404.html und 500.html fehlten +**Status**: ✅ Bereits erstellt und hinzugefügt + +## Debugging-Schritte + +### 1. Container komplett neu bauen +```bash +cd C:\Users\Administrator\Documents\GitHub\v2-Docker\v2 +docker-compose down +docker-compose build --no-cache +docker-compose up -d +``` + +### 2. Logs überprüfen +```bash +docker logs admin-panel --tail 100 +``` + +### 3. Test-Routen +- `/simple-test` - Sollte "Simple test works!" zeigen +- `/debug-routes` - Zeigt alle registrierten Routen +- `/test-db` - Testet Datenbankverbindung + +### 4. Login-Test +1. Gehe zu https://admin-panel-undso.z5m7q9dk3ah2v1plx6ju.com/ +2. Logge dich mit den Admin-Credentials ein +3. Versuche dann /customers-licenses aufzurufen + +## Code-Fixes bereits implementiert + +### 1. Datenbankverbindungen +- Alle kritischen Funktionen verwenden jetzt `conn = get_connection()` mit normalem Cursor +- Verhindert Dictionary/Tuple Konflikte + +### 2. Spaltennamen korrigiert +- `is_active` statt `active` für licenses Tabelle +- `is_active` statt `active` für sessions Tabelle +- `is_test` statt `is_test_license` +- Entfernt: phone, address, notes aus customers (existieren nicht) + +### 3. Blueprint-Referenzen +- Alle url_for() Aufrufe haben korrekte Blueprint-Präfixe +- z.B. `url_for('auth.login')` statt `url_for('login')` + +## Nächste Schritte + +1. **Container neu bauen** (siehe oben) +2. **Einloggen** und testen ob /customers-licenses funktioniert +3. **Falls weiterhin Fehler**: Docker Logs nach "CUSTOMERS-LICENSES ROUTE CALLED" durchsuchen +4. **Alternative**: Temporär auf die große app.py.backup zurückwechseln: + ```bash + cp app.py.backup app.py + docker-compose restart admin-panel + ``` + +## Bekannte funktionierende Routen (nach Login) +- `/` - Dashboard +- `/customers` - Kundenliste +- `/licenses` - Lizenzliste +- `/resources` - Ressourcen +- `/audit` - Audit Log +- `/sessions` - Sessions + +## Debug-Informationen in customer_routes.py +Die customers_licenses Funktion hat erweiterte Logging-Ausgaben: +- "=== CUSTOMERS-LICENSES ROUTE CALLED ===" +- "=== QUERY RETURNED X ROWS ===" +- Details über Datentypen der Ergebnisse + +Diese erscheinen in den Docker Logs und helfen bei der Fehlersuche. \ No newline at end of file diff --git a/v2_adminpanel/MIGRATION_DISCREPANCIES.md b/v2_adminpanel/MIGRATION_DISCREPANCIES.md new file mode 100644 index 0000000..7aa7632 --- /dev/null +++ b/v2_adminpanel/MIGRATION_DISCREPANCIES.md @@ -0,0 +1,156 @@ +# Migration Discrepancies - Backup vs Current Blueprint Structure + +## 1. Missing Routes + +### Authentication/Profile Routes (Not in any blueprint) +- `/profile` - User profile page +- `/profile/change-password` - Change password endpoint +- `/profile/setup-2fa` - Setup 2FA page +- `/profile/enable-2fa` - Enable 2FA endpoint +- `/profile/disable-2fa` - Disable 2FA endpoint +- `/heartbeat` - Session heartbeat endpoint + +### Customer API Routes (Missing from api_routes.py) +- `/api/customer//licenses` - Get licenses for a customer +- `/api/customer//quick-stats` - Get quick stats for a customer + +### Resource Routes (Missing from resource_routes.py) +- `/resources` - Main resources page +- `/resources/add` - Add new resources page +- `/resources/quarantine/` - Quarantine a resource +- `/resources/release` - Release resources from quarantine +- `/resources/history/` - View resource history +- `/resources/metrics` - Resource metrics page +- `/resources/report` - Resource report page + +### Main Dashboard Route (Missing) +- `/` - Main dashboard (currently in backup shows dashboard with stats) + +## 2. Database Column Discrepancies + +### Column Name Differences +- **created_by** - Used in backup_history table but not consistently referenced +- **is_test_license** vs **is_test** - The database uses `is_test` but some code might reference `is_test_license` + +### Session Table Aliases +The sessions table has multiple column aliases that need to be handled: +- `login_time` (alias for `started_at`) +- `last_activity` (alias for `last_heartbeat`) +- `logout_time` (alias for `ended_at`) +- `active` (alias for `is_active`) + +## 3. Template Name Mismatches + +### Templates Referenced in Backup +- `login.html` - Login page +- `verify_2fa.html` - 2FA verification +- `profile.html` - User profile +- `setup_2fa.html` - 2FA setup +- `backup_codes.html` - 2FA backup codes +- `dashboard.html` - Main dashboard +- `index.html` - Create license form +- `batch_result.html` - Batch operation results +- `batch_form.html` - Batch form +- `edit_license.html` - Edit license +- `edit_customer.html` - Edit customer +- `create_customer.html` - Create customer +- `customers_licenses.html` - Customer-license overview +- `sessions.html` - Sessions list +- `audit_log.html` - Audit log +- `backups.html` - Backup management +- `blocked_ips.html` - Blocked IPs +- `resources.html` - Resources list +- `add_resources.html` - Add resources form +- `resource_history.html` - Resource history +- `resource_metrics.html` - Resource metrics +- `resource_report.html` - Resource report + +## 4. URL_FOR References That Need Blueprint Prefixes + +### In Templates and Redirects +- `url_for('login')` → `url_for('auth.login')` +- `url_for('logout')` → `url_for('auth.logout')` +- `url_for('verify_2fa')` → `url_for('auth.verify_2fa')` +- `url_for('profile')` → `url_for('auth.profile')` (needs implementation) +- `url_for('index')` → `url_for('main.index')` or appropriate blueprint +- `url_for('blocked_ips')` → `url_for('admin.blocked_ips')` +- `url_for('audit_log')` → `url_for('admin.audit_log')` +- `url_for('backups')` → `url_for('admin.backups')` + +## 5. Missing Functions/Middleware + +### Authentication Decorators +- `@login_required` decorator implementation needs to be verified +- `@require_2fa` decorator (if used) + +### Helper Functions +- `get_connection()` - Database connection helper +- `log_audit()` - Audit logging function +- `create_backup()` - Backup creation function +- Rate limiting functions for login attempts + +### Session Management +- Session timeout handling +- Heartbeat mechanism for active sessions + +## 6. API Endpoint Inconsistencies + +### URL Prefix Issues +- API routes in backup don't use `/api` prefix consistently +- Some use `/api/...` while others are at root level + +### Missing API Endpoints +- `/api/generate-license-key` - Generate license key +- `/api/global-search` - Global search functionality + +## 7. Export Routes Organization + +### Current vs Expected +- Export routes might need different URL structure +- Check if all export types are covered: + - `/export/licenses` + - `/export/audit` + - `/export/customers` + - `/export/sessions` + - `/export/resources` + +## 8. Special Configurations + +### Missing Configurations +- TOTP/2FA configuration +- Backup encryption settings +- Rate limiting configuration +- Session timeout settings + +### Environment Variables +- Check if all required environment variables are properly loaded +- Database connection parameters +- Secret keys and encryption keys + +## 9. JavaScript/AJAX Endpoints + +### API calls that might be broken +- Device management endpoints +- Quick edit functionality +- Bulk operations +- Resource allocation checks + +## 10. Permission/Access Control + +### Missing or Incorrect Access Control +- All routes need `@login_required` decorator +- Some routes might need additional permission checks +- API routes need proper authentication + +## Action Items + +1. **Implement missing profile/auth routes** in auth_routes.py +2. **Add missing customer API routes** to api_routes.py +3. **Create complete resource management blueprint** with all routes +4. **Fix main dashboard route** - decide which blueprint should handle "/" +5. **Update all url_for() calls** in templates to use blueprint prefixes +6. **Verify database column names** are consistent throughout +7. **Check template names** match between routes and actual files +8. **Implement heartbeat mechanism** for session management +9. **Add missing helper functions** to appropriate modules +10. **Test all export routes** work correctly with new structure \ No newline at end of file diff --git a/v2_adminpanel/app.py b/v2_adminpanel/app.py index e1ef39a..4513b67 100644 --- a/v2_adminpanel/app.py +++ b/v2_adminpanel/app.py @@ -44,15 +44,21 @@ scheduler.start() logging.basicConfig(level=logging.INFO) # Import and register blueprints -from routes.auth_routes import auth_bp -from routes.admin_routes import admin_bp -from routes.api_routes import api_bp -from routes.batch_routes import batch_bp -from routes.customer_routes import customer_bp -from routes.export_routes import export_bp -from routes.license_routes import license_bp -from routes.resource_routes import resource_bp -from routes.session_routes import session_bp +try: + from routes.auth_routes import auth_bp + from routes.admin_routes import admin_bp + from routes.api_routes import api_bp + from routes.batch_routes import batch_bp + from routes.customer_routes import customer_bp + from routes.export_routes import export_bp + from routes.license_routes import license_bp + from routes.resource_routes import resource_bp + from routes.session_routes import session_bp + print("All blueprints imported successfully!") +except Exception as e: + print(f"Blueprint import error: {str(e)}") + import traceback + traceback.print_exc() # Register all blueprints app.register_blueprint(auth_bp) @@ -66,6 +72,27 @@ app.register_blueprint(resource_bp) app.register_blueprint(session_bp) +# Debug routes to test +@app.route('/test-customers-licenses') +def test_route(): + return "Test route works! If you see this, routing is working." + +@app.route('/direct-customers-licenses') +def direct_customers_licenses(): + """Direct route without blueprint""" + try: + return render_template("customers_licenses.html", customers=[]) + except Exception as e: + return f"Error: {str(e)}" + +@app.route('/debug-routes') +def debug_routes(): + """Show all registered routes""" + routes = [] + for rule in app.url_map.iter_rules(): + routes.append(f"{rule.endpoint}: {rule.rule}") + return "
".join(sorted(routes)) + # Scheduled Backup Job def scheduled_backup(): """Erstellt ein automatisches Backup""" @@ -91,13 +118,21 @@ scheduler.add_job( # Error handlers @app.errorhandler(404) def not_found(e): - return render_template('404.html'), 404 + try: + return render_template('404.html'), 404 + except: + return "404 - Page not found", 404 @app.errorhandler(500) def server_error(e): - logging.error(f"Server error: {str(e)}") - return render_template('500.html'), 500 + import traceback + error_msg = f"Server error: {str(e)}\n{traceback.format_exc()}" + logging.error(error_msg) + try: + return render_template('500.html'), 500 + except: + return f"500 - Internal Server Error\n\n{error_msg}", 500 # Context processors @@ -112,5 +147,32 @@ def inject_global_vars(): } +# Simple test route that should always work +@app.route('/simple-test') +def simple_test(): + return "Simple test works!" + +@app.route('/test-db') +def test_db(): + """Test database connection""" + try: + import psycopg2 + conn = psycopg2.connect( + host=os.getenv("POSTGRES_HOST", "postgres"), + port=os.getenv("POSTGRES_PORT", "5432"), + dbname=os.getenv("POSTGRES_DB"), + user=os.getenv("POSTGRES_USER"), + password=os.getenv("POSTGRES_PASSWORD") + ) + cur = conn.cursor() + cur.execute("SELECT COUNT(*) FROM customers") + count = cur.fetchone()[0] + cur.close() + conn.close() + return f"Database works! Customers count: {count}" + except Exception as e: + import traceback + return f"Database error: {str(e)}
{traceback.format_exc()}
" + if __name__ == "__main__": app.run(host="0.0.0.0", port=5000) \ No newline at end of file diff --git a/v2_adminpanel/auth/decorators.py b/v2_adminpanel/auth/decorators.py index fda9c05..9e0b004 100644 --- a/v2_adminpanel/auth/decorators.py +++ b/v2_adminpanel/auth/decorators.py @@ -12,7 +12,7 @@ def login_required(f): @wraps(f) def decorated_function(*args, **kwargs): if 'logged_in' not in session: - return redirect(url_for('login')) + return redirect(url_for('auth.login')) # Check if session has expired if 'last_activity' in session: @@ -36,7 +36,7 @@ def login_required(f): pass session.clear() flash('Ihre Sitzung ist abgelaufen. Bitte melden Sie sich erneut an.', 'warning') - return redirect(url_for('login')) + return redirect(url_for('auth.login')) # Activity is NOT automatically updated # Only on explicit user actions (done by heartbeat) diff --git a/v2_adminpanel/routes/__pycache__/__init__.cpython-312.pyc b/v2_adminpanel/routes/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..d28b39d Binary files /dev/null and b/v2_adminpanel/routes/__pycache__/__init__.cpython-312.pyc differ diff --git a/v2_adminpanel/routes/__pycache__/customer_routes.cpython-312.pyc b/v2_adminpanel/routes/__pycache__/customer_routes.cpython-312.pyc new file mode 100644 index 0000000..ccb85f5 Binary files /dev/null and b/v2_adminpanel/routes/__pycache__/customer_routes.cpython-312.pyc differ diff --git a/v2_adminpanel/routes/admin_routes.py b/v2_adminpanel/routes/admin_routes.py index e26c12e..10f683f 100644 --- a/v2_adminpanel/routes/admin_routes.py +++ b/v2_adminpanel/routes/admin_routes.py @@ -20,8 +20,9 @@ admin_bp = Blueprint('admin', __name__) @login_required def dashboard(): try: - with get_db_connection() as conn: - with get_db_cursor(conn) as cur: + conn = get_connection() + cur = conn.cursor() + try: # Hole Statistiken mit sicheren Defaults # Anzahl aktiver Lizenzen cur.execute("SELECT COUNT(*) FROM licenses WHERE is_active = true") @@ -123,6 +124,9 @@ def dashboard(): hourly_sessions=hourly_sessions, resource_stats=resource_stats, username=session.get('username')) + finally: + cur.close() + conn.close() except Exception as e: current_app.logger.error(f"Dashboard error: {str(e)}") diff --git a/v2_adminpanel/routes/customer_routes.py b/v2_adminpanel/routes/customer_routes.py index 2f84c22..e99a6a9 100644 --- a/v2_adminpanel/routes/customer_routes.py +++ b/v2_adminpanel/routes/customer_routes.py @@ -1,3 +1,4 @@ +import os import logging from datetime import datetime from zoneinfo import ZoneInfo @@ -12,6 +13,11 @@ from models import get_customers, get_customer_by_id # Create Blueprint customer_bp = Blueprint('customers', __name__) +# Test route +@customer_bp.route("/test-customers") +def test_customers(): + return "Customer blueprint is working!" + @customer_bp.route("/customers") @login_required @@ -23,9 +29,6 @@ def customers(): @customer_bp.route("/customer/edit/", methods=["GET", "POST"]) @login_required def edit_customer(customer_id): - conn = get_connection() - cur = conn.cursor() - if request.method == "POST": try: # Get current customer data for comparison @@ -34,51 +37,44 @@ def edit_customer(customer_id): flash('Kunde nicht gefunden!', 'error') return redirect(url_for('customers.customers')) - # Update customer data - new_values = { - 'name': request.form['name'], - 'email': request.form['email'], - 'phone': request.form.get('phone', ''), - 'address': request.form.get('address', ''), - 'notes': request.form.get('notes', '') - } - - cur.execute(""" - UPDATE customers - SET name = %s, email = %s, phone = %s, address = %s, notes = %s - WHERE id = %s - """, ( - new_values['name'], - new_values['email'], - new_values['phone'], - new_values['address'], - new_values['notes'], - customer_id - )) - - conn.commit() - - # Log changes - log_audit('UPDATE', 'customer', customer_id, - old_values={ - 'name': current_customer['name'], - 'email': current_customer['email'], - 'phone': current_customer.get('phone', ''), - 'address': current_customer.get('address', ''), - 'notes': current_customer.get('notes', '') - }, - new_values=new_values) - - flash('Kunde erfolgreich aktualisiert!', 'success') - return redirect(url_for('customers.customers')) - + with get_db_connection() as conn: + cur = conn.cursor() + try: + # Update customer data (nur name und email existieren in der DB) + new_values = { + 'name': request.form['name'], + 'email': request.form['email'] + } + + cur.execute(""" + UPDATE customers + SET name = %s, email = %s + WHERE id = %s + """, ( + new_values['name'], + new_values['email'], + customer_id + )) + + conn.commit() + + # Log changes + log_audit('UPDATE', 'customer', customer_id, + old_values={ + 'name': current_customer['name'], + 'email': current_customer['email'] + }, + new_values=new_values) + + flash('Kunde erfolgreich aktualisiert!', 'success') + return redirect(url_for('customers.customers')) + finally: + cur.close() + except Exception as e: - conn.rollback() logging.error(f"Fehler beim Aktualisieren des Kunden: {str(e)}") flash('Fehler beim Aktualisieren des Kunden!', 'error') - finally: - cur.close() - conn.close() + return redirect(url_for('customers.customers')) # GET request customer_data = get_customer_by_id(customer_id) @@ -100,15 +96,12 @@ def create_customer(): # Insert new customer name = request.form['name'] email = request.form['email'] - phone = request.form.get('phone', '') - address = request.form.get('address', '') - notes = request.form.get('notes', '') cur.execute(""" - INSERT INTO customers (name, email, phone, address, notes, created_at) - VALUES (%s, %s, %s, %s, %s, %s) + INSERT INTO customers (name, email, created_at) + VALUES (%s, %s, %s) RETURNING id - """, (name, email, phone, address, notes, datetime.now())) + """, (name, email, datetime.now())) customer_id = cur.fetchone()[0] conn.commit() @@ -117,10 +110,7 @@ def create_customer(): log_audit('CREATE', 'customer', customer_id, new_values={ 'name': name, - 'email': email, - 'phone': phone, - 'address': address, - 'notes': notes + 'email': email }) flash(f'Kunde {name} erfolgreich erstellt!', 'success') @@ -187,49 +177,69 @@ def delete_customer(customer_id): @login_required def customers_licenses(): """Zeigt die Übersicht von Kunden und deren Lizenzen""" - conn = get_connection() - cur = conn.cursor() + import logging + import psycopg2 + logging.info("=== CUSTOMERS-LICENSES ROUTE CALLED ===") try: - # Hole alle Kunden mit ihren Lizenzen - cur.execute(""" - SELECT - c.id as customer_id, - c.name as customer_name, - c.email as customer_email, - c.created_at as customer_created, - COUNT(l.id) as license_count, - COUNT(CASE WHEN l.active = true THEN 1 END) as active_licenses, - COUNT(CASE WHEN l.is_test = true THEN 1 END) as test_licenses, - MAX(l.created_at) as last_license_created - FROM customers c - LEFT JOIN licenses l ON c.id = l.customer_id - GROUP BY c.id, c.name, c.email, c.created_at - ORDER BY c.name - """) + # Direkte Verbindung ohne Helper-Funktionen + conn = psycopg2.connect( + host=os.getenv("POSTGRES_HOST", "postgres"), + port=os.getenv("POSTGRES_PORT", "5432"), + dbname=os.getenv("POSTGRES_DB"), + user=os.getenv("POSTGRES_USER"), + password=os.getenv("POSTGRES_PASSWORD") + ) + conn.set_client_encoding('UTF8') + cur = conn.cursor() - customers = [] - for row in cur.fetchall(): - customers.append({ - 'id': row[0], - 'name': row[1], - 'email': row[2], - 'created_at': row[3], - 'license_count': row[4], - 'active_licenses': row[5], - 'test_licenses': row[6], - 'last_license_created': row[7] - }) - - return render_template("customers_licenses.html", customers=customers) + try: + # Hole alle Kunden mit ihren Lizenzen + cur.execute(""" + SELECT + c.id, + c.name, + c.email, + c.created_at, + COUNT(l.id), + COUNT(CASE WHEN l.is_active = true THEN 1 END), + COUNT(CASE WHEN l.is_test = true THEN 1 END), + MAX(l.created_at) + FROM customers c + LEFT JOIN licenses l ON c.id = l.customer_id + GROUP BY c.id, c.name, c.email, c.created_at + ORDER BY c.name + """) + + customers = [] + results = cur.fetchall() + logging.info(f"=== QUERY RETURNED {len(results)} ROWS ===") + + for idx, row in enumerate(results): + logging.info(f"Row {idx}: Type={type(row)}, Length={len(row) if hasattr(row, '__len__') else 'N/A'}") + customers.append({ + 'id': row[0], + 'name': row[1], + 'email': row[2], + 'created_at': row[3], + 'license_count': row[4], + 'active_licenses': row[5], + 'test_licenses': row[6], + 'last_license_created': row[7] + }) + + return render_template("customers_licenses.html", customers=customers) + + finally: + cur.close() + conn.close() except Exception as e: - logging.error(f"Fehler beim Laden der Kunden-Lizenz-Übersicht: {str(e)}") - flash('Fehler beim Laden der Daten!', 'error') + import traceback + error_details = f"Fehler beim Laden der Kunden-Lizenz-Übersicht: {str(e)}\nType: {type(e)}\nTraceback: {traceback.format_exc()}" + logging.error(error_details) + flash(f'Datenbankfehler: {str(e)}', 'error') return redirect(url_for('admin.dashboard')) - finally: - cur.close() - conn.close() @customer_bp.route("/api/customer//licenses") @@ -251,17 +261,17 @@ def api_customer_licenses(customer_id): l.id, l.license_key, l.license_type, - l.active, + l.is_active, l.is_test, l.valid_from, l.valid_until, l.device_limit, l.created_at, - (SELECT COUNT(*) FROM sessions s WHERE s.license_key = l.license_key AND s.active = true) as active_sessions, - (SELECT COUNT(DISTINCT device_id) FROM sessions s WHERE s.license_key = l.license_key) as registered_devices, + (SELECT COUNT(*) FROM sessions s WHERE s.license_key = l.license_key AND s.is_active = true) as active_sessions, + (SELECT COUNT(DISTINCT hardware_id) FROM sessions s WHERE s.license_key = l.license_key) as registered_devices, CASE WHEN l.valid_until < CURRENT_DATE THEN 'expired' - WHEN l.active = false THEN 'inactive' + WHEN l.is_active = false THEN 'inactive' ELSE 'active' END as status FROM licenses l @@ -314,7 +324,7 @@ def api_customer_quick_stats(customer_id): cur.execute(""" SELECT COUNT(l.id) as total_licenses, - COUNT(CASE WHEN l.active = true THEN 1 END) as active_licenses, + COUNT(CASE WHEN l.is_active = true THEN 1 END) as active_licenses, COUNT(CASE WHEN l.is_test = true THEN 1 END) as test_licenses, SUM(l.device_limit) as total_device_limit FROM licenses l diff --git a/v2_adminpanel/routes/resource_routes.py b/v2_adminpanel/routes/resource_routes.py index fe2dc0b..dcc43ef 100644 --- a/v2_adminpanel/routes/resource_routes.py +++ b/v2_adminpanel/routes/resource_routes.py @@ -133,54 +133,55 @@ def resources(): def add_resource(): """Neue Ressource hinzufügen""" if request.method == 'POST': - conn = get_connection() - cur = conn.cursor() - try: - resource_type = request.form['resource_type'] - resource_value = request.form['resource_value'].strip() - is_test = 'is_test' in request.form - - # Prüfe ob Ressource bereits existiert - cur.execute(""" - SELECT id FROM resource_pools - WHERE resource_type = %s AND resource_value = %s - """, (resource_type, resource_value)) - - if cur.fetchone(): - flash(f'Ressource {resource_value} existiert bereits!', 'error') - return redirect(url_for('resources.add_resource')) - - # Füge neue Ressource hinzu - cur.execute(""" - INSERT INTO resource_pools (resource_type, resource_value, status, is_test, created_by) - VALUES (%s, %s, 'available', %s, %s) - RETURNING id - """, (resource_type, resource_value, is_test, session['username'])) - - resource_id = cur.fetchone()[0] - conn.commit() - - # Audit-Log - log_audit('CREATE', 'resource', resource_id, - new_values={ - 'resource_type': resource_type, - 'resource_value': resource_value, - 'is_test': is_test - }) - - flash(f'Ressource {resource_value} erfolgreich hinzugefügt!', 'success') - return redirect(url_for('resources.resources')) - + with get_db_connection() as conn: + cur = conn.cursor() + try: + resource_type = request.form['resource_type'] + resource_value = request.form['resource_value'].strip() + is_test = 'is_test' in request.form + + # Prüfe ob Ressource bereits existiert + cur.execute(""" + SELECT id FROM resource_pools + WHERE resource_type = %s AND resource_value = %s + """, (resource_type, resource_value)) + + if cur.fetchone(): + flash(f'Ressource {resource_value} existiert bereits!', 'error') + return redirect(url_for('resources.add_resource')) + + # Füge neue Ressource hinzu (ohne created_by) + cur.execute(""" + INSERT INTO resource_pools (resource_type, resource_value, status, is_test, status_changed_by) + VALUES (%s, %s, 'available', %s, %s) + RETURNING id + """, (resource_type, resource_value, is_test, session.get('username', 'system'))) + + resource_id = cur.fetchone()[0] + conn.commit() + + # Audit-Log + log_audit('CREATE', 'resource', resource_id, + new_values={ + 'resource_type': resource_type, + 'resource_value': resource_value, + 'is_test': is_test + }) + + flash(f'Ressource {resource_value} erfolgreich hinzugefügt!', 'success') + return redirect(url_for('resources.resources')) + finally: + cur.close() + except Exception as e: - conn.rollback() + import traceback logging.error(f"Fehler beim Hinzufügen der Ressource: {str(e)}") - flash('Fehler beim Hinzufügen der Ressource!', 'error') - finally: - cur.close() - conn.close() + logging.error(f"Traceback: {traceback.format_exc()}") + flash(f'Fehler: {str(e)}', 'error') + return redirect(url_for('resources.resources')) - return render_template('add_resource.html') + return render_template('add_resources.html') @resource_bp.route('/resources/quarantine/', methods=['POST']) diff --git a/v2_adminpanel/templates/404.html b/v2_adminpanel/templates/404.html new file mode 100644 index 0000000..a36c93b --- /dev/null +++ b/v2_adminpanel/templates/404.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} + +{% block title %}Seite nicht gefunden{% endblock %} + +{% block content %} +
+
+
+
+
+

404

+

Seite nicht gefunden

+

Die angeforderte Seite konnte nicht gefunden werden.

+ Zur Startseite +
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/v2_adminpanel/templates/500.html b/v2_adminpanel/templates/500.html new file mode 100644 index 0000000..0185023 --- /dev/null +++ b/v2_adminpanel/templates/500.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} + +{% block title %}Serverfehler{% endblock %} + +{% block content %} +
+
+
+
+
+

500

+

Interner Serverfehler

+

Es ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut.

+ Zur Startseite +
+
+
+
+
+{% endblock %} \ No newline at end of file diff --git a/v2_adminpanel/templates/customers_licenses.html b/v2_adminpanel/templates/customers_licenses.html index ff92edd..94eeb9e 100644 --- a/v2_adminpanel/templates/customers_licenses.html +++ b/v2_adminpanel/templates/customers_licenses.html @@ -56,23 +56,23 @@ {% if customers %} {% for customer in customers %}
-
{{ customer[1] }}
- {{ customer[2] }} +
{{ customer.name }}
+ {{ customer.email }}
- {{ customer[4] }} - {% if customer[5] > 0 %} - {{ customer[5] }} + {{ customer.license_count }} + {% if customer.active_licenses > 0 %} + {{ customer.active_licenses }} {% endif %} - {% if customer[6] > 0 %} - {{ customer[6] }} + {% if customer.test_licenses > 0 %} + {{ customer.test_licenses }} {% endif %}