summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKjetil Orbekk <kjetil.orbekk@gmail.com>2020-01-29 19:15:28 -0500
committerKjetil Orbekk <kjetil.orbekk@gmail.com>2020-01-29 19:15:28 -0500
commitc8db39dea2cf50dd1fa6c499600e09818b8db44a (patch)
treeb2eb940f9769323cd1d3e1c92bbe3c16d38d9dd6 /src
parent6c16bcd190328443f15029fc3ee2467b6c270eed (diff)
Add database support
Diffstat (limited to 'src')
-rw-r--r--src/db.rs36
-rw-r--r--src/error.rs36
-rw-r--r--src/lib.rs51
-rw-r--r--src/main.rs109
-rw-r--r--src/models.rs26
-rw-r--r--src/schema.rs20
-rw-r--r--src/server.rs50
-rw-r--r--src/strava.rs32
8 files changed, 262 insertions, 98 deletions
diff --git a/src/db.rs b/src/db.rs
new file mode 100644
index 0000000..71490d8
--- /dev/null
+++ b/src/db.rs
@@ -0,0 +1,36 @@
+use crate::models;
+use crate::error::Error;
+use diesel::connection::Connection;
+use diesel::pg::PgConnection;
+use diesel::RunQueryDsl;
+use rand::Rng;
+use rand;
+use base64;
+use bcrypt;
+
+pub const COST: u32 = 12;
+
+pub fn create_config(conn: &PgConnection, config: &models::Config) -> Result<(), Error> {
+ use crate::schema::config;
+
+ conn.transaction(|| {
+ diesel::delete(config::table).execute(conn)?;
+
+ diesel::insert_into(config::table)
+ .values(config)
+ .execute(conn)?;
+ Ok(())
+ })
+}
+
+pub fn adduser(conn: &PgConnection, username: &str, password: &str) -> Result<(), Error> {
+ use crate::schema::users;
+
+ let hashed = bcrypt::hash(password, COST)?;
+ let rows = diesel::insert_into(users::table)
+ .values(models::User::new(username, &hashed)).execute(conn)?;
+ if rows != 1 {
+ Err(Error::AlreadyExists)?;
+ }
+ Ok(())
+}
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..6ab0741
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,36 @@
+use std::convert::From;
+use std::error::Error as StdError;
+use bcrypt::BcryptError;
+use diesel::result::Error as DieselErr;
+use std::fmt;
+
+#[derive(Debug)]
+pub enum Error {
+ DieselError(DieselErr),
+ PasswordError(BcryptError),
+ AlreadyExists,
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ Error::DieselError(ref e) => e.fmt(f),
+ Error::PasswordError(ref e) => e.fmt(f),
+ Error::AlreadyExists => f.write_str("AlreadyExists"),
+ }
+ }
+}
+
+impl From<DieselErr> for Error {
+ fn from(e: DieselErr) -> Error {
+ Error::DieselError(e)
+ }
+}
+
+impl From<BcryptError> for Error {
+ fn from(e: BcryptError) -> Error {
+ Error::PasswordError(e)
+ }
+}
+
+impl StdError for Error {}
diff --git a/src/lib.rs b/src/lib.rs
index d7f7b98..5f8118b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -2,12 +2,9 @@
#![feature(decl_macro)]
#[macro_use]
extern crate rocket;
-use rocket::response;
-use rocket::State;
-use rocket_contrib::templates::Template;
-use std::collections::HashMap;
-mod strava;
+#[macro_use]
+extern crate diesel;
#[derive(Debug)]
pub struct Config {
@@ -16,41 +13,9 @@ pub struct Config {
pub base_url: String,
}
-#[get("/")]
-fn index() -> Template {
- let mut context = HashMap::new();
- context.insert("parent", "layout");
- context.insert("message", "Hello, World");
- Template::render("index", context)
-}
-
-#[get("/link_strava_callback?<code>")]
-fn link_strava_callback(config: State<Config>, code: String) -> String {
- strava::exchange_token(&config.client_id, &config.client_secret, &code);
- "OK".to_string()
-}
-
-#[get("/link_strava")]
-fn link_strava(config: State<Config>) -> response::Redirect {
- response::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_server(config: Config) {
- rocket::ignite()
- .manage(config)
- .mount("/", routes![index, link_strava, link_strava_callback])
- .attach(Template::fairing())
- .launch();
-}
+pub mod error;
+pub mod db;
+pub mod models;
+mod schema;
+pub mod server;
+mod strava;
diff --git a/src/main.rs b/src/main.rs
index 32333f1..f88b7e2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,36 +1,85 @@
#[macro_use]
extern crate clap;
+use clap::App;
+use clap::Arg;
+use clap::SubCommand;
+use diesel::connection::Connection;
+use diesel::pg::PgConnection;
fn main() {
- let matches = clap_app!(pjournal =>
- (version: "0.1")
- (author: "KJ Ørbekk <kj@orbekk.com>")
- (about: "Practice Journaling")
- (@arg strava_client_secret:
- --strava_client_secret
- +required +takes_value
- "Client secret for strava authentication")
- (@arg strava_client_id:
- --strava_client_id
- +required +takes_value
- "Client id for strava authentication")
- (@arg base_url:
- --base_url
- +takes_value
- "Endpoint for this web app")
- )
- .get_matches();
+ let matches = App::new("pjournal")
+ .version("0.1")
+ .author("KJ Ørbekk <kj@orbekk.com>")
+ .about("Practice Journaling")
+ .arg(
+ Arg::with_name("database_url")
+ .long("database_url")
+ .required(true)
+ .takes_value(true)
+ .help("URL to postgresql database"),
+ )
+ .arg(
+ Arg::with_name("base_url")
+ .long("base_url")
+ .takes_value(true)
+ .help("Endpoint for this web server"),
+ )
+ .subcommand(
+ SubCommand::with_name("init")
+ .about("initialize database config")
+ .arg(
+ Arg::with_name("rocket_secret_key")
+ .long("rocket_secret_key")
+ .takes_value(true)
+ .required(true)
+ .help("Secret passed to rocket for encrypted cookies"),
+ )
+ .arg(
+ Arg::with_name("strava_client_secret")
+ .long("strava_client_secret")
+ .takes_value(true)
+ .required(true)
+ .help("Client secret for strava authentication"),
+ )
+ .arg(
+ Arg::with_name("strava_client_id")
+ .long("strava_client_id")
+ .takes_value(true)
+ .required(true)
+ .help("Client id for strava authentication"),
+ ),
+ )
+ .subcommand(
+ SubCommand::with_name("adduser")
+ .about("add a user account")
+ .arg(Arg::with_name("USERNAME").required(true).index(1))
+ .arg(Arg::with_name("PASSWORD").required(true).index(2)),
+ )
+ .get_matches();
- let config = pjournal::Config {
- client_id: matches
- .value_of("strava_client_id")
- .unwrap().to_string(),
- client_secret: matches
- .value_of("strava_client_secret")
- .unwrap().to_string(),
- base_url: matches
- .value_of("base_url")
- .unwrap_or("http://localhost:8000").to_string(),
- };
- pjournal::start_server(config);
+ let base_url = matches
+ .value_of("base_url")
+ .unwrap_or("http://localhost:8000");
+
+ let db_url = matches.value_of("database_url").unwrap();
+ let conn = PgConnection::establish(db_url).unwrap();
+
+ if let Some(matches) = matches.subcommand_matches("init") {
+ let config = pjournal::models::Config {
+ strava_client_id: matches.value_of("strava_client_id").unwrap(),
+ strava_client_secret: matches.value_of("strava_client_secret").unwrap(),
+ rocket_secret_key: matches.value_of("rocket_secret_key").unwrap(),
+ };
+
+ pjournal::db::create_config(&conn, &config);
+ } else if let Some(matches) = matches.subcommand_matches("adduser") {
+ let user = matches.value_of("USERNAME").unwrap();
+ let password = matches.value_of("PASSWORD").unwrap();
+ pjournal::db::adduser(&conn, user, password).unwrap();
+ } else {
+ let config = pjournal::server::Params {
+ base_url: base_url.to_string(),
+ };
+ pjournal::server::start(db_url, config);
+ }
}
diff --git a/src/models.rs b/src/models.rs
new file mode 100644
index 0000000..8bee887
--- /dev/null
+++ b/src/models.rs
@@ -0,0 +1,26 @@
+use crate::schema::config;
+use crate::schema::users;
+
+#[derive(Insertable, Queryable)]
+#[table_name = "config"]
+pub struct Config<'a> {
+ pub strava_client_secret: &'a str,
+ pub strava_client_id: &'a str,
+ pub rocket_secret_key: &'a str,
+}
+
+#[derive(Insertable, Queryable)]
+#[table_name = "users"]
+pub struct User<'a> {
+ pub username: &'a str,
+ password: &'a str,
+}
+
+impl<'a> User<'a> {
+ pub fn new(username: &'a str, password: &'a str) -> User<'a> {
+ User {
+ username: username,
+ password: password,
+ }
+ }
+}
diff --git a/src/schema.rs b/src/schema.rs
new file mode 100644
index 0000000..809706c
--- /dev/null
+++ b/src/schema.rs
@@ -0,0 +1,20 @@
+table! {
+ config (singleton) {
+ strava_client_secret -> Varchar,
+ strava_client_id -> Varchar,
+ rocket_secret_key -> Varchar,
+ singleton -> Bool,
+ }
+}
+
+table! {
+ users (username) {
+ username -> Varchar,
+ password -> Varchar,
+ }
+}
+
+allow_tables_to_appear_in_same_query!(
+ config,
+ users,
+);
diff --git a/src/server.rs b/src/server.rs
index 6f05afe..2c7baad 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -1,11 +1,17 @@
+use rocket::config;
+use rocket::config::Environment;
+use rocket::config::Value;
use rocket::response;
use rocket::State;
use rocket_contrib::templates::Template;
use std::collections::HashMap;
-use crate::Config;
use crate::strava;
+pub struct Params {
+ pub base_url: String,
+}
+
#[get("/")]
fn index() -> Template {
let mut context = HashMap::new();
@@ -15,30 +21,36 @@ fn index() -> Template {
}
#[get("/link_strava_callback?<code>")]
-fn link_strava_callback(config: State<Config>, code: String) -> Result<String, impl std::error::Error> {
- strava::exchange_token(
- &config.client_id, &config.client_secret, &code)
+fn link_strava_callback(
+ config: State<Params>,
+ code: String,
+) -> Result<String, impl std::error::Error> {
+ strava::exchange_token("&config.client_id", "&config.client_secret", &code)
.map(|t| format!("{:#?}", t))
}
#[get("/link_strava")]
-fn link_strava(config: State<Config>) -> response::Redirect {
- response::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))
- )
+fn link_strava(config: State<Params>) -> response::Redirect {
+ response::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(config: Config) {
+pub fn start(db_url: &str, config: 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));
+
rocket::ignite()
.manage(config)
.mount("/", routes![index, link_strava, link_strava_callback])
diff --git a/src/strava.rs b/src/strava.rs
index 490964b..dcd8dc1 100644
--- a/src/strava.rs
+++ b/src/strava.rs
@@ -1,15 +1,35 @@
use reqwest;
+use serde::Deserialize;
+use serde::Serialize;
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct AthleteSummary {
+ id: i64,
+ username: String,
+ firstname: String,
+ lastname: String,
+}
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct Token {
+ expires_in: i64,
+ refresh_token: String,
+ access_token: String,
+ athlete: AthleteSummary,
+}
pub fn exchange_token(
client_id: &str,
client_secret: &str,
- code: &str) {
+ code: &str,
+) -> Result<Token, reqwest::Error> {
let client = reqwest::blocking::Client::new();
- let params = [("client_id", client_id),
- ("client_secret", client_secret),
- ("code", code)];
+ let params = [
+ ("client_id", client_id),
+ ("client_secret", client_secret),
+ ("code", code),
+ ];
let uri = "https://www.strava.com/oauth/token";
let req = client.post(uri).form(&params);
- let mut res = req.send().unwrap().text();
- println!("{:?}", res);
+ req.send().map(|r| r.json())?
}