import { setupAuth, getAuthenticatedDid, authVerifier } from "./utils/auth.ts"; import { setupSystemDb } from "./utils/dbsystem.ts"; import { didDocument } from "./utils/diddoc.ts"; import { cachedFetch, searchParamsToJson, withCors } from "./utils/server.ts"; import { IndexServer, IndexServerConfig } from "./indexserver.ts"; import { extractDid } from "./utils/identity.ts"; import { config } from "./config.ts"; import { compile, devWatch } from "./shared-landing/build.ts"; // ------------------------------------------ // AppView Setup // ------------------------------------------ const indexServerConfig: IndexServerConfig = { baseDbPath: "./dbs/index/registered-users", // The directory for user databases systemDbPath: "./dbs/index/registered-users/system.db", // The path for the main system database }; export const genericIndexServer = new IndexServer(indexServerConfig); setupSystemDb(genericIndexServer.systemDB); let { js, html, css } = await compile({ target: "index", initialData: { config: config.indexServer, users: (await genericIndexServer.unspeccedGetRegisteredUsers()) ?? [], }, }); // add me lol genericIndexServer.systemDB.exec(` INSERT OR IGNORE INTO users (did, role, registrationdate, onboardingstatus) VALUES ( 'did:plc:mn45tewwnse5btfftvd3powc', 'admin', datetime('now'), 'ready' ); INSERT OR IGNORE INTO users (did, role, registrationdate, onboardingstatus) VALUES ( 'did:web:did12.whey.party', 'admin', datetime('now'), 'ready' ); `); genericIndexServer.start(); // ------------------------------------------ // XRPC Method Implementations // ------------------------------------------ // const indexServerRoutes = new Set([ // "/xrpc/app.bsky.actor.getProfile", // "/xrpc/app.bsky.actor.getProfiles", // "/xrpc/app.bsky.feed.getActorFeeds", // "/xrpc/app.bsky.feed.getFeedGenerator", // "/xrpc/app.bsky.feed.getFeedGenerators", // "/xrpc/app.bsky.feed.getPosts", // "/xrpc/party.whey.app.bsky.feed.getActorLikesPartial", // "/xrpc/party.whey.app.bsky.feed.getAuthorFeedPartial", // "/xrpc/party.whey.app.bsky.feed.getLikesPartial", // "/xrpc/party.whey.app.bsky.feed.getPostThreadPartial", // "/xrpc/party.whey.app.bsky.feed.getQuotesPartial", // "/xrpc/party.whey.app.bsky.feed.getRepostedByPartial", // // more federated endpoints, not planned yet, lexicons will come later // /* // app.bsky.graph.getLists // doesnt need to because theres no items[], and its self ProfileViewBasic // app.bsky.graph.getList // needs to be Partial-ed (items[] union with ProfileViewRef) // app.bsky.graph.getActorStarterPacks // maybe doesnt need to be Partial-ed because its self ProfileViewBasic // app.bsky.feed.getListFeed // uhh actually already exists its getListFeedPartial // */ // "/xrpc/party.whey.app.bsky.feed.getListFeedPartial", // ]); const placeholderselfcheckstatus = { "#skylite_index:/xrpc/app.bsky.actor.getProfile": "green", "#skylite_index:/xrpc/app.bsky.actor.getProfiles": "green", "#skylite_index:/xrpc/app.bsky.feed.getActorFeeds": "green", "#skylite_index:/xrpc/app.bsky.feed.getFeedGenerator": "green", "#skylite_index:/xrpc/app.bsky.feed.getFeedGenerators": "green", "#skylite_index:/xrpc/app.bsky.feed.getPosts": "green", "#skylite_index:/xrpc/app.bsky.graph.getLists": "black", "#skylite_index:/xrpc/app.bsky.graph.getList": "black", "#skylite_index:/xrpc/app.bsky.graph.getActorStarterPacks": "black", "#skylite_index:/xrpc/party.whey.app.bsky.feed.getActorLikesPartial": "green", "#skylite_index:/xrpc/party.whey.app.bsky.feed.getAuthorFeedPartial": "green", "#skylite_index:/xrpc/party.whey.app.bsky.feed.getLikesPartial": "orange", "#skylite_index:/xrpc/party.whey.app.bsky.feed.getPostThreadPartial": "green", "#skylite_index:/xrpc/party.whey.app.bsky.feed.getQuotesPartial": "orange", "#skylite_index:/xrpc/party.whey.app.bsky.feed.getRepostedByPartial": "orange", "#skylite_index:/xrpc/party.whey.app.bsky.feed.getListFeedPartial": "black", "constellation:/links": "green", "constellation:/links/distinct-dids": "green", "constellation:/links/count": "green", "constellation:/links/count/distinct-dids": "green", "constellation:/links/all": "green", }; //console.log("ready to serve"); Deno.serve( { port: config.indexServer.port }, async (req: Request): Promise => { const url = new URL(req.url); const pathname = url.pathname; const searchParams = searchParamsToJson(url.searchParams); const publicdir = "/public"; if (pathname.startsWith(publicdir)) { const filepath = decodeURIComponent(pathname.slice(publicdir.length)); try { const file = await Deno.open("./public" + filepath, { read: true }); return new Response(file.readable); } catch { return new Response("404 Not Found", { status: 404 }); } } const todopleasespecthis = "/_unspecced"; if (pathname.startsWith(todopleasespecthis)) { const unspeccedroute = decodeURIComponent( pathname.slice(todopleasespecthis.length) ); if (unspeccedroute === "/config") { const safeconfig = { inviteOnly: config.indexServer.inviteOnly, //port: number, did: config.indexServer.did, host: config.indexServer.host, }; return new Response(JSON.stringify(safeconfig), { headers: withCors({ "content-type": "application/json; charset=utf-8", }), }); } if (unspeccedroute === "/users") { const res = await genericIndexServer.unspeccedGetRegisteredUsers(); return new Response(JSON.stringify(res), { headers: withCors({ "content-type": "application/json; charset=utf-8", }), }); } if (unspeccedroute === "/apitest") { return new Response(JSON.stringify(placeholderselfcheckstatus), { headers: withCors({ "content-type": "application/json; charset=utf-8", }), }); } } if (html && js) { if (pathname === "/" || pathname === "") { return new Response(html, { headers: withCors({ "content-type": "text/html; charset=utf-8" }), }); } if (pathname === "/landing-index.js") { return new Response(js, { headers: withCors({ "content-type": "application/javascript; charset=utf-8", }), }); } } else { if (pathname === "/" || pathname === "") { return new Response(`server is compiling your webpage. loading...`, { headers: withCors({ "content-type": "text/html; charset=utf-8" }), }); } } if (pathname === "/app.css") { return new Response(css, { headers: withCors({ "content-type": "text/css; charset=utf-8", }), }); } if (pathname === "/.well-known/did.json") { return new Response( JSON.stringify( didDocument( "index", config.indexServer.did, config.indexServer.host, "whatever" ) ), { headers: withCors({ "Content-Type": "application/json" }), } ); } if (pathname === "/health") { return new Response("OK", { status: 200, headers: withCors({ "Content-Type": "text/plain", }), }); } if (req.method === "OPTIONS") { return new Response(null, { status: 204, headers: { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, OPTIONS", "Access-Control-Allow-Headers": "*", }, }); } console.log(`request for "${pathname}"`); const constellation = pathname.startsWith("/links"); if (constellation) { const target = searchParams?.target as string; const safeDid = extractDid(target); const targetserver = genericIndexServer.handlesDid(safeDid); if (targetserver) { return genericIndexServer.constellationAPIHandler(req); } else { return new Response( JSON.stringify({ error: "User not found", }), { status: 404, headers: withCors({ "Content-Type": "application/json" }), } ); } } else { // indexServerRoutes.has(pathname) return await genericIndexServer.indexServerHandler(req); } } ); devWatch({ target: "index", initialData: { config: config.indexServer, users: (await genericIndexServer.unspeccedGetRegisteredUsers()) ?? [], }, onBuild: ({ js: newjs, html: newhtml, css: newcss }) => { js = newjs; html = newhtml; css = newcss; }, });