diff --git a/actix/Cargo.lock b/actix/Cargo.lock index 3bd25ee..82d4a5d 100644 --- a/actix/Cargo.lock +++ b/actix/Cargo.lock @@ -971,6 +971,7 @@ version = "0.1.0" dependencies = [ "actix-files", "actix-web", + "rand", "regex", "rusqlite", ] diff --git a/actix/Cargo.toml b/actix/Cargo.toml index 5acdb6e..23bee14 100644 --- a/actix/Cargo.toml +++ b/actix/Cargo.toml @@ -10,3 +10,4 @@ actix-web = "4" actix-files = "0.6.2" rusqlite = "0.29.0" regex = "1.7.3" +rand = "0.8.5" diff --git a/actix/src/database.rs b/actix/src/database.rs index f8b3ac0..c14b5ca 100644 --- a/actix/src/database.rs +++ b/actix/src/database.rs @@ -16,7 +16,6 @@ pub fn find_url(shortlink: &str) -> String { longlink = link.unwrap(); } - add_hit(shortlink); longlink } @@ -37,7 +36,7 @@ pub fn getall() -> Vec { links } -fn add_hit(shortlink: &str) -> () { +pub fn add_hit(shortlink: &str) -> () { let db = Connection::open("./urls.sqlite").expect("Unable to open database!"); db.execute( "UPDATE urls SET hits = hits + 1 WHERE short_url = ?1", @@ -45,3 +44,15 @@ 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!"); + + match db.execute( + "INSERT INTO urls (long_url, short_url, hits) VALUES (?1, ?2, ?3)", + (longlink, shortlink, 0), + ) { + Ok(_) => true, + Err(_) => false, + } +} diff --git a/actix/src/main.rs b/actix/src/main.rs index 30abed0..094e7d1 100644 --- a/actix/src/main.rs +++ b/actix/src/main.rs @@ -2,7 +2,7 @@ use std::env; use actix_files::{Files, NamedFile}; use actix_web::{ - get, + get, post, web::{self, Redirect}, App, HttpResponse, HttpServer, Responder, }; @@ -12,9 +12,19 @@ mod utils; // Define the routes // Add new links +#[post("/api/new")] +async fn add_link(req: String) -> HttpResponse { + let out = utils::add_link(req); + if out.0 { + println!("ok{}", out.1); + HttpResponse::Ok().body(out.1) + } else { + println!("bad{}", out.1); + HttpResponse::BadRequest().body(out.1) + } +} // Return all active links - #[get("/api/all")] async fn getall() -> HttpResponse { HttpResponse::Ok().body(utils::getall()) @@ -36,8 +46,10 @@ async fn error404() -> impl Responder { // Handle a given shortlink #[get("/{shortlink}")] async fn link_handler(shortlink: web::Path) -> impl Responder { - let longlink = utils::get_longurl(shortlink); + let shortlink_str = shortlink.to_string(); + let longlink = utils::get_longurl(shortlink_str); if longlink == "".to_string() { + database::add_hit(shortlink.as_str()); Redirect::to("/err/404") } else { Redirect::to(longlink).permanent() @@ -52,6 +64,7 @@ async fn main() -> std::io::Result<()> { .service(error404) .service(getall) .service(siteurl) + .service(add_link) .service(Files::new("/", "./resources/").index_file("index.html")) }) .bind(("0.0.0.0", 2000))? diff --git a/actix/src/utils.rs b/actix/src/utils.rs index c0c9a05..edc7258 100644 --- a/actix/src/utils.rs +++ b/actix/src/utils.rs @@ -1,8 +1,8 @@ use crate::database; -use actix_web::web; +use rand::seq::SliceRandom; use regex::Regex; -pub fn get_longurl(shortlink: web::Path) -> String { +pub fn get_longurl(shortlink: String) -> String { if validate_link(&shortlink) { database::find_url(shortlink.as_str()) } else { @@ -19,3 +19,63 @@ pub fn getall() -> String { let links = database::getall(); links.join("\n") } + +pub fn add_link(req: String) -> (bool, String) { + let chunks: Vec<&str> = req.split(';').collect(); + let longlink = chunks[0].to_string(); + + let mut shortlink; + if chunks.len() > 1 { + shortlink = chunks[1].to_string().to_lowercase(); + if shortlink == "".to_string() { + shortlink = random_name(); + } + } else { + shortlink = random_name(); + } + + if validate_link(shortlink.as_str()) && get_longurl(shortlink.clone()) == "".to_string() { + (database::add_link(shortlink.clone(), longlink), shortlink) + } else { + (false, "shortUrl not valid or already in use".to_string()) + } +} + +fn random_name() -> String { + #[rustfmt::skip] + static ADJECTIVES: [&str; 108] = ["admiring", "adoring", "affectionate", "agitated", "amazing", "angry", "awesome", "beautiful", + "blissful", "bold", "boring", "brave", "busy", "charming", "clever", "compassionate", "competent", "condescending", "confident", "cool", + "cranky", "crazy", "dazzling", "determined", "distracted", "dreamy", "eager", "ecstatic", "elastic", "elated", "elegant", "eloquent", "epic", + "exciting", "fervent", "festive", "flamboyant", "focused", "friendly", "frosty", "funny", "gallant", "gifted", "goofy", "gracious", + "great", "happy", "hardcore", "heuristic", "hopeful", "hungry", "infallible", "inspiring", "intelligent", "interesting", "jolly", + "jovial", "keen", "kind", "laughing", "loving", "lucid", "magical", "modest", "musing", "mystifying", "naughty", "nervous", "nice", + "nifty", "nostalgic", "objective", "optimistic", "peaceful", "pedantic", "pensive", "practical", "priceless", "quirky", "quizzical", + "recursing", "relaxed", "reverent", "romantic", "sad", "serene", "sharp", "silly", "sleepy", "stoic", "strange", "stupefied", "suspicious", + "sweet", "tender", "thirsty", "trusting", "unruffled", "upbeat", "vibrant", "vigilant", "vigorous", "wizardly", "wonderful", "xenodochial", + "youthful", "zealous", "zen"]; + #[rustfmt::skip] + static NAMES: [&str; 241] = ["agnesi", "albattani", "allen", "almeida", "antonelli", "archimedes", "ardinghelli", "aryabhata", "austin", + "babbage", "banach", "banzai", "bardeen", "bartik", "bassi", "beaver", "bell", "benz", "bhabha", "bhaskara", "black", "blackburn", "blackwell", + "bohr", "booth", "borg", "bose", "bouman", "boyd", "brahmagupta", "brattain", "brown", "buck", "burnell", "cannon", "carson", "cartwright", + "carver", "cauchy", "cerf", "chandrasekhar", "chaplygin", "chatelet", "chatterjee", "chaum", "chebyshev", "clarke", "cohen", "colden", "cori", + "cray", "curie", "curran", "darwin", "davinci", "dewdney", "dhawan", "diffie", "dijkstra", "dirac", "driscoll", "dubinsky", "easley", "edison", + "einstein", "elbakyan", "elgamal", "elion", "ellis", "engelbart", "euclid", "euler", "faraday", "feistel", "fermat", "fermi", "feynman", "franklin", + "gagarin", "galileo", "galois", "ganguly", "gates", "gauss", "germain", "goldberg", "goldstine", "goldwasser", "golick", "goodall", "gould", "greider", + "grothendieck", "haibt", "hamilton", "hardy", "haslett", "hawking", "heisenberg", "hellman", "hermann", "herschel", "hertz", "heyrovsky", "hodgkin", + "hofstadter", "hoover", "hopper", "hugle", "hypatia", "ishizaka", "jackson", "jang", "jemison", "jennings", "jepsen", "johnson", "joliot", "jones", + "kalam", "kapitsa", "kare", "keldysh", "keller", "kepler", "khayyam", "khorana", "kilby", "kirch", "knuth", "kowalevski", "lalande", "lamarr", + "lamport", "leakey", "leavitt", "lederberg", "lehmann", "lewin", "lichterman", "liskov", "lovelace", "lumiere", "mahavira", "margulis", "matsumoto", + "maxwell", "mayer", "mccarthy", "mcclintock", "mclaren", "mclean", "mcnulty", "meitner", "mendel", "mendeleev", "meninsky", "merkle", "mestorf", + "mirzakhani", "montalcini", "moore", "morse", "moser", "murdock", "napier", "nash", "neumann", "newton", "nightingale", "nobel", "noether", "northcutt", + "noyce", "panini", "pare", "pascal", "pasteur", "payne", "perlman", "pike", "poincare", "poitras", "proskuriakova", "ptolemy", "raman", "ramanujan", + "rhodes", "ride", "riemann", "ritchie", "robinson", "roentgen", "rosalind", "rubin", "saha", "sammet", "sanderson", "satoshi", "shamir", "shannon", + "shaw", "shirley", "shockley", "shtern", "sinoussi", "snyder", "solomon", "spence", "stonebraker", "sutherland", "swanson", "swartz", "swirles", + "taussig", "tesla", "tharp", "thompson", "torvalds", "tu", "turing", "varahamihira", "vaughan", "vaughn", "villani", "visvesvaraya", "volhard", + "wescoff", "weierstrass", "wilbur", "wiles", "williams", "williamson", "wilson", "wing", "wozniak", "wright", "wu", "yalow", "yonath", "zhukovsky"]; + + format!( + "{0}-{1}", + NAMES.choose(&mut rand::thread_rng()).unwrap(), + ADJECTIVES.choose(&mut rand::thread_rng()).unwrap() + ) +}