fresh start
This commit is contained in:
commit
6ce10f673e
10 changed files with 1652 additions and 0 deletions
316
templates/dns_records.html
Normal file
316
templates/dns_records.html
Normal file
|
@ -0,0 +1,316 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>DNS Records Viewer</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
h1 {
|
||||
color: #333;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.nav {
|
||||
margin: 20px 0;
|
||||
}
|
||||
.nav-link {
|
||||
display: inline-block;
|
||||
padding: 8px 16px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.nav-link:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.filter-section {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 15px;
|
||||
background-color: #f9f9f9;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.filter-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.filter-group label {
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.filter-group select, .filter-group input {
|
||||
padding: 8px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
min-width: 150px;
|
||||
}
|
||||
.filter-buttons {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
button {
|
||||
padding: 8px 16px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
button.reset {
|
||||
background-color: #f44336;
|
||||
}
|
||||
button:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
table-layout: fixed;
|
||||
}
|
||||
th, td {
|
||||
padding: 12px 15px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #ddd;
|
||||
word-break: break-word;
|
||||
}
|
||||
th {
|
||||
background-color: #f2f2f2;
|
||||
font-weight: bold;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
tr:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
/* History elements removed */
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 3px 7px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9em;
|
||||
font-weight: normal;
|
||||
}
|
||||
.record-type-badge {
|
||||
background-color: #e0e0e0;
|
||||
color: #333;
|
||||
}
|
||||
.sld-badge {
|
||||
background-color: #d1e7dd;
|
||||
color: #0f5132;
|
||||
}
|
||||
.tld-badge {
|
||||
background-color: #cfe2ff;
|
||||
color: #0a58ca;
|
||||
}
|
||||
.service-badge {
|
||||
background-color: #fff3cd;
|
||||
color: #664d03;
|
||||
}
|
||||
.api-section {
|
||||
margin-top: 30px;
|
||||
padding: 15px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 5px;
|
||||
}
|
||||
code {
|
||||
background: #f0f0f0;
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
font-family: monospace;
|
||||
}
|
||||
.count-badge {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
background-color: #6c757d;
|
||||
color: white;
|
||||
border-radius: 10px;
|
||||
font-size: 0.8em;
|
||||
margin-left: 10px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.tooltip {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.tooltip .tooltiptext {
|
||||
visibility: hidden;
|
||||
width: 200px;
|
||||
background-color: #555;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
bottom: 125%;
|
||||
left: 50%;
|
||||
margin-left: -100px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
.tooltip:hover .tooltiptext {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.filter-section {
|
||||
flex-direction: column;
|
||||
}
|
||||
.filter-group {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>DNS Entry Viewer</h1>
|
||||
|
||||
<div class="nav">
|
||||
<a href="/" class="nav-link">SLD View</a>
|
||||
<a href="/dns-records" class="nav-link">DNS Records</a>
|
||||
</div>
|
||||
|
||||
<form class="filter-section" method="get" action="/dns-records">
|
||||
<div class="filter-group">
|
||||
<label for="upload-filter">Upload:</label>
|
||||
<select id="upload-filter" name="upload_id">
|
||||
<option value="">All Uploads</option>
|
||||
{% for upload in uploads %}
|
||||
<option value="{{ upload.id }}" {% if request.query_params.get('upload_id') == upload.id %}selected{% endif %}>
|
||||
{{ upload.filename }} - {{ upload.timestamp.replace('T', ' ').split('.')[0] }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label for="record-type">Record Type:</label>
|
||||
<select id="record-type" name="record_type">
|
||||
<option value="">All Types</option>
|
||||
{% for type in unique_values.record_type %}
|
||||
<option value="{{ type }}" {% if request.query_params.get('record_type') == type %}selected{% endif %}>{{ type }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label for="record-class">Record Class:</label>
|
||||
<select id="record-class" name="record_class">
|
||||
<option value="">All Classes</option>
|
||||
{% for class in unique_values.record_class %}
|
||||
<option value="{{ class }}" {% if request.query_params.get('record_class') == class %}selected{% endif %}>{{ class }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label for="tld">TLD:</label>
|
||||
<select id="tld" name="tld">
|
||||
<option value="">All TLDs</option>
|
||||
{% for tld in unique_values.tld %}
|
||||
<option value="{{ tld }}" {% if request.query_params.get('tld') == tld %}selected{% endif %}>{{ tld }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label for="sld">SLD:</label>
|
||||
<select id="sld" name="sld">
|
||||
<option value="">All SLDs</option>
|
||||
{% for sld in unique_values.sld %}
|
||||
<option value="{{ sld }}" {% if request.query_params.get('sld') == sld %}selected{% endif %}>{{ sld }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<label for="domain-search">Domain Search:</label>
|
||||
<input type="text" id="domain-search" name="domain" placeholder="Enter domain name..." value="{{ request.query_params.get('domain', '') }}">
|
||||
</div>
|
||||
<div class="filter-buttons">
|
||||
<button type="submit">Apply Filters</button>
|
||||
<a href="/dns-records" class="reset-button" style="padding: 8px 16px; background-color: #f44336; color: white; text-decoration: none; border-radius: 4px; font-weight: bold; display: inline-block;">Reset</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<h2>DNS Records <span class="count-badge">{{ entries|length }}</span></h2>
|
||||
|
||||
{% if entries %}
|
||||
<table id="dns-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 20%;">Domain</th>
|
||||
<th style="width: 8%;">TTL</th>
|
||||
<th style="width: 7%;">Class</th>
|
||||
<th style="width: 8%;">Type</th>
|
||||
<th style="width: 40%;">Data</th>
|
||||
<th style="width: 17%;">Last Updated</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in entries %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if entry.get('service') %}
|
||||
<span class="badge service-badge">{{ entry.service }}</span>
|
||||
{% endif %}
|
||||
{% if entry.get('subdomain') %}
|
||||
{{ entry.subdomain }}.
|
||||
{% endif %}
|
||||
<span class="badge sld-badge">{{ entry.sld }}</span>.
|
||||
<span class="badge tld-badge">{{ entry.tld }}</span>
|
||||
</td>
|
||||
<td>{{ entry.ttl }}</td>
|
||||
<td>{{ entry.record_class }}</td>
|
||||
<td><span class="badge record-type-badge">{{ entry.record_type }}</span></td>
|
||||
<td class="tooltip">
|
||||
{{ entry.record_data }}
|
||||
{% if entry.record_type == "SOA" or entry.record_data|length > 50 %}
|
||||
<span class="tooltiptext">{{ entry.record_data }}</span>
|
||||
{% endif %}
|
||||
|
||||
<!-- History elements removed -->
|
||||
</td>
|
||||
<td>
|
||||
{{ entry.timestamp.replace('T', ' ').split('.')[0] }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No DNS entries found. Please upload a CSV file to get started.</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="api-section">
|
||||
<h2>API Endpoints</h2>
|
||||
<p>Get all DNS entries: <code>/api/dns</code></p>
|
||||
<p>Get filtered DNS entries: <code>/api/dns?record_type=A&tld=de</code></p>
|
||||
<p>Filter by upload: <code>/api/dns?upload_id={upload_id}</code></p>
|
||||
<p>Get unique filter values: <code>/api/dns/types</code></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- All JavaScript removed, using server-side FastAPI for filtering -->
|
||||
<script>
|
||||
// No JavaScript needed - all filtering handled by FastAPI
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
259
templates/index.html
Normal file
259
templates/index.html
Normal file
|
@ -0,0 +1,259 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Domain Viewer</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
h1, h2 {
|
||||
color: #333;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.nav {
|
||||
margin: 20px 0;
|
||||
}
|
||||
.nav-link {
|
||||
display: inline-block;
|
||||
padding: 8px 16px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
font-weight: bold;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.nav-link:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
th, td {
|
||||
padding: 12px 15px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
th {
|
||||
background-color: #f2f2f2;
|
||||
font-weight: bold;
|
||||
}
|
||||
tr:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.sld-badge {
|
||||
display: inline-block;
|
||||
padding: 3px 7px;
|
||||
background-color: #d1e7dd;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9em;
|
||||
color: #0f5132;
|
||||
}
|
||||
.tld-badge {
|
||||
display: inline-block;
|
||||
padding: 3px 7px;
|
||||
background-color: #cfe2ff;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9em;
|
||||
color: #0a58ca;
|
||||
}
|
||||
.api-section {
|
||||
margin-top: 30px;
|
||||
padding: 15px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 5px;
|
||||
}
|
||||
code {
|
||||
background: #f0f0f0;
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
font-family: monospace;
|
||||
}
|
||||
.upload-form {
|
||||
background-color: #f9f9f9;
|
||||
padding: 20px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.form-control {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.btn {
|
||||
padding: 10px 15px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
}
|
||||
.btn:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
.delete-button {
|
||||
display: inline-block;
|
||||
padding: 6px 12px;
|
||||
background-color: #dc3545;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9em;
|
||||
font-weight: bold;
|
||||
}
|
||||
.delete-button:hover {
|
||||
background-color: #c82333;
|
||||
color: white;
|
||||
}
|
||||
.upload-history {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
.filter-form {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.filter-select {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Domain Management System</h1>
|
||||
|
||||
<div class="nav">
|
||||
<a href="/" class="nav-link">SLD View</a>
|
||||
<a href="/dns-records" class="nav-link">DNS Records</a>
|
||||
</div>
|
||||
|
||||
<div class="upload-form">
|
||||
<h2>Upload CSV File</h2>
|
||||
<form action="/upload" method="post" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label for="file">CSV File:</label>
|
||||
<input type="file" id="file" name="file" class="form-control" accept=".csv" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="description">Description (optional):</label>
|
||||
<input type="text" id="description" name="description" class="form-control" placeholder="Enter a description for this upload">
|
||||
</div>
|
||||
<button type="submit" class="btn">Upload</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% if uploads %}
|
||||
<div class="upload-history">
|
||||
<h2>Upload History</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Filename</th>
|
||||
<th>Description</th>
|
||||
<th>Domains</th>
|
||||
<th>DNS Records</th>
|
||||
<th>View</th>
|
||||
<th>Delete</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for upload in uploads %}
|
||||
<tr>
|
||||
<td>{{ upload.timestamp.replace('T', ' ').split('.')[0] }}</td>
|
||||
<td>{{ upload.filename }}</td>
|
||||
<td>{{ upload.description or "N/A" }}</td>
|
||||
<td>{{ upload.domains_count }}</td>
|
||||
<td>{{ upload.records_count }}</td>
|
||||
<td>
|
||||
<a href="/?upload_id={{ upload.id }}" class="nav-link">View</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="/delete-upload/{{ upload.id }}" class="delete-button" onclick="return confirm('Are you sure you want to delete this upload? This will remove all associated domain and DNS records.')">Delete</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="filter-form">
|
||||
<h2>Domain List</h2>
|
||||
<form id="filterForm" method="get">
|
||||
<label for="upload_filter">Filter by upload:</label>
|
||||
<select id="upload_filter" name="upload_id" class="filter-select" onchange="this.form.submit()">
|
||||
<option value="">All uploads</option>
|
||||
{% for upload in uploads %}
|
||||
<option value="{{ upload.id }}" {% if request.query_params.get('upload_id') == upload.id %}selected{% endif %}>
|
||||
{{ upload.filename }} - {{ upload.timestamp.replace('T', ' ').split('.')[0] }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="api-section">
|
||||
<h3>API Endpoints</h3>
|
||||
<p>Get all uploads: <code>/api/uploads</code></p>
|
||||
<p>Get all domains: <code>/api/slds</code></p>
|
||||
<p>Get domains by SLD: <code>/api/slds/{sld}</code></p>
|
||||
<p>Filter by upload: <code>/api/slds?upload_id={upload_id}</code></p>
|
||||
</div>
|
||||
|
||||
{% if domains %}
|
||||
<p>Found {{ domains|length }} domains{% if request.query_params.get('upload_id') %} in this upload{% endif %}.</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>SLD</th>
|
||||
<th>TLD</th>
|
||||
<th>Subdomain</th>
|
||||
<th>Full Domain</th>
|
||||
<th>Upload Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in domains %}
|
||||
<tr>
|
||||
<td><span class="sld-badge">{{ item.sld }}</span></td>
|
||||
<td><span class="tld-badge">{{ item.tld }}</span></td>
|
||||
<td>{{ item.get('subdomain', 'N/A') }}</td>
|
||||
<td>{{ item.full_domain }}</td>
|
||||
<td>{{ item.timestamp.replace('T', ' ').split('.')[0] if item.get('timestamp') else 'N/A' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p>No domains found. Please upload a CSV file to get started.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue