fix(stats): recover activity from authentication sessions

This commit is contained in:
saltbo
2026-07-22 10:14:52 -04:00
parent 2d215a22b2
commit e4750f84ce
2 changed files with 39 additions and 15 deletions
+22 -13
View File
@@ -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';
+17 -2
View File
@@ -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,
})