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

server: deno fmt

vielle.dev 854ac924 ec6e2994

verified
Changed files
+32 -26
server
+8 -6
server/src/index.ts
··· 1 1 import root from "./routes/root.ts"; 2 2 import user from "./routes/user.ts"; 3 3 import backfill from "./backfill/index.ts"; 4 - import { PORT, ROOT_DOMAIN, SUBDOMAIN_REGEX, clearCookies } from "./utils.ts"; 4 + import { clearCookies, PORT, ROOT_DOMAIN, SUBDOMAIN_REGEX } from "./utils.ts"; 5 5 6 6 const db = await backfill; 7 7 ··· 37 37 req, 38 38 isDidSubdomain 39 39 ? { 40 - did: `did:${subdomain.at(-1) === "did-plc" ? "plc" : "web"}:${subdomain.slice(0, -1).join(".")}`, 41 - } 40 + did: `did:${subdomain.at(-1) === "did-plc" ? "plc" : "web"}:${ 41 + subdomain.slice(0, -1).join(".") 42 + }`, 43 + } 42 44 : { 43 - handle: subdomain.join(".") as `${string}.${string}`, 44 - } 45 + handle: subdomain.join(".") as `${string}.${string}`, 46 + }, 45 47 ); 46 48 return new Response(res.body, { 47 49 ...res, ··· 56 58 `Could not resolve domain "${subdomain.join(".")}.${ROOT_DOMAIN}"`, 57 59 { 58 60 status: 404, 59 - } 61 + }, 60 62 ); 61 63 });
+1 -1
server/src/routes/root.ts
··· 1 1 import { ROOT_DOMAIN } from "../utils.ts"; 2 - import index from "../www/index.html#denoRawImport=text.ts" with { type: "text" }; 2 + import index from "../www/index.html" with { type: "text" }; 3 3 import ascii from "./ascii.txt" with { type: "text" }; 4 4 5 5 function route(
+20 -17
server/src/routes/user.ts
··· 6 6 } from "@atcute/identity-resolver"; 7 7 import { and, eq } from "drizzle-orm"; 8 8 import { routes } from "../db/schema.ts"; 9 - import { ROOT_DOMAIN, type db } from "../utils.ts"; 9 + import { type db, ROOT_DOMAIN } from "../utils.ts"; 10 10 import ascii from "./ascii.txt" with { type: "text" }; 11 11 12 12 const handleResolver = new CompositeHandleResolver({ ··· 24 24 req: Request, 25 25 user: 26 26 | { handle: `${string}.${string}` } 27 - | { did: `did:plc:${string}` | `did:web:${string}` } 27 + | { did: `did:plc:${string}` | `did:web:${string}` }, 28 28 ): Promise<Response> { 29 29 // if handle: resolve did 30 30 let did: `did:${"plc" | "web"}:${string}`; ··· 41 41 { 42 42 status: 500, 43 43 statusText: "Internal Server Error", 44 - } 44 + }, 45 45 ); 46 46 } 47 47 } else did = user.did; 48 48 49 49 // look up in db 50 - const db_res = 51 - ( 52 - await db 53 - .select() 54 - .from(routes) 55 - .where( 56 - and( 57 - eq(routes.did, did), 58 - eq(routes.url_route, new URL(req.url).pathname) 59 - ) 60 - ) 61 - ).at(0) ?? 50 + const db_res = ( 51 + await db 52 + .select() 53 + .from(routes) 54 + .where( 55 + and( 56 + eq(routes.did, did), 57 + eq(routes.url_route, new URL(req.url).pathname), 58 + ), 59 + ) 60 + ).at(0) ?? 62 61 ( 63 62 await db 64 63 .select() ··· 72 71 404: The user has no atcities site or is missing a 404 page. 73 72 74 73 If you're the owner of this account, head to https://atcities.dev/ for more information. 75 - The index of this account is at https://${"handle" in user ? user.handle : user.did.split(":").at(-1) + ".did-" + user.did.split(":").at(1)}.${ROOT_DOMAIN}/ 74 + The index of this account is at https://${ 75 + "handle" in user 76 + ? user.handle 77 + : user.did.split(":").at(-1) + ".did-" + user.did.split(":").at(1) 78 + }.${ROOT_DOMAIN}/ 76 79 `); 77 80 } 78 81 try { 79 82 const file = await Deno.readFile( 80 - `./blobs/${db_res.did}/${db_res.blob_cid}` 83 + `./blobs/${db_res.did}/${db_res.blob_cid}`, 81 84 ); 82 85 return new Response(file, { 83 86 headers: {
+2 -1
server/src/utils.ts
··· 58 58 */ 59 59 export function urlToRkey(url: string): string | undefined { 60 60 // contains 0-9A-Za-z + special valid chars and / seperator. also can contain %XX with XX being hex 61 - if (!url.match(/^([a-zA-Z0-9/\-._~!$&'()*+,;=:@]|(%[0-9a-fA-F]{2}))*$/gm)) 61 + if (!url.match(/^([a-zA-Z0-9/\-._~!$&'()*+,;=:@]|(%[0-9a-fA-F]{2}))*$/gm)) { 62 62 return; 63 + } 63 64 return ( 64 65 url 65 66 // : replace is hoisted so it doesnt replace colons from elsewhere
+1 -1
server/src/www/index.html
··· 1 - <!doctype html> 1 + <!DOCTYPE html> 2 2 <html lang="en"> 3 3 <head> 4 4 <meta charset="UTF-8" />