Config files for my server. Except not my secrets

Add custom landing page proper

renders cool in firefox but normal everywhere else
shout out http link header

vielle.dev 25ae04de 73ff45e0

verified
Changed files
+189 -39
.vscode
caddy
landing
+3
.vscode/settings.json
··· 1 + { 2 + "deno.enable": true 3 + }
+2 -39
caddy/Caddyfile
··· 80 80 on_demand 81 81 } 82 82 83 - handle / { 84 - header Content-Type text/plain; charset=utf-8 85 - header Access-Control-Allow-Origin * 86 - respond <<BODY 87 - | | 88 - | | 89 - | | 90 - +---#---------------------#---+ 91 - | [Used Car Salesman voice] | 92 - | Open Your | 93 - | __ ___ _ _ ___ | 94 - | | \ / \| \| |/ __| | 95 - | _ | - || - || || | | | 96 - | |_||__/ \___/|_|\_|\___| | 97 - | ___ _ _ ___ | 98 - | | __|| || | | __| | 99 - | | _| | || |_ | _| | 100 - | |_| |_||___||___| | 101 - | | 102 - | At dong.vielle.dev TODAY | 103 - | for a chance to win £50 off | 104 - | a purchase of 3 | 105 - | or more potatoes!! | 106 - +-----------------------------+ 107 - 108 - Welcome to my PDS! 109 - It's running off a pi on my floor 110 - and isnt always online at the time of writing. 111 - 112 - Invite codes are required 113 - DM me if you somehow trust this enough 114 - 115 - PDS Source: https://github.com/bluesky-social/atproto 116 - Setup Guide: https://vielle.dev/blog/pds-on-a-pi 117 - Protocol: https://atproto.com 118 - 119 - 120 - BODY 200 121 - } 83 + @landing path / /css 84 + reverse_proxy @landing landing:8000 122 85 123 86 # secure due to tailscale wireguard stuff 124 87 # i cant configure https on the pi???
+4
compose.yaml
··· 8 8 source: ./prs.refreshToken 9 9 target: /app/.refreshToken 10 10 11 + landing: 12 + build: ./landing 13 + restart: unless-stopped 14 + 11 15 caddy: 12 16 build: 13 17 context: ./
+7
landing/Dockerfile
··· 1 + FROM denoland/deno:latest 2 + WORKDIR /app 3 + 4 + COPY ./ /app 5 + RUN deno bundle --output bundle.js /app/landing.ts 6 + 7 + CMD deno --allow-net bundle.js
+173
landing/landing.ts
··· 1 + const css = ` 2 + @property --gradient-offset { 3 + syntax: "<length>"; 4 + inherits: false; 5 + initial-value: 0px; 6 + } 7 + 8 + @keyframes scroll { 9 + from { 10 + --gradient-offset: 0px; 11 + } 12 + 13 + to { 14 + --gradient-offset: var(--gradient-gap); 15 + } 16 + } 17 + 18 + html { 19 + background: 20 + repeating-linear-gradient( 21 + #fff1, 22 + #0000 5px, 23 + #fff1 10px 24 + ), 25 + black; 26 + min-height: 100%; 27 + font-family: 'Monaspace neon var', monospace; 28 + font-weight: bold; 29 + } 30 + 31 + html, body { 32 + margin: 0; 33 + } 34 + 35 + pre { 36 + /* config */ 37 + --light: lime; 38 + --dark: green; 39 + --gradient-gap: 10px; 40 + --gradient-size: 5px; 41 + --gradient-offset: 0px; 42 + --gradient-max: calc(var(--gradient-gap) * 2 + var(--gradient-size)); 43 + 44 + /* override some resource://content-accessible/plaintext.css styles in ff */ 45 + white-space: pre !important; 46 + width: max-content; 47 + padding: 1em; 48 + margin-block: 0; 49 + padding-block-start: 0; 50 + 51 + text-shadow: 0 0 5px lime; 52 + color: lime; 53 + @supports (background-clip: text) { 54 + color: transparent; 55 + background: repeating-linear-gradient( 56 + var(--light), 57 + var(--light) calc(var(--gradient-gap) - var(--gradient-offset)), 58 + var(--dark) calc(var(--gradient-gap) - var(--gradient-offset)), 59 + var(--dark) calc(var(--gradient-gap) - var(--gradient-offset) + var(--gradient-size)), 60 + var(--light) calc(var(--gradient-gap) - var(--gradient-offset) + var(--gradient-size)), 61 + var(--light) var(--gradient-max) 62 + ) text; 63 + animation: 10s infinite scroll linear; 64 + } 65 + } 66 + `; 67 + 68 + const starter = ` | | 69 + | | 70 + | | 71 + +---#---------------------#---+ 72 + | [Used Car Salesman voice] | 73 + | Open Your | 74 + | __ ___ _ _ ___ | 75 + | | \\ / \\| \\| |/ __| | 76 + | _ | - || - || || | | | 77 + | |_||__/ \\___/|_|\\_|\\___| | 78 + | ___ _ _ ___ | 79 + | | __|| || | | __| | 80 + | | _| | || |_ | _| | 81 + | |_| |_||___||___| | 82 + | | 83 + | At dong.vielle.dev TODAY | 84 + | for a chance to win £50 off | 85 + | a purchase of 3 | 86 + | or more potatoes!! | 87 + +-----------------------------+ 88 + 89 + 90 + Welcome to my PDS! 91 + It's running off a pi on my floor 92 + and isnt always online at the time of writing. 93 + 94 + Invite codes are required 95 + DM me if you somehow trust this enough 96 + 97 + PDS Source: https://github.com/bluesky-social/atproto 98 + Setup Guide: https://vielle.dev/blog/pds-on-a-pi 99 + Protocol: https://atproto.com 100 + `; 101 + 102 + Deno.serve({ port: 8000 }, (req) => { 103 + switch (new URL(req.url).pathname) { 104 + case "/css": 105 + return new Response(css, { 106 + headers: { 107 + "Content-Type": "text/css; charset=utf-8", 108 + "Access-Control-Allow-Origin": "*", 109 + }, 110 + }); 111 + case "/": { 112 + const body = new ReadableStream({ 113 + async start(controller) { 114 + const users = fetch("http://pi:8000/xrpc/com.atproto.sync.listRepos") 115 + .then((res) => res.json() as Promise<{ repos: { did: string }[] }>) 116 + .then((res) => res.repos) 117 + .then((res) => 118 + res.map((repo) => ({ 119 + display: fetch( 120 + `http://pi:8000/xrpc/com.atproto.repo.getRecord?repo=${repo.did}&collection=app.bsky.actor.profile&rkey=self` 121 + ) 122 + .then((res) => res.json()) 123 + .then((profile) => profile.value.displayName), 124 + handle: fetch( 125 + repo.did.startsWith("did:plc") 126 + ? "https://plc.directory/" + repo.did 127 + : `https://${repo.did.replace("did:web:", "")}/.well-known/did.json` 128 + ) 129 + .then((res) => res.json()) 130 + .then((doc) => doc.alsoKnownAs[0].replace("at://", "")), 131 + did: repo.did, 132 + })) 133 + ) 134 + .then(async (users) => 135 + ( 136 + await Promise.all( 137 + users.map( 138 + async (x) => 139 + `\n- ${x.did}@${await x.handle}: ${await x.display}` 140 + ) 141 + ) 142 + ).join("\n") 143 + ); 144 + 145 + for (const line of starter) { 146 + await new Promise((res) => setTimeout(res, 10)); 147 + controller.enqueue(line); 148 + } 149 + 150 + controller.enqueue(""); 151 + for (const user of "\n\nAccounts:" + (await users)) { 152 + await new Promise((res) => setTimeout(res, 10)); 153 + controller.enqueue(user); 154 + } 155 + 156 + controller.close(); 157 + }, 158 + }); 159 + 160 + return new Response(body.pipeThrough(new TextEncoderStream()), { 161 + headers: { 162 + "Content-Type": "text/text; charset=utf-8", 163 + "Access-Control-Allow-Origin": "*", 164 + Link: "</css>; rel=stylesheet", 165 + }, 166 + }); 167 + } 168 + } 169 + 170 + return new Response("404", { 171 + status: 404, 172 + }); 173 + });