Files
v2-Docker/v2_adminpanel/templates/add_resources.html
Claude Project Manager 0d7d888502 Initial commit
2025-07-05 17:51:16 +02:00

439 Zeilen
16 KiB
HTML

Diese Datei enthält unsichtbare Unicode-Zeichen

Diese Datei enthält unsichtbare Unicode-Zeichen, die für Menschen nicht unterscheidbar sind, aber von einem Computer unterschiedlich verarbeitet werden können. Wenn du glaubst, dass das absichtlich so ist, kannst du diese Warnung ignorieren. Benutze den „Escape“-Button, um versteckte Zeichen anzuzeigen.

{% extends "base.html" %}
{% block title %}Ressourcen hinzufügen{% endblock %}
{% block extra_css %}
<style>
/* Card Styling */
.main-card {
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
border: none;
}
/* Preview Section */
.preview-card {
background-color: #f8f9fa;
border: 2px dashed #dee2e6;
transition: all 0.3s ease;
}
.preview-card.active {
border-color: #28a745;
background-color: #e8f5e9;
}
/* Format Examples */
.example-card {
height: 100%;
transition: transform 0.2s ease;
}
.example-card:hover {
transform: translateY(-3px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
.example-code {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 0.25rem;
padding: 1rem;
font-family: 'Courier New', monospace;
font-size: 0.875rem;
margin: 0;
}
/* Resource Type Selector */
.resource-type-selector {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
margin-bottom: 1.5rem;
}
.resource-type-option {
padding: 1.5rem;
border: 2px solid #e9ecef;
border-radius: 0.5rem;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
background-color: #fff;
}
.resource-type-option:hover {
border-color: #007bff;
background-color: #f8f9fa;
}
.resource-type-option.selected {
border-color: #007bff;
background-color: #e7f3ff;
}
.resource-type-option .icon {
font-size: 2.5rem;
margin-bottom: 0.5rem;
}
/* Textarea Styling */
.resource-input {
font-family: 'Courier New', monospace;
background-color: #f8f9fa;
border: 2px solid #dee2e6;
transition: border-color 0.3s ease;
}
.resource-input:focus {
background-color: #fff;
border-color: #80bdff;
}
/* Stats Display */
.stats-display {
display: flex;
justify-content: space-around;
padding: 1rem;
background-color: #f8f9fa;
border-radius: 0.5rem;
}
.stat-item {
text-align: center;
}
.stat-value {
font-size: 2rem;
font-weight: bold;
color: #007bff;
}
.stat-label {
font-size: 0.875rem;
color: #6c757d;
text-transform: uppercase;
}
</style>
{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<div class="row justify-content-center">
<div class="col-lg-10">
<!-- Header -->
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h1 class="mb-0">Ressourcen hinzufügen</h1>
<p class="text-muted mb-0">Fügen Sie neue Domains, IPs oder Telefonnummern zum Pool hinzu</p>
</div>
<a href="{{ url_for('resources.resources', show_test=show_test) }}" class="btn btn-secondary">
← Zurück zur Übersicht
</a>
</div>
<form method="post" action="{{ url_for('resources.add_resources', show_test=show_test) }}" id="addResourceForm">
<!-- Resource Type Selection -->
<div class="card main-card mb-4">
<div class="card-header bg-white">
<h5 class="mb-0">1⃣ Ressourcentyp wählen</h5>
</div>
<div class="card-body">
<input type="hidden" name="resource_type" id="resource_type" required>
<div class="resource-type-selector">
<div class="resource-type-option" data-type="domain">
<div class="icon">🌐</div>
<h6 class="mb-0">Domain</h6>
<small class="text-muted">Webseiten-Adressen</small>
</div>
<div class="resource-type-option" data-type="ipv4">
<div class="icon">🖥️</div>
<h6 class="mb-0">IPv4</h6>
<small class="text-muted">IP-Adressen</small>
</div>
<div class="resource-type-option" data-type="phone">
<div class="icon">📱</div>
<h6 class="mb-0">Telefon</h6>
<small class="text-muted">Telefonnummern</small>
</div>
</div>
</div>
</div>
<!-- Resource Input -->
<div class="card main-card mb-4">
<div class="card-header bg-white">
<h5 class="mb-0">2⃣ Ressourcen eingeben</h5>
</div>
<div class="card-body">
<div class="mb-3">
<label for="resources_text" class="form-label">
Ressourcen (eine pro Zeile)
</label>
<textarea name="resources_text"
id="resources_text"
class="form-control resource-input"
rows="12"
required
placeholder="Bitte wählen Sie zuerst einen Ressourcentyp aus..."></textarea>
<div class="form-text">
<i class="fas fa-info-circle"></i>
Geben Sie jede Ressource in eine neue Zeile ein. Duplikate werden automatisch übersprungen.
</div>
</div>
<!-- Live Preview -->
<div class="preview-card p-3" id="preview">
<h6 class="mb-3">📊 Live-Vorschau</h6>
<div class="stats-display">
<div class="stat-item">
<div class="stat-value" id="validCount">0</div>
<div class="stat-label">Gültig</div>
</div>
<div class="stat-item">
<div class="stat-value text-warning" id="duplicateCount">0</div>
<div class="stat-label">Duplikate</div>
</div>
<div class="stat-item">
<div class="stat-value text-danger" id="invalidCount">0</div>
<div class="stat-label">Ungültig</div>
</div>
</div>
<div id="errorList" class="mt-3" style="display: none;">
<div class="alert alert-danger">
<strong>Fehler gefunden:</strong>
<ul id="errorMessages" class="mb-0"></ul>
</div>
</div>
</div>
</div>
</div>
<!-- Format Examples -->
<div class="card main-card mb-4">
<div class="card-header bg-white">
<h5 class="mb-0">💡 Format-Beispiele</h5>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-4">
<div class="card example-card h-100">
<div class="card-body">
<h6 class="card-title">
<span class="text-primary">🌐</span> Domains
</h6>
<pre class="example-code">example.com
test-domain.net
meine-seite.de
subdomain.example.org
my-website.io</pre>
<div class="alert alert-info mt-3 mb-0">
<small>
<strong>Format:</strong> Ohne http(s)://<br>
<strong>Erlaubt:</strong> Buchstaben, Zahlen, Punkt, Bindestrich
</small>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card example-card h-100">
<div class="card-body">
<h6 class="card-title">
<span class="text-primary">🖥️</span> IPv4-Adressen
</h6>
<pre class="example-code">192.168.1.10
192.168.1.11
10.0.0.1
172.16.0.5
8.8.8.8</pre>
<div class="alert alert-info mt-3 mb-0">
<small>
<strong>Format:</strong> xxx.xxx.xxx.xxx<br>
<strong>Bereich:</strong> 0-255 pro Oktett
</small>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card example-card h-100">
<div class="card-body">
<h6 class="card-title">
<span class="text-primary">📱</span> Telefonnummern
</h6>
<pre class="example-code">+491701234567
+493012345678
+33123456789
+441234567890
+12125551234</pre>
<div class="alert alert-info mt-3 mb-0">
<small>
<strong>Format:</strong> Mit Ländervorwahl<br>
<strong>Start:</strong> Immer mit +
</small>
</div>
</div>
</div>
</div>
</div>
<!-- Test Data Option -->
<div class="form-check mt-3">
<input class="form-check-input" type="checkbox" id="is_test" name="is_test" {% if show_test %}checked{% endif %}>
<label class="form-check-label" for="is_test">
Als Testdaten markieren
</label>
</div>
</div>
</div>
<!-- Submit Buttons -->
<div class="d-flex justify-content-between">
<button type="button" class="btn btn-secondary" onclick="window.location.href='{{ url_for('resources.resources', show_test=show_test) }}'">
<i class="fas fa-times"></i> Abbrechen
</button>
<button type="submit" class="btn btn-success btn-lg" id="submitBtn" disabled>
<i class="fas fa-plus-circle"></i> Ressourcen hinzufügen
</button>
</div>
</form>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const typeOptions = document.querySelectorAll('.resource-type-option');
const typeInput = document.getElementById('resource_type');
const textArea = document.getElementById('resources_text');
const submitBtn = document.getElementById('submitBtn');
const form = document.getElementById('addResourceForm');
// Preview elements
const validCount = document.getElementById('validCount');
const duplicateCount = document.getElementById('duplicateCount');
const invalidCount = document.getElementById('invalidCount');
const errorList = document.getElementById('errorList');
const errorMessages = document.getElementById('errorMessages');
const preview = document.getElementById('preview');
let selectedType = null;
// Placeholder texts for different types
const placeholders = {
domain: `example.com
test-site.net
my-domain.org
subdomain.example.com`,
ipv4: `192.168.1.1
10.0.0.1
172.16.0.1
8.8.8.8`,
phone: `+491234567890
+4930123456
+33123456789
+12125551234`
};
// Resource type selection
typeOptions.forEach(option => {
option.addEventListener('click', function() {
typeOptions.forEach(opt => opt.classList.remove('selected'));
this.classList.add('selected');
selectedType = this.dataset.type;
typeInput.value = selectedType;
textArea.placeholder = placeholders[selectedType] || '';
textArea.disabled = false;
updatePreview();
});
});
// Validation functions
function validateDomain(domain) {
const domainRegex = /^(?!:\/\/)([a-zA-Z0-9-_]+\.)*[a-zA-Z0-9][a-zA-Z0-9-_]+\.[a-zA-Z]{2,}$/;
return domainRegex.test(domain);
}
function validateIPv4(ip) {
const ipRegex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
return ipRegex.test(ip);
}
function validatePhone(phone) {
const phoneRegex = /^\+[1-9]\d{6,14}$/;
return phoneRegex.test(phone);
}
// Update preview function
function updatePreview() {
if (!selectedType) {
preview.classList.remove('active');
submitBtn.disabled = true;
return;
}
const lines = textArea.value.split('\n').filter(line => line.trim() !== '');
const uniqueResources = new Set();
const errors = [];
let valid = 0;
let duplicates = 0;
let invalid = 0;
lines.forEach((line, index) => {
const trimmed = line.trim();
if (uniqueResources.has(trimmed)) {
duplicates++;
return;
}
let isValid = false;
switch(selectedType) {
case 'domain':
isValid = validateDomain(trimmed);
break;
case 'ipv4':
isValid = validateIPv4(trimmed);
break;
case 'phone':
isValid = validatePhone(trimmed);
break;
}
if (isValid) {
valid++;
uniqueResources.add(trimmed);
} else {
invalid++;
errors.push(`Zeile ${index + 1}: "${trimmed}"`);
}
});
// Update counts
validCount.textContent = valid;
duplicateCount.textContent = duplicates;
invalidCount.textContent = invalid;
// Show/hide error list
if (errors.length > 0) {
errorList.style.display = 'block';
errorMessages.innerHTML = errors.map(err => `<li>${err}</li>`).join('');
} else {
errorList.style.display = 'none';
}
// Enable/disable submit button
submitBtn.disabled = valid === 0 || invalid > 0;
// Update preview appearance
if (lines.length > 0) {
preview.classList.add('active');
} else {
preview.classList.remove('active');
}
}
// Live validation
textArea.addEventListener('input', updatePreview);
// Form submission
form.addEventListener('submit', function(e) {
if (submitBtn.disabled) {
e.preventDefault();
alert('Bitte beheben Sie alle Fehler bevor Sie fortfahren.');
}
});
// Initial state
textArea.disabled = true;
});
</script>
{% endblock %}