All files / realm/blong-access/adapter/db accessSessionCleanup.ts

100% Statements 45/45
80% Branches 4/5
100% Functions 1/1
100% Lines 45/45

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 461x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 7x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 30x 7x  
import {type IMeta, handler} from '@feasibleone/blong';
 
type KnexQb = any;
 
/** Default "delete after" interval (seconds) — sessions older than this are purged. */
const DEFAULT_DELETE_AFTER = 24 * 60 * 60;
 
/**
 * `access.session.cleanup` — purge stale sessions.
 *
 * Dialect-neutral (no stored procedure, no SQL date functions): the cutoff is
 * computed in JS and passed as a bound parameter.  A session row is deleted
 * when ANY of these hold (relative to `now - deleteAfter`):
 *  - revoked more than `deleteAfter` ago (`isRevoked = 1` and `revokedAt`
 *    before the cutoff),
 *  - inactive for more than `deleteAfter` (`lastActivityAt` before the
 *    cutoff),
 *  - expired (refresh-token lifetime passed) more than `deleteAfter` ago.
 *
 * Call it periodically (scheduled orchestrator) or lazily from the login /
 * refresh / restore flows to keep the table bounded.
 *
 * Wire: `access.session.cleanup`.
 */
export default handler(() => ({
    async accessSessionCleanup(
        params: {
            /** "Delete after" interval in seconds (default 24 h). */
            deleteAfter?: number;
        },
        $meta: IMeta,
    ): Promise<{deleted: number}> {
        const qb: KnexQb = this.config?.context?.queryBuilder;
        if (!qb) throw new Error('Database not available');
        const cutoff = new Date(Date.now() - (params.deleteAfter ?? DEFAULT_DELETE_AFTER) * 1000);
        const deleted = await qb('access_session')
            .where(function (this: KnexQb) {
                this.where('isRevoked', 1).andWhere('revokedAt', '<', cutoff);
            })
            .orWhere('lastActivityAt', '<', cutoff)
            .orWhere('expiresAt', '<', cutoff)
            .del();
        return {deleted};
    },
}));