import Database from "better-sqlite3" import { type OAuthTokenResponse } from "@/bank/sparebank1.ts" import dayjs, { type Dayjs } from "dayjs" export type TokenResponse = { key: TokenKey token: string expires_at: Dayjs } export type TokenResponseRaw = { key: TokenResponse["key"] token: TokenResponse["token"] expires_at: string } export type TokenKey = "access-token" | "refresh-token" export function createDb(filepath: string) { const db = new Database(filepath) db.pragma("journal_mode = WAL") db.exec( "CREATE TABLE IF NOT EXISTS tokens ('key' VARCHAR PRIMARY KEY, token VARCHAR NOT NULL, expires_at DATETIME NOT NULL)", ) return db } export function insertTokens( db: Database.Database, oAuthToken: OAuthTokenResponse, ): void { insertAccessToken(db, oAuthToken.access_token, oAuthToken.expires_in) insertRefreshToken( db, oAuthToken.refresh_token, oAuthToken.refresh_token_absolute_expires_in, ) } function insertAccessToken( db: Database.Database, accessToken: string, expiresIn: number, ) { insert(db, "access-token", accessToken, expiresIn) } function insertRefreshToken( db: Database.Database, refreshToken: string, expiresIn: number, ): void { insert(db, "refresh-token", refreshToken, expiresIn) } function insert( db: Database.Database, key: TokenKey, token: string, expiresIn: number, ): void { db.prepare("INSERT OR REPLACE INTO tokens VALUES (?, ?, ?)").run( key, token, dayjs().add(expiresIn, "seconds").toISOString(), ) } export function fetchToken( db: Database.Database, tokenKey: TokenKey, ): TokenResponse | null { const response = db .prepare("SELECT * FROM tokens WHERE key = ?") .get(tokenKey) as TokenResponseRaw | null return ( response && { ...response, expires_at: dayjs(response.expires_at), } ) } export function clearTokens(db: Database.Database): void { db.prepare("DELETE FROM tokens WHERE key in ( ?, ? )").run([ "access-token", "refresh-token", ] as TokenKey[]) }