Add source tracking to dashboard
authorStefan Gasser <redacted>
Wed, 21 Jan 2026 06:56:56 +0000 (07:56 +0100)
committerStefan Gasser <redacted>
Wed, 21 Jan 2026 06:56:56 +0000 (07:56 +0100)
- Add API requests counter to stats section
- Add Source column to logs table with badges
  - API badge (amber) for /api/mask requests
  - PROXY badge (gray) for OpenAI/Anthropic proxy requests
- Backward compatible: existing logs show as PROXY

src/services/logger.ts
src/views/dashboard/page.tsx

index 4637a03bc6e7685866bacaa37e6f29cd5a0a490e..bd1a952d46dbb0d9567756aef5dc5341fc09a9ee 100644 (file)
@@ -34,6 +34,7 @@ export interface Stats {
   pii_percentage: number;
   openai_requests: number;
   local_requests: number;
+  api_requests: number;
   avg_scan_time_ms: number;
   total_tokens: number;
   requests_last_hour: number;
@@ -169,13 +170,16 @@ export class Logger {
       .prepare(`SELECT COUNT(*) as count FROM request_logs WHERE pii_detected = 1`)
       .get() as { count: number };
 
-    // Upstream vs Local
+    // Upstream vs Local vs API
     const openaiResult = this.db
       .prepare(`SELECT COUNT(*) as count FROM request_logs WHERE provider = 'openai'`)
       .get() as { count: number };
     const localResult = this.db
       .prepare(`SELECT COUNT(*) as count FROM request_logs WHERE provider = 'local'`)
       .get() as { count: number };
+    const apiResult = this.db
+      .prepare(`SELECT COUNT(*) as count FROM request_logs WHERE provider = 'api'`)
+      .get() as { count: number };
 
     // Average scan time
     const scanTimeResult = this.db
@@ -208,6 +212,7 @@ export class Logger {
       pii_percentage: total > 0 ? Math.round((pii / total) * 100 * 10) / 10 : 0,
       openai_requests: openaiResult.count,
       local_requests: localResult.count,
+      api_requests: apiResult.count,
       avg_scan_time_ms: Math.round(scanTimeResult.avg || 0),
       total_tokens: tokensResult.total,
       requests_last_hour: hourResult.count,
index 1cb16bcdad558f6fc40c7b36236525ae0e818e5c..963434f4b58302a5275bfefeb44fc9e51f23fa1a 100644 (file)
@@ -244,7 +244,7 @@ const Header: FC = () => (
 const StatsGrid: FC = () => (
        <div
                id="stats-grid"
-               class="grid grid-cols-4 gap-4 mb-8 [&[data-mode='route']]:grid-cols-6"
+               class="grid grid-cols-5 gap-4 mb-8 [&[data-mode='route']]:grid-cols-7"
        >
                <StatCard label="Total Requests" valueId="total-requests" />
                <StatCard
@@ -254,6 +254,7 @@ const StatsGrid: FC = () => (
                        valueId="pii-requests"
                        accent="accent"
                />
+               <StatCard label="API Requests" valueId="api-requests" accent="accent" />
                <StatCard label="Avg PII Scan" valueId="avg-scan" accent="teal" />
                <StatCard label="Requests/Hour" valueId="requests-hour" />
                <StatCard
@@ -377,6 +378,9 @@ const LogsSection: FC = () => (
                                                        <th class="bg-elevated font-mono text-[0.65rem] font-medium uppercase tracking-widest text-text-muted px-4 py-3.5 text-left border-b border-border sticky top-0">
                                                                Time
                                                        </th>
+                                                       <th class="bg-elevated font-mono text-[0.65rem] font-medium uppercase tracking-widest text-text-muted px-4 py-3.5 text-left border-b border-border sticky top-0">
+                                                               Source
+                                                       </th>
                                                        <th class="bg-elevated font-mono text-[0.65rem] font-medium uppercase tracking-widest text-text-muted px-4 py-3.5 text-left border-b border-border sticky top-0">
                                                                Status
                                                        </th>
@@ -402,7 +406,7 @@ const LogsSection: FC = () => (
                                        </thead>
                                        <tbody id="logs-body">
                                                <tr>
-                                                       <td colSpan={8}>
+                                                       <td colSpan={9}>
                                                                <div class="flex flex-col justify-center items-center p-10 gap-3">
                                                                        <div class="loader-bars">
                                                                                <div class="loader-bar" />
@@ -439,6 +443,7 @@ async function fetchStats() {
     }
 
     document.getElementById('total-requests').textContent = data.total_requests.toLocaleString();
+    document.getElementById('api-requests').textContent = data.api_requests.toLocaleString();
     document.getElementById('avg-scan').textContent = data.avg_scan_time_ms + 'ms';
     document.getElementById('requests-hour').textContent = data.requests_last_hour.toLocaleString();
 
@@ -561,7 +566,7 @@ async function fetchLogs() {
     const tbody = document.getElementById('logs-body');
 
     if (data.logs.length === 0) {
-      tbody.innerHTML = '<tr><td colspan="8"><div class="text-center py-10 text-text-muted"><div class="text-2xl mb-3 opacity-40">📋</div><div class="text-sm">No requests yet</div></div></td></tr>';
+      tbody.innerHTML = '<tr><td colspan="9"><div class="text-center py-10 text-text-muted"><div class="text-2xl mb-3 opacity-40">📋</div><div class="text-sm">No requests yet</div></div></td></tr>';
       return;
     }
 
@@ -587,12 +592,17 @@ async function fetchLogs() {
         ? '<span class="inline-flex items-center px-2 py-1 rounded-sm font-mono text-[0.6rem] font-medium uppercase tracking-wide bg-error/10 text-error">' + log.status_code + '</span>'
         : '<span class="inline-flex items-center px-2 py-1 rounded-sm font-mono text-[0.6rem] font-medium uppercase tracking-wide bg-success/10 text-success">OK</span>';
 
+      const sourceBadge = log.provider === 'api'
+        ? '<span class="inline-flex items-center px-2 py-1 rounded-sm font-mono text-[0.6rem] font-medium uppercase tracking-wide bg-accent/10 text-accent">API</span>'
+        : '<span class="inline-flex items-center px-2 py-1 rounded-sm font-mono text-[0.6rem] font-medium uppercase tracking-wide bg-elevated text-text-muted">PROXY</span>';
+
       const mainRow =
         '<tr id="log-' + logId + '" class="cursor-pointer transition-colors hover:bg-elevated ' + (isExpanded ? 'log-row-expanded bg-elevated' : '') + '" onclick="toggleRow(' + logId + ')">' +
           '<td class="text-sm px-4 py-3 border-b border-border-subtle align-middle">' +
             '<span id="arrow-' + logId + '" class="arrow-icon inline-flex items-center justify-center w-[18px] h-[18px] mr-2 rounded-sm bg-elevated text-text-muted text-[0.65rem] transition-transform ' + (isExpanded ? 'rotate-90 bg-accent/10 text-accent' : '') + '">â–¶</span>' +
             '<span class="font-mono text-[0.7rem] text-text-secondary">' + time + '</span>' +
           '</td>' +
+          '<td class="text-sm px-4 py-3 border-b border-border-subtle align-middle">' + sourceBadge + '</td>' +
           '<td class="text-sm px-4 py-3 border-b border-border-subtle align-middle">' + statusBadge + '</td>' +
           '<td class="route-only text-sm px-4 py-3 border-b border-border-subtle align-middle">' +
             '<span class="inline-flex items-center px-2 py-1 rounded-sm font-mono text-[0.6rem] font-medium uppercase tracking-wide ' +
@@ -619,7 +629,7 @@ async function fetchLogs() {
 
       const detailRow =
         '<tr id="detail-' + logId + '" class="' + (isExpanded ? 'detail-row-visible' : 'hidden') + '">' +
-          '<td colspan="8" class="p-0 bg-detail border-b border-border-subtle">' +
+          '<td colspan="9" class="p-0 bg-detail border-b border-border-subtle">' +
             '<div class="p-4 px-5 animate-slide-down">' + detailContent + '</div>' +
           '</td>' +
         '</tr>';
git clone https://git.99rst.org/PROJECT