an attempt to make a lightweight, easily self-hostable, scoped bluesky appview
38
fork

Configure Feed

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

at e064ac2d24eba9e7bce793499cc4548510844263 174 lines 5.6 kB view raw
1import { setupAuth, getAuthenticatedDid, authVerifier } from "./utils/auth.ts"; 2import { setupSystemDb } from "./utils/dbsystem.ts"; 3import { didDocument } from "./utils/diddoc.ts"; 4import { cachedFetch, searchParamsToJson, withCors } from "./utils/server.ts"; 5import { IndexServer, IndexServerConfig } from "./indexserver.ts"; 6import { extractDid } from "./utils/identity.ts"; 7import { config } from "./config.ts"; 8import { compile, devWatch } from "./shared-landing/build.ts"; 9let { js, html, css } = await compile("index"); 10 11// ------------------------------------------ 12// AppView Setup 13// ------------------------------------------ 14 15const indexServerConfig: IndexServerConfig = { 16 baseDbPath: "./dbs/index/registered-users", // The directory for user databases 17 systemDbPath: "./dbs/index/registered-users/system.db", // The path for the main system database 18}; 19export const genericIndexServer = new IndexServer(indexServerConfig); 20setupSystemDb(genericIndexServer.systemDB); 21 22// add me lol 23genericIndexServer.systemDB.exec(` 24 INSERT OR IGNORE INTO users (did, role, registrationdate, onboardingstatus) 25 VALUES ( 26 'did:plc:mn45tewwnse5btfftvd3powc', 27 'admin', 28 datetime('now'), 29 'ready' 30 ); 31 32 INSERT OR IGNORE INTO users (did, role, registrationdate, onboardingstatus) 33 VALUES ( 34 'did:web:did12.whey.party', 35 'admin', 36 datetime('now'), 37 'ready' 38 ); 39`); 40 41genericIndexServer.start(); 42 43// ------------------------------------------ 44// XRPC Method Implementations 45// ------------------------------------------ 46 47// const indexServerRoutes = new Set([ 48// "/xrpc/app.bsky.actor.getProfile", 49// "/xrpc/app.bsky.actor.getProfiles", 50// "/xrpc/app.bsky.feed.getActorFeeds", 51// "/xrpc/app.bsky.feed.getFeedGenerator", 52// "/xrpc/app.bsky.feed.getFeedGenerators", 53// "/xrpc/app.bsky.feed.getPosts", 54// "/xrpc/party.whey.app.bsky.feed.getActorLikesPartial", 55// "/xrpc/party.whey.app.bsky.feed.getAuthorFeedPartial", 56// "/xrpc/party.whey.app.bsky.feed.getLikesPartial", 57// "/xrpc/party.whey.app.bsky.feed.getPostThreadPartial", 58// "/xrpc/party.whey.app.bsky.feed.getQuotesPartial", 59// "/xrpc/party.whey.app.bsky.feed.getRepostedByPartial", 60// // more federated endpoints, not planned yet, lexicons will come later 61// /* 62// app.bsky.graph.getLists // doesnt need to because theres no items[], and its self ProfileViewBasic 63// app.bsky.graph.getList // needs to be Partial-ed (items[] union with ProfileViewRef) 64// app.bsky.graph.getActorStarterPacks // maybe doesnt need to be Partial-ed because its self ProfileViewBasic 65 66// app.bsky.feed.getListFeed // uhh actually already exists its getListFeedPartial 67// */ 68// "/xrpc/party.whey.app.bsky.feed.getListFeedPartial", 69// ]); 70 71 72 73//console.log("ready to serve"); 74Deno.serve( 75 { port: config.indexServer.port }, 76 async (req: Request): Promise<Response> => { 77 const url = new URL(req.url); 78 const pathname = url.pathname; 79 const searchParams = searchParamsToJson(url.searchParams); 80 81 if (html && js) { 82 if (pathname === "/" || pathname === "") { 83 return new Response(html, { 84 headers: withCors({ "content-type": "text/html; charset=utf-8" }), 85 }); 86 } 87 if (pathname === "/landing-index.js") { 88 return new Response(js, { 89 headers: withCors({ 90 "content-type": "application/javascript; charset=utf-8", 91 }), 92 }); 93 } 94 } else { 95 if (pathname === "/" || pathname === "") { 96 return new Response(`server is compiling your webpage. loading...`, { 97 headers: withCors({ "content-type": "text/html; charset=utf-8" }), 98 }); 99 } 100 } 101 if (pathname === "/app.css") { 102 return new Response(css, { 103 headers: withCors({ 104 "content-type": "text/css; charset=utf-8", 105 }), 106 }); 107 } 108 109 110 if (pathname === "/.well-known/did.json") { 111 return new Response( 112 JSON.stringify( 113 didDocument( 114 "index", 115 config.indexServer.did, 116 config.indexServer.host, 117 "whatever" 118 ) 119 ), 120 { 121 headers: withCors({ "Content-Type": "application/json" }), 122 } 123 ); 124 } 125 if (pathname === "/health") { 126 return new Response("OK", { 127 status: 200, 128 headers: withCors({ 129 "Content-Type": "text/plain", 130 }), 131 }); 132 } 133 if (req.method === "OPTIONS") { 134 return new Response(null, { 135 status: 204, 136 headers: { 137 "Access-Control-Allow-Origin": "*", 138 "Access-Control-Allow-Methods": "GET, POST, OPTIONS", 139 "Access-Control-Allow-Headers": "*", 140 }, 141 }); 142 } 143 console.log(`request for "${pathname}"`); 144 const constellation = pathname.startsWith("/links"); 145 146 if (constellation) { 147 const target = searchParams?.target as string; 148 const safeDid = extractDid(target); 149 const targetserver = genericIndexServer.handlesDid(safeDid); 150 if (targetserver) { 151 return genericIndexServer.constellationAPIHandler(req); 152 } else { 153 return new Response( 154 JSON.stringify({ 155 error: "User not found", 156 }), 157 { 158 status: 404, 159 headers: withCors({ "Content-Type": "application/json" }), 160 } 161 ); 162 } 163 } else { 164 // indexServerRoutes.has(pathname) 165 return await genericIndexServer.indexServerHandler(req); 166 } 167 } 168); 169 170devWatch("index", ({ js: newjs, html: newhtml, css: newcss }) => { 171 js = newjs; 172 html = newhtml; 173 css = newcss; 174});