A fullstack app for indexing standard.site documents
8
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 107 lines 2.7 kB view raw
1import { Hono } from "hono"; 2import type { 3 Bindings, 4 ResolvedDocumentRow, 5 Document, 6 Publication, 7 BskyPostRef, 8} from "../types"; 9 10const feed = new Hono<{ Bindings: Bindings }>(); 11 12/** 13 * Transforms a database row into a Document object for the API response. 14 */ 15function rowToDocument(row: ResolvedDocumentRow): Document { 16 // Build publication object if we have publication data 17 let publication: Publication | undefined; 18 if (row.pub_url && row.pub_name) { 19 publication = { 20 url: row.pub_url, 21 name: row.pub_name, 22 description: row.pub_description || undefined, 23 iconCid: row.pub_icon_cid || undefined, 24 iconUrl: row.pub_icon_url || undefined, 25 }; 26 } 27 28 // Parse bskyPostRef if present 29 let bskyPostRef: BskyPostRef | undefined; 30 if (row.bsky_post_ref) { 31 try { 32 bskyPostRef = JSON.parse(row.bsky_post_ref); 33 } catch { 34 // Ignore parse errors 35 } 36 } 37 38 // Parse tags if present 39 let tags: string[] | undefined; 40 if (row.tags) { 41 try { 42 tags = JSON.parse(row.tags); 43 } catch { 44 // Ignore parse errors 45 } 46 } 47 48 return { 49 uri: row.uri, 50 did: row.did, 51 rkey: row.rkey, 52 title: row.title || "Untitled", 53 description: row.description || undefined, 54 path: row.path || undefined, 55 site: row.site || undefined, 56 textContent: row.text_content || undefined, 57 coverImageCid: row.cover_image_cid || undefined, 58 coverImageUrl: row.cover_image_url || undefined, 59 bskyPostRef, 60 tags, 61 publishedAt: row.published_at || undefined, 62 updatedAt: row.updated_at || undefined, 63 publication, 64 viewUrl: row.view_url || undefined, 65 pdsEndpoint: row.pds_endpoint || undefined, 66 }; 67} 68 69// Get feed of documents with resolved URLs (server-side resolution) 70feed.get("/", async (c) => { 71 try { 72 const db = c.env.DB; 73 const limit = Number(c.req.query("limit")) || 50; 74 const offset = Number(c.req.query("offset")) || 0; 75 76 const { results } = await db 77 .prepare( 78 `SELECT uri, did, rkey, title, description, path, site, text_content, 79 cover_image_cid, cover_image_url, bsky_post_ref, tags, 80 published_at, updated_at, pub_url, pub_name, pub_description, 81 pub_icon_cid, pub_icon_url, view_url, pds_endpoint, 82 resolved_at, stale_at, verified 83 FROM resolved_documents 84 WHERE verified = 1 85 ORDER BY published_at DESC 86 LIMIT ? OFFSET ?`, 87 ) 88 .bind(limit, offset) 89 .all<ResolvedDocumentRow>(); 90 91 const documents = (results || []).map(rowToDocument); 92 93 return c.json({ 94 count: documents.length, 95 limit, 96 offset, 97 documents, 98 }); 99 } catch (error) { 100 return c.json( 101 { error: "Failed to fetch feed", details: String(error) }, 102 500, 103 ); 104 } 105}); 106 107export default feed;