use rocket::http::Cookies; use rocket::http::Cookie; use rocket::config::Config; use rocket::config::Environment; use rocket::config::Value; use rocket::http::Status; use rocket::request; use rocket::request::Form; use rocket::request::FromForm; use rocket::request::FromRequest; use rocket::request::Request; use rocket::response::Redirect; use rocket::State; use rocket_contrib::templates::Template; use std::collections::HashMap; use crate::db; use crate::error::Error; use crate::strava; pub struct Params { pub base_url: String, } #[database("db")] pub struct Db(diesel::PgConnection); #[derive(Debug)] pub struct LoggedInUser { pub username: String, } impl<'a, 'r> FromRequest<'a, 'r> for LoggedInUser { type Error = Error; fn from_request(request: &'a Request<'r>) -> request::Outcome { let conn = request.guard::() .map_failure(|(s, ())| (s, Error::InternalError))?; let user = (|| { let username = request.cookies() .get_private("user") .map(|cookie| cookie.value().to_string()) .ok_or(Error::NotFound)?; if db::user_exists(&conn, &username)? { Ok(LoggedInUser{username: username}) } else { Err(Error::NotFound) } })(); use request::Outcome; match user { Ok(user) => Outcome::Success(user), Err(Error::NotFound) => Outcome::Forward(()), Err(e) => Outcome::Failure((Status::InternalServerError, e)), } } } #[get("/")] fn index(user: Option) -> Template { let mut context = HashMap::new(); context.insert("parent", "layout".to_string()); context.insert("message", "Hello, World".to_string()); for user in user { context.insert("user", user.username); } Template::render("index", context) } #[get("/login?")] fn login(failed: bool) -> Template { let mut context = HashMap::new(); context.insert("parent", "layout"); if failed { context.insert("message", "Incorrect username or password"); } Template::render("login", context) } #[derive(FromForm)] struct LoginData { username: String, password: String, } #[post("/login", data = "")] fn login_submit(conn: Db, data: Form, mut cookies: Cookies) -> Result { match db::authenticate(&*conn, &data.username, &data.password) { Ok(_user) => { cookies.add_private(Cookie::new("user", data.username.clone())); Ok(Redirect::to(uri!(index).to_string())) }, Err(Error::NotFound) => Ok(Redirect::to(uri!(login: failed = true).to_string())), Err(e) => Err(e), } } #[get("/link_strava_callback?")] fn link_strava_callback( config: State, code: String, ) -> Result { strava::exchange_token("&config.client_id", "&config.client_secret", &code) .map(|t| format!("{:#?}", t)) } #[get("/link_strava")] fn link_strava(config: State) -> Redirect { Redirect::to(format!( concat!( "https://www.strava.com/oauth/authorize?", "client_id={}&", "response_type=code&", "redirect_uri={}&", "approval_prompt=force&", "scope=read", ), "config.client_id", format!("{}/link_strava_callback", config.base_url) )) } pub fn start(conn: diesel::PgConnection, db_url: &str, params: Params) { let mut database_config = HashMap::new(); let mut databases = HashMap::new(); database_config.insert("url", Value::from(db_url)); databases.insert("db", Value::from(database_config)); let persistent_config = db::get_config(&conn).expect("loading config"); let config = Config::build(Environment::Development) .extra("databases", databases) .secret_key(persistent_config.rocket_secret_key) .finalize() .unwrap(); rocket::custom(config) .manage(params) .mount( "/", routes![ index, login, login_submit, link_strava, link_strava_callback ], ) .attach(Template::fairing()) .attach(Db::fairing()) .launch(); }