events: add events table and prepared statements
This commit is contained in:
parent
44f7a8c5d7
commit
0cc3dc808e
1 changed files with 59 additions and 0 deletions
59
src/db.js
59
src/db.js
|
|
@ -56,6 +56,22 @@ db.exec(`
|
|||
|
||||
CREATE INDEX IF NOT EXISTS idx_bifrost_joins_email ON bifrost_joins(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_bifrost_joins_clicked_at ON bifrost_joins(clicked_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS events (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
event_type TEXT NOT NULL,
|
||||
email TEXT NOT NULL,
|
||||
occurred_at INTEGER NOT NULL,
|
||||
session_id TEXT,
|
||||
device_type TEXT,
|
||||
os TEXT,
|
||||
browser TEXT,
|
||||
user_agent TEXT,
|
||||
meta TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_events_email ON events(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_events_type_time ON events(event_type, occurred_at);
|
||||
`);
|
||||
|
||||
// ─── Migrations ──────────────────────────────────────────────
|
||||
|
|
@ -173,6 +189,49 @@ export const q = {
|
|||
countJoins: db.prepare(`SELECT COUNT(*) AS n FROM bifrost_joins`),
|
||||
countUniqueJoiners: db.prepare(`SELECT COUNT(DISTINCT email) AS n FROM bifrost_joins`),
|
||||
|
||||
// events — engagement tracking. One row per landmark event
|
||||
// (login, timeline_view) with device fields parsed from the UA.
|
||||
// Read-only from the app side; written once per event, never updated.
|
||||
recordEvent: db.prepare(
|
||||
`INSERT INTO events
|
||||
(event_type, email, occurred_at, session_id, device_type, os, browser, user_agent, meta)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
||||
),
|
||||
listEvents: db.prepare(
|
||||
`SELECT id, event_type, email, occurred_at, session_id, device_type, os, browser, user_agent, meta
|
||||
FROM events ORDER BY occurred_at DESC LIMIT ?`
|
||||
),
|
||||
listEventsByType: db.prepare(
|
||||
`SELECT id, event_type, email, occurred_at, session_id, device_type, os, browser, user_agent, meta
|
||||
FROM events WHERE event_type = ? ORDER BY occurred_at DESC LIMIT ?`
|
||||
),
|
||||
listEventsForEmail: db.prepare(
|
||||
`SELECT id, event_type, occurred_at, session_id, device_type, os, browser, meta
|
||||
FROM events WHERE email = ? ORDER BY occurred_at DESC`
|
||||
),
|
||||
// Per-user summary: pivot login + timeline_view counts onto one row.
|
||||
summariseEvents: db.prepare(
|
||||
`SELECT email,
|
||||
SUM(CASE WHEN event_type = 'login' THEN 1 ELSE 0 END) AS logins,
|
||||
SUM(CASE WHEN event_type = 'timeline_view' THEN 1 ELSE 0 END) AS timeline_views,
|
||||
MAX(occurred_at) AS last_seen
|
||||
FROM events
|
||||
GROUP BY email
|
||||
ORDER BY last_seen DESC`
|
||||
),
|
||||
countEventsByType: db.prepare(
|
||||
`SELECT event_type,
|
||||
COUNT(*) AS total,
|
||||
COUNT(DISTINCT email) AS unique_users
|
||||
FROM events GROUP BY event_type ORDER BY event_type`
|
||||
),
|
||||
deviceBreakdown: db.prepare(
|
||||
`SELECT device_type, COUNT(*) AS n
|
||||
FROM events
|
||||
WHERE device_type IS NOT NULL
|
||||
GROUP BY device_type ORDER BY n DESC`
|
||||
),
|
||||
|
||||
// cleanup
|
||||
cleanup: {
|
||||
sessions: db.prepare('DELETE FROM sessions WHERE expires_at < ?'),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue