diff --git a/actix/src/database.rs b/actix/src/database.rs index 6c411a7..7935290 100644 --- a/actix/src/database.rs +++ b/actix/src/database.rs @@ -1,8 +1,6 @@ use rusqlite::Connection; -pub fn find_url(shortlink: &str) -> String { - let db = Connection::open("./urls.sqlite").expect("Unable to open database!"); - +pub fn find_url(shortlink: &str, db: &Connection) -> String { let mut statement = db .prepare_cached("SELECT long_url FROM urls WHERE short_url = ?1") .unwrap(); @@ -19,8 +17,7 @@ pub fn find_url(shortlink: &str) -> String { longlink } -pub fn getall() -> Vec { - let db = Connection::open("./urls.sqlite").expect("Unable to open database!"); +pub fn getall(db: &Connection) -> Vec { let mut statement = db.prepare_cached("SELECT * FROM urls").unwrap(); let mut data = statement.query([]).unwrap(); @@ -36,8 +33,7 @@ pub fn getall() -> Vec { links } -pub fn add_hit(shortlink: &str) -> () { - let db = Connection::open("./urls.sqlite").expect("Unable to open database!"); +pub fn add_hit(shortlink: &str, db: &Connection) -> () { db.execute( "UPDATE urls SET hits = hits + 1 WHERE short_url = ?1", [shortlink], @@ -45,9 +41,7 @@ pub fn add_hit(shortlink: &str) -> () { .unwrap(); } -pub fn add_link(shortlink: String, longlink: String) -> bool { - let db = Connection::open("./urls.sqlite").expect("Unable to open database!"); - +pub fn add_link(shortlink: String, longlink: String, db: &Connection) -> bool { match db.execute( "INSERT INTO urls (long_url, short_url, hits) VALUES (?1, ?2, ?3)", (longlink, shortlink, 0), @@ -57,8 +51,22 @@ pub fn add_link(shortlink: String, longlink: String) -> bool { } } -pub fn delete_link(shortlink: String) -> () { - let db = Connection::open("./urls.sqlite").expect("Unable to open database!"); +pub fn delete_link(shortlink: String, db: &Connection) -> () { db.execute("DELETE FROM urls WHERE short_url = ?1", [shortlink]) .unwrap(); } + +pub fn open_db(path: String) -> Connection { + let db = Connection::open(path).expect("Unable to open database!"); + db.execute( + "CREATE TABLE IF NOT EXISTS urls ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + long_url TEXT NOT NULL, + short_url TEXT NOT NULL, + hits INTEGER NOT NULL + )", + [], + ) + .unwrap(); + db +} diff --git a/actix/src/main.rs b/actix/src/main.rs index 96929a1..ebf5c8c 100644 --- a/actix/src/main.rs +++ b/actix/src/main.rs @@ -6,15 +6,21 @@ use actix_web::{ web::{self, Redirect}, App, HttpResponse, HttpServer, Responder, }; +use rusqlite::Connection; mod database; mod utils; +// This struct represents state +struct AppState { + db: Connection, +} + // Define the routes // Add new links #[post("/api/new")] -async fn add_link(req: String) -> HttpResponse { - let out = utils::add_link(req); +async fn add_link(req: String, data: web::Data) -> HttpResponse { + let out = utils::add_link(req, &data.db); if out.0 { println!("ok{}", out.1); HttpResponse::Ok().body(out.1) @@ -26,8 +32,8 @@ async fn add_link(req: String) -> HttpResponse { // Return all active links #[get("/api/all")] -async fn getall() -> HttpResponse { - HttpResponse::Ok().body(utils::getall()) +async fn getall(data: web::Data) -> HttpResponse { + HttpResponse::Ok().body(utils::getall(&data.db)) } // Get the site URL @@ -45,21 +51,21 @@ async fn error404() -> impl Responder { // Handle a given shortlink #[get("/{shortlink}")] -async fn link_handler(shortlink: web::Path) -> impl Responder { +async fn link_handler(shortlink: web::Path, data: web::Data) -> impl Responder { let shortlink_str = shortlink.to_string(); - let longlink = utils::get_longurl(shortlink_str); + let longlink = utils::get_longurl(shortlink_str, &data.db); if longlink == "".to_string() { Redirect::to("/err/404") } else { - database::add_hit(shortlink.as_str()); + database::add_hit(shortlink.as_str(), &data.db); Redirect::to(longlink).permanent() } } // Delete a given shortlink #[delete("/api/del/{shortlink}")] -async fn delete_link(shortlink: web::Path) -> HttpResponse { - database::delete_link(shortlink.to_string()); +async fn delete_link(shortlink: web::Path, data: web::Data) -> HttpResponse { + database::delete_link(shortlink.to_string(), &data.db); HttpResponse::Ok().body("") } @@ -67,6 +73,9 @@ async fn delete_link(shortlink: web::Path) -> HttpResponse { async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() + .app_data(web::Data::new(AppState { + db: database::open_db("./urls.sqlite".to_string()), + })) .service(link_handler) .service(error404) .service(getall) diff --git a/actix/src/utils.rs b/actix/src/utils.rs index edc7258..4559da0 100644 --- a/actix/src/utils.rs +++ b/actix/src/utils.rs @@ -1,10 +1,11 @@ use crate::database; use rand::seq::SliceRandom; use regex::Regex; +use rusqlite::Connection; -pub fn get_longurl(shortlink: String) -> String { +pub fn get_longurl(shortlink: String, db: &Connection) -> String { if validate_link(&shortlink) { - database::find_url(shortlink.as_str()) + database::find_url(shortlink.as_str(), db) } else { "".to_string() } @@ -15,12 +16,12 @@ fn validate_link(link: &str) -> bool { re.is_match(link) } -pub fn getall() -> String { - let links = database::getall(); +pub fn getall(db: &Connection) -> String { + let links = database::getall(db); links.join("\n") } -pub fn add_link(req: String) -> (bool, String) { +pub fn add_link(req: String, db: &Connection) -> (bool, String) { let chunks: Vec<&str> = req.split(';').collect(); let longlink = chunks[0].to_string(); @@ -34,8 +35,11 @@ pub fn add_link(req: String) -> (bool, String) { shortlink = random_name(); } - if validate_link(shortlink.as_str()) && get_longurl(shortlink.clone()) == "".to_string() { - (database::add_link(shortlink.clone(), longlink), shortlink) + if validate_link(shortlink.as_str()) && get_longurl(shortlink.clone(), db) == "".to_string() { + ( + database::add_link(shortlink.clone(), longlink, db), + shortlink, + ) } else { (false, "shortUrl not valid or already in use".to_string()) }