You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
6.2 KiB
211 lines
6.2 KiB
use axum::{ |
|
extract::{Path, Query, State}, |
|
http::StatusCode, |
|
response::Redirect, |
|
routing::{get, patch}, |
|
Extension, Form, Router, |
|
}; |
|
use entity::link; |
|
use sea_orm::{entity::prelude::*, ActiveValue, Set}; |
|
use tera::Context; |
|
use tracing::error; // use sea_orm::FuckYouForNotKnowingAboutThisTrait; |
|
|
|
use crate::{ |
|
data::{AppState, CurrentUser, LinkForm}, |
|
utils, |
|
}; |
|
use std::{collections::HashMap, sync::Arc}; |
|
|
|
/// crud routes for |
|
pub fn get_routes() -> Router<Arc<AppState>> { |
|
Router::new() |
|
.route("/", get(list).post(|| async { "make a new link" })) |
|
.route( |
|
"/:name", |
|
patch(|| async { "change a link" }).delete(|| async { "yeet a link" }), |
|
) |
|
// admins can change/delete all, users their own |
|
} |
|
|
|
pub async fn create( |
|
State(state): State<Arc<AppState>>, |
|
Extension(user): Extension<CurrentUser>, |
|
Form(form): Form<LinkForm>, |
|
) -> Result<Redirect, String> { |
|
// what an utter vomit coffin of a function, wow |
|
// todo: clean this up |
|
let mut tries = 0; |
|
let target = form.target; |
|
let mut source = form.source.unwrap_or(String::new()); |
|
match source.as_str() { |
|
"" => loop { |
|
source = utils::gen_id(state.opts.id_len, state.opts.charset.clone()); |
|
match try_insert_link(&state.db, source.clone(), target.clone(), user.id).await { |
|
Ok(_) => break, |
|
Err(e) => match e { |
|
DbErr::RecordNotInserted => { |
|
tries += 1; |
|
if tries > 5 { |
|
return Err(utils::render_status_code( |
|
StatusCode::INTERNAL_SERVER_ERROR, |
|
) |
|
.await); |
|
} |
|
continue; |
|
} |
|
_ => { |
|
return Err( |
|
utils::render_status_code(StatusCode::INTERNAL_SERVER_ERROR).await |
|
) |
|
} |
|
}, |
|
} |
|
}, |
|
_ => match try_insert_link(&state.db, source.clone(), target.clone(), user.id).await { |
|
Ok(_) => (), |
|
Err(e) => { |
|
return Err(utils::render_status_code(match e { |
|
DbErr::RecordNotInserted => StatusCode::BAD_REQUEST, |
|
_ => StatusCode::INTERNAL_SERVER_ERROR, |
|
}) |
|
.await) |
|
} |
|
}, |
|
} |
|
|
|
Ok(Redirect::to(&format!("/gay/link/{}", source))) |
|
} |
|
|
|
async fn try_insert_link( |
|
db: &DatabaseConnection, |
|
source: String, |
|
target: String, |
|
userid: i32, |
|
) -> Result<(), DbErr> { |
|
let link = link::ActiveModel { |
|
user_id: ActiveValue::Set(userid), |
|
source: ActiveValue::Set(source), |
|
target: ActiveValue::Set(target), |
|
id: ActiveValue::NotSet, |
|
}; |
|
|
|
let _res = link::Entity::insert(link).exec(db).await?; |
|
|
|
Ok(()) |
|
} |
|
|
|
pub async fn update( |
|
State(state): State<Arc<AppState>>, |
|
Extension(user): Extension<CurrentUser>, |
|
Path(source): Path<String>, |
|
Form(form): Form<LinkForm>, |
|
) -> Result<Redirect, String> { |
|
let mut link: link::ActiveModel = match link::Entity::find() |
|
.filter(link::Column::Source.eq(source)) |
|
.one(&state.db) |
|
.await |
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) |
|
.and_then(|l| l.ok_or(StatusCode::NOT_FOUND)) |
|
.map_err(|e| utils::render_status_code(e)) |
|
{ |
|
Ok(l) => l.into(), |
|
Err(f) => Err(f.await)?, // let me do async closures you cowards |
|
}; |
|
|
|
if (!user.is_admin) |
|
&& link |
|
.user_id |
|
.clone() |
|
.into_value() |
|
.expect("the not null field shouldn't be null probably lmao") |
|
!= Value::from(user.id) |
|
{ |
|
return Err(utils::render_status_code(StatusCode::UNAUTHORIZED).await); |
|
} |
|
|
|
link.target = Set(form.target); |
|
if let Some(source) = form.source { |
|
link.source = Set(source); |
|
} |
|
|
|
let source = link |
|
.source |
|
.clone() |
|
.into_value() |
|
.expect("the field is literally not nullable what the fuck"); |
|
match link |
|
.update(&state.db) |
|
.await |
|
.map_err(|_| utils::render_status_code(StatusCode::INTERNAL_SERVER_ERROR)) |
|
{ |
|
Ok(_) => (), |
|
Err(f) => Err(f.await)?, |
|
}; |
|
|
|
Ok(Redirect::to(&format!("/gay/link/{}", source))) |
|
} |
|
|
|
pub async fn delete( |
|
State(state): State<Arc<AppState>>, |
|
Extension(user): Extension<CurrentUser>, |
|
Path(source): Path<String>, |
|
) -> Result<Redirect, String> { |
|
let link = match link::Entity::find() |
|
.filter(link::Column::Source.eq(source)) |
|
.one(&state.db) |
|
.await |
|
{ |
|
Err(_) => Err(utils::render_status_code(StatusCode::INTERNAL_SERVER_ERROR).await)?, |
|
Ok(None) => Err(utils::render_status_code(StatusCode::NOT_FOUND).await)?, |
|
Ok(Some(l)) => { |
|
if (!user.is_admin) && l.user_id != user.id { |
|
Err(utils::render_status_code(StatusCode::UNAUTHORIZED).await)? |
|
} |
|
l |
|
}, |
|
}; |
|
|
|
match link.delete(&state.db).await { |
|
Err(_) => Err(utils::render_status_code(StatusCode::INTERNAL_SERVER_ERROR).await)?, |
|
Ok(_) => () |
|
}; |
|
|
|
Ok(Redirect::to("/gay/link")) |
|
} |
|
|
|
pub async fn list( |
|
State(state): State<Arc<AppState>>, |
|
Query(params): Query<HashMap<String, String>>, |
|
Extension(user): Extension<CurrentUser>, |
|
) -> Result<String, StatusCode> { |
|
let mut query = link::Entity::find(); |
|
|
|
if !(user.is_admin && params.contains_key("all")) { |
|
query = query.filter(link::Column::UserId.eq(user.id)); |
|
} |
|
|
|
let links = query |
|
.all(&state.db) |
|
.await |
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; |
|
|
|
let mut context = Context::new(); |
|
|
|
context.insert("user", &user); |
|
context.insert("links", &links); |
|
|
|
Ok(utils::render_template("", Some(context)) |
|
.await |
|
.map_err(|e| { |
|
error!("oopsie woopsie, we had a little rendering fucky wucky! {e}"); |
|
StatusCode::INTERNAL_SERVER_ERROR |
|
})?) |
|
} |
|
|
|
pub async fn access(State(state): State<Arc<AppState>>, Path(source): Path<String>) -> Redirect { |
|
state |
|
.cache |
|
.get(&source) |
|
.map(|target| Redirect::to(&target)) |
|
.unwrap_or(Redirect::to("/404")) |
|
}
|
|
|