feat(session): add pairing_approved table and getPairingStore()

This commit is contained in:
William Valentin
2026-02-09 21:46:51 -08:00
parent 1e1a68924e
commit ecd3aca7c1
2 changed files with 73 additions and 0 deletions
+35
View File
@@ -1,5 +1,6 @@
import Database from 'better-sqlite3';
import type { Message } from '../models/types.js';
import type { PairingStore, ApprovedSender } from '../channels/pairing.js';
/** Parse a duration string like '30d', '7d', '12h' to milliseconds. Returns null if invalid or '0'. */
export function parseDuration(s: string): number | null {
@@ -28,6 +29,13 @@ export class SessionStore {
created_at INTEGER NOT NULL DEFAULT (unixepoch())
);
CREATE INDEX IF NOT EXISTS idx_messages_session ON messages(session_id);
CREATE TABLE IF NOT EXISTS pairing_approved (
channel TEXT NOT NULL,
sender_id TEXT NOT NULL,
approved_at INTEGER NOT NULL,
code_used TEXT NOT NULL,
PRIMARY KEY (channel, sender_id)
);
`);
}
@@ -101,6 +109,33 @@ export class SessionStore {
return stale.map(r => r.session_id);
}
getPairingStore(): PairingStore {
return {
loadApproved: (): ApprovedSender[] => {
const rows = this.db.prepare(
'SELECT channel, sender_id, approved_at, code_used FROM pairing_approved'
).all() as Array<{ channel: string; sender_id: string; approved_at: number; code_used: string }>;
return rows.map(r => ({
channel: r.channel,
senderId: r.sender_id,
approvedAt: r.approved_at,
codeUsed: r.code_used,
}));
},
saveApproved: (sender: ApprovedSender): void => {
this.db.prepare(`
INSERT OR REPLACE INTO pairing_approved (channel, sender_id, approved_at, code_used)
VALUES (?, ?, ?, ?)
`).run(sender.channel, sender.senderId, sender.approvedAt, sender.codeUsed);
},
removeApproved: (channel: string, senderId: string): void => {
this.db.prepare(
'DELETE FROM pairing_approved WHERE channel = ? AND sender_id = ?'
).run(channel, senderId);
},
};
}
close(): void {
this.db.close();
}