Unnötige Reddis und Rabbit MQ entfernt
Dieser Commit ist enthalten in:
72
v2_adminpanel/migrations/add_fake_constraint.sql
Normale Datei
72
v2_adminpanel/migrations/add_fake_constraint.sql
Normale Datei
@@ -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 $$;
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
))
|
||||
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
@@ -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("""
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -26,6 +26,11 @@
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<!-- Customer Type Indicator -->
|
||||
<div id="customerTypeIndicator" class="alert d-none mb-3" role="alert">
|
||||
<i class="fas fa-info-circle"></i> <span id="customerTypeMessage"></span>
|
||||
</div>
|
||||
|
||||
<form method="post" action="{{ url_for('batch.batch_create') }}" accept-charset="UTF-8">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-12">
|
||||
@@ -173,14 +178,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Test Data Checkbox -->
|
||||
<div class="form-check mt-3">
|
||||
<input class="form-check-input" type="checkbox" id="isFake" name="is_fake">
|
||||
<label class="form-check-label" for="isFake">
|
||||
<i class="fas fa-flask"></i> Als Fake-Daten markieren
|
||||
<small class="text-muted">(wird von der Software ignoriert)</small>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary btn-lg">
|
||||
@@ -316,6 +313,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
emailDiv.style.display = 'block';
|
||||
nameInput.required = true;
|
||||
emailInput.required = true;
|
||||
|
||||
// Zeige Indikator für neuen Kunden
|
||||
showCustomerTypeIndicator('new');
|
||||
} else {
|
||||
// Verstecke Eingabefelder bei bestehendem Kunden
|
||||
nameDiv.style.display = 'none';
|
||||
@@ -324,6 +324,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
emailInput.required = false;
|
||||
nameInput.value = '';
|
||||
emailInput.value = '';
|
||||
|
||||
// Zeige Indikator basierend auf Kundendaten
|
||||
if (e.params.data.is_fake !== undefined) {
|
||||
showCustomerTypeIndicator(e.params.data.is_fake ? 'fake' : 'real');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -333,6 +338,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
document.getElementById('emailDiv').style.display = 'none';
|
||||
document.getElementById('customerName').required = false;
|
||||
document.getElementById('email').required = false;
|
||||
hideCustomerTypeIndicator();
|
||||
});
|
||||
|
||||
// Resource Availability Check
|
||||
@@ -460,5 +466,28 @@ function updateBatchResourceStatus(data, totalDomains, totalIpv4, totalPhones, q
|
||||
submitButton.classList.remove('btn-secondary');
|
||||
}
|
||||
}
|
||||
|
||||
// Funktion zur Anzeige des Kundentyp-Indikators
|
||||
function showCustomerTypeIndicator(type) {
|
||||
const indicator = document.getElementById('customerTypeIndicator');
|
||||
const message = document.getElementById('customerTypeMessage');
|
||||
|
||||
indicator.classList.remove('d-none', 'alert-info', 'alert-warning', 'alert-success');
|
||||
|
||||
if (type === 'new') {
|
||||
indicator.classList.add('alert-info');
|
||||
message.textContent = 'Neue Kunden werden in der Testphase als TEST-Kunden erstellt. Alle Batch-Lizenzen werden automatisch als TEST-Lizenzen markiert.';
|
||||
} else if (type === 'fake') {
|
||||
indicator.classList.add('alert-warning');
|
||||
message.textContent = 'Dies ist ein TEST-Kunde. Alle Batch-Lizenzen werden automatisch als TEST-Lizenzen markiert und von der Software ignoriert.';
|
||||
} else if (type === 'real') {
|
||||
indicator.classList.add('alert-success');
|
||||
message.textContent = 'Dies ist ein PRODUKTIV-Kunde. Alle Batch-Lizenzen werden als produktive Lizenzen erstellt.';
|
||||
}
|
||||
}
|
||||
|
||||
function hideCustomerTypeIndicator() {
|
||||
document.getElementById('customerTypeIndicator').classList.add('d-none');
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -65,12 +65,16 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-check mt-3">
|
||||
<input class="form-check-input" type="checkbox" id="isTest" name="is_fake" {% if license.is_fake %}checked{% endif %}>
|
||||
<label class="form-check-label" for="isTest">
|
||||
<i class="fas fa-flask"></i> Als Fake-Daten markieren
|
||||
<small class="text-muted">(wird von der Software ignoriert)</small>
|
||||
</label>
|
||||
<div class="alert {% if license.is_fake %}alert-warning{% else %}alert-success{% endif %} mt-3" role="alert">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<strong>Status:</strong>
|
||||
{% if license.is_fake %}
|
||||
TEST-Lizenz (wird von der Software ignoriert)
|
||||
{% else %}
|
||||
PRODUKTIV-Lizenz
|
||||
{% endif %}
|
||||
<br>
|
||||
<small class="text-muted">Der Status wird vom Kunden geerbt und kann nicht direkt geändert werden.</small>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
<a href="{{ url_for('customers.customers_licenses') }}" class="btn btn-secondary">← Zurück zur Übersicht</a>
|
||||
</div>
|
||||
|
||||
<!-- Customer Type Indicator -->
|
||||
<div id="customerTypeIndicator" class="alert d-none mb-3" role="alert">
|
||||
<i class="fas fa-info-circle"></i> <span id="customerTypeMessage"></span>
|
||||
</div>
|
||||
|
||||
<form method="post" action="{{ url_for('licenses.create_license') }}" accept-charset="UTF-8">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-12">
|
||||
@@ -152,14 +157,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Test Data Checkbox -->
|
||||
<div class="form-check mt-3">
|
||||
<input class="form-check-input" type="checkbox" id="isTest" name="is_fake">
|
||||
<label class="form-check-label" for="isTest">
|
||||
<i class="fas fa-flask"></i> Als Fake-Daten markieren
|
||||
<small class="text-muted">(wird von der Software ignoriert)</small>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-primary">➕ Lizenz erstellen</button>
|
||||
@@ -368,6 +365,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
emailDiv.style.display = 'block';
|
||||
nameInput.required = true;
|
||||
emailInput.required = true;
|
||||
|
||||
// Zeige Indikator für neuen Kunden
|
||||
showCustomerTypeIndicator('new');
|
||||
} else {
|
||||
// Verstecke Eingabefelder bei bestehendem Kunden
|
||||
nameDiv.style.display = 'none';
|
||||
@@ -376,6 +376,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
emailInput.required = false;
|
||||
nameInput.value = '';
|
||||
emailInput.value = '';
|
||||
|
||||
// Zeige Indikator basierend auf Kundendaten
|
||||
if (e.params.data.is_fake !== undefined) {
|
||||
showCustomerTypeIndicator(e.params.data.is_fake ? 'fake' : 'real');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -385,6 +390,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
document.getElementById('emailDiv').style.display = 'none';
|
||||
document.getElementById('customerName').required = false;
|
||||
document.getElementById('email').required = false;
|
||||
hideCustomerTypeIndicator();
|
||||
});
|
||||
|
||||
// Resource Availability Check
|
||||
@@ -529,5 +535,28 @@ function updateResourceStatus(data, domainCount, ipv4Count, phoneCount) {
|
||||
statusElement.textContent = message;
|
||||
statusElement.className = hasIssue ? 'text-danger' : 'text-success';
|
||||
}
|
||||
|
||||
// Funktion zur Anzeige des Kundentyp-Indikators
|
||||
function showCustomerTypeIndicator(type) {
|
||||
const indicator = document.getElementById('customerTypeIndicator');
|
||||
const message = document.getElementById('customerTypeMessage');
|
||||
|
||||
indicator.classList.remove('d-none', 'alert-info', 'alert-warning', 'alert-success');
|
||||
|
||||
if (type === 'new') {
|
||||
indicator.classList.add('alert-info');
|
||||
message.textContent = 'Neue Kunden werden in der Testphase als TEST-Kunden erstellt. Die Lizenz wird automatisch als TEST-Lizenz markiert.';
|
||||
} else if (type === 'fake') {
|
||||
indicator.classList.add('alert-warning');
|
||||
message.textContent = 'Dies ist ein TEST-Kunde. Die Lizenz wird automatisch als TEST-Lizenz markiert und von der Software ignoriert.';
|
||||
} else if (type === 'real') {
|
||||
indicator.classList.add('alert-success');
|
||||
message.textContent = 'Dies ist ein PRODUKTIV-Kunde. Die Lizenz wird als produktive Lizenz erstellt.';
|
||||
}
|
||||
}
|
||||
|
||||
function hideCustomerTypeIndicator() {
|
||||
document.getElementById('customerTypeIndicator').classList.add('d-none');
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren