this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

logic for day one done

+120 -146
+3 -1
shared/challenges_markdown/one/part_one.md
··· 1 1 # Welcome to at://advent! 2 2 3 - An atproto adventure to learn [atproto](https://atproto.com/) in an interactive and 3 + at://advent is an atproto adventure to learn [atproto](https://atproto.com/) in an interactive and 4 4 hopefully fun way in the style of advent of code. 5 5 6 6 The majority of these challenges can be done without writing any code, but using community tooling. But you may find ··· 14 14 - [atproto.at](https://atproto.at/) - A web browser base atproto explorer (and more). Will allow you to view records, 15 15 create records, PLC data, download/view a users repo exports, and listen to the jetstream for that user. May need to 16 16 turn on developer mode for the more advance options. 17 + - [various sdks](https://atproto.com/sdks) - atproto.com has a list of various sdks if you'd like to write the code to 18 + solve these challenges. 17 19 18 20 We are going to start simple. Everyone knows they have a handle, but `did` you know you also have a unique id called a 19 21 [did](https://atproto.com/specs/did)? This is a unique identifier for your identity that never changes. This comes in
+90 -133
shared/src/advent/challenges/day_one.rs
··· 1 - use crate::OAuthAgentType; 2 1 use crate::advent::day::Day; 3 2 use crate::advent::{AdventChallenge, AdventError, ChallengeCheckResponse}; 4 3 use crate::atrium::safe_check_unknown_record_parse; 5 4 use crate::lexicons::codes::advent; 5 + use crate::{HandleResolver, OAuthAgentType}; 6 6 use async_trait::async_trait; 7 - use atrium_api::types::Collection; 8 - use log::info; 7 + use atrium_api::agent::Configure; 8 + use atrium_common::resolver::Resolver; 9 9 use sqlx::PgPool; 10 10 11 11 pub struct DayOne { 12 12 pub pool: PgPool, 13 13 pub oauth_client: Option<OAuthAgentType>, 14 + pub handle_resolver: HandleResolver, 15 + } 16 + 17 + impl DayOne { 18 + pub fn new( 19 + pool: PgPool, 20 + oauth_client: Option<OAuthAgentType>, 21 + handle_resolver: HandleResolver, 22 + ) -> Self { 23 + Self { 24 + pool, 25 + oauth_client, 26 + handle_resolver, 27 + } 28 + } 14 29 } 15 30 16 31 #[async_trait] ··· 31 46 true 32 47 } 33 48 49 + fn requires_manual_verification_part_two(&self) -> bool { 50 + true 51 + } 52 + 34 53 async fn check_part_one( 35 54 &self, 36 55 did: String, 37 - _verification_code: Option<String>, 56 + verification_code: Option<String>, 38 57 ) -> Result<ChallengeCheckResponse, AdventError> { 39 58 let client = self 40 59 .oauth_client ··· 43 62 "No oauth client. This should not happen".to_string(), 44 63 ))?; 45 64 46 - let record_res = client 47 - .api 48 - .com 49 - .atproto 50 - .repo 51 - .get_record( 52 - atrium_api::com::atproto::repo::get_record::ParametersData { 53 - cid: None, 54 - collection: advent::challenge::Day::NSID.parse().unwrap(), 55 - repo: did.parse().unwrap(), 56 - rkey: "1".parse().unwrap(), 57 - } 58 - .into(), 59 - ) 60 - .await; 61 - 62 - let record = match record_res { 63 - Ok(r) => r, 64 - Err(e) => { 65 - log::error!("Error getting record: {}", e); 66 - return Ok(ChallengeCheckResponse::Incorrect("Does not appear to be a record in your repo in the collection codes.advent.challenge.day with the record key of 1".to_string())); 65 + let users_did = match client.did().await { 66 + None => { 67 + return Ok(ChallengeCheckResponse::Incorrect( 68 + "You are not logged in!".to_string(), 69 + )); 67 70 } 71 + Some(did) => did, 68 72 }; 73 + let submitted_did = verification_code.unwrap_or_default(); 69 74 70 - let Some(challenge) = self.get_days_challenge(&did).await? else { 71 - log::error!("Could not find a challenge record for day: 1 for the user: {did:?}"); 72 - return Err(AdventError::ShouldNotHappen( 73 - "Could not find challenge record".to_string(), 75 + if users_did.to_string() != submitted_did { 76 + return Ok(ChallengeCheckResponse::Incorrect( 77 + format!( 78 + "{} is not the correct did that for the account you are logged in as! Try again.", 79 + submitted_did 80 + ) 81 + .to_string(), 74 82 )); 75 - }; 76 - 77 - let record_data = match safe_check_unknown_record_parse::<advent::challenge::day::RecordData>( 78 - record.value.clone(), 79 - ) { 80 - Ok(rd) => rd, 81 - Err(e) => { 82 - log::error!("Error parsing record: {e}"); 83 - return Ok(ChallengeCheckResponse::Incorrect(format!( 84 - "There is a record at the correct location, but it does not seem like it is correct. Try again:\n{e}" 85 - ))); 86 - } 87 - }; 88 - 89 - let verification_code = 90 - challenge 91 - .verification_code_one 92 - .ok_or(AdventError::ShouldNotHappen( 93 - "no verification code for day 1 challenge :/".to_string(), 94 - ))?; 95 - 96 - Ok(if record_data.part_one == verification_code { 97 - ChallengeCheckResponse::Correct 98 - } else { 99 - ChallengeCheckResponse::Incorrect(format!( 100 - "The code {} is incorrect", 101 - record_data.part_one 102 - )) 103 - }) 83 + } 84 + Ok(ChallengeCheckResponse::Correct) 104 85 } 105 86 106 87 ///TODO this is just a straight copy and paste of part one since it's a proof of concept needs to share code better between the two 107 88 async fn check_part_two( 108 89 &self, 109 90 did: String, 110 - _verification_code: Option<String>, 91 + verification_code: Option<String>, 111 92 ) -> Result<ChallengeCheckResponse, AdventError> { 112 - match &self.oauth_client { 113 - None => Err(AdventError::ShouldNotHappen( 93 + let client = self 94 + .oauth_client 95 + .as_ref() 96 + .ok_or(AdventError::ShouldNotHappen( 114 97 "No oauth client. This should not happen".to_string(), 115 - )), 116 - Some(client) => { 117 - match client 118 - .api 119 - .com 120 - .atproto 121 - .repo 122 - .get_record( 123 - atrium_api::com::atproto::repo::get_record::ParametersData { 124 - cid: None, 125 - collection: advent::challenge::Day::NSID.parse().unwrap(), 126 - repo: did.parse().unwrap(), 127 - rkey: "1".parse().unwrap(), 128 - } 129 - .into(), 130 - ) 131 - .await 132 - { 133 - Ok(record) => { 134 - //TODO trouble, and make it double 135 - let challenge = self.get_days_challenge(&did).await?; 98 + ))?; 99 + 100 + let users_did = match client.did().await { 101 + None => { 102 + return Ok(ChallengeCheckResponse::Incorrect( 103 + "You are not logged in!".to_string(), 104 + )); 105 + } 106 + Some(did) => did, 107 + }; 136 108 137 - match challenge { 138 - None => { 139 - log::error!( 140 - "Could not find a challenge record for day: {} for the user: {}", 141 - self.day(), 142 - did.clone() 143 - ); 144 - Err(AdventError::ShouldNotHappen( 145 - "Could not find a challenge record".to_string(), 146 - )) 147 - } 148 - Some(challenge) => { 149 - let parse_record_result = 150 - safe_check_unknown_record_parse::< 151 - advent::challenge::day::RecordData, 152 - >(record.value.clone()); 109 + let did_doc = self 110 + .handle_resolver 111 + .resolve(&users_did) 112 + .await 113 + .map_err(|err| { 114 + log::error!("Failed to resolve DID document: {}", err); 115 + AdventError::ShouldNotHappen(format!("Failed to resolve DID document: {}", err)) 116 + })?; 153 117 154 - match parse_record_result { 155 - Ok(record_data) => match record_data.part_two { 156 - None => { 157 - Ok(ChallengeCheckResponse::Incorrect("The record is there, it's the right kind. But aren't you forgetting something?".to_string())) 158 - } 159 - Some(part_two_code) => { 160 - match part_two_code 161 - == challenge 162 - .verification_code_two 163 - .unwrap_or("".to_string()) 164 - { 165 - true => Ok(ChallengeCheckResponse::Correct), 166 - false => { 167 - Ok(ChallengeCheckResponse::Incorrect(format!( 168 - "The code {} is incorrect", 169 - record_data.part_one 170 - ))) 171 - } 172 - } 173 - } 174 - }, 175 - Err(err) => { 176 - log::error!("Error parsing record: {}", err); 177 - Ok(ChallengeCheckResponse::Incorrect(format!( 178 - "There is a record at the correct location, but it does not seem like it is correct. Try again:\n{err}" 179 - ))) 180 - } 181 - } 182 - } 183 - } 184 - } 185 - Err(err) => { 186 - log::error!("Error getting record: {}", err); 187 - Ok(ChallengeCheckResponse::Incorrect("Does not appear to be a record in your repo in the collection codes.advent.challenge.day with the record key of 1".to_string())) 188 - } 118 + let multi_base_key = match did_doc.get_signing_key() { 119 + Some(verification_method) => match verification_method.public_key_multibase.clone() { 120 + Some(key) => key, 121 + None => { 122 + return Ok(ChallengeCheckResponse::Incorrect( 123 + "We couldn't find a signing key for your DID? Hmmm this should be there...." 124 + .to_string(), 125 + )); 189 126 } 127 + }, 128 + None => { 129 + return Ok(ChallengeCheckResponse::Incorrect( 130 + "We couldn't find a signing key for your DID? Hmmm this should be there...." 131 + .to_string(), 132 + )); 190 133 } 134 + }; 135 + 136 + let verification_code = verification_code.unwrap_or_default(); 137 + 138 + if multi_base_key.to_string() != verification_code { 139 + return Ok(ChallengeCheckResponse::Incorrect( 140 + format!( 141 + "{} is not the correct key that for the account you are logged in as! Try again.", 142 + verification_code 143 + ) 144 + .to_string(), 145 + )); 191 146 } 147 + 148 + Ok(ChallengeCheckResponse::Correct) 192 149 } 193 150 }
+4
web/Dockerfile
··· 1 + FROM ubuntu:latest 2 + LABEL authors="baileytownsend" 3 + 4 + ENTRYPOINT ["top", "-b"]
+20 -10
web/src/handlers/day.rs
··· 1 - use crate::error_response; 2 1 use crate::session::{AxumSessionStore, FlashMessage, get_flash_message, set_flash_message}; 3 2 use crate::templates::{HtmlTemplate, day::DayTemplate}; 3 + use crate::{AppState, error_response}; 4 4 use atrium_api::agent::Agent; 5 5 use atrium_api::types::string::Did; 6 6 use axum::{ ··· 22 22 23 23 fn pick_day( 24 24 day: Day, 25 - pool: PgPool, 25 + state: AppState, 26 26 oauth_client: Option<OAuthAgentType>, 27 27 ) -> Result<Box<dyn AdventChallenge + Send + Sync>, AdventError> { 28 28 match day { 29 - Day::One => Ok(Box::new(DayOne { pool, oauth_client })), 30 - Day::Two => Ok(Box::new(DayTwo { pool, oauth_client })), 31 - Day::Three => Ok(Box::new(DayThree { pool, oauth_client })), 29 + Day::One => Ok(Box::new(DayOne::new( 30 + state.postgres_pool, 31 + oauth_client, 32 + state.handle_resolver, 33 + ))), 34 + Day::Two => Ok(Box::new(DayTwo { 35 + pool: state.postgres_pool, 36 + oauth_client, 37 + })), 38 + Day::Three => Ok(Box::new(DayThree { 39 + pool: state.postgres_pool, 40 + oauth_client, 41 + })), 32 42 _ => Err(AdventError::InvalidDay(0)), // Day::Three => {} 33 43 // Day::Four => {} 34 44 // Day::Five => {} ··· 67 77 68 78 pub async fn view_day_handler( 69 79 Path(id): Path<u8>, 70 - State(pool): State<PgPool>, 80 + app_state: State<AppState>, 71 81 session: AxumSessionStore, 72 82 ) -> Result<impl IntoResponse, Response> { 73 83 let day = Day::from(id); 74 84 75 85 let did = session.get_did(); 76 86 let did_clone = did.clone(); 77 - let challenge = pick_day(day, pool, None).map_err(|err| { 87 + let challenge = pick_day(day, app_state.0, None).map_err(|err| { 78 88 log::error!("Error picking day: {err}"); 79 89 error_response(StatusCode::INTERNAL_SERVER_ERROR, "Error picking day") 80 90 })?; ··· 267 277 ///This is the endpoint to verify the day's challenge 268 278 pub async fn post_day_handler( 269 279 Path(day): Path<u8>, 270 - State(pool): State<PgPool>, 280 + state: State<AppState>, 271 281 State(oauth_client): State<OAuthClientType>, 272 282 mut session: AxumSessionStore, 273 283 Form(form): Form<PostDayForm>, ··· 282 292 let did = Did::new(did.to_string()) 283 293 .map_err(log_and_respond(StatusCode::BAD_REQUEST, "Invalid DID"))?; 284 294 295 + let day = Day::from(day); 285 296 let client = oauth_client.restore(&did).await.map_err(log_and_respond( 286 297 StatusCode::INTERNAL_SERVER_ERROR, 287 298 "There was an error restoring the oauth client", 288 299 ))?; 289 300 290 301 let agent = Agent::new(client); 291 - let day = Day::from(day); 292 302 293 - let challenge = pick_day(day, pool, Some(agent)).map_err(log_and_respond( 303 + let challenge = pick_day(day, state.0, Some(agent)).map_err(log_and_respond( 294 304 StatusCode::INTERNAL_SERVER_ERROR, 295 305 "Error picking the day", 296 306 ))?;
+3 -2
web/src/main.rs
··· 4 4 templates::error::ErrorTemplate, 5 5 templates::home::{DayStatus, HomeTemplate}, 6 6 }; 7 + use atrium_common::resolver::Resolver; 7 8 use atrium_identity::{ 8 9 did::{CommonDidResolver, CommonDidResolverConfig, DEFAULT_PLC_DIRECTORY_URL}, 9 10 handle::{AtprotoHandleResolver, AtprotoHandleResolverConfig}, ··· 65 66 redis_pool: bb8::Pool<RedisConnectionManager>, 66 67 oauth_client: OAuthClientType, 67 68 //Used to get did to handle leaving because I figured we'd need it 68 - _handle_resolver: HandleResolver, 69 + handle_resolver: HandleResolver, 69 70 } 70 71 71 72 fn oauth_scopes() -> Vec<Scope> { ··· 182 183 postgres_pool, 183 184 redis_pool, 184 185 oauth_client: client, 185 - _handle_resolver: handle_resolver, 186 + handle_resolver, 186 187 }; 187 188 //HACK Yeah I don't like it either - bt 188 189 let prod: bool = env::var("PROD")