2025-10-08 14:23:07 +03:00
|
|
|
<?php
|
|
|
|
|
$title = $title ?? 'Import Progress';
|
|
|
|
|
$pageTitle = $title;
|
|
|
|
|
$pageDescription = 'Progressive data import with real-time progress';
|
|
|
|
|
$pageIcon = 'fas fa-tasks';
|
|
|
|
|
ob_start();
|
|
|
|
|
?>
|
|
|
|
|
|
|
|
|
|
<div class="max-w-4xl mx-auto">
|
|
|
|
|
<!-- Header -->
|
|
|
|
|
<div class="mb-6">
|
|
|
|
|
<div class="flex items-center justify-between">
|
|
|
|
|
<div>
|
|
|
|
|
<h1 class="text-2xl font-bold text-gray-900"><?= htmlspecialchars($title) ?></h1>
|
|
|
|
|
<p class="text-gray-600 mt-1">
|
|
|
|
|
<?php
|
|
|
|
|
$descriptions = [
|
|
|
|
|
'tld_list' => 'Importing complete TLD list from IANA',
|
|
|
|
|
'rdap' => 'Importing RDAP servers for existing TLDs',
|
|
|
|
|
'whois' => 'Importing WHOIS & Registry data via RDAP API (with HTML fallback)',
|
|
|
|
|
'check_updates' => 'Checking for IANA updates',
|
|
|
|
|
'complete_workflow' => 'Complete TLD import workflow (TLD List → RDAP → WHOIS & Registry Data)'
|
|
|
|
|
];
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
echo htmlspecialchars($descriptions[$import_type] ?? 'Processing import');
|
2025-10-08 14:23:07 +03:00
|
|
|
?>
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
<a href="/tld-registry" class="inline-flex items-center px-4 py-2 border border-gray-300 text-gray-700 text-sm rounded-lg hover:bg-gray-50 transition-colors font-medium">
|
|
|
|
|
<i class="fas fa-arrow-left mr-2"></i>
|
|
|
|
|
Back to TLD Registry
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Progress Card -->
|
|
|
|
|
<div class="bg-white rounded-lg border border-gray-200 p-6 mb-6">
|
|
|
|
|
<div class="flex items-center justify-between mb-4">
|
|
|
|
|
<h2 class="text-lg font-semibold text-gray-900">Import Status</h2>
|
|
|
|
|
<div id="status-badge" class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-yellow-100 text-yellow-800">
|
|
|
|
|
<i class="fas fa-clock mr-2"></i>
|
|
|
|
|
<span id="status-text">Starting...</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Progress Bar -->
|
|
|
|
|
<div class="mb-4">
|
|
|
|
|
<div class="flex justify-between text-sm text-gray-600 mb-2">
|
|
|
|
|
<span id="progress-text">0 of 0 items processed</span>
|
|
|
|
|
<span id="percentage-text">0%</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="w-full bg-gray-200 rounded-full h-3">
|
|
|
|
|
<div id="progress-bar" class="bg-blue-600 h-3 rounded-full transition-all duration-300" style="width: 0%"></div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Step Progress (for complete workflow) -->
|
|
|
|
|
<div id="step-progress" class="mb-4" style="display: none;">
|
|
|
|
|
<div class="text-sm text-gray-600 mb-2">Workflow Steps:</div>
|
|
|
|
|
<div class="grid grid-cols-3 gap-2">
|
|
|
|
|
<div class="step-item bg-gray-100 rounded-lg p-2 text-center">
|
|
|
|
|
<div class="text-xs font-medium text-gray-600">Step 1</div>
|
|
|
|
|
<div class="text-xs text-gray-500">TLD List</div>
|
|
|
|
|
<div id="step-1-status" class="text-xs text-gray-400">Pending</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="step-item bg-gray-100 rounded-lg p-2 text-center">
|
|
|
|
|
<div class="text-xs font-medium text-gray-600">Step 2</div>
|
|
|
|
|
<div class="text-xs text-gray-500">RDAP</div>
|
|
|
|
|
<div id="step-2-status" class="text-xs text-gray-400">Pending</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="step-item bg-gray-100 rounded-lg p-2 text-center">
|
|
|
|
|
<div class="text-xs font-medium text-gray-600">Step 3</div>
|
|
|
|
|
<div class="text-xs text-gray-500">WHOIS & Registry</div>
|
|
|
|
|
<div id="step-3-status" class="text-xs text-gray-400">Pending</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Statistics -->
|
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
|
|
|
|
<div class="bg-gray-50 rounded-lg p-4">
|
|
|
|
|
<div class="flex items-center">
|
|
|
|
|
<div class="w-10 h-10 bg-blue-100 rounded-lg flex items-center justify-center mr-3">
|
|
|
|
|
<i class="fas fa-list text-blue-600"></i>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<p class="text-sm text-gray-500">Total</p>
|
|
|
|
|
<p id="total-count" class="text-xl font-semibold text-gray-900">0</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="bg-gray-50 rounded-lg p-4">
|
|
|
|
|
<div class="flex items-center">
|
|
|
|
|
<div class="w-10 h-10 bg-green-100 rounded-lg flex items-center justify-center mr-3">
|
|
|
|
|
<i class="fas fa-check text-green-600"></i>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<p class="text-sm text-gray-500">Processed</p>
|
|
|
|
|
<p id="processed-count" class="text-xl font-semibold text-gray-900">0</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="bg-gray-50 rounded-lg p-4">
|
|
|
|
|
<div class="flex items-center">
|
|
|
|
|
<div class="w-10 h-10 bg-red-100 rounded-lg flex items-center justify-center mr-3">
|
|
|
|
|
<i class="fas fa-times text-red-600"></i>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<p class="text-sm text-gray-500">Failed</p>
|
|
|
|
|
<p id="failed-count" class="text-xl font-semibold text-gray-900">0</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="bg-gray-50 rounded-lg p-4">
|
|
|
|
|
<div class="flex items-center">
|
|
|
|
|
<div class="w-10 h-10 bg-orange-100 rounded-lg flex items-center justify-center mr-3">
|
|
|
|
|
<i class="fas fa-hourglass-half text-orange-600"></i>
|
|
|
|
|
</div>
|
|
|
|
|
<div>
|
|
|
|
|
<p class="text-sm text-gray-500">Remaining</p>
|
|
|
|
|
<p id="remaining-count" class="text-xl font-semibold text-gray-900">0</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Log Output -->
|
|
|
|
|
<div class="bg-white rounded-lg border border-gray-200 p-6">
|
|
|
|
|
<h3 class="text-lg font-semibold text-gray-900 mb-4">Import Log</h3>
|
|
|
|
|
<div id="log-output" class="bg-gray-900 text-green-400 p-4 rounded-lg font-mono text-sm h-64 overflow-y-auto">
|
|
|
|
|
<div class="text-gray-500">Initializing import process...</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
let logId = <?= json_encode($log_id) ?>;
|
|
|
|
|
let importType = <?= json_encode($import_type) ?>;
|
|
|
|
|
let isComplete = false;
|
|
|
|
|
let totalProcessed = 0;
|
|
|
|
|
let totalFailed = 0;
|
|
|
|
|
|
|
|
|
|
// Show step progress for complete workflow
|
|
|
|
|
if (importType === 'complete_workflow') {
|
|
|
|
|
document.getElementById('step-progress').style.display = 'block';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function addLogMessage(message, type = 'info') {
|
|
|
|
|
const logOutput = document.getElementById('log-output');
|
|
|
|
|
const timestamp = new Date().toLocaleTimeString();
|
|
|
|
|
const colorClass = type === 'error' ? 'text-red-400' : type === 'success' ? 'text-green-400' : 'text-blue-400';
|
|
|
|
|
|
|
|
|
|
const logEntry = document.createElement('div');
|
|
|
|
|
logEntry.className = colorClass;
|
|
|
|
|
logEntry.innerHTML = `[${timestamp}] ${message}`;
|
|
|
|
|
|
|
|
|
|
logOutput.appendChild(logEntry);
|
|
|
|
|
logOutput.scrollTop = logOutput.scrollHeight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateProgress(data) {
|
|
|
|
|
const total = data.total || 0;
|
|
|
|
|
const processed = data.processed || 0;
|
|
|
|
|
const failed = data.failed || 0;
|
|
|
|
|
const remaining = data.remaining || 0;
|
|
|
|
|
|
|
|
|
|
// Update counts (use absolute values, not cumulative)
|
|
|
|
|
document.getElementById('total-count').textContent = total;
|
|
|
|
|
document.getElementById('processed-count').textContent = processed;
|
|
|
|
|
document.getElementById('failed-count').textContent = failed;
|
|
|
|
|
document.getElementById('remaining-count').textContent = remaining;
|
|
|
|
|
|
|
|
|
|
// Update progress bar
|
|
|
|
|
const totalToProcess = processed + remaining;
|
|
|
|
|
const percentage = totalToProcess > 0 ? Math.round((processed / totalToProcess) * 100) : 0;
|
|
|
|
|
|
|
|
|
|
document.getElementById('progress-bar').style.width = percentage + '%';
|
|
|
|
|
document.getElementById('progress-text').textContent = `${processed} of ${totalToProcess} items processed`;
|
|
|
|
|
document.getElementById('percentage-text').textContent = percentage + '%';
|
|
|
|
|
|
|
|
|
|
// Update step progress for complete workflow
|
|
|
|
|
if (importType === 'complete_workflow' && data.message) {
|
|
|
|
|
updateStepProgress(data.message, processed, total);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update status
|
|
|
|
|
const statusBadge = document.getElementById('status-badge');
|
|
|
|
|
const statusText = document.getElementById('status-text');
|
|
|
|
|
|
|
|
|
|
if (data.status === 'complete') {
|
|
|
|
|
statusBadge.className = 'inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800';
|
|
|
|
|
statusText.innerHTML = '<i class="fas fa-check mr-2"></i>Complete';
|
|
|
|
|
isComplete = true;
|
Add CSRF, CAPTCHA, and input validation improvements
Introduces CSRF protection to all sensitive controller actions, integrates configurable CAPTCHA (reCAPTCHA v2/v3, Turnstile) for authentication and registration flows, and centralizes input validation via a new InputValidator helper. Adds new helpers and services for CSRF and CAPTCHA, updates settings and migration for CAPTCHA configuration, and enhances logging and error handling in TLD registry import processes. Also improves validation for user, domain, group, and profile inputs throughout the application.
2025-10-10 00:04:12 +03:00
|
|
|
|
|
|
|
|
// Show the actual completion message from API
|
|
|
|
|
const completionMessage = data.message || 'Import completed successfully!';
|
|
|
|
|
addLogMessage(completionMessage, 'success');
|
2025-10-08 14:23:07 +03:00
|
|
|
|
|
|
|
|
// Mark all steps as completed for complete workflow
|
|
|
|
|
if (importType === 'complete_workflow') {
|
|
|
|
|
for (let i = 1; i <= 3; i++) {
|
|
|
|
|
updateStepStatus(i, 'completed');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (data.status === 'in_progress') {
|
|
|
|
|
statusBadge.className = 'inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-800';
|
|
|
|
|
statusText.innerHTML = '<i class="fas fa-spinner fa-spin mr-2"></i>In Progress';
|
|
|
|
|
addLogMessage(data.message || 'Processing batch...', 'info');
|
|
|
|
|
} else if (data.status === 'error') {
|
|
|
|
|
statusBadge.className = 'inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-red-100 text-red-800';
|
|
|
|
|
statusText.innerHTML = '<i class="fas fa-exclamation-triangle mr-2"></i>Error';
|
|
|
|
|
addLogMessage(data.message || 'An error occurred', 'error');
|
|
|
|
|
isComplete = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function checkProgress() {
|
|
|
|
|
if (isComplete) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fetch(`/tld-registry/api/import-progress?log_id=${logId}`)
|
|
|
|
|
.then(response => response.json())
|
|
|
|
|
.then(data => {
|
|
|
|
|
if (data.error) {
|
|
|
|
|
addLogMessage('Error: ' + data.error, 'error');
|
|
|
|
|
isComplete = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateProgress(data);
|
|
|
|
|
|
|
|
|
|
if (data.status !== 'complete' && data.status !== 'error') {
|
|
|
|
|
setTimeout(checkProgress, 2000); // Check again in 2 seconds
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
addLogMessage('Network error: ' + error.message, 'error');
|
|
|
|
|
isComplete = true;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateStepProgress(message, currentStep, totalSteps) {
|
|
|
|
|
// Extract step number from message (handle both /3 and /4 formats)
|
|
|
|
|
const stepMatch = message.match(/Step (\d+)\/(\d+)/);
|
|
|
|
|
if (stepMatch) {
|
|
|
|
|
const stepNumber = parseInt(stepMatch[1]);
|
|
|
|
|
const totalSteps = parseInt(stepMatch[2]);
|
|
|
|
|
|
|
|
|
|
// Check if this step is completed
|
|
|
|
|
const isCompleted = message.toLowerCase().includes('completed');
|
|
|
|
|
|
|
|
|
|
if (isCompleted) {
|
|
|
|
|
// Mark all steps up to and including this one as completed
|
|
|
|
|
for (let i = 1; i <= stepNumber; i++) {
|
|
|
|
|
updateStepStatus(i, 'completed');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mark next step as in progress if not the last step
|
|
|
|
|
if (stepNumber < totalSteps) {
|
|
|
|
|
updateStepStatus(stepNumber + 1, 'in_progress');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Step is in progress
|
|
|
|
|
// Mark previous steps as completed
|
|
|
|
|
for (let i = 1; i < stepNumber; i++) {
|
|
|
|
|
updateStepStatus(i, 'completed');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mark current step as in progress
|
|
|
|
|
updateStepStatus(stepNumber, 'in_progress');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateStepStatus(stepNumber, status) {
|
|
|
|
|
const stepElement = document.getElementById(`step-${stepNumber}-status`);
|
|
|
|
|
const stepItem = stepElement.closest('.step-item');
|
|
|
|
|
|
|
|
|
|
if (status === 'completed') {
|
|
|
|
|
stepElement.textContent = 'Completed';
|
|
|
|
|
stepElement.className = 'text-xs text-green-600';
|
|
|
|
|
stepItem.className = 'step-item bg-green-100 rounded-lg p-2 text-center';
|
|
|
|
|
} else if (status === 'in_progress') {
|
|
|
|
|
stepElement.textContent = 'In Progress';
|
|
|
|
|
stepElement.className = 'text-xs text-blue-600';
|
|
|
|
|
stepItem.className = 'step-item bg-blue-100 rounded-lg p-2 text-center';
|
|
|
|
|
} else if (status === 'failed') {
|
|
|
|
|
stepElement.textContent = 'Failed';
|
|
|
|
|
stepElement.className = 'text-xs text-red-600';
|
|
|
|
|
stepItem.className = 'step-item bg-red-100 rounded-lg p-2 text-center';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Start checking progress
|
|
|
|
|
checkProgress();
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<?php
|
|
|
|
|
$content = ob_get_clean();
|
|
|
|
|
include __DIR__ . '/../layout/base.php';
|
|
|
|
|
?>
|