use crate::error::TwinHError; use crate::repo; use crate::templates; use hyper::body::HttpBody as _; use hyper::Method; use hyper::StatusCode; use hyper::{Body, Request, Response}; use serde::{Deserialize, Serialize}; pub async fn router(req: Request) -> Result, TwinHError> { match (req.method(), req.uri().path()) { (&Method::GET, "/") => index(req).await, (&Method::GET, "/cars") => cars(req).await, (&Method::GET, "/login") => login().await, (&Method::GET, "/login/google") => login_google().await, (&Method::GET, "/login/google/code") => login_google_code(req).await, (&Method::GET, "/suggestions") => suggestions().await, _ => Ok(Response::builder() .status(StatusCode::NOT_FOUND) .body("Not found.".into()) .unwrap()), } } async fn suggestions() -> Result, TwinHError> { todo!() } async fn login() -> Result, TwinHError> { Ok(Response::new( templates::REGISTRY.render("login", &"").unwrap().into(), )) } async fn login_google() -> Result, TwinHError> { let uri = hyper::Uri::builder() .scheme("https") .authority("accounts.google.com") .path_and_query(format!( "{}?client_id={}&redirect_uri={}&response_type={}&scope={}&state={}", "/o/oauth2/v2/auth", "???", "http://localhost:3000/login/google/code", "code", "openid%20profile%20email", "BLARGH" )) .build() .unwrap(); let resp = Response::builder() .header(hyper::header::LOCATION, uri.to_string()) .status(StatusCode::TEMPORARY_REDIRECT) .body("".into()) .unwrap(); Ok(resp) } #[derive(Serialize)] struct AuthRequest { client_id: String, client_secret: String, grant_type: &'static str, redirect_uri: String, code: String, } impl Default for AuthRequest { fn default() -> Self { Self { client_id: "???".into(), client_secret: "???".into(), grant_type: "authorization_code", redirect_uri: String::new(), code: String::new(), } } } async fn login_google_code(req: Request) -> Result, TwinHError> { let query = req.uri().query().unwrap_or_default(); let query = serde_urlencoded::de::from_str::(query).unwrap(); if query.state != "BLARGH" { dbg!("tampering?"); } // get access token let auth_body = AuthRequest { code: query.code, redirect_uri: "http://localhost:3000/login/google/code".to_owned(), ..Default::default() }; let https = hyper_rustls::HttpsConnector::with_native_roots(); let client: hyper::Client<_, hyper::Body> = hyper::Client::builder().build(https); let req = Request::builder() .method(Method::POST) .uri("https://oauth2.googleapis.com/token") .header( hyper::header::CONTENT_TYPE, "application/x-www-form-urlencoded", ) .body(serde_urlencoded::ser::to_string(auth_body).unwrap().into()) .unwrap(); let mut resp = client.request(req).await.unwrap(); use std::io::prelude::*; while let Some(chunk) = resp.body_mut().data().await { std::io::stdout().write_all(&chunk.unwrap()).unwrap(); } let resp = Response::builder() .header(hyper::header::LOCATION, "/".to_string()) .status(StatusCode::TEMPORARY_REDIRECT) .body("Successful login, redirecting...".into()) .unwrap(); Ok(resp) } pub struct AccessToken { access_token: String, expires_in: String, scope: String, token_type: String, } async fn cars(req: Request) -> Result, TwinHError> { let cars = repo::get_all_cars().unwrap(); todo!() } async fn index(req: Request) -> Result, TwinHError> { let query = req.uri().query().unwrap_or_default(); let filter = serde_urlencoded::de::from_str::(query).unwrap(); let mut data = PartsData::default(); data.makes = Some(vec!["Hudson".into(), "Essex".into(), "Terraplane".into()]); if let Some(make) = filter.make { if make.eq("Hudson") { data.models = Some(vec!["Hornet".into(), "Wasp".into(), "Jet".into()]); } else if make.eq("Essex") { data.models = Some(vec!["Super Six".into()]); } data.selected_make = Some(make); } data.parts = Some(Vec::new()); if let Some(model) = filter.model { data.parts = Some(vec!["1".into(), "2".into(), "3".into()]); data.selected_model = Some(model); } let body = templates::REGISTRY.render("index", &data).unwrap(); let resp = Response::builder() .header(hyper::header::CONTENT_TYPE, "text/html; charset=utf-8") .body(body.into()) .unwrap(); Ok(resp) } #[derive(Debug, Deserialize)] struct GoogleOAuthQuery { code: String, state: String, } #[derive(Debug, Deserialize)] struct PartsQuery { make: Option, year: Option, model: Option, engine: Option, } #[derive(Debug, Serialize, Default)] struct PartsData { makes: Option>, years: Option>, models: Option>, engines: Option>, selected_make: Option, selected_year: Option, selected_model: Option, selected_engine: Option, parts: Option>, }