diff options
Diffstat (limited to 'server/src/main.rs')
| -rw-r--r-- | server/src/main.rs | 142 |
1 files changed, 28 insertions, 114 deletions
diff --git a/server/src/main.rs b/server/src/main.rs index 515fc8f..4e5429b 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,131 +1,45 @@ extern crate openssl; -#[macro_use] extern crate diesel; -#[macro_use] extern crate diesel_migrations; -#[macro_use] extern crate serde_derive; - +#[macro_use] +extern crate diesel; +#[macro_use] +extern crate diesel_migrations; +#[macro_use] +extern crate serde_derive; +#[macro_use] +extern crate log; + +mod database; +mod messages; +mod parsing; +mod routes; mod schema; -use schema::links; - -mod types; -use types::*; - -use actix_web::{middleware, web, HttpServer, App, HttpResponse, Result, HttpRequest}; -use actix_web::web::{Json, Path, Data}; -use diesel::{PgConnection, RunQueryDsl, QueryDsl, ExpressionMethods, QueryResult}; -use diesel::r2d2::{ConnectionManager, Pool}; -use actix_files::{Files, NamedFile}; - -fn establish_connection() -> Pool<ConnectionManager<PgConnection>> { - let password = std::env::var("POSTGRES_PASSWORD").expect("Cannot find POSTGRES_PASSWORD in environment."); - let database_url = format!("postgresql://postgres:{}@postgres/shorest", password); - let manager = ConnectionManager::<PgConnection>::new(database_url); - Pool::builder().max_size(4).build(manager).expect("Failed to create pool.") -} - -fn make_url(url_to_check: &str) -> Result<String, ()> { - let url_object = match url::Url::parse(url_to_check) { - Ok(result_url) => result_url, - Err(_) => return Err(()) - }; - if !url_object.cannot_be_a_base() && - url_object.has_host() && - url_object.host_str().map_or(false, |h| h.contains('.')) && - url_object.domain().is_some() - { - Ok(format!("https://{}{}{}", - url_object.domain().unwrap(), - url_object.path(), - url_object.query().map_or("".to_owned(), |q| format!("?{}", q))) - ) - } else { - Err(()) - } -} -fn add_entry_to_database(entry: Entry, connection: &PgConnection) -> QueryResult<Entry> { - diesel::insert_into(links::table).values(&entry).get_result::<Entry>(connection) -} - -fn get_url_from_database(url_hash: &String, connection: &PgConnection) -> Result<String, diesel::result::Error> { - links::table.filter(links::hash.eq(url_hash)).select(links::url).first(connection) -} - -fn get_hash_from_string(to_hash: &String) -> String { - let mut hasher = crc32fast::Hasher::new(); - hasher.update(to_hash.as_bytes()); - base64::encode_config(hasher.finalize().to_ne_bytes(), base64::URL_SAFE_NO_PAD).chars().take(3).collect() -} - -fn add_to_database_safely(mut hash: String, user_url: String, connection: &PgConnection) -> String { - match get_url_from_database(&hash, connection) { - Ok(other_url) => { - if user_url != other_url { - hash = add_to_database_safely(get_hash_from_string(&hash), user_url, connection); - } - } - Err(_) => { - add_entry_to_database(Entry { hash: hash.clone() , url: user_url }, connection).unwrap(); - } - }; - hash -} - -async fn root(req: HttpRequest) -> HttpResponse { - NamedFile::open("./client/index.html").unwrap().into_response(&req).unwrap() -} - -async fn shorten(params: Json<UserData>, state: Data<PoolState>) -> HttpResponse { - let user_url = match make_url(¶ms.url) { - Ok(parse_result) => parse_result, - Err(_) => { - return HttpResponse::BadRequest().json(ErrorResponse{error: "The URL you entered does not follow the proper URL format.".to_string()}); - }, - }; - let hash = add_to_database_safely(get_hash_from_string(&user_url), user_url, &state.get().expect("Could not get a connection from pool")); - - HttpResponse::Ok().json(UserResponse{ hash }) -} - -async fn redirect(info: Path<String>, state: Data<PoolState>) -> HttpResponse { - match get_url_from_database(&info, &state.get().expect("Could not get a connection from pool")) { - Ok(url) => HttpResponse::TemporaryRedirect().header("Location", url).finish(), - Err(_) => HttpResponse::TemporaryRedirect().header("Location", "/").finish() - } -} - -embed_migrations!("migrations"); - -fn run_migrations(pool: &PoolState) { - let migration_connection = &pool.get().expect("Could not connect to database to run migrations"); - embedded_migrations::run_with_output(migration_connection, &mut std::io::stdout()); -} +use actix_files::Files; +use actix_web::{middleware, web, App, HttpServer}; #[actix_rt::main] async fn main() -> std::io::Result<()> { - std::env::set_var("RUST_LOG", "actix_web=info"); + std::env::set_var("RUST_LOG", "shorest,actix_web=info"); env_logger::init(); - let pool = establish_connection(); - run_migrations(&pool); + info!("Starting shorest {}!", std::env!("CARGO_PKG_VERSION")); + + let pool = database::establish_connection(); + database::run_migrations(&pool); HttpServer::new(move || { App::new() .wrap(middleware::Logger::default()) .data(pool.clone()) .service( - web::resource("/") - .route(web::get().to(root)) - .route(web::post().to(shorten)) - ) - .service( - web::resource("/{hash}") - .route(web::get().to(redirect)) - ) - .service( - Files::new("/client/", "./client/") + web::resource("/") + .route(web::get().to(routes::root)) + .route(web::post().to(routes::shorten)), ) + .service(web::resource("/{hash:[^/]{3}$}").route(web::get().to(routes::redirect))) + .service(Files::new("/", "./client/")) }) - .bind(("0.0.0.0", 80))? - .run() - .await + .bind(("0.0.0.0", 80))? + .run() + .await } |
