feat: cookie-based auth, SSR dashboard, JS-optional login

This commit is contained in:
M1
2026-03-16 17:25:59 +04:00
parent 8e4cb84599
commit ef56b47b09
9 changed files with 253 additions and 163 deletions
-1
View File
@@ -152,7 +152,6 @@
</main>
<script>
if (!requireAuth()) throw 'auth';
const monitorId = window.location.pathname.split('/').pop();
let editQuery = null;
+28 -6
View File
@@ -12,17 +12,39 @@
</div>
<div id="monitors-list" class="space-y-3">
<div class="text-center py-16 text-gray-600">Loading...</div>
<% if (it.monitors && it.monitors.length === 0) { %>
<div id="empty-state" class="text-center py-16">
<p class="text-gray-500 mb-4">No monitors yet</p>
<a href="/dashboard/monitors/new" class="bg-blue-600 hover:bg-blue-500 text-white text-sm font-medium px-6 py-3 rounded-lg transition-colors inline-block">Create your first monitor</a>
</div>
<% } else if (it.monitors) { %>
<% it.monitors.forEach(function(m) { %>
<a href="/dashboard/monitors/<%= m.id %>" data-monitor-id="<%= m.id %>" class="block bg-gray-900 hover:bg-gray-800/80 border border-gray-800 rounded-xl p-4 transition-colors group">
<div class="flex items-center justify-between">
<div class="flex items-center gap-3 min-w-0">
<span class="status-dot w-2.5 h-2.5 rounded-full <%= m.last_ping == null ? 'bg-gray-600' : (m.last_ping.up ? 'bg-green-500' : 'bg-red-500') %>"></span>
<div class="min-w-0">
<div class="font-medium text-gray-100 group-hover:text-white truncate"><%= m.name %></div>
<div class="text-xs text-gray-500 truncate"><%= m.url %></div>
</div>
</div>
<div class="flex items-center gap-6 shrink-0 ml-4">
<div class="text-right">
<div class="text-sm text-gray-300 stat-latency"><%= m.last_ping ? m.last_ping.latency_ms + 'ms' : '—' %></div>
<div class="text-xs text-gray-500 stat-last"><%= m.last_ping ? '<span class="timestamp" data-ts="' + new Date(m.last_ping.checked_at).getTime() + '">just now</span>' : 'no pings' %></div>
</div>
<div class="text-xs px-2 py-1 rounded <%= m.enabled ? 'bg-gray-800 text-gray-400' : 'bg-yellow-900/30 text-yellow-500' %>"><%= m.enabled ? m.interval_s + 's' : 'paused' %></div>
</div>
</div>
</a>
<% }) %>
<% } else { %>
<div class="text-center py-16 text-gray-600">Loading...</div>
</div>
<div id="empty-state" class="hidden text-center py-16">
<p class="text-gray-500 mb-4">No monitors yet</p>
<a href="/dashboard/monitors/new" class="bg-blue-600 hover:bg-blue-500 text-white text-sm font-medium px-6 py-3 rounded-lg transition-colors inline-block">Create your first monitor</a>
</div>
</main>
<script>
if (!requireAuth()) throw 'auth';
async function load() {
try {
-1
View File
@@ -90,7 +90,6 @@
</main>
<script>
if (!requireAuth()) throw 'auth';
let currentQuery = null;
// Show body section for non-GET methods
+3 -6
View File
@@ -84,14 +84,10 @@
<script>
if (!requireAuth()) throw 'auth';
const key = localStorage.getItem('pingql_key');
async function loadSettings() {
const data = await api('/account/settings');
document.getElementById('primary-key').textContent = key;
document.getElementById('primary-key').textContent = data.account_id;
document.getElementById('created-at').textContent = new Date(data.created_at).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
if (data.has_email) {
@@ -155,7 +151,8 @@
async function confirmReset() {
if (!confirm('Rotate your primary key?\n\nYour current key will stop working immediately. Make sure to copy the new one.')) return;
const data = await api('/account/reset-key', { method: 'POST', body: {} });
localStorage.setItem('pingql_key', data.key);
// Re-auth with new key (updates cookie)
await fetch('/account/login', { method: 'POST', credentials: 'same-origin', headers: {'Content-Type':'application/json'}, body: JSON.stringify({ key: data.key }) });
document.getElementById('primary-key').textContent = data.key;
location.reload();
}