From e4750f84cec07c6b7e7c92569bb9bcc9d1aaf36d Mon Sep 17 00:00:00 2001 From: saltbo Date: Wed, 22 Jul 2026 10:14:52 -0400 Subject: [PATCH] fix(stats): recover activity from authentication sessions --- scripts/backfill-admin-stats.ts | 35 +++++++++++++-------- server/scripts/backfill-admin-stats.test.ts | 19 +++++++++-- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/scripts/backfill-admin-stats.ts b/scripts/backfill-admin-stats.ts index e443cee9..3b3e670f 100644 --- a/scripts/backfill-admin-stats.ts +++ b/scripts/backfill-admin-stats.ts @@ -155,23 +155,32 @@ SELECT CAST(registered_user.created_at / 1000 AS INTEGER) FROM user registered_user; +WITH activity_facts AS ( + SELECT user_id, created_at * 1000 AS occurred_at + FROM audit_events + WHERE action = 'user_access' AND user_id IS NOT NULL + + UNION ALL + + SELECT user_id, MAX(created_at, updated_at) AS occurred_at + FROM session +), +latest_activity AS ( + SELECT user_id, MAX(occurred_at) AS occurred_at + FROM activity_facts + GROUP BY user_id +) UPDATE user SET last_active_at = ( - SELECT MAX(ae.created_at * 1000) - FROM audit_events ae - WHERE ae.action = 'user_access' AND ae.user_id = user.id + SELECT latest_activity.occurred_at + FROM latest_activity + WHERE latest_activity.user_id = user.id ) WHERE EXISTS ( - SELECT 1 FROM audit_events ae - WHERE ae.action = 'user_access' AND ae.user_id = user.id -) -AND ( - last_active_at IS NULL - OR last_active_at < ( - SELECT MAX(ae.created_at * 1000) - FROM audit_events ae - WHERE ae.action = 'user_access' AND ae.user_id = user.id - ) + SELECT 1 + FROM latest_activity + WHERE latest_activity.user_id = user.id + AND (user.last_active_at IS NULL OR user.last_active_at < latest_activity.occurred_at) ); DELETE FROM audit_events WHERE action = 'user_access'; diff --git a/server/scripts/backfill-admin-stats.test.ts b/server/scripts/backfill-admin-stats.test.ts index 6c06dc8a..4d9eaeb8 100644 --- a/server/scripts/backfill-admin-stats.test.ts +++ b/server/scripts/backfill-admin-stats.test.ts @@ -49,6 +49,9 @@ describe('admin stats backfill', () => { const historyStartMs = Date.parse('2026-04-01T00:10:00.000Z') const eventMs = Date.parse('2026-07-10T09:10:00.000Z') const eventHourMs = Date.parse('2026-07-10T09:00:00.000Z') + const sessionCreatedMs = Date.parse('2026-07-10T08:00:00.000Z') + const sessionUpdatedMs = Date.parse('2026-07-10T10:20:00.000Z') + const newerExistingActivityMs = Date.parse('2026-07-10T11:30:00.000Z') const snapshotObservedAt = '2026-07-10T09:50:00.000Z' const eventSec = Math.floor(eventMs / 1000) const currentHourMs = Date.parse('2026-07-10T12:00:00.000Z') @@ -60,7 +63,9 @@ describe('admin stats backfill', () => { db.exec(` CREATE TABLE user (id TEXT PRIMARY KEY, created_at INTEGER NOT NULL DEFAULT 0, last_active_at INTEGER); CREATE TABLE account (id TEXT PRIMARY KEY, user_id TEXT NOT NULL, provider_id TEXT NOT NULL, created_at INTEGER NOT NULL); - CREATE TABLE session (id TEXT PRIMARY KEY, user_id TEXT NOT NULL, created_at INTEGER NOT NULL); + CREATE TABLE session ( + id TEXT PRIMARY KEY, user_id TEXT NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL + ); CREATE TABLE organization (id TEXT PRIMARY KEY, metadata TEXT, created_at INTEGER NOT NULL); CREATE TABLE member ( id TEXT PRIMARY KEY, organization_id TEXT NOT NULL, user_id TEXT NOT NULL, created_at INTEGER NOT NULL @@ -121,12 +126,16 @@ describe('admin stats backfill', () => { ); INSERT INTO user VALUES - ('u0', 0, NULL), + ('u0', 0, ${newerExistingActivityMs}), ('u1', ${firstExactHour + 600_000}, NULL), ('u2', ${firstExactHour + 601_000}, NULL); INSERT INTO account VALUES ('a1', 'u1', 'github', ${firstExactHour + 600_000}), ('a2', 'u2', 'github', ${firstExactHour + 601_000}); + INSERT INTO session VALUES + ('session-u0', 'u0', ${sessionCreatedMs}, ${eventHourMs}), + ('session-u1', 'u1', ${sessionCreatedMs}, ${sessionCreatedMs + 1_800_000}), + ('session-u2', 'u2', ${sessionCreatedMs}, ${sessionUpdatedMs}); INSERT INTO organization VALUES ('o1', '{"type":"personal"}', ${historyStartMs}), ('o2', '{"type":"personal"}', ${historyStartMs + 1000}); @@ -303,6 +312,12 @@ describe('admin stats backfill', () => { expect(db.prepare("SELECT last_active_at AS lastActiveAt FROM user WHERE id = 'u1'").get()).toEqual({ lastActiveAt: eventSec * 1000, }) + expect(db.prepare("SELECT last_active_at AS lastActiveAt FROM user WHERE id = 'u2'").get()).toEqual({ + lastActiveAt: sessionUpdatedMs, + }) + expect(db.prepare("SELECT last_active_at AS lastActiveAt FROM user WHERE id = 'u0'").get()).toEqual({ + lastActiveAt: newerExistingActivityMs, + }) expect(db.prepare("SELECT COUNT(*) AS value FROM audit_events WHERE action = 'user_access'").get()).toEqual({ value: 0, })