439 Zeilen
16 KiB
HTML
439 Zeilen
16 KiB
HTML
{% 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 %} |