diff options
| author | Ellie Huxtable <e@elm.sh> | 2021-04-20 17:07:11 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-20 16:07:11 +0000 |
| commit | 34888827f8a06de835cbe5833a06914f28cce514 (patch) | |
| tree | 8b56f20e50065cd2c222d5e8e067ec55cf1947a1 /src/remote | |
| parent | Optimise docker (#34) (diff) | |
| download | atuin-34888827f8a06de835cbe5833a06914f28cce514.zip | |
Switch to Warp + SQLx, use async, switch to Rust stable (#36)
* Switch to warp + sql, use async and stable rust
* Update CI to use stable
Diffstat (limited to '')
| -rw-r--r-- | src/remote/database.rs | 22 | ||||
| -rw-r--r-- | src/remote/mod.rs | 5 | ||||
| -rw-r--r-- | src/remote/server.rs | 61 | ||||
| -rw-r--r-- | src/remote/views.rs | 185 | ||||
| -rw-r--r-- | src/server/auth.rs (renamed from src/remote/auth.rs) | 2 | ||||
| -rw-r--r-- | src/server/models.rs (renamed from src/remote/models.rs) | 43 |
6 files changed, 18 insertions, 300 deletions
diff --git a/src/remote/database.rs b/src/remote/database.rs deleted file mode 100644 index 03973ca1..00000000 --- a/src/remote/database.rs +++ /dev/null @@ -1,22 +0,0 @@ -use diesel::pg::PgConnection; -use diesel::prelude::*; -use eyre::{eyre, Result}; - -use crate::settings::Settings; - -#[database("atuin")] -pub struct AtuinDbConn(diesel::PgConnection); - -// TODO: connection pooling -pub fn establish_connection(settings: &Settings) -> Result<PgConnection> { - if settings.server.db_uri == "default_uri" { - Err(eyre!( - "Please configure your database! Set db_uri in config.toml" - )) - } else { - let database_url = &settings.server.db_uri; - let conn = PgConnection::establish(database_url)?; - - Ok(conn) - } -} diff --git a/src/remote/mod.rs b/src/remote/mod.rs deleted file mode 100644 index 7147b88e..00000000 --- a/src/remote/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod auth; -pub mod database; -pub mod models; -pub mod server; -pub mod views; diff --git a/src/remote/server.rs b/src/remote/server.rs deleted file mode 100644 index ee481ca4..00000000 --- a/src/remote/server.rs +++ /dev/null @@ -1,61 +0,0 @@ -use std::collections::HashMap; - -use crate::remote::database::establish_connection; -use crate::settings::Settings; - -use super::database::AtuinDbConn; - -use eyre::Result; -use rocket::config::{Config, Environment, LoggingLevel, Value}; - -// a bunch of these imports are generated by macros, it's easier to wildcard -#[allow(clippy::clippy::wildcard_imports)] -use super::views::*; - -#[allow(clippy::clippy::wildcard_imports)] -use super::auth::*; - -embed_migrations!("migrations"); - -pub fn launch(settings: &Settings, host: String, port: u16) -> Result<()> { - let settings: Settings = settings.clone(); // clone so rocket can manage it - - let mut database_config = HashMap::new(); - let mut databases = HashMap::new(); - - database_config.insert("url", Value::from(settings.server.db_uri.clone())); - databases.insert("atuin", Value::from(database_config)); - - let connection = establish_connection(&settings)?; - - embedded_migrations::run(&connection).expect("failed to run migrations"); - - let config = Config::build(Environment::Production) - .address(host) - .log_level(LoggingLevel::Normal) - .port(port) - .extra("databases", databases) - .finalize() - .unwrap(); - - let app = rocket::custom(config); - - app.mount( - "/", - routes![ - index, - register, - add_history, - login, - get_user, - sync_count, - sync_list - ], - ) - .manage(settings) - .attach(AtuinDbConn::fairing()) - .register(catchers![internal_error, bad_request]) - .launch(); - - Ok(()) -} diff --git a/src/remote/views.rs b/src/remote/views.rs deleted file mode 100644 index 08dff13e..00000000 --- a/src/remote/views.rs +++ /dev/null @@ -1,185 +0,0 @@ -use chrono::Utc; -use rocket::http::uri::Uri; -use rocket::http::RawStr; -use rocket::http::{ContentType, Status}; -use rocket::request::FromFormValue; -use rocket::request::Request; -use rocket::response; -use rocket::response::{Responder, Response}; -use rocket_contrib::databases::diesel; -use rocket_contrib::json::{Json, JsonValue}; - -use self::diesel::prelude::*; - -use crate::api::AddHistoryRequest; -use crate::schema::history; -use crate::settings::HISTORY_PAGE_SIZE; - -use super::database::AtuinDbConn; -use super::models::{History, NewHistory, User}; - -#[derive(Debug)] -pub struct ApiResponse { - pub json: JsonValue, - pub status: Status, -} - -impl<'r> Responder<'r> for ApiResponse { - fn respond_to(self, req: &Request) -> response::Result<'r> { - Response::build_from(self.json.respond_to(req).unwrap()) - .status(self.status) - .header(ContentType::JSON) - .ok() - } -} - -#[get("/")] -pub const fn index() -> &'static str { - "\"Through the fathomless deeps of space swims the star turtle Great A\u{2019}Tuin, bearing on its back the four giant elephants who carry on their shoulders the mass of the Discworld.\"\n\t-- Sir Terry Pratchett" -} - -#[catch(500)] -pub fn internal_error(_req: &Request) -> ApiResponse { - ApiResponse { - status: Status::InternalServerError, - json: json!({"status": "error", "message": "an internal server error has occured"}), - } -} - -#[catch(400)] -pub fn bad_request(_req: &Request) -> ApiResponse { - ApiResponse { - status: Status::InternalServerError, - json: json!({"status": "error", "message": "bad request. don't do that."}), - } -} - -#[post("/history", data = "<add_history>")] -#[allow( - clippy::clippy::cast_sign_loss, - clippy::cast_possible_truncation, - clippy::clippy::needless_pass_by_value -)] -pub fn add_history( - conn: AtuinDbConn, - user: User, - add_history: Json<Vec<AddHistoryRequest>>, -) -> ApiResponse { - let new_history: Vec<NewHistory> = add_history - .iter() - .map(|h| NewHistory { - client_id: h.id.as_str(), - hostname: h.hostname.to_string(), - user_id: user.id, - timestamp: h.timestamp.naive_utc(), - data: h.data.as_str(), - }) - .collect(); - - match diesel::insert_into(history::table) - .values(&new_history) - .on_conflict_do_nothing() - .execute(&*conn) - { - Ok(_) => ApiResponse { - status: Status::Ok, - json: json!({"status": "ok", "message": "history added"}), - }, - Err(_) => ApiResponse { - status: Status::BadRequest, - json: json!({"status": "error", "message": "failed to add history"}), - }, - } -} - -#[get("/sync/count")] -#[allow(clippy::wildcard_imports, clippy::needless_pass_by_value)] -pub fn sync_count(conn: AtuinDbConn, user: User) -> ApiResponse { - use crate::schema::history::dsl::*; - - // we need to return the number of history items we have for this user - // in the future I'd like to use something like a merkel tree to calculate - // which day specifically needs syncing - let count = history - .filter(user_id.eq(user.id)) - .count() - .first::<i64>(&*conn); - - if count.is_err() { - error!("failed to count: {}", count.err().unwrap()); - - return ApiResponse { - json: json!({"message": "internal server error"}), - status: Status::InternalServerError, - }; - } - - ApiResponse { - status: Status::Ok, - json: json!({"count": count.ok()}), - } -} - -pub struct UtcDateTime(chrono::DateTime<Utc>); - -impl<'v> FromFormValue<'v> for UtcDateTime { - type Error = &'v RawStr; - - fn from_form_value(form_value: &'v RawStr) -> Result<UtcDateTime, &'v RawStr> { - let time = Uri::percent_decode(form_value.as_bytes()).map_err(|_| form_value)?; - let time = time.to_string(); - - match chrono::DateTime::parse_from_rfc3339(time.as_str()) { - Ok(t) => Ok(UtcDateTime(t.with_timezone(&Utc))), - Err(e) => { - error!("failed to parse time {}, got: {}", time, e); - Err(form_value) - } - } - } -} - -// Request a list of all history items added to the DB after a given timestamp. -// Provide the current hostname, so that we don't send the client data that -// originated from them -#[get("/sync/history?<sync_ts>&<history_ts>&<host>")] -#[allow(clippy::wildcard_imports, clippy::needless_pass_by_value)] -pub fn sync_list( - conn: AtuinDbConn, - user: User, - sync_ts: UtcDateTime, - history_ts: UtcDateTime, - host: String, -) -> ApiResponse { - use crate::schema::history::dsl::*; - - // we need to return the number of history items we have for this user - // in the future I'd like to use something like a merkel tree to calculate - // which day specifically needs syncing - // TODO: Allow for configuring the page size, both from params, and setting - // the max in config. 100 is fine for now. - let h = history - .filter(user_id.eq(user.id)) - .filter(hostname.ne(host)) - .filter(created_at.ge(sync_ts.0.naive_utc())) - .filter(timestamp.ge(history_ts.0.naive_utc())) - .order(timestamp.asc()) - .limit(HISTORY_PAGE_SIZE) - .load::<History>(&*conn); - - if let Err(e) = h { - error!("failed to load history: {}", e); - - return ApiResponse { - json: json!({"message": "internal server error"}), - status: Status::InternalServerError, - }; - } - - let user_data: Vec<String> = h.unwrap().iter().map(|i| i.data.to_string()).collect(); - - ApiResponse { - status: Status::Ok, - json: json!({ "history": user_data }), - } -} diff --git a/src/remote/auth.rs b/src/server/auth.rs index cf61b077..52a73108 100644 --- a/src/remote/auth.rs +++ b/src/server/auth.rs @@ -1,3 +1,4 @@ +/* use self::diesel::prelude::*; use eyre::Result; use rocket::http::Status; @@ -218,3 +219,4 @@ pub fn login(conn: AtuinDbConn, login: Json<LoginRequest>) -> ApiResponse { json: json!({"session": session.token}), } } +*/ diff --git a/src/remote/models.rs b/src/server/models.rs index 7f6f7766..fbf1897e 100644 --- a/src/remote/models.rs +++ b/src/server/models.rs @@ -1,10 +1,6 @@ use chrono::prelude::*; -use crate::schema::{history, sessions, users}; - -#[derive(Deserialize, Serialize, Identifiable, Queryable, Associations)] -#[table_name = "history"] -#[belongs_to(User)] +#[derive(sqlx::FromRow)] pub struct History { pub id: i64, pub client_id: String, // a client generated ID @@ -17,7 +13,16 @@ pub struct History { pub created_at: NaiveDateTime, } -#[derive(Identifiable, Queryable, Associations)] +pub struct NewHistory<'a> { + pub client_id: &'a str, + pub user_id: i64, + pub hostname: &'a str, + pub timestamp: chrono::NaiveDateTime, + + pub data: &'a str, +} + +#[derive(sqlx::FromRow)] pub struct User { pub id: i64, pub username: String, @@ -25,35 +30,19 @@ pub struct User { pub password: String, } -#[derive(Queryable, Identifiable, Associations)] -#[belongs_to(User)] +#[derive(sqlx::FromRow)] pub struct Session { pub id: i64, pub user_id: i64, pub token: String, } -#[derive(Insertable)] -#[table_name = "history"] -pub struct NewHistory<'a> { - pub client_id: &'a str, - pub user_id: i64, - pub hostname: String, - pub timestamp: chrono::NaiveDateTime, - - pub data: &'a str, -} - -#[derive(Insertable)] -#[table_name = "users"] -pub struct NewUser<'a> { - pub username: &'a str, - pub email: &'a str, - pub password: &'a str, +pub struct NewUser { + pub username: String, + pub email: String, + pub password: String, } -#[derive(Insertable)] -#[table_name = "sessions"] pub struct NewSession<'a> { pub user_id: i64, pub token: &'a str, |
