Skip to content

Commit c8183ef

Browse files
committed
feat(firestore): added scan_logs collection for admin analytics
- writeScanLog writes email, score, mode, fileName to scan_logs/{autoId} after each scan - updated firestore.rules with write-only scan_logs rule
1 parent ce9f341 commit c8183ef

File tree

2 files changed

+29
-1
lines changed

2 files changed

+29
-1
lines changed

firestore.rules

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,10 @@ service cloud.firestore {
55
match /users/{userId}/scans/{scanId} {
66
allow read, write: if request.auth != null && request.auth.uid == userId;
77
}
8+
9+
// any authenticated user can create a scan log entry (write-only, no reads from client)
10+
match /scan_logs/{logId} {
11+
allow create: if request.auth != null;
12+
}
813
}
914
}

src/lib/stores/scores.svelte.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {
1010
doc,
1111
query,
1212
orderBy,
13-
limit
13+
limit,
14+
serverTimestamp
1415
} from 'firebase/firestore';
1516
import { db } from '$lib/firebase';
1617
import { authStore } from './auth.svelte';
@@ -133,6 +134,9 @@ class ScoresStore {
133134
const docRef = await addDoc(scansRef, sanitized);
134135
console.warn('[scores] saved scan to history:', docRef.id);
135136

137+
// write to top-level scan_logs for admin visibility
138+
this.writeScanLog(sanitized, uid);
139+
136140
// prune old scans beyond the cap
137141
const allScansQuery = query(scansRef, orderBy('timestamp', 'desc'));
138142
const allSnap = await getDocs(allScansQuery);
@@ -150,6 +154,25 @@ class ScoresStore {
150154
}
151155
}
152156

157+
/** log scan to top-level scan_logs collection for admin browsing */
158+
private async writeScanLog(entry: Omit<ScanHistoryEntry, 'id'>, uid: string) {
159+
try {
160+
const user = authStore.user;
161+
await addDoc(collection(db, 'scan_logs'), {
162+
uid,
163+
email: user?.email ?? null,
164+
displayName: user?.displayName ?? null,
165+
fileName: entry.fileName ?? null,
166+
mode: entry.mode,
167+
averageScore: entry.averageScore,
168+
passingCount: entry.passingCount,
169+
createdAt: serverTimestamp()
170+
});
171+
} catch {
172+
// non-critical, don't break the scan flow
173+
}
174+
}
175+
153176
async clearHistory() {
154177
if (!browser || !authStore.isAuthenticated || !authStore.user) return;
155178

0 commit comments

Comments
 (0)