about summary refs log tree commit diff
path: root/server/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/main.rs')
-rw-r--r--server/src/main.rs142
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(&params.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
 }