working on backend

This commit is contained in:
HotSwapp
2025-08-15 22:04:43 -05:00
parent abc7f289d1
commit 0347284556
16 changed files with 3929 additions and 33 deletions

View File

@@ -160,6 +160,22 @@
<div id="adminTabContent" class="space-y-6">
<!-- Overview Tab -->
<div id="overview" role="tabpanel" class="space-y-6">
<!-- Live Batch Progress -->
<div class="bg-white dark:bg-neutral-900 rounded-xl shadow-sm dark:shadow-none border border-neutral-200 dark:border-neutral-700">
<div class="px-6 py-4 border-b border-neutral-200 dark:border-neutral-700 flex items-center justify-between">
<h3 class="text-lg font-semibold text-neutral-900 dark:text-white flex items-center gap-2">
<i class="fa-solid fa-list-check text-primary-600 dark:text-primary-400"></i>
Live Batch Progress
</h3>
<button id="refreshBatchesBtn" class="text-neutral-400 hover:text-neutral-600 dark:text-neutral-500 dark:hover:text-neutral-300">
<i class="fa-solid fa-rotate-right"></i>
</button>
</div>
<div class="p-6">
<div id="batchProgressList" class="space-y-3" aria-live="polite"></div>
<div id="batchProgressEmpty" class="text-sm text-neutral-500 dark:text-neutral-400">No active batches</div>
</div>
</div>
<!-- Quick Stats Overview -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- System Statistics -->
@@ -1083,7 +1099,17 @@
</div>
<div class="mt-3">
<label class="block text-sm font-medium mb-1">Webhook Secret</label>
<input id="routeWebhookSecret" class="w-full px-3 py-2 bg-white dark:bg-neutral-800 border border-neutral-300 dark:border-neutral-600 rounded-lg" placeholder="Optional (leave blank to keep existing)">
<div class="relative">
<input id="routeWebhookSecret" type="password" class="w-full px-3 py-2 pr-20 bg-white dark:bg-neutral-800 border border-neutral-300 dark:border-neutral-600 rounded-lg" placeholder="Optional (leave blank to keep existing)">
<div class="absolute inset-y-0 right-0 flex items-center">
<button type="button" class="px-2 py-1 text-neutral-500 hover:text-neutral-700 dark:text-neutral-400 dark:hover:text-neutral-200" onclick="toggleWebhookSecretVisibility()" title="Toggle visibility">
<i id="webhook-secret-eye" class="fas fa-eye text-sm"></i>
</button>
<button type="button" class="px-2 py-1 text-primary-600 hover:text-primary-800 dark:text-primary-400 dark:hover:text-primary-200" onclick="generateRandomSecret()" title="Generate random secret">
<i class="fas fa-dice text-sm"></i>
</button>
</div>
</div>
</div>
<p class="text-xs text-neutral-500 dark:text-neutral-400 mt-2">At least one of Email or Webhook URL must be provided. Secret is only updated if a new value is entered.</p>
</form>
@@ -1325,6 +1351,9 @@ function showCreateNotificationRouteModal() {
document.getElementById('routeScope').disabled = false;
document.getElementById('routeIdentifier').readOnly = false;
document.getElementById('qdroRouteForm').reset();
// Reset secret field to password type
document.getElementById('routeWebhookSecret').type = 'password';
document.getElementById('webhook-secret-eye').className = 'fas fa-eye text-sm';
openModal('qdroRouteModal');
}
@@ -1348,6 +1377,9 @@ function editNotificationRoute(scope, identifier) {
document.getElementById('routeWebhookUrl').value = link ? link.getAttribute('href') : '';
document.getElementById('routeWebhookSecret').value = '';
}
// Reset secret field to password type
document.getElementById('routeWebhookSecret').type = 'password';
document.getElementById('webhook-secret-eye').className = 'fas fa-eye text-sm';
openModal('qdroRouteModal');
}
@@ -1416,6 +1448,30 @@ async function deleteNotificationRoute(scope, identifier) {
}
}
// Webhook Secret UI helpers
function toggleWebhookSecretVisibility() {
const input = document.getElementById('routeWebhookSecret');
const eye = document.getElementById('webhook-secret-eye');
if (input.type === 'password') {
input.type = 'text';
eye.className = 'fas fa-eye-slash text-sm';
} else {
input.type = 'password';
eye.className = 'fas fa-eye text-sm';
}
}
function generateRandomSecret() {
// Generate a secure random string (base64url-safe)
const array = new Uint8Array(32); // 32 bytes = 256 bits
crypto.getRandomValues(array);
const secret = Array.from(array, byte =>
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'[byte % 64]
).join('');
document.getElementById('routeWebhookSecret').value = secret;
showAlert('Random secret generated', 'success');
}
// Basic HTML escaping helpers
function escapeHtml(str) {
if (str == null) return '';