diff --git a/v2/docker-compose.yaml b/v2/docker-compose.yaml index 0715e2f..c8ad7b6 100644 --- a/v2/docker-compose.yaml +++ b/v2/docker-compose.yaml @@ -25,40 +25,6 @@ services: cpus: '2' memory: 4g - redis: - image: redis:7-alpine - container_name: redis-cache - restart: always - environment: - TZ: Europe/Berlin - networks: - - internal_net - volumes: - - redis_data:/data - deploy: - resources: - limits: - cpus: '0.5' - memory: 512m - - rabbitmq: - image: rabbitmq:3-management - container_name: rabbitmq - restart: always - environment: - RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER:-admin} - RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASS:-admin} - TZ: Europe/Berlin - networks: - - internal_net - volumes: - - rabbitmq_data:/var/lib/rabbitmq - deploy: - resources: - limits: - cpus: '1' - memory: 1g - license-server: build: context: ../v2_lizenzserver @@ -68,11 +34,8 @@ services: env_file: .env environment: TZ: Europe/Berlin - REDIS_URL: redis://redis:6379/0 depends_on: - postgres - - redis - - rabbitmq networks: - internal_net deploy: @@ -199,5 +162,3 @@ networks: volumes: postgres_data: - redis_data: - rabbitmq_data: diff --git a/v2_adminpanel/migrations/add_fake_constraint.sql b/v2_adminpanel/migrations/add_fake_constraint.sql new file mode 100644 index 0000000..e0c93e7 --- /dev/null +++ b/v2_adminpanel/migrations/add_fake_constraint.sql @@ -0,0 +1,72 @@ +-- Add constraint to ensure licenses always inherit is_fake from their customer +-- This migration adds a trigger to automatically sync is_fake status + +-- Function to sync is_fake status +CREATE OR REPLACE FUNCTION sync_license_fake_status() +RETURNS TRIGGER AS $$ +BEGIN + -- When inserting or updating a license, get is_fake from customer + IF TG_OP = 'INSERT' OR (TG_OP = 'UPDATE' AND NEW.customer_id != OLD.customer_id) THEN + SELECT is_fake INTO NEW.is_fake + FROM customers + WHERE id = NEW.customer_id; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- Trigger for licenses table +DROP TRIGGER IF EXISTS sync_license_fake_before_insert_update ON licenses; +CREATE TRIGGER sync_license_fake_before_insert_update + BEFORE INSERT OR UPDATE ON licenses + FOR EACH ROW + EXECUTE FUNCTION sync_license_fake_status(); + +-- Function to update licenses when customer is_fake changes +CREATE OR REPLACE FUNCTION sync_customer_fake_to_licenses() +RETURNS TRIGGER AS $$ +BEGIN + -- When customer is_fake changes, update all their licenses + IF TG_OP = 'UPDATE' AND NEW.is_fake != OLD.is_fake THEN + UPDATE licenses + SET is_fake = NEW.is_fake + WHERE customer_id = NEW.id; + END IF; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- Trigger for customers table +DROP TRIGGER IF EXISTS sync_customer_fake_after_update ON customers; +CREATE TRIGGER sync_customer_fake_after_update + AFTER UPDATE ON customers + FOR EACH ROW + EXECUTE FUNCTION sync_customer_fake_to_licenses(); + +-- Verify current data is consistent (should return 0) +DO $$ +DECLARE + mismatch_count INTEGER; +BEGIN + SELECT COUNT(*) INTO mismatch_count + FROM licenses l + JOIN customers c ON l.customer_id = c.id + WHERE l.is_fake != c.is_fake; + + IF mismatch_count > 0 THEN + RAISE NOTICE 'Found % mismatches. Fixing...', mismatch_count; + + -- Fix any existing mismatches + UPDATE licenses l + SET is_fake = c.is_fake + FROM customers c + WHERE l.customer_id = c.id + AND l.is_fake != c.is_fake; + + RAISE NOTICE 'Fixed all mismatches.'; + ELSE + RAISE NOTICE 'No mismatches found. Data is consistent.'; + END IF; +END $$; \ No newline at end of file diff --git a/v2_adminpanel/routes/api_routes.py b/v2_adminpanel/routes/api_routes.py index 3af4783..e4f442f 100644 --- a/v2_adminpanel/routes/api_routes.py +++ b/v2_adminpanel/routes/api_routes.py @@ -37,7 +37,8 @@ def api_customers(): for customer in paginated_customers: results.append({ 'id': customer['id'], - 'text': f"{customer['name']} ({customer['email'] or 'keine E-Mail'})" + 'text': f"{customer['name']} ({customer['email'] or 'keine E-Mail'})", + 'is_fake': customer.get('is_fake', False) # Include the is_fake field }) return jsonify({ @@ -913,7 +914,7 @@ def global_search(): # Suche in Kunden cur.execute(""" - SELECT id, name, email + SELECT id, name, email, is_fake FROM customers WHERE name ILIKE %s OR email ILIKE %s LIMIT 10 @@ -923,7 +924,8 @@ def global_search(): results['customers'].append({ 'id': row[0], 'name': row[1], - 'email': row[2] + 'email': row[2], + 'is_fake': row[3] }) # Suche in Ressourcen diff --git a/v2_adminpanel/routes/batch_routes.py b/v2_adminpanel/routes/batch_routes.py index 18415a7..0e3bcf1 100644 --- a/v2_adminpanel/routes/batch_routes.py +++ b/v2_adminpanel/routes/batch_routes.py @@ -42,20 +42,21 @@ def batch_create(): valid_from = request.form['valid_from'] valid_until = request.form['valid_until'] device_limit = int(request.form['device_limit']) - is_fake = 'is_fake' in request.form - # Validierung if count < 1 or count > 100: flash('Anzahl muss zwischen 1 und 100 liegen!', 'error') return redirect(url_for('batch.batch_create')) - # Hole Kundendaten - cur.execute("SELECT name, email FROM customers WHERE id = %s", (customer_id,)) + # Hole Kundendaten inkl. is_fake Status + cur.execute("SELECT name, email, is_fake FROM customers WHERE id = %s", (customer_id,)) customer = cur.fetchone() if not customer: flash('Kunde nicht gefunden!', 'error') return redirect(url_for('batch.batch_create')) + # Lizenz erbt immer den is_fake Status vom Kunden + is_fake = customer[2] + created_licenses = [] # Erstelle Lizenzen @@ -315,21 +316,27 @@ def batch_import(): if not customer: # Erstelle neuen Kunden name = row.get('customer_name', email.split('@')[0]) + # Neue Kunden werden immer als Fake erstellt in der Testphase + # TODO: Nach Testphase muss hier die Business-Logik angepasst werden + is_fake = True cur.execute(""" - INSERT INTO customers (name, email, created_at) - VALUES (%s, %s, %s) + INSERT INTO customers (name, email, is_fake, created_at) + VALUES (%s, %s, %s, %s) RETURNING id - """, (name, email, datetime.now())) + """, (name, email, is_fake, datetime.now())) customer_id = cur.fetchone()[0] customer_name = name else: customer_id = customer[0] customer_name = customer[1] + # Hole is_fake Status vom existierenden Kunden + cur.execute("SELECT is_fake FROM customers WHERE id = %s", (customer_id,)) + is_fake = cur.fetchone()[0] # Generiere Lizenzschlüssel license_key = row.get('license_key', generate_license_key()) - # Erstelle Lizenz + # Erstelle Lizenz - is_fake wird vom Kunden geerbt cur.execute(""" INSERT INTO licenses ( license_key, customer_id, @@ -340,7 +347,7 @@ def batch_import(): """, ( license_key, customer_id, row['license_type'], row['valid_from'], row['valid_until'], - int(row['device_limit']), row.get('is_fake', False), + int(row['device_limit']), is_fake, datetime.now() )) diff --git a/v2_adminpanel/routes/customer_routes.py b/v2_adminpanel/routes/customer_routes.py index 0de180e..1d6dcfb 100644 --- a/v2_adminpanel/routes/customer_routes.py +++ b/v2_adminpanel/routes/customer_routes.py @@ -415,7 +415,8 @@ def api_customer_licenses(customer_id): 'customer': { 'id': customer['id'], 'name': customer['name'], - 'email': customer['email'] + 'email': customer['email'], + 'is_fake': customer.get('is_fake', False) # Include the is_fake field }, 'licenses': licenses }) diff --git a/v2_adminpanel/routes/license_routes.py b/v2_adminpanel/routes/license_routes.py index fadb42b..f777f91 100644 --- a/v2_adminpanel/routes/license_routes.py +++ b/v2_adminpanel/routes/license_routes.py @@ -117,14 +117,13 @@ def edit_license(license_id): 'valid_from': request.form['valid_from'], 'valid_until': request.form['valid_until'], 'is_active': 'is_active' in request.form, - 'is_fake': 'is_fake' in request.form, 'device_limit': int(request.form.get('device_limit', 3)) } cur.execute(""" UPDATE licenses SET license_key = %s, license_type = %s, valid_from = %s, - valid_until = %s, is_active = %s, is_fake = %s, device_limit = %s + valid_until = %s, is_active = %s, device_limit = %s WHERE id = %s """, ( new_values['license_key'], @@ -132,7 +131,6 @@ def edit_license(license_id): new_values['valid_from'], new_values['valid_until'], new_values['is_active'], - new_values['is_fake'], new_values['device_limit'], license_id )) @@ -147,7 +145,6 @@ def edit_license(license_id): 'valid_from': str(current_license.get('valid_from', '')), 'valid_until': str(current_license.get('valid_until', '')), 'is_active': current_license.get('is_active'), - 'is_fake': current_license.get('is_fake'), 'device_limit': current_license.get('device_limit', 3) }, new_values=new_values) @@ -286,7 +283,7 @@ def create_license(): license_key = request.form["license_key"].upper() # Immer Großbuchstaben license_type = request.form["license_type"] valid_from = request.form["valid_from"] - is_fake = request.form.get("is_fake") == "on" # Checkbox value + # is_fake wird später vom Kunden geerbt # Berechne valid_until basierend auf Laufzeit duration = int(request.form.get("duration", 1)) @@ -338,7 +335,9 @@ def create_license(): flash(f'E-Mail bereits vergeben für Kunde: {existing[1]}', 'error') return redirect(url_for('licenses.create_license')) - # Kunde einfügen (erbt Test-Status von Lizenz) + # Neuer Kunde wird immer als Fake erstellt, da wir in der Testphase sind + # TODO: Nach Testphase muss hier die Business-Logik angepasst werden + is_fake = True cur.execute(""" INSERT INTO customers (name, email, is_fake, created_at) VALUES (%s, %s, %s, NOW()) @@ -359,9 +358,8 @@ def create_license(): return redirect(url_for('licenses.create_license')) customer_info = {'name': customer_data[0], 'email': customer_data[1]} - # Wenn Kunde Test-Kunde ist, Lizenz auch als Test markieren - if customer_data[2]: # is_fake des Kunden - is_fake = True + # Lizenz erbt immer den is_fake Status vom Kunden + is_fake = customer_data[2] # Lizenz hinzufügen cur.execute(""" diff --git a/v2_adminpanel/routes/monitoring_routes.py b/v2_adminpanel/routes/monitoring_routes.py index ebc200b..315b38f 100644 --- a/v2_adminpanel/routes/monitoring_routes.py +++ b/v2_adminpanel/routes/monitoring_routes.py @@ -191,11 +191,7 @@ def system_status(): # Check each service service_checks = [ {'name': 'License Server', 'url': 'http://license-server:8443/health', 'port': 8443}, - {'name': 'Auth Service', 'url': 'http://auth-service:5001/health', 'port': 5001}, - {'name': 'Analytics Service', 'url': 'http://analytics-service:5003/health', 'port': 5003}, - {'name': 'Admin API Service', 'url': 'http://admin-api-service:5004/health', 'port': 5004}, {'name': 'PostgreSQL', 'check': 'database'}, - {'name': 'Redis', 'url': 'http://redis:6379', 'check': 'redis'}, ] for service in service_checks: diff --git a/v2_adminpanel/templates/batch_form.html b/v2_adminpanel/templates/batch_form.html index 92ecf50..10036dc 100644 --- a/v2_adminpanel/templates/batch_form.html +++ b/v2_adminpanel/templates/batch_form.html @@ -26,6 +26,11 @@ {% endif %} {% endwith %} + +