mirror of
https://github.com/eliasrenman/url-shortener.git
synced 2026-03-16 20:16:06 +01:00
feat: inital redirect api
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -165,6 +165,7 @@ dependencies = [
|
|||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"serde",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"windows-link",
|
"windows-link",
|
||||||
]
|
]
|
||||||
@@ -1092,6 +1093,7 @@ dependencies = [
|
|||||||
"rocket_codegen",
|
"rocket_codegen",
|
||||||
"rocket_http",
|
"rocket_http",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
"state",
|
"state",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"time",
|
"time",
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
name = "url-shortener"
|
name = "url-shortener"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
build = "build.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = "0.4.40"
|
chrono = { version = "0.4.40", features = ["serde"] }
|
||||||
diesel = { version = "2.2.8", features = ["sqlite", "chrono"] }
|
diesel = { version = "2.2.8", features = ["sqlite", "chrono"] }
|
||||||
dotenvy = "0.15.7"
|
dotenvy = "0.15.7"
|
||||||
rocket = "0.5.1"
|
rocket = { version = "0.5.1", features = ["json"] }
|
||||||
serde = "1.0.219"
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
|
|||||||
13
build.rs
13
build.rs
@@ -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!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
-- This file should undo anything in `up.sql`
|
-- 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;
|
||||||
|
|||||||
@@ -14,6 +14,6 @@ UPDATE urls
|
|||||||
SET
|
SET
|
||||||
updated_at = STRFTIME ('%Y-%m-%d %H:%M:%f', 'NOW')
|
updated_at = STRFTIME ('%Y-%m-%d %H:%M:%f', 'NOW')
|
||||||
WHERE
|
WHERE
|
||||||
URLS = NEW.URLS;
|
url = NEW.url;
|
||||||
|
|
||||||
END;
|
END;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use super::schema::urls;
|
|||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
|
|
||||||
#[derive(Queryable, Selectable)]
|
#[derive(Queryable, Selectable, AsChangeset)]
|
||||||
#[diesel(table_name = urls)]
|
#[diesel(table_name = urls)]
|
||||||
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
|
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
|
||||||
pub struct Urls {
|
pub struct Urls {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use self::db::models::Urls;
|
use self::db::models::Urls;
|
||||||
use crate::db::schema::urls::dsl::*;
|
use super::schema::urls::dsl::*;
|
||||||
|
use super::schema::urls::*;
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use diesel::{delete, dsl::insert_into, prelude::*, result::Error};
|
use diesel::{delete, dsl::insert_into, prelude::*, result::Error};
|
||||||
@@ -15,7 +16,7 @@ pub fn upsert_entry(
|
|||||||
time_to_live: Option<NaiveDateTime>,
|
time_to_live: Option<NaiveDateTime>,
|
||||||
) -> Result<usize, Error> {
|
) -> Result<usize, Error> {
|
||||||
let connection = &mut establish_connection();
|
let connection = &mut establish_connection();
|
||||||
insert_into(urls)
|
insert_into(table)
|
||||||
.values((url.eq(path), destination_url.eq(dest), ttl.eq(time_to_live)))
|
.values((url.eq(path), destination_url.eq(dest), ttl.eq(time_to_live)))
|
||||||
.on_conflict(url)
|
.on_conflict(url)
|
||||||
.do_update()
|
.do_update()
|
||||||
|
|||||||
10
src/dto/mod.rs
Normal file
10
src/dto/mod.rs
Normal 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>,
|
||||||
|
}
|
||||||
@@ -1,8 +1,27 @@
|
|||||||
use rocket::{http::Status, response::Redirect};
|
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)> {
|
pub fn handle_redirect(url: &str) -> Result<Redirect, (Status, &'static str)> {
|
||||||
let row = get_entry(url).map_err(|_| (Status::NotFound, "Redirect Not found"))?;
|
let row = get_entry(url).map_err(|_| (Status::NotFound, "Redirect Not found"))?;
|
||||||
Ok(Redirect::to(row.destination_url))
|
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")
|
||||||
|
}
|
||||||
|
|||||||
22
src/main.rs
22
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 db;
|
||||||
|
mod dto;
|
||||||
mod handlers;
|
mod handlers;
|
||||||
|
|
||||||
|
use db::establish_connection;
|
||||||
|
use handlers::url::{handle_delete, handle_redirect, handle_upsert};
|
||||||
use rocket::fs::{FileServer, relative};
|
use rocket::fs::{FileServer, relative};
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
use rocket::{http::Status, response::Redirect};
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
@@ -11,10 +14,21 @@ extern crate rocket;
|
|||||||
#[launch]
|
#[launch]
|
||||||
fn rocket() -> _ {
|
fn rocket() -> _ {
|
||||||
rocket::build()
|
rocket::build()
|
||||||
.mount("/", routes![shortner])
|
.mount("/", routes![shortner,])
|
||||||
|
.mount("/api", routes![upsert, delete])
|
||||||
.mount("/", FileServer::from(relative!("web/dist")))
|
.mount("/", FileServer::from(relative!("web/dist")))
|
||||||
}
|
}
|
||||||
#[get("/<url>")]
|
#[get("/<url>")]
|
||||||
fn shortner(url: &str) -> Result<Redirect, (Status, &'static str)> {
|
fn shortner(url: &str) -> Result<Redirect, (Status, &'static str)> {
|
||||||
handle_redirect(url)
|
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)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user