use crate::model::{PasteCreate, PasteDelete, PasteEdit, PasteError, PasteEditMetadata, Paste};
use crate::database::Database;
use dorsal::DefaultReturn;
use axum::response::IntoResponse;
use axum::{
extract::{Path, State, Query},
routing::{get, post},
Json, Router,
};
use axum_extra::extract::cookie::CookieJar;
pub fn routes(database: Database) -> Router {
Router::new()
.route("/new", post(create_paste))
.route("/:url", get(get_paste_by_url))
.route("/:url/delete", post(delete_paste_by_url))
.route("/:url/edit", post(edit_paste_by_url))
.route("/:url/metadata", post(edit_paste_metadata_by_url))
.route("/auth/callback", get(callback_request))
.route("/auth/logout", get(logout_request))
.with_state(database)
}
async fn create_paste(
State(database): State<Database>,
Json(paste_to_create): Json<PasteCreate>,
) -> Result<Json<DefaultReturn<(String, Paste)>>, PasteError> {
let res = database.create_paste(paste_to_create).await;
match res {
Ok(paste) => Ok(Json(DefaultReturn {
success: true,
message: String::from("Paste created"),
payload: paste,
})),
Err(e) => Err(e),
}
}
async fn delete_paste_by_url(
State(database): State<Database>,
Path(url): Path<String>,
Json(paste_to_delete): Json<PasteDelete>,
) -> Result<Json<DefaultReturn<()>>, PasteError> {
match database
.delete_paste_by_url(url, paste_to_delete.password)
.await
{
Ok(_) => Ok(Json(DefaultReturn {
success: true,
message: String::from("Paste deleted"),
payload: (),
})),
Err(e) => Err(e),
}
}
async fn edit_paste_by_url(
State(database): State<Database>,
Path(url): Path<String>,
Json(paste_to_edit): Json<PasteEdit>,
) -> Result<Json<DefaultReturn<()>>, PasteError> {
match database
.edit_paste_by_url(
url,
paste_to_edit.password,
paste_to_edit.new_content,
paste_to_edit.new_url,
paste_to_edit.new_password,
)
.await
{
Ok(_) => Ok(Json(DefaultReturn {
success: true,
message: String::from("Paste updated"),
payload: (),
})),
Err(e) => Err(e),
}
}
async fn edit_paste_metadata_by_url(
State(database): State<Database>,
Path(url): Path<String>,
Json(paste_to_edit): Json<PasteEditMetadata>,
) -> Result<Json<DefaultReturn<()>>, PasteError> {
match database
.edit_paste_metadata_by_url(url, paste_to_edit.password, paste_to_edit.metadata)
.await
{
Ok(_) => Ok(Json(DefaultReturn {
success: true,
message: String::from("Paste updated"),
payload: (),
})),
Err(e) => Err(e),
}
}
pub async fn get_paste_by_url(
State(database): State<Database>,
Path(url): Path<String>,
) -> Result<Json<DefaultReturn<Paste>>, PasteError> {
match database.get_paste_by_url(url).await {
Ok(p) => Ok(Json(DefaultReturn {
success: true,
message: String::from("Paste exists"),
payload: p,
})),
Err(e) => Err(e),
}
}
pub async fn not_found() -> impl IntoResponse {
Json(DefaultReturn::<u16> {
success: false,
message: String::from("Path does not exist"),
payload: 404,
})
}
#[derive(serde::Deserialize)]
pub struct CallbackQueryProps {
pub uid: String, }
pub async fn callback_request(
State(database): State<Database>,
Query(params): Query<CallbackQueryProps>,
) -> impl IntoResponse {
if database.options.guppy == false {
return (
[
("Content-Type".to_string(), "text/plain".to_string()),
("Set-Cookit".to_string(), String::new()),
],
"Server does not support guppy.",
);
}
(
[
("Content-Type".to_string(), "text/html".to_string()),
(
"Set-Cookie".to_string(),
format!(
"__Secure-Token={}; SameSite=Lax; Secure; Path=/; HostOnly=true; HttpOnly=true; Max-Age={}",
params.uid,
60 * 60 * 24 * 365
),
),
],
"<head>
<meta http-equiv=\"Refresh\" content=\"0; URL=/\" />
</head>"
)
}
pub async fn logout_request(State(database): State<Database>, jar: CookieJar) -> impl IntoResponse {
if database.options.guppy == false {
return (
[
("Content-Type".to_string(), "text/plain".to_string()),
("Set-Cookit".to_string(), String::new()),
],
"Server does not support guppy.",
);
}
if let Some(_) = jar.get("__Secure-Token") {
return (
[
("Content-Type".to_string(), "text/plain".to_string()),
(
"Set-Cookie".to_string(),
"__Secure-Token=refresh; SameSite=Strict; Secure; Path=/; HostOnly=true; HttpOnly=true; Max-Age=0".to_string(),
),
],
"You have been signed out. You can now close this tab.",
);
}
(
[
("Content-Type".to_string(), "text/plain".to_string()),
("Set-Cookit".to_string(), String::new()),
],
"Failed to sign out of account.",
)
}