diff --git a/Cargo.lock b/Cargo.lock index fc1f87d..1b3e90c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -165,6 +165,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-link", ] @@ -1092,6 +1093,7 @@ dependencies = [ "rocket_codegen", "rocket_http", "serde", + "serde_json", "state", "tempfile", "time", diff --git a/Cargo.toml b/Cargo.toml index 663c026..291269f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,11 +2,11 @@ name = "url-shortener" version = "0.1.0" edition = "2024" -build = "build.rs" + [dependencies] -chrono = "0.4.40" -diesel = { version = "2.2.8", features = ["sqlite","chrono"] } +chrono = { version = "0.4.40", features = ["serde"] } +diesel = { version = "2.2.8", features = ["sqlite", "chrono"] } dotenvy = "0.15.7" -rocket = "0.5.1" -serde = "1.0.219" +rocket = { version = "0.5.1", features = ["json"] } +serde = { version = "1.0.219", features = ["derive"] } diff --git a/build.rs b/build.rs deleted file mode 100644 index 906ddcc..0000000 --- a/build.rs +++ /dev/null @@ -1,13 +0,0 @@ -use std::process::Command; - -fn main() { - let status = Command::new("bun") - .args(["run", "build"]) - .current_dir("web") // Change working directory to "web" - .status() - .expect("Failed to run bun build"); - - if !status.success() { - panic!("Bun build failed!"); - } -} diff --git a/migrations/2025-03-16-110640_url_entry/down.sql b/migrations/2025-03-16-110640_url_entry/down.sql index 8e1bb88..c8a8e6f 100644 --- a/migrations/2025-03-16-110640_url_entry/down.sql +++ b/migrations/2025-03-16-110640_url_entry/down.sql @@ -1,4 +1,4 @@ -- This file should undo anything in `up.sql` -DROP TRIGGER url_update_at_trigger; +DROP TRIGGER urls_update_at_trigger; -DROP TABLE url; +DROP TABLE urls; diff --git a/migrations/2025-03-16-110640_url_entry/up.sql b/migrations/2025-03-16-110640_url_entry/up.sql index 7237dd4..8b98ddd 100644 --- a/migrations/2025-03-16-110640_url_entry/up.sql +++ b/migrations/2025-03-16-110640_url_entry/up.sql @@ -14,6 +14,6 @@ UPDATE urls SET updated_at = STRFTIME ('%Y-%m-%d %H:%M:%f', 'NOW') WHERE - URLS = NEW.URLS; + url = NEW.url; END; diff --git a/src/db/models.rs b/src/db/models.rs index 94eb572..6c2628a 100644 --- a/src/db/models.rs +++ b/src/db/models.rs @@ -2,7 +2,7 @@ use super::schema::urls; use chrono::NaiveDateTime; use diesel::prelude::*; -#[derive(Queryable, Selectable)] +#[derive(Queryable, Selectable, AsChangeset)] #[diesel(table_name = urls)] #[diesel(check_for_backend(diesel::sqlite::Sqlite))] pub struct Urls { diff --git a/src/db/url.rs b/src/db/url.rs index 1a55ef7..dcf8907 100644 --- a/src/db/url.rs +++ b/src/db/url.rs @@ -1,5 +1,6 @@ use self::db::models::Urls; -use crate::db::schema::urls::dsl::*; +use super::schema::urls::dsl::*; +use super::schema::urls::*; use crate::*; use chrono::NaiveDateTime; use diesel::{delete, dsl::insert_into, prelude::*, result::Error}; @@ -15,7 +16,7 @@ pub fn upsert_entry( time_to_live: Option, ) -> Result { let connection = &mut establish_connection(); - insert_into(urls) + insert_into(table) .values((url.eq(path), destination_url.eq(dest), ttl.eq(time_to_live))) .on_conflict(url) .do_update() diff --git a/src/dto/mod.rs b/src/dto/mod.rs new file mode 100644 index 0000000..3f2c34c --- /dev/null +++ b/src/dto/mod.rs @@ -0,0 +1,10 @@ +use chrono::NaiveDateTime; +use rocket::serde::{Deserialize, Serialize}; + +#[serde(rename_all = "camelCase")] +#[derive(Serialize, Deserialize)] +pub struct UpsertUrlDto<'r> { + pub url: &'r str, + pub destination_url: &'r str, + pub ttl: Option, +} diff --git a/src/handlers/url.rs b/src/handlers/url.rs index 1dc97e6..15d35ed 100644 --- a/src/handlers/url.rs +++ b/src/handlers/url.rs @@ -1,8 +1,27 @@ use rocket::{http::Status, response::Redirect}; -use crate::db::url::get_entry; +use crate::{ + db::url::{delete_entry, get_entry, upsert_entry}, + dto::UpsertUrlDto, +}; pub fn handle_redirect(url: &str) -> Result { let row = get_entry(url).map_err(|_| (Status::NotFound, "Redirect Not found"))?; Ok(Redirect::to(row.destination_url)) } + +pub fn handle_upsert(dto: UpsertUrlDto<'_>) -> (Status, &'static str) { + let row = upsert_entry(dto.url, dto.destination_url, dto.ttl); + if row.is_err() { + return (Status::BadRequest, "Failed to upsert redirect"); + } + (Status::Ok, "Successfully upserted redirect") +} + +pub fn handle_delete(url: &str) -> (Status, &'static str) { + let row = delete_entry(url); + if row.is_err() { + return (Status::BadRequest, "Failed to delete redirect"); + } + (Status::Ok, "Successfully deleted redirect") +} diff --git a/src/main.rs b/src/main.rs index 8c26b8e..eda1ec0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,12 @@ -use db::establish_connection; -use handlers::url::handle_redirect; -use rocket::{http::Status, response::Redirect}; mod db; +mod dto; mod handlers; + +use db::establish_connection; +use handlers::url::{handle_delete, handle_redirect, handle_upsert}; use rocket::fs::{FileServer, relative}; +use rocket::serde::json::Json; +use rocket::{http::Status, response::Redirect}; #[macro_use] extern crate rocket; @@ -11,10 +14,21 @@ extern crate rocket; #[launch] fn rocket() -> _ { rocket::build() - .mount("/", routes![shortner]) + .mount("/", routes![shortner,]) + .mount("/api", routes![upsert, delete]) .mount("/", FileServer::from(relative!("web/dist"))) } #[get("/")] fn shortner(url: &str) -> Result { handle_redirect(url) } + +#[post("/write", format = "json", data = "")] +fn upsert(upsert: Json>) -> (Status, &'static str) { + handle_upsert(upsert.0) +} + +#[delete("/")] +fn delete(url: &str) -> (Status, &'static str) { + handle_delete(url) +}