···11+-- Records synced from external tap instance
22+CREATE TABLE IF NOT EXISTS repo_records (
33+ id INTEGER PRIMARY KEY AUTOINCREMENT,
44+ did TEXT NOT NULL,
55+ rkey TEXT NOT NULL,
66+ collection TEXT NOT NULL,
77+ cid TEXT,
88+ synced_at TEXT DEFAULT (datetime('now')),
99+ UNIQUE(did, collection, rkey)
1010+);
1111+1212+CREATE INDEX IF NOT EXISTS idx_repo_records_collection ON repo_records(collection);
1313+CREATE INDEX IF NOT EXISTS idx_repo_records_did ON repo_records(did);
1414+CREATE INDEX IF NOT EXISTS idx_repo_records_rkey ON repo_records(rkey DESC);
1515+1616+-- Cache for resolved PDS endpoints
1717+CREATE TABLE IF NOT EXISTS pds_cache (
1818+ did TEXT PRIMARY KEY,
1919+ pds_endpoint TEXT NOT NULL,
2020+ cached_at TEXT DEFAULT (datetime('now'))
2121+);
2222+2323+-- Sync metadata to track last sync
2424+CREATE TABLE IF NOT EXISTS sync_metadata (
2525+ key TEXT PRIMARY KEY,
2626+ value TEXT NOT NULL,
2727+ updated_at TEXT DEFAULT (datetime('now'))
2828+);
2929+3030+-- Pre-resolved documents for fast feed serving
3131+CREATE TABLE IF NOT EXISTS resolved_documents (
3232+ uri TEXT PRIMARY KEY,
3333+ did TEXT NOT NULL,
3434+ rkey TEXT NOT NULL,
3535+ title TEXT,
3636+ path TEXT,
3737+ site TEXT,
3838+ content TEXT, -- JSON blob
3939+ text_content TEXT,
4040+ published_at TEXT,
4141+ view_url TEXT,
4242+ resolved_at TEXT DEFAULT (datetime('now')),
4343+ stale_at TEXT -- When this record should be re-resolved
4444+);
4545+4646+CREATE INDEX IF NOT EXISTS idx_resolved_documents_rkey ON resolved_documents(rkey DESC);
4747+CREATE INDEX IF NOT EXISTS idx_resolved_documents_stale ON resolved_documents(stale_at);
···11+import { Hono } from "hono";
22+import type { Bindings } from "../types";
33+44+const health = new Hono<{ Bindings: Bindings }>();
55+66+health.get("/", (c) => {
77+ return c.json({ status: "ok", timestamp: new Date().toISOString() });
88+});
99+1010+export default health;
+5
packages/server/src/routes/index.ts
···11+export { default as health } from "./health";
22+export { default as webhook } from "./webhook";
33+export { default as feed } from "./feed";
44+export { default as stats } from "./stats";
55+export { default as records } from "./records";
+38
packages/server/src/routes/records.ts
···11+import { Hono } from "hono";
22+import type { Bindings } from "../types";
33+44+const records = new Hono<{ Bindings: Bindings }>();
55+66+records.get("/:did", async (c) => {
77+ try {
88+ const db = c.env.DB;
99+ const did = c.req.param("did");
1010+ const limit = Number(c.req.query("limit")) || 20;
1111+ const offset = Number(c.req.query("offset")) || 0;
1212+1313+ const { results } = await db
1414+ .prepare(
1515+ `SELECT * FROM repo_records
1616+ WHERE did = ? AND collection = 'site.standard.document'
1717+ ORDER BY rkey DESC
1818+ LIMIT ? OFFSET ?`
1919+ )
2020+ .bind(did, limit, offset)
2121+ .all();
2222+2323+ return c.json({
2424+ did,
2525+ count: results?.length || 0,
2626+ limit,
2727+ offset,
2828+ records: results || [],
2929+ });
3030+ } catch (error) {
3131+ return c.json(
3232+ { error: "Failed to fetch records", details: String(error) },
3333+ 500
3434+ );
3535+ }
3636+});
3737+3838+export default records;
+38
packages/server/src/routes/stats.ts
···11+import { Hono } from "hono";
22+import type { Bindings } from "../types";
33+44+const stats = new Hono<{ Bindings: Bindings }>();
55+66+stats.get("/", async (c) => {
77+ try {
88+ const db = c.env.DB;
99+ const [records, pdsCache, recordCache, pubCache] = await Promise.all([
1010+ db
1111+ .prepare("SELECT COUNT(*) as count FROM repo_records")
1212+ .first<{ count: number }>(),
1313+ db
1414+ .prepare("SELECT COUNT(*) as count FROM pds_cache")
1515+ .first<{ count: number }>(),
1616+ db
1717+ .prepare("SELECT COUNT(*) as count FROM record_cache")
1818+ .first<{ count: number }>(),
1919+ db
2020+ .prepare("SELECT COUNT(*) as count FROM publication_cache")
2121+ .first<{ count: number }>(),
2222+ ]);
2323+2424+ return c.json({
2525+ repo_records: records?.count || 0,
2626+ pds_cache: pdsCache?.count || 0,
2727+ record_cache: recordCache?.count || 0,
2828+ publication_cache: pubCache?.count || 0,
2929+ });
3030+ } catch (error) {
3131+ return c.json(
3232+ { error: "Failed to fetch stats", details: String(error) },
3333+ 500
3434+ );
3535+ }
3636+});
3737+3838+export default stats;