TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1import { and, eq, inArray, lt, sql } from 'drizzle-orm';2import type { Database } from '@rareindex/database';3import { users, sessions, trustedDevices, authCodes, rateLimits, collections, collectionItems, watchlists, watchlistItems, alerts, alertEvents, notifications, savedSearches, priceTargets, recoveryCodes, loginEvents, apiKeys, userBadges, uploads, collectionSnapshots } from '@rareindex/database';4import { logger } from '@rareindex/shared';56const log = logger.child({ job: 'account.maintenance' });78/** Purge accounts past their grace period, expire codes/sessions, trim rate-limit rows. */9export async function runMaintenance(db: Database, now = new Date()): Promise<{ purged: number }> {10 const due = await db.select({ id: users.id }).from(users).where(and(sql`${users.deletedAt} is not null`, lt(users.purgeAfter, now)));11 for (const u of due) {12 const cols = await db.select({ id: collections.id }).from(collections).where(eq(collections.userId, u.id));13 const colIds = cols.map((c) => c.id);14 if (colIds.length) {15 await db.delete(collectionItems).where(inArray(collectionItems.collectionId, colIds));16 await db.delete(collectionSnapshots).where(inArray(collectionSnapshots.collectionId, colIds));17 await db.delete(collections).where(inArray(collections.id, colIds));18 }19 const wls = await db.select({ id: watchlists.id }).from(watchlists).where(eq(watchlists.userId, u.id));20 if (wls.length) {21 await db.delete(watchlistItems).where(inArray(watchlistItems.watchlistId, wls.map((w) => w.id)));22 await db.delete(watchlists).where(eq(watchlists.userId, u.id));23 }24 for (const t of [alertEvents, alerts, notifications, savedSearches, priceTargets, recoveryCodes, trustedDevices, sessions, loginEvents, apiKeys, userBadges, uploads]) {25 await db.delete(t).where(eq((t as unknown as { userId: typeof users.id }).userId, u.id));26 }27 await db.delete(authCodes).where(eq(authCodes.userId, u.id));28 await db.delete(users).where(eq(users.id, u.id));29 log.info({ userId: u.id }, 'account purged after grace period');30 }31 await db.delete(authCodes).where(lt(authCodes.expiresAt, new Date(now.getTime() - 7 * 86_400_000)));32 await db.delete(sessions).where(lt(sessions.expiresAt, new Date(now.getTime() - 30 * 86_400_000)));33 await db.delete(rateLimits).where(lt(rateLimits.resetAt, new Date(now.getTime() - 86_400_000)));34 return { purged: due.length };35}36