wren/lost-property Public Public: anyone can read this; nobody can change it.

workbenchthis machine
Files b22929f92e5a52d5b767141cad1712e731975be5
68 lines · 2.3 KB · ~675 tok Raw History
1
/**
2
 * The collection: items that were lost and the notes visitors leave on
3
 * them. One SQLite file under FORGE_APP_DATA; production and each preview
4
 * get their own, so a preview starts from production's data and never
5
 * writes back to it.
6
 */
7
8
import { Database } from "bun:sqlite";
9
import { mkdirSync } from "node:fs";
10
import { join } from "node:path";
11
12
const dataDir = process.env.FORGE_APP_DATA ?? "./data";
13
mkdirSync(dataDir, { recursive: true });
14
export const db = new Database(join(dataDir, "app.db"));
15
db.exec("PRAGMA journal_mode = WAL");
16
db.exec(
17
  `CREATE TABLE IF NOT EXISTS item (
18
    id INTEGER PRIMARY KEY,
19
    description TEXT NOT NULL,
20
    created_at TEXT NOT NULL DEFAULT (datetime('now'))
21
  )`,
22
);
23
db.exec(
24
  `CREATE TABLE IF NOT EXISTS note (
25
    id INTEGER PRIMARY KEY,
26
    item_id INTEGER NOT NULL REFERENCES item(id),
27
    kind TEXT NOT NULL CHECK (kind IN ('sighting', 'match')),
28
    body TEXT NOT NULL,
29
    created_at TEXT NOT NULL DEFAULT (datetime('now'))
30
  )`,
31
);
32
33
export type Item = { id: number; description: string; created_at: string };
34
export type ItemWithCount = Item & { note_count: number };
35
export type NoteKind = "sighting" | "match";
36
export type Note = { id: number; item_id: number; kind: NoteKind; body: string; created_at: string };
37
38
export function createItem(description: string): number {
39
  const row = db
40
    .query<{ id: number }, [string]>("INSERT INTO item (description) VALUES (?1) RETURNING id")
41
    .get(description);
42
  return row!.id;
43
}
44
45
export function getItem(id: number): Item | undefined {
46
  return db.query<Item, [number]>("SELECT id, description, created_at FROM item WHERE id = ?1").get(id) ?? undefined;
47
}
48
49
export function listItems(): ItemWithCount[] {
50
  return db
51
    .query<
52
      ItemWithCount,
53
      []
54
    >(`SELECT item.id, item.description, item.created_at,
55
              (SELECT count(*) FROM note WHERE note.item_id = item.id) AS note_count
56
       FROM item ORDER BY item.id DESC LIMIT 100`)
57
    .all();
58
}
59
60
export function listNotes(itemId: number): Note[] {
61
  return db
62
    .query<Note, [number]>("SELECT id, item_id, kind, body, created_at FROM note WHERE item_id = ?1 ORDER BY id ASC")
63
    .all(itemId);
64
}
65
66
export function addNote(itemId: number, kind: NoteKind, body: string): void {
67
  db.query("INSERT INTO note (item_id, kind, body) VALUES (?1, ?2, ?3)").run(itemId, kind, body);
68
}
↑↓ navigate ↵ open esc close
↑↓ navigate ↵ open esc close

Keyboard shortcuts