Add DNS monitoring and refresh functionality

Introduce DNS monitoring: add DnsService (comprehensive DNS lookup, crt.sh discovery, Cloudflare detection, IP enrichment) and a new DnsRecord model to persist snapshots, manage diffs, and provide queries/stats. Update DomainController to support a dns_monitoring_enabled flag, refactor WHOIS/DNS refresh logic into performWhoisRefresh/performDnsRefresh, and add endpoints for refreshWhois, refreshDns and refreshAll; send notifications when DNS monitoring is toggled. Add UI templates/tabs for DNS, billing, notifications, overview, SSL and WHOIS and wire DNS data into the domain view; expose cached IP details. Add cron/check_dns.php and migration 027_add_dns_monitoring.sql (and include it in installer migration lists). Other tweaks: safer EmailHelper subject handling, TldRegistry search improvements, domain sorting using an effective status (expiring_soon), Discord channel null-safe fields, settings UI additions (domain_view_template and cron staleness warnings), and route/migration updates. This enables scheduled and manual DNS scans with persistent records and notifications.
This commit is contained in:
Hosteroid
2026-03-08 14:32:05 +02:00
parent db094d6d8b
commit 8559e903b9
29 changed files with 4493 additions and 100 deletions

View File

@@ -0,0 +1,279 @@
<!-- BILLING TAB CONTENT -->
<!-- Preview Banner -->
<div class="mb-3 bg-amber-50 dark:bg-amber-500/10 border border-amber-200 dark:border-amber-800 rounded-lg p-3">
<div class="flex items-center">
<i class="fas fa-flask text-amber-600 dark:text-amber-400 mr-2" style="font-size: 12px;"></i>
<div>
<p class="text-xs font-semibold text-amber-900 dark:text-amber-300">Preview</p>
<p class="text-xs text-amber-800 dark:text-amber-400 mt-0.5">Financial tracking is coming soon. This is a design preview with sample data and might change in the future.</p>
</div>
</div>
</div>
<!-- Purchase Source Info -->
<div class="bg-blue-50 dark:bg-blue-500/10 border border-blue-200 dark:border-blue-800 rounded-lg p-3 mb-3">
<div class="flex items-center justify-between">
<div class="flex items-center">
<i class="fas fa-shopping-cart text-blue-600 dark:text-blue-400 mr-2" style="font-size: 14px;"></i>
<div>
<p class="text-xs font-semibold text-blue-900 dark:text-blue-300">Purchased From</p>
<p class="text-xs text-blue-700 dark:text-blue-400 mt-0.5">Sedo Marketplace - $1,200.00 on Jan 15, 2020</p>
</div>
</div>
<a href="https://sedo.com/account" target="_blank" class="inline-flex items-center px-3 py-1.5 bg-blue-600 text-white text-xs rounded hover:bg-blue-700 transition-colors font-medium">
<i class="fas fa-external-link-alt mr-1" style="font-size: 9px;"></i>
Go to Seller
</a>
</div>
</div>
<!-- Financial Summary Cards -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-3 mb-3">
<div class="bg-white dark:bg-slate-800 rounded-lg border border-gray-200 dark:border-slate-700 p-3">
<div class="flex items-center justify-between">
<div>
<p class="text-xs font-medium text-gray-500 dark:text-slate-400 uppercase">Purchase Price</p>
<p class="text-lg font-semibold text-gray-900 dark:text-white mt-0.5">$1,200.00</p>
<p class="text-xs text-gray-500 dark:text-slate-400 mt-0.5">Jan 15, 2020</p>
</div>
<div class="w-8 h-8 bg-blue-50 dark:bg-blue-500/10 rounded flex items-center justify-center">
<i class="fas fa-shopping-cart text-blue-600 dark:text-blue-400" style="font-size: 14px;"></i>
</div>
</div>
</div>
<div class="bg-white dark:bg-slate-800 rounded-lg border border-gray-200 dark:border-slate-700 p-3">
<div class="flex items-center justify-between">
<div>
<p class="text-xs font-medium text-gray-500 dark:text-slate-400 uppercase">Total Renewals</p>
<p class="text-lg font-semibold text-orange-600 dark:text-orange-400 mt-0.5">$450.00</p>
<p class="text-xs text-gray-500 dark:text-slate-400 mt-0.5">3 payments</p>
</div>
<div class="w-8 h-8 bg-orange-50 dark:bg-orange-500/10 rounded flex items-center justify-center">
<i class="fas fa-redo text-orange-600 dark:text-orange-400" style="font-size: 14px;"></i>
</div>
</div>
</div>
<div class="bg-white dark:bg-slate-800 rounded-lg border border-gray-200 dark:border-slate-700 p-3">
<div class="flex items-center justify-between">
<div>
<p class="text-xs font-medium text-gray-500 dark:text-slate-400 uppercase">Total Invested</p>
<p class="text-lg font-semibold text-indigo-600 dark:text-indigo-400 mt-0.5">$1,700.00</p>
<p class="text-xs text-gray-500 dark:text-slate-400 mt-0.5">All expenses</p>
</div>
<div class="w-8 h-8 bg-indigo-50 dark:bg-indigo-500/10 rounded flex items-center justify-center">
<i class="fas fa-wallet text-indigo-600 dark:text-indigo-400" style="font-size: 14px;"></i>
</div>
</div>
</div>
<div class="bg-white dark:bg-slate-800 rounded-lg border border-gray-200 dark:border-slate-700 p-3">
<div class="flex items-center justify-between">
<div>
<p class="text-xs font-medium text-gray-500 dark:text-slate-400 uppercase">Profit/Loss</p>
<p class="text-lg font-semibold text-gray-400 dark:text-slate-500 mt-0.5">-</p>
<p class="text-xs text-gray-500 dark:text-slate-400 mt-0.5">Not sold</p>
</div>
<div class="w-8 h-8 bg-gray-50 dark:bg-slate-700 rounded flex items-center justify-center">
<i class="fas fa-chart-line text-gray-600 dark:text-slate-400" style="font-size: 14px;"></i>
</div>
</div>
</div>
</div>
<!-- Next Renewal Alert -->
<div class="bg-orange-50 dark:bg-orange-500/10 border border-orange-200 dark:border-orange-800 rounded-lg p-3 mb-3">
<div class="flex items-center justify-between">
<div class="flex items-center">
<i class="fas fa-calendar-alt text-orange-600 dark:text-orange-400 mr-2" style="font-size: 14px;"></i>
<div>
<p class="text-xs font-semibold text-orange-900 dark:text-orange-300">Next Renewal Due</p>
<p class="text-xs text-orange-700 dark:text-orange-400 mt-0.5">Jan 15, 2026 <span class="font-semibold">(65 days)</span> - Estimated: $150.00</p>
</div>
</div>
<button class="inline-flex items-center px-3 py-1.5 bg-orange-600 text-white text-xs rounded hover:bg-orange-700 transition-colors font-medium">
<i class="fas fa-plus mr-1" style="font-size: 9px;"></i>
Add Payment
</button>
</div>
</div>
<!-- Action Buttons -->
<div class="flex gap-2 mb-3">
<button class="inline-flex items-center px-3 py-2 bg-primary text-white text-xs rounded-lg hover:bg-primary-dark transition-colors font-medium"><i class="fas fa-plus mr-1.5" style="font-size: 10px;"></i>Add Transaction</button>
<button class="inline-flex items-center px-3 py-2 bg-green-600 text-white text-xs rounded-lg hover:bg-green-700 transition-colors font-medium"><i class="fas fa-hand-holding-usd mr-1.5" style="font-size: 10px;"></i>Record Sale</button>
<button class="inline-flex items-center px-3 py-2 bg-indigo-600 text-white text-xs rounded-lg hover:bg-indigo-700 transition-colors font-medium"><i class="fas fa-file-invoice-dollar mr-1.5" style="font-size: 10px;"></i>Export Report</button>
</div>
<!-- Transaction History (static sample) -->
<div class="bg-white dark:bg-slate-800 rounded-lg border border-gray-200 dark:border-slate-700 overflow-hidden">
<div class="px-4 py-2 border-b border-gray-200 dark:border-slate-700 bg-gray-50 dark:bg-slate-900">
<h3 class="text-xs font-semibold text-gray-700 dark:text-slate-300 uppercase tracking-wider flex items-center">
<i class="fas fa-history text-gray-400 dark:text-slate-500 mr-2" style="font-size: 10px;"></i>
Transaction History
<span class="ml-2 px-1.5 py-0.5 bg-primary text-white text-xs font-semibold rounded">5</span>
</h3>
</div>
<div class="overflow-x-auto">
<table class="min-w-full">
<thead class="bg-gray-50 dark:bg-slate-900">
<tr>
<th class="px-4 py-2 text-left text-xs font-semibold text-gray-600 dark:text-slate-400 uppercase">Date</th>
<th class="px-4 py-2 text-left text-xs font-semibold text-gray-600 dark:text-slate-400 uppercase">Type</th>
<th class="px-4 py-2 text-left text-xs font-semibold text-gray-600 dark:text-slate-400 uppercase">Amount</th>
<th class="px-4 py-2 text-left text-xs font-semibold text-gray-600 dark:text-slate-400 uppercase">Company/Seller</th>
<th class="px-4 py-2 text-left text-xs font-semibold text-gray-600 dark:text-slate-400 uppercase">Invoice</th>
<th class="px-4 py-2 text-left text-xs font-semibold text-gray-600 dark:text-slate-400 uppercase">Payment Method</th>
<th class="px-4 py-2 text-left text-xs font-semibold text-gray-600 dark:text-slate-400 uppercase">Status</th>
<th class="px-4 py-2 text-left text-xs font-semibold text-gray-600 dark:text-slate-400 uppercase">Notes</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-slate-700">
<tr class="hover:bg-gray-50 dark:hover:bg-slate-700">
<td class="px-4 py-3 text-xs text-gray-900 dark:text-white whitespace-nowrap">Jan 15, 2020</td>
<td class="px-4 py-3 whitespace-nowrap"><span class="inline-flex items-center px-2 py-0.5 bg-blue-100 dark:bg-blue-500/10 text-blue-800 dark:text-blue-400 text-xs font-semibold rounded"><i class="fas fa-shopping-cart mr-1" style="font-size: 9px;"></i>Purchase</span></td>
<td class="px-4 py-3 text-xs font-semibold text-gray-900 dark:text-white whitespace-nowrap">$1,200.00</td>
<td class="px-4 py-3 text-xs text-gray-900 dark:text-white whitespace-nowrap">Sedo Marketplace</td>
<td class="px-4 py-3 text-xs font-mono text-blue-600 dark:text-blue-400 whitespace-nowrap">SEDO-2020-001234</td>
<td class="px-4 py-3 text-xs text-gray-600 dark:text-slate-400 whitespace-nowrap">Credit Card</td>
<td class="px-4 py-3 whitespace-nowrap"><span class="inline-flex items-center px-2 py-0.5 bg-green-100 dark:bg-green-500/10 text-green-800 dark:text-green-400 text-xs font-semibold rounded"><i class="fas fa-check-circle mr-1" style="font-size: 8px;"></i>Paid</span></td>
<td class="px-4 py-3 text-xs text-gray-600 dark:text-slate-400">Initial domain purchase from Sedo marketplace</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Financial Charts -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-3 mt-3">
<!-- Expense Breakdown (Donut Chart) -->
<div class="bg-white dark:bg-slate-800 rounded-lg border border-gray-200 dark:border-slate-700 overflow-hidden">
<div class="px-4 py-2 border-b border-gray-200 dark:border-slate-700 bg-gray-50 dark:bg-slate-900">
<h3 class="text-xs font-semibold text-gray-700 dark:text-slate-300 uppercase tracking-wider flex items-center">
<i class="fas fa-chart-pie text-gray-400 dark:text-slate-500 mr-2" style="font-size: 10px;"></i>
Expense Breakdown
</h3>
</div>
<div class="p-4">
<canvas id="expenseChart" height="200"></canvas>
</div>
</div>
<!-- Expense Timeline (Line Chart) -->
<div class="bg-white dark:bg-slate-800 rounded-lg border border-gray-200 dark:border-slate-700 overflow-hidden">
<div class="px-4 py-2 border-b border-gray-200 dark:border-slate-700 bg-gray-50 dark:bg-slate-900">
<h3 class="text-xs font-semibold text-gray-700 dark:text-slate-300 uppercase tracking-wider flex items-center">
<i class="fas fa-chart-line text-gray-400 dark:text-slate-500 mr-2" style="font-size: 10px;"></i>
Expense Timeline
</h3>
</div>
<div class="p-4">
<canvas id="timelineChart" height="200"></canvas>
</div>
</div>
</div>
<!-- Chart.js CDN -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
<script>
// Expense Breakdown Donut Chart
const expenseCtx = document.getElementById('expenseChart')?.getContext('2d');
if (expenseCtx) {
new Chart(expenseCtx, {
type: 'doughnut',
data: {
labels: ['Initial Purchase', 'Renewals', 'Transfers', 'Other'],
datasets: [{
data: [1200, 450, 50, 0],
backgroundColor: [
'rgba(59, 130, 246, 0.8)',
'rgba(251, 146, 60, 0.8)',
'rgba(168, 85, 247, 0.8)',
'rgba(156, 163, 175, 0.8)'
],
borderColor: [
'rgb(59, 130, 246)',
'rgb(251, 146, 60)',
'rgb(168, 85, 247)',
'rgb(156, 163, 175)'
],
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom',
labels: {
padding: 15,
font: { size: 11 },
generateLabels: function(chart) {
const data = chart.data;
if (data.labels.length && data.datasets.length) {
return data.labels.map((label, i) => {
const value = data.datasets[0].data[i];
return { text: `${label}: $${value.toFixed(2)}`, fillStyle: data.datasets[0].backgroundColor[i], hidden: false, index: i };
});
}
return [];
}
}
},
tooltip: {
callbacks: {
label: function(context) {
const label = context.label || '';
const value = context.parsed || 0;
const total = context.dataset.data.reduce((a, b) => a + b, 0);
const percentage = ((value / total) * 100).toFixed(1);
return `${label}: $${value.toFixed(2)} (${percentage}%)`;
}
}
}
}
}
});
}
// Expense Timeline Chart
const timelineCtx = document.getElementById('timelineChart')?.getContext('2d');
if (timelineCtx) {
new Chart(timelineCtx, {
type: 'line',
data: {
labels: ['2020', '2021', '2022', '2023', '2024'],
datasets: [{
label: 'Cumulative Investment',
data: [1200, 1350, 1500, 1650, 1700],
borderColor: 'rgb(99, 102, 241)',
backgroundColor: 'rgba(99, 102, 241, 0.1)',
tension: 0.3,
fill: true,
pointRadius: 4,
pointHoverRadius: 6,
pointBackgroundColor: 'rgb(99, 102, 241)',
pointBorderColor: '#fff',
pointBorderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: { legend: { display: false }, tooltip: { callbacks: { label: function(context) { return `Total Invested: $${context.parsed.y.toFixed(2)}`; } } } },
scales: {
y: {
beginAtZero: true,
ticks: { callback: function(value) { return '$' + value; }, font: { size: 10 } },
grid: { color: 'rgba(0, 0, 0, 0.05)' }
},
x: {
ticks: { font: { size: 10 } },
grid: { display: false }
}
}
}
});
}
</script>