A fullstack app for indexing standard.site documents
1import { Hono } from "hono";
2import { cors } from "hono/cors";
3import type { Bindings } from "./types";
4import { health, webhook, feed, stats, records, admin, rss } from "./routes";
5import { processDocument } from "./utils";
6
7const app = new Hono<{ Bindings: Bindings }>();
8
9// Middleware
10app.use("*", cors());
11
12// Mount routes
13app.route("/health", health);
14app.route("/webhook", webhook);
15app.route("/feed", feed);
16app.route("/stats", stats);
17app.route("/records", records);
18app.route("/admin", admin);
19app.route("/rss.xml", rss);
20
21// 404 handler
22app.notFound((c) => {
23 return c.json({ error: "Not found" }, 404);
24});
25
26// Export for Cloudflare Workers
27export default {
28 fetch: app.fetch,
29 async scheduled(event: ScheduledEvent, env: Bindings, ctx: ExecutionContext) {
30 const batchSize = 50;
31 // Select stale documents
32 const { results } = await env.DB.prepare(
33 `SELECT did, rkey FROM resolved_documents
34 WHERE stale_at < datetime('now') OR stale_at IS NULL
35 LIMIT ?`,
36 )
37 .bind(batchSize)
38 .all<{ did: string; rkey: string }>();
39
40 if (results && results.length > 0) {
41 const messages = results.map((row) => ({
42 body: {
43 did: row.did,
44 collection: "site.standard.document",
45 rkey: row.rkey,
46 },
47 }));
48
49 // Send to queue
50 await env.RESOLUTION_QUEUE.sendBatch(messages);
51 console.log(`Queued ${messages.length} documents for resolution`);
52 }
53 },
54 async queue(batch: MessageBatch<any>, env: Bindings) {
55 for (const message of batch.messages) {
56 try {
57 const { did, collection, rkey } = message.body;
58 await processDocument(env.DB, did, collection, rkey);
59 message.ack();
60 } catch (error) {
61 console.error("Queue processing error:", error);
62 message.retry();
63 }
64 }
65 },
66};