Constellation, Spacedust, Slingshot, UFOs: atproto crates and services for microcosm

wip: handle resolution failures + missed files

+1 -1
who-am-i/src/oauth.rs
··· 181 181 }; 182 182 if bare_handle.is_empty() { 183 183 return Err(ResolveHandleError::InvalidHandle( 184 - bare_handle.to_string(), 184 + at_uri_handle.to_string(), 185 185 "empty handle", 186 186 )); 187 187 }
+31 -10
who-am-i/src/server.rs
··· 125 125 headers: HeaderMap, 126 126 ) -> impl IntoResponse { 127 127 let err = |reason, check_frame| { 128 - let info = json!({ 129 - "reason": reason, 130 - "check_frame": check_frame, 131 - }); 132 - RenderHtml("prompt-error", engine.clone(), info).into_response() 128 + let info = json!({ "reason": reason, "check_frame": check_frame }); 129 + let html = RenderHtml("prompt-error", engine.clone(), info); 130 + (StatusCode::BAD_REQUEST, html).into_response() 133 131 }; 134 132 135 133 let Some(referrer) = headers.get(REFERER) else { ··· 194 192 }): State<AppState>, 195 193 Query(params): Query<UserInfoParams>, 196 194 ) -> impl IntoResponse { 195 + let err = |status, reason| (status, Json(json!({ "reason": reason }))).into_response(); 196 + 197 197 let Some(task_handle) = resolve_handles.take(&params.fetch_key) else { 198 - return "oops, task does not exist or is gone".into_response(); 198 + return err(StatusCode::NOT_FOUND, "fetch key does not exist or expired"); 199 199 }; 200 - if let Ok(handle) = task_handle.await.unwrap() { 201 - Json(json!({ "handle": handle })).into_response() 202 - } else { 203 - "no handle?".into_response() 200 + 201 + match task_handle.await { 202 + Err(task_err) => { 203 + eprintln!("task join error? {task_err:?}"); 204 + err(StatusCode::INTERNAL_SERVER_ERROR, "server errored") 205 + } 206 + Ok(Err(ResolveHandleError::ResolutionFailed(atrium_identity::Error::NotFound))) => { 207 + err(StatusCode::NOT_FOUND, "handle not found") 208 + } 209 + Ok(Err(ResolveHandleError::ResolutionFailed(e))) => { 210 + eprintln!("handle resolution failed: {e:?}"); 211 + err( 212 + StatusCode::INTERNAL_SERVER_ERROR, 213 + "handle resolution failed", 214 + ) 215 + } 216 + Ok(Err(ResolveHandleError::NoHandle)) => err( 217 + StatusCode::INTERNAL_SERVER_ERROR, 218 + "resolved identity but did not find a handle", 219 + ), 220 + Ok(Err(ResolveHandleError::InvalidHandle(_h, reason))) => err( 221 + StatusCode::INTERNAL_SERVER_ERROR, 222 + &format!("handle appears invalid: {reason}"), 223 + ), 224 + Ok(Ok(handle)) => Json(json!({ "handle": handle })).into_response(), 204 225 } 205 226 } 206 227
who-am-i/static/favicon.ico

This is a binary file and will not be displayed.

+146
who-am-i/static/style.css
··· 1 + body { 2 + color: #434; 3 + font-family: 'Iowan Old Style', 'Palatino Linotype', 'URW Palladio L', P052, serif; 4 + margin: 0; 5 + min-height: 100vh; 6 + padding: 0; 7 + } 8 + .wrap { 9 + border: 2px solid #221828; 10 + border-radius: 0.5rem; 11 + box-sizing: border-box; 12 + overflow: hidden; 13 + display: flex; 14 + flex-direction: column; 15 + height: 100vh; 16 + } 17 + .wrap.unframed { 18 + border-radius: 0; 19 + border-width: 0.4rem; 20 + } 21 + header { 22 + background: #221828; 23 + display: flex; 24 + justify-content: space-between; 25 + padding: 0 0.25rem; 26 + color: #c9b; 27 + display: flex; 28 + gap: 0.5rem; 29 + align-items: baseline; 30 + } 31 + header > * { 32 + flex-basis: 33%; 33 + } 34 + header > .empty { 35 + font-size: 0.8rem; 36 + opacity: 0.5; 37 + } 38 + header > .title { 39 + text-align: center; 40 + } 41 + header > a.micro { 42 + text-decoration: none; 43 + font-size: 0.8rem; 44 + text-align: right; 45 + opacity: 0.5; 46 + } 47 + header > a.micro:hover { 48 + opacity: 1; 49 + } 50 + main { 51 + background: #ccc; 52 + display: flex; 53 + flex-direction: column; 54 + flex-grow: 1; 55 + padding: 0.25rem 0.5rem; 56 + } 57 + .mini-content { 58 + margin: 1rem auto 0; 59 + padding: 1rem 0.5rem; 60 + max-width: 21rem; 61 + } 62 + 63 + p { 64 + margin: 1rem 0 0; 65 + text-align: center; 66 + } 67 + .parent-host { 68 + font-weight: bold; 69 + color: #48c; 70 + display: inline-block; 71 + padding: 0 0.125rem; 72 + border-radius: 0.25rem; 73 + border: 1px solid #aaa; 74 + font-size: 0.8rem; 75 + } 76 + 77 + #loader { 78 + display: flex; 79 + flex-grow: 1; 80 + justify-content: center; 81 + align-items: center; 82 + } 83 + .spinner { 84 + animation: rotation 1.618s ease-in-out infinite; 85 + border-radius: 50%; 86 + border: 3px dashed #434; 87 + box-sizing: border-box; 88 + display: inline-block; 89 + height: 1.5em; 90 + width: 1.5em; 91 + } 92 + @keyframes rotation { 93 + 0% { transform: rotate(0deg) } 94 + 100% { transform: rotate(360deg) } 95 + } 96 + 97 + #user-info { 98 + flex-grow: 1; 99 + display: flex; 100 + flex-direction: column; 101 + justify-content: center; 102 + } 103 + #action { 104 + background: #eee; 105 + display: flex; 106 + justify-content: space-between; 107 + padding: 0.5rem 0.25rem 0.5rem 0.5rem; 108 + font-size: 0.8rem; 109 + align-items: baseline; 110 + border-radius: 0.5rem; 111 + border: 1px solid #bbb; 112 + cursor: pointer; 113 + } 114 + #action:hover { 115 + background: #fff; 116 + } 117 + #allow { 118 + background: transparent; 119 + border: none; 120 + border-left: 1px solid #bbb; 121 + padding: 0 0.5rem; 122 + color: #375; 123 + font: inherit; 124 + cursor: pointer; 125 + } 126 + #action:hover #allow { 127 + color: #285; 128 + } 129 + 130 + #or { 131 + font-size: 0.8rem; 132 + text-align: center; 133 + } 134 + #or p { 135 + margin: 0 0 1rem; 136 + } 137 + 138 + input#handle { 139 + border: none; 140 + border-bottom: 1px dashed #aaa; 141 + background: transparent; 142 + } 143 + 144 + .hidden { 145 + display: none !important; 146 + }
+17
who-am-i/templates/base-base.hbs
··· 1 + <!doctype html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="utf-8" /> 5 + <title>who-am-i</title> 6 + <meta name="description" content="{{> description }}"> 7 + <meta property="og:type" content="website"> 8 + <meta property="og:description" content="{{> description}}"> 9 + <!-- <meta property="og:image" content=""> --> 10 + 11 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 12 + <link href="/style.css" rel="stylesheet" type="text/css" /> 13 + </head> 14 + <body> 15 + {{> body}} 16 + </body> 17 + </html>
+28
who-am-i/templates/base-framed.hbs
··· 1 + {{#*inline "description"}}{{/inline}} 2 + 3 + {{#*inline "body"}} 4 + <div class="wrap"> 5 + <header> 6 + <div class="empty">🔒</div> 7 + <code class="title" style="font-family: monospace;" 8 + >who-am-i</code> 9 + <a href="https://microcosm.blue" target="_blank" class="micro" 10 + ><span style="color: #f396a9">m</span 11 + ><span style="color: #f49c5c">i</span 12 + ><span style="color: #c7b04c">c</span 13 + ><span style="color: #92be4c">r</span 14 + ><span style="color: #4ec688">o</span 15 + ><span style="color: #51c2b6">c</span 16 + ><span style="color: #54bed7">o</span 17 + ><span style="color: #8fb1f1">s</span 18 + ><span style="color: #ce9df1">m</span 19 + ></a> 20 + </header> 21 + 22 + <main> 23 + {{> main}} 24 + </main> 25 + </div> 26 + {{/inline}} 27 + 28 + {{#> base-base}}{{/base-base}}
+26
who-am-i/templates/base-full.hbs
··· 1 + {{#*inline "body"}} 2 + <div class="wrap unframed"> 3 + <header> 4 + <div class="empty">🔒</div> 5 + <code class="title" style="font-family: monospace;" 6 + >who-am-i</code> 7 + <a href="https://microcosm.blue" target="_blank" class="micro" 8 + ><span style="color: #f396a9">m</span 9 + ><span style="color: #f49c5c">i</span 10 + ><span style="color: #c7b04c">c</span 11 + ><span style="color: #92be4c">r</span 12 + ><span style="color: #4ec688">o</span 13 + ><span style="color: #51c2b6">c</span 14 + ><span style="color: #54bed7">o</span 15 + ><span style="color: #8fb1f1">s</span 16 + ><span style="color: #ce9df1">m</span 17 + ></a> 18 + </header> 19 + 20 + <main> 21 + {{> main}} 22 + </main> 23 + </div> 24 + {{/inline}} 25 + 26 + {{#> base-base}}{{/base-base}}
+9
who-am-i/templates/hello.hbs
··· 1 + {{#*inline "description"}}A little identity-verifying auth service for microcosm demos{{/inline}} 2 + 3 + {{#*inline "main"}} 4 + <div class="mini-content"> 5 + This is a little identity-verifying service for microcosm demos. 6 + </div> 7 + {{/inline}} 8 + 9 + {{#> base-full}}{{/base-full}}
+17
who-am-i/templates/prompt-error.hbs
··· 1 + {{#*inline "main"}} 2 + <div class="prompt-error"> 3 + <p class="went-wrong">Something went wrong :(</p> 4 + <p class="reason">{{ reason }}</p> 5 + <p id="maybe-not-in-iframe" class="hidden"> 6 + Possibly related: this prompt is meant to be shown in an iframe, but it seems like it's not. 7 + </p> 8 + </div> 9 + 10 + <script> 11 + if ({{{json check_frame}}} && window.self === window.top) { 12 + document.getElementById('maybe-not-in-iframe').classList.remove('hidden'); 13 + } 14 + </script> 15 + {{/inline}} 16 + 17 + {{#> base-framed}}{{/base-framed}}