feat: inital redirect api

This commit is contained in:
Elias Renman
2025-03-16 23:42:00 +01:00
parent c39af75112
commit 02a35cc555
10 changed files with 62 additions and 29 deletions

2
Cargo.lock generated
View File

@@ -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",

View File

@@ -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"] }

View File

@@ -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!");
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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<NaiveDateTime>,
) -> Result<usize, Error> {
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()

10
src/dto/mod.rs Normal file
View File

@@ -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<NaiveDateTime>,
}

View File

@@ -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<Redirect, (Status, &'static str)> {
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")
}

View File

@@ -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("/<url>")]
fn shortner(url: &str) -> Result<Redirect, (Status, &'static str)> {
handle_redirect(url)
}
#[post("/write", format = "json", data = "<upsert>")]
fn upsert(upsert: Json<dto::UpsertUrlDto<'_>>) -> (Status, &'static str) {
handle_upsert(upsert.0)
}
#[delete("/<url>")]
fn delete(url: &str) -> (Status, &'static str) {
handle_delete(url)
}