Add QR code tracker to admin panel
Each QR code gets a /qr/<slug> redirect that logs scans (IP, user agent, timestamp) to disk. The admin QR Codes view lets you add, edit, enable/disable, and delete codes, with per-code scan history inline. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
STUDY_COMMENTS_FILE,
|
||||
STUDY_CERTIFICATES_FILE,
|
||||
EPISODE_SCRIPTS_FILE,
|
||||
QR_CODES_FILE,
|
||||
REPLY_TEMPLATES_FILE,
|
||||
REPLY_HISTORY_FILE,
|
||||
PODCAST_CHECKLIST_FILE,
|
||||
@@ -1216,3 +1217,29 @@ export function sanitizeStudyCommunityPosts(value) {
|
||||
})
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
// ── QR Codes ───────────────────────────────────────────────────────────────
|
||||
|
||||
export function queueQrCodesWrite() {
|
||||
state.qrCodesWritePromise = state.qrCodesWritePromise
|
||||
.then(async () => {
|
||||
await mkdir(DATA_DIR, { recursive: true })
|
||||
await writeFile(QR_CODES_FILE, JSON.stringify({ codes: state.qrCodes, scans: state.qrScans }, null, 2), 'utf8')
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('[qr-codes] failed to write:', err)
|
||||
})
|
||||
}
|
||||
|
||||
export function loadQrCodesFromDisk() {
|
||||
return readFile(QR_CODES_FILE, 'utf8')
|
||||
.then(raw => {
|
||||
const parsed = JSON.parse(raw)
|
||||
state.qrCodes = Array.isArray(parsed?.codes) ? parsed.codes : []
|
||||
state.qrScans = Array.isArray(parsed?.scans) ? parsed.scans : []
|
||||
})
|
||||
.catch(() => {
|
||||
state.qrCodes = []
|
||||
state.qrScans = []
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user