|
| 1 | +#[macro_use] |
| 2 | +extern crate diesel; |
| 3 | + |
| 4 | +use std::env; |
| 5 | +use std::error::Error; |
| 6 | +use std::sync::Mutex; |
| 7 | + |
| 8 | +use log::debug; |
| 9 | +use log::error; |
| 10 | +use log::info; |
| 11 | +use log::trace; |
| 12 | +use log::warn; |
| 13 | + |
| 14 | +use diesel::expression::AsExpression; |
| 15 | +use diesel::prelude::*; |
| 16 | +use diesel::query_builder::AsQuery; |
| 17 | +use diesel::MysqlConnection; |
| 18 | + |
| 19 | +use rouille::router; |
| 20 | + |
| 21 | +use serde; |
| 22 | +use serde_json; |
| 23 | + |
| 24 | +use dotenv::dotenv; |
| 25 | + |
| 26 | +use self::errors::WebdevError; |
| 27 | +use self::errors::WebdevErrorKind; |
| 28 | +use self::schema::users; |
| 29 | + |
| 30 | +mod errors; |
| 31 | +mod models; |
| 32 | +mod schema; |
| 33 | + |
| 34 | +fn main() { |
| 35 | + dotenv().ok(); |
| 36 | + |
| 37 | + simplelog::TermLogger::init(simplelog::LevelFilter::Trace, simplelog::Config::default()) |
| 38 | + .unwrap(); |
| 39 | + |
| 40 | + info!("Connecting to database"); |
| 41 | + |
| 42 | + let database_url = env::var("DATABASE_URL").expect("DATABSE_URL needs to be set"); |
| 43 | + |
| 44 | + debug!("Connecting to {}", database_url); |
| 45 | + |
| 46 | + let connection = match MysqlConnection::establish(&database_url) { |
| 47 | + Ok(c) => c, |
| 48 | + Err(e) => { |
| 49 | + error!("Could not connect to database: {}", e); |
| 50 | + return; |
| 51 | + } |
| 52 | + }; |
| 53 | + |
| 54 | + debug!("Connected to database"); |
| 55 | + |
| 56 | + let connection_mutex = Mutex::new(connection); |
| 57 | + |
| 58 | + info!("Starting server on 0.0.0.0:8000"); |
| 59 | + |
| 60 | + rouille::start_server("0.0.0.0:8000", move |request| { |
| 61 | + debug!( |
| 62 | + "Handling request {} {} from {}", |
| 63 | + request.method(), |
| 64 | + request.url(), |
| 65 | + request.remote_addr() |
| 66 | + ); |
| 67 | + |
| 68 | + let current_connection = match connection_mutex.lock() { |
| 69 | + Ok(c) => c, |
| 70 | + Err(_e) => { |
| 71 | + error!("Could not lock database"); |
| 72 | + return rouille::Response::from(WebdevError::new(WebdevErrorKind::Database)); |
| 73 | + } |
| 74 | + }; |
| 75 | + |
| 76 | + let response = handle_request(request, ¤t_connection); |
| 77 | + |
| 78 | + match response { |
| 79 | + Ok(json) => { |
| 80 | + if let Some(j) = json { |
| 81 | + rouille::Response::json(&j) |
| 82 | + } else { |
| 83 | + rouille::Response::empty_204() |
| 84 | + } |
| 85 | + } |
| 86 | + Err(e) => { |
| 87 | + if let Some(err_source) = e.source() { |
| 88 | + error!("Error processing request: {}", err_source); |
| 89 | + } else { |
| 90 | + error!("Error processing request"); |
| 91 | + } |
| 92 | + |
| 93 | + rouille::Response::from(e) |
| 94 | + } |
| 95 | + } |
| 96 | + }); |
| 97 | +} |
| 98 | + |
| 99 | +fn handle_request( |
| 100 | + request: &rouille::Request, |
| 101 | + database_connection: &MysqlConnection, |
| 102 | +) -> Result<Option<String>, WebdevError> { |
| 103 | + router!(request, |
| 104 | + (GET) (/users) => { |
| 105 | + handle_get( |
| 106 | + request.get_param("first_name"), |
| 107 | + request.get_param("last_name"), |
| 108 | + if let Some(p) = request.get_param("banner_id") { Some(p.parse()?) } else { None }, |
| 109 | + request.get_param("email"), |
| 110 | + database_connection |
| 111 | + ).map(|s| Some(s)) |
| 112 | + }, |
| 113 | + (POST) (/users/create) => { |
| 114 | + let request_body = request.data().ok_or(WebdevError::new(WebdevErrorKind::Format))?; |
| 115 | + let new_user: models::NewUser = serde_json::from_reader(request_body)?; |
| 116 | + |
| 117 | + handle_insert(new_user, database_connection).map(|s| Some(s)) |
| 118 | + }, |
| 119 | + (DELETE) (/users/{id: u64}) => { |
| 120 | + handle_delete(id, database_connection).map(|_| None) |
| 121 | + }, |
| 122 | + _ => Err(WebdevError::new(WebdevErrorKind::NotFound)) |
| 123 | + ) |
| 124 | +} |
| 125 | + |
| 126 | +fn handle_get( |
| 127 | + first_name_filter: Option<String>, |
| 128 | + last_name_filter: Option<String>, |
| 129 | + banner_id_filter: Option<u32>, |
| 130 | + email_filter: Option<String>, |
| 131 | + database_connection: &MysqlConnection, |
| 132 | +) -> Result<String, WebdevError> { |
| 133 | + let mut users_query = users::table.as_query().into_boxed(); |
| 134 | + |
| 135 | + if let Some(p) = first_name_filter { |
| 136 | + users_query = users_query.filter(users::first_name.eq(p)); |
| 137 | + } |
| 138 | + |
| 139 | + if let Some(p) = last_name_filter { |
| 140 | + users_query = users_query.filter(users::last_name.eq(p)); |
| 141 | + } |
| 142 | + |
| 143 | + if let Some(p) = banner_id_filter { |
| 144 | + users_query = users_query.filter(users::banner_id.eq(p)); |
| 145 | + } |
| 146 | + |
| 147 | + if let Some(p) = email_filter { |
| 148 | + users_query = users_query.filter(users::email.eq(p)); |
| 149 | + } |
| 150 | + |
| 151 | + let all_users = users_query.load::<models::User>(database_connection)?; |
| 152 | + Ok(serde_json::to_string(&all_users)?) |
| 153 | +} |
| 154 | + |
| 155 | +fn handle_insert( |
| 156 | + new_user: models::NewUser, |
| 157 | + database_connection: &MysqlConnection, |
| 158 | +) -> Result<String, WebdevError> { |
| 159 | + |
| 160 | + diesel::insert_into(users::table) |
| 161 | + .values(new_user) |
| 162 | + .execute(database_connection)?; |
| 163 | + |
| 164 | + let inserted_user = users::table |
| 165 | + .filter(diesel::dsl::sql("id = LAST_INSERT_ID()")) |
| 166 | + .load::<models::User>(database_connection)?; |
| 167 | + |
| 168 | + Ok(serde_json::to_string(&inserted_user)?) |
| 169 | +} |
| 170 | + |
| 171 | +fn handle_delete(id: u64, database_connection: &MysqlConnection) -> Result<(), WebdevError> { |
| 172 | + diesel::delete(users::table.filter(users::id.eq(id))).execute(database_connection)?; |
| 173 | + |
| 174 | + Ok(()) |
| 175 | +} |
0 commit comments