[WIP] A (somewhat barebones) atproto app for creating custom sites without hosting!

server: resolve route to record in src/user.ts

+82 -2
+82 -2
src/user.ts
··· 1 + /// <reference types="@atcute/atproto" /> 2 + import { Client, simpleFetchHandler } from "@atcute/client"; 3 + import { is } from "@atcute/lexicons"; 1 4 import { 2 5 CompositeHandleResolver, 3 6 DohJsonHandleResolver, ··· 6 9 PlcDidDocumentResolver, 7 10 WebDidDocumentResolver, 8 11 } from "@atcute/identity-resolver"; 12 + import { DevAtcitiesRoute } from "./lexicons/index.ts"; 9 13 10 14 const handleResolver = new CompositeHandleResolver({ 11 15 strategy: "race", ··· 110 114 pds: string, 111 115 route: string 112 116 ): Promise<Response> { 113 - return new Response("404 NOT FOUND: " + route, { 114 - status: 404, 117 + // if the url ends in `/` (and isnt the root), perma redirect to unslashed version 118 + // rationale: /dvd/ is the folder dvd, /index.html/ looks Weird 119 + // atcities.dev won't do folder listings (in v0.1 at least) because that requires finding EVERY rkey 120 + 121 + if (route !== "/" && route.endsWith("/")) 122 + return new Response(undefined, { 123 + status: 308, 124 + statusText: "Permanent Redirect", 125 + headers: { 126 + Location: route.slice(0, -1), 127 + }, 128 + }); 129 + 130 + // get client to pds 131 + const client = new Client({ 132 + handler: simpleFetchHandler({ service: pds }), 115 133 }); 134 + 135 + try { 136 + // note: / urls are reserved for special routes (404) 137 + // any path should be prefixed with a / 138 + // this is not enforced here so that this function can be used for special routes 139 + const targetRkey = urlToRkey(route); 140 + console.log("trying:", targetRkey, "for", route); 141 + 142 + if (!targetRkey) throw "invalid url"; 143 + 144 + const { ok, data } = await client.get("com.atproto.repo.getRecord", { 145 + params: { 146 + collection: "dev.atcities.route", 147 + repo: did, 148 + rkey: targetRkey, 149 + }, 150 + }); 151 + 152 + if (!ok) { 153 + switch (data.error) { 154 + case "InvalidRequest": 155 + case "ExpiredToken": 156 + case "InvalidToken": { 157 + // tokens arent used and the request should be structured fine 158 + // this is an unexpected error so its fine to 500 exit 159 + throw "Internal Error"; 160 + } 161 + case "RecordNotFound": { 162 + // 404 error so try load 404 page 163 + if (route !== "404") { 164 + const r404 = await getRoute(did, pds, "404"); 165 + return new Response(r404.body, { 166 + status: 404, 167 + statusText: "Not Found", 168 + headers: r404.headers, 169 + }); 170 + } 171 + return new Response("Could not find page.", { 172 + status: 404, 173 + statusText: "Not Found", 174 + }); 175 + } 176 + default: 177 + throw "Unhandled exception"; 178 + } 179 + } 180 + 181 + if (!is(DevAtcitiesRoute.mainSchema, data.value)) 182 + return new Response( 183 + "Malformed record for at://" + did + "/dev.atcities.route/" + targetRkey 184 + ); 185 + 186 + return new Response("Loading " + targetRkey, { 187 + status: 404, 188 + statusText: "Not Found", 189 + }); 190 + } catch (e) { 191 + console.error(e); 192 + return new Response("Something went wrong loading this route", { 193 + status: 500, 194 + }); 195 + } 116 196 } 117 197 118 198 export default async function (