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<Body>) -> Result<Response<Body>, 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<Response<Body>, TwinHError> {
todo!()
}
async fn login() -> Result<Response<Body>, TwinHError> {
Ok(Response::new(
templates::REGISTRY.render("login", &"").unwrap().into(),
))
}
async fn login_google() -> Result<Response<Body>, 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<Body>) -> Result<Response<Body>, TwinHError> {
let query = req.uri().query().unwrap_or_default();
let query = serde_urlencoded::de::from_str::<GoogleOAuthQuery>(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<Body>) -> Result<Response<Body>, TwinHError> {
let cars = repo::get_all_cars().unwrap();
todo!()
}
async fn index(req: Request<Body>) -> Result<Response<Body>, TwinHError> {
let query = req.uri().query().unwrap_or_default();
let filter = serde_urlencoded::de::from_str::<PartsQuery>(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<String>,
year: Option<String>,
model: Option<String>,
engine: Option<String>,
}
#[derive(Debug, Serialize, Default)]
struct PartsData {
makes: Option<Vec<String>>,
years: Option<Vec<String>>,
models: Option<Vec<String>>,
engines: Option<Vec<String>>,
selected_make: Option<String>,
selected_year: Option<String>,
selected_model: Option<String>,
selected_engine: Option<String>,
parts: Option<Vec<String>>,
}