diff options
| author | Fuwn <[email protected]> | 2021-05-12 23:48:04 -0700 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2021-05-12 23:48:04 -0700 |
| commit | 6c2ea68bbef0e1fc0b50b80f113b0ebcdf745a40 (patch) | |
| tree | a2330c4408f17cb1fc637f5263ee1432c1b62c2b /src | |
| download | lime-old-6c2ea68bbef0e1fc0b50b80f113b0ebcdf745a40.tar.xz lime-old-6c2ea68bbef0e1fc0b50b80f113b0ebcdf745a40.zip | |
feat: :star:
Diffstat (limited to 'src')
| -rw-r--r-- | src/db/mod.rs | 56 | ||||
| -rw-r--r-- | src/db/models.rs | 18 | ||||
| -rw-r--r-- | src/db/schema.rs | 9 | ||||
| -rw-r--r-- | src/main.rs | 44 | ||||
| -rw-r--r-- | src/routes/api.rs | 87 | ||||
| -rw-r--r-- | src/routes/mod.rs | 5 | ||||
| -rw-r--r-- | src/routes/ui.rs | 55 | ||||
| -rw-r--r-- | src/structure.rs | 21 |
8 files changed, 295 insertions, 0 deletions
diff --git a/src/db/mod.rs b/src/db/mod.rs new file mode 100644 index 0000000..a607593 --- /dev/null +++ b/src/db/mod.rs @@ -0,0 +1,56 @@ +// Copyleft (ɔ) 2021-2021 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +pub mod models; +mod schema; + +use diesel::{insert_into, prelude::*, update}; + +use crate::db::models::{Link, LinkForm}; + +fn establish_connection() -> SqliteConnection { + let database_url = std::env::var("DATABASE_URL").unwrap_or_else(|_| "lime.sqlite3".to_string()); + SqliteConnection::establish(&database_url) + .unwrap_or_else(|_| panic!("error connecting to {}", database_url)) +} + +pub fn insert_link(form: LinkForm) -> Result<(), Box<dyn std::error::Error>> { + use schema::links::dsl::*; + + if find_link(form.short, false).is_err() { + insert_into(links) + .values((long.eq(form.long), short.eq(form.short), ip.eq(form.ip))) + .execute(&establish_connection())?; + } else { + bail!("short url already exists"); + } + + Ok(()) +} + +pub fn find_link(short_c: &str, tick: bool) -> Result<Link, Box<dyn std::error::Error>> { + use schema::links::dsl::*; + + let results = links + .filter(short.eq(short_c)) + .load::<Link>(&establish_connection()) + .unwrap(); + + if results.is_empty() { + bail!("no entry found with the short url: /{}", short_c) + } else { + let long_c = results[0].clone(); + + if tick { + update(links.find(&long_c.short)) + .set(uses.eq(long_c.uses + 1)) + .execute(&establish_connection())?; + } + + if long_c.disabled { + bail!("short url disabled") + } + + Ok(long_c) + } +} diff --git a/src/db/models.rs b/src/db/models.rs new file mode 100644 index 0000000..1b465b6 --- /dev/null +++ b/src/db/models.rs @@ -0,0 +1,18 @@ +// Copyleft (ɔ) 2021-2021 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#[derive(Queryable, Debug, Clone)] +pub struct Link { + pub long: String, + pub short: String, + pub disabled: bool, + pub ip: String, + pub uses: i32, +} + +#[derive(Deserialize, Debug)] +pub struct LinkForm<'a> { + pub long: &'a str, + pub short: &'a str, + pub ip: &'a str, +} diff --git a/src/db/schema.rs b/src/db/schema.rs new file mode 100644 index 0000000..8d4006b --- /dev/null +++ b/src/db/schema.rs @@ -0,0 +1,9 @@ +table! { + links (short) { + long -> Text, + short -> Text, + disabled -> Bool, + ip -> Text, + uses -> Int4, + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..018bae6 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,44 @@ +// Copyleft (ɔ) 2021-2021 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#[macro_use] +extern crate actix_web; +#[macro_use] +extern crate serde_derive; +#[macro_use] +extern crate diesel; +#[macro_use] +extern crate simple_error; + +pub mod routes; +pub mod structure; + +pub mod db; + +use actix_web::web; +use routes::*; + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + dotenv::dotenv().ok(); + + actix_web::HttpServer::new(|| { + actix_web::App::new() + .wrap(actix_cors::Cors::default().allow_any_origin()) + .service(ui::index) + .service(ui::handle) + .service(ui::statistics) + .service( + web::scope("/api/v1") + .service(api::create) + .service(api::statistics) + .service(api::index), + ) + }) + .bind(format!( + "0.0.0.0:{}", + std::env::var("PORT").expect("no port was provided... why.") + ))? + .run() + .await +} diff --git a/src/routes/api.rs b/src/routes/api.rs new file mode 100644 index 0000000..48e8fe2 --- /dev/null +++ b/src/routes/api.rs @@ -0,0 +1,87 @@ +// Copyleft (ɔ) 2021-2021 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +use actix_web::{web, HttpRequest, HttpResponse}; +use askama::Template; + +use crate::{ + db::{find_link, insert_link, models::LinkForm}, + structure::{PostCreateShort, StatisticsApi, TextTemplate}, +}; + +#[post("/create")] +pub fn create(req: HttpRequest, params: web::Form<PostCreateShort>) -> HttpResponse { + if let Err(e) = insert_link(LinkForm { + long: ¶ms.long, + short: ¶ms.short, + ip: &req.peer_addr().unwrap().ip().to_string(), + }) { + HttpResponse::Ok().body( + TextTemplate { + text: e.to_string().as_str(), + } + .render() + .unwrap(), + ) + } else { + HttpResponse::Ok().body( + TextTemplate { + text: &format!("short url created: /{}", params.short), + } + .render() + .unwrap(), + ) + } +} + +#[get("/statistics")] +pub fn statistics(req: HttpRequest) -> HttpResponse { + let queries = qstring::QString::from(req.query_string()); + + let result = find_link(queries.get("short").unwrap_or(""), false); + + HttpResponse::Ok().json(if let Err(e) = result { + StatisticsApi { + long: e.to_string(), + disabled: true, + uses: 0, + } + } else { + let usable = result.unwrap(); + StatisticsApi { + long: usable.long, + disabled: usable.disabled, + uses: usable.uses, + } + }) +} + +#[get("/")] +pub fn index() -> HttpResponse { + HttpResponse::Ok().body( + r#"# lime api +## routes +if a route requires a parameter, it will be notated like <this>. +for example; if a route is notated as "/api/v1/route?<parameter>", you can +access that route via the url +"http://this.domain/api/v1/route?parameter=something" + +- /api/v1 + - /: index page (you are here) + - /statistics?<short>: get information about a short url; long, disabled, uses + - /create: a post route which takes a form; long and short, creates a new + short url + +### contributing +if you'd like to support the project in anyway, check out the repository! +https://github.com/fuwn/lime + +### supporting +if you would like to support my development ventures, visit my github profile +here :3 https://github.com/fuwn + +### license +gnu general public license v3.0 (gpl-3.0-only) +https://github.com/fuwn/lime/blob/main/license"#, + ) +} diff --git a/src/routes/mod.rs b/src/routes/mod.rs new file mode 100644 index 0000000..ada6781 --- /dev/null +++ b/src/routes/mod.rs @@ -0,0 +1,5 @@ +// Copyleft (ɔ) 2021-2021 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +pub mod api; +pub mod ui; diff --git a/src/routes/ui.rs b/src/routes/ui.rs new file mode 100644 index 0000000..b4cd42a --- /dev/null +++ b/src/routes/ui.rs @@ -0,0 +1,55 @@ +// Copyleft (ɔ) 2021-2021 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +use actix_web::{web, HttpResponse}; +use askama::Template; + +use crate::{db::find_link, structure::TextTemplate}; + +#[get("/")] +pub fn index() -> HttpResponse { + HttpResponse::Ok().body(include_str!("../../templates/index.html")) +} + +#[get("/{short}")] +pub async fn handle(info: web::Path<String>) -> HttpResponse { + let result = find_link(&info.0, true); + + if let Err(ref e) = result { + HttpResponse::Ok().body( + TextTemplate { + text: e.to_string().as_str(), + } + .render() + .unwrap(), + ) + } else { + HttpResponse::Ok().body(format!( + "<script>location.href=\"/{}\"</script>", + result.unwrap().long, + )) + } +} + +#[get("/{short}/statistics")] +pub async fn statistics(info: web::Path<String>) -> HttpResponse { + let result = find_link(&info.0, false); + + if let Err(ref e) = result { + HttpResponse::Ok().body( + TextTemplate { + text: e.to_string().as_str(), + } + .render() + .unwrap(), + ) + } else { + HttpResponse::Ok().body( + TextTemplate { + text: &format!("/{} has {} uses", info.0, result.unwrap().uses), + } + .render() + .unwrap(), + ) + } +} diff --git a/src/structure.rs b/src/structure.rs new file mode 100644 index 0000000..1774f0c --- /dev/null +++ b/src/structure.rs @@ -0,0 +1,21 @@ +// Copyleft (ɔ) 2021-2021 Fuwn +// SPDX-License-Identifier: GPL-3.0-only + +#[derive(Serialize, Deserialize, Debug)] +pub struct PostCreateShort { + pub long: String, + pub short: String, +} + +#[derive(askama::Template)] +#[template(path = "text.html")] +pub struct TextTemplate<'a> { + pub text: &'a str, +} + +#[derive(Serialize, Deserialize)] +pub struct StatisticsApi { + pub long: String, + pub disabled: bool, + pub uses: i32, +} |