aboutsummaryrefslogtreecommitdiff
path: root/web/api/src/db.ts
blob: 39fe53650e922ffb3fe4e5733b55d9cb3ce9cd9d (plain)
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import Database from 'better-sqlite3';
import { resolve } from 'node:path';

// TEMPORARY dev wiring. The API reads directly from the SvelteKit UI's
// SQLite file so "create a key in the UI → use it in the API" works with
// zero infra. This whole module gets replaced when the API moves to
// @tidyindex/core + Drizzle + Neon (or self-hosted Postgres via Hyperdrive).
const DB_PATH = resolve(
  process.env.DATABASE_PATH ?? '../ui/data/dashboard.db'
);

export const db = new Database(DB_PATH);
db.pragma('foreign_keys = ON');

export interface ApiKeyLookup {
  keyId: string;
  name: string;
  scopes: string[];
  account: {
    id: string;
    email: string | null;
    plan: string;
  };
}

const lookupStmt = db.prepare<
  [string],
  {
    key_id: string;
    key_name: string;
    key_scopes: string;
    account_id: string;
    account_email: string | null;
    account_plan: string;
  }
>(`
  SELECT
    k.id         AS key_id,
    k.name       AS key_name,
    k.scopes     AS key_scopes,
    a.id         AS account_id,
    a.email      AS account_email,
    a.plan       AS account_plan
  FROM api_keys k
  JOIN accounts a ON a.id = k.account_id
  WHERE k.key_hash = ? AND k.active = 1
`);

const touchStmt = db.prepare<[number, string]>(
  `UPDATE api_keys SET last_used_at = ? WHERE id = ?`
);

export function lookupApiKey(hash: string): ApiKeyLookup | null {
  const row = lookupStmt.get(hash);
  if (!row) return null;

  // Update last_used_at so the UI shows the key as active. Cheap,
  // synchronous, runs on the hot path — fine for dev; in prod this
  // becomes a buffered write on the AccountMeter Durable Object.
  touchStmt.run(Date.now(), row.key_id);

  return {
    keyId: row.key_id,
    name: row.key_name,
    scopes: JSON.parse(row.key_scopes) as string[],
    account: {
      id: row.account_id,
      email: row.account_email,
      plan: row.account_plan
    }
  };
}