more routers

main
Zynh Ludwig 2024-08-29 23:22:55 -07:00
parent ea7dee4dfd
commit b7bcd9c5f1
3 changed files with 135 additions and 111 deletions

View File

@ -7,7 +7,7 @@ use axum::{
middleware::{self, Next}, middleware::{self, Next},
response::{Html, IntoResponse, Redirect}, response::{Html, IntoResponse, Redirect},
routing::get, routing::get,
Json, Router, Router,
}; };
use axum_extra::TypedHeader; use axum_extra::TypedHeader;
use tower_http::{services::ServeDir, trace::TraceLayer}; use tower_http::{services::ServeDir, trace::TraceLayer};
@ -15,6 +15,8 @@ use tower_http::{services::ServeDir, trace::TraceLayer};
use std::{io, net::SocketAddr}; use std::{io, net::SocketAddr};
mod router { mod router {
pub mod link;
pub mod records;
pub mod upload; pub mod upload;
} }
mod cache; mod cache;
@ -24,11 +26,12 @@ mod views;
use util::{headers::ForwardedFor, logging, ssr, sweeper}; use util::{headers::ForwardedFor, logging, ssr, sweeper};
use router::*;
use state::*; use state::*;
use views::*; use views::*;
use upload::get_upload_router; use router::link::get_link_router;
use router::records::get_records_router;
use router::upload::get_upload_router;
#[tokio::main] #[tokio::main]
async fn main() -> io::Result<()> { async fn main() -> io::Result<()> {
@ -41,23 +44,13 @@ async fn main() -> io::Result<()> {
sweeper::spawn(state.clone()); sweeper::spawn(state.clone());
// Records views
let record_router = Router::new()
.route("/", get(records))
.route("/links", get(records_links));
// Link pages
let link_router = Router::new()
.route("/:id", get(link).delete(link_delete))
.route("/:id/remaining", get(remaining));
// Router Setup // Router Setup
let app = Router::new() let app = Router::new()
.route("/", get(welcome)) .route("/", get(welcome))
.route("/download/:id", get(download)) .route("/download/:id", get(download))
.nest("/upload", get_upload_router()) .nest("/upload", get_upload_router())
.nest("/records", record_router) .nest("/records", get_records_router())
.nest("/link", link_router) .nest("/link", get_link_router())
.with_state(state) .with_state(state)
.fallback_service(ServeDir::new("dist")) .fallback_service(ServeDir::new("dist"))
.layer(TraceLayer::new_for_http()) .layer(TraceLayer::new_for_http())
@ -68,24 +61,6 @@ async fn main() -> io::Result<()> {
Ok(()) Ok(())
} }
async fn remaining(
State(state): State<AppState>,
axum::extract::Path(id): axum::extract::Path<String>,
) -> impl IntoResponse {
let records = state.records.lock().await;
if let Some(record) = records.get(&id) {
let downloads_remaining = record.downloads_remaining();
let plural = if downloads_remaining > 1 { "s" } else { "" };
let out = format!(
"You have {} download{} remaining!",
downloads_remaining, plural
);
Html(out)
} else {
Html("?".to_string())
}
}
async fn welcome() -> impl IntoResponse { async fn welcome() -> impl IntoResponse {
let cat_fact = views::get_cat_fact().await; let cat_fact = views::get_cat_fact().await;
Html(ssr::render(move || { Html(ssr::render(move || {
@ -93,84 +68,6 @@ async fn welcome() -> impl IntoResponse {
})) }))
} }
async fn records(State(state): State<AppState>) -> impl IntoResponse {
Json(state.records.lock().await.clone())
}
// This function is to remain ugly until that time in which I properly hide
// this behind some kind of authentication
async fn records_links(State(state): State<AppState>) -> impl IntoResponse {
let records = state.records.lock().await.clone();
Html(ssr::render(move || {
leptos::view! {
<HtmxPage>
<div class="form-wrapper">
<div class="column-container">
<ul>
{records
.keys()
.map(|key| {
leptos::view! {
<li class="link-wrapper">
<a href="/link/{key}">{key}</a>
<button
style="margin-left: 1em;"
hx-target="closest .link-wrapper"
hx-swap="outerHTML"
hx-delete="/link/{key}"
>
X
</button>
</li>
}
})
.collect::<Vec<_>>()}
</ul>
</div>
</div>
</HtmxPage>
}
}))
}
async fn link(
axum::extract::Path(id): axum::extract::Path<String>,
State(mut state): State<AppState>,
) -> Result<Html<String>, Redirect> {
{
let mut records = state.records.lock().await;
if let Some(record) = records
.get_mut(&id)
.filter(|record| record.can_be_downloaded())
{
return Ok(Html(ssr::render({
let record = record.clone();
|| {
leptos::view! { <DownloadLinkPage id=id record=record /> }
}
})));
}
}
// TODO: This....
state.remove_record(&id).await.unwrap();
Err(Redirect::to("/404.html"))
}
async fn link_delete(
axum::extract::Path(id): axum::extract::Path<String>,
State(mut state): State<AppState>,
) -> Result<Html<String>, (StatusCode, String)> {
state
.remove_record(&id)
.await
.map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()))?;
Ok(Html("".to_string()))
}
async fn log_source( async fn log_source(
ConnectInfo(addr): ConnectInfo<SocketAddr>, ConnectInfo(addr): ConnectInfo<SocketAddr>,
forwarded_for: Option<TypedHeader<ForwardedFor>>, forwarded_for: Option<TypedHeader<ForwardedFor>>,

72
src/router/link.rs Normal file
View File

@ -0,0 +1,72 @@
use axum::{
extract::State,
response::{Html, IntoResponse, Redirect},
routing::get,
Router,
};
use reqwest::StatusCode;
use crate::{util::ssr, AppState, AsyncRemoveRecord, DownloadLinkPage};
pub fn get_link_router() -> Router<AppState> {
// Link pages
Router::new()
.route("/:id", get(link).delete(link_delete))
.route("/:id/remaining", get(remaining))
}
async fn link(
axum::extract::Path(id): axum::extract::Path<String>,
State(mut state): State<AppState>,
) -> Result<Html<String>, Redirect> {
{
let mut records = state.records.lock().await;
if let Some(record) = records
.get_mut(&id)
.filter(|record| record.can_be_downloaded())
{
return Ok(Html(ssr::render({
let record = record.clone();
|| {
leptos::view! { <DownloadLinkPage id=id record=record /> }
}
})));
}
}
// TODO: This....
state.remove_record(&id).await.unwrap();
Err(Redirect::to("/404.html"))
}
async fn link_delete(
axum::extract::Path(id): axum::extract::Path<String>,
State(mut state): State<AppState>,
) -> Result<Html<String>, (StatusCode, String)> {
state
.remove_record(&id)
.await
.map_err(|err| (StatusCode::INTERNAL_SERVER_ERROR, err.to_string()))?;
Ok(Html("".to_string()))
}
async fn remaining(
State(state): State<AppState>,
axum::extract::Path(id): axum::extract::Path<String>,
) -> impl IntoResponse {
let records = state.records.lock().await;
if let Some(record) = records.get(&id) {
let downloads_remaining = record.downloads_remaining();
let plural = if downloads_remaining > 1 { "s" } else { "" };
let out = format!(
"You have {} download{} remaining!",
downloads_remaining, plural
);
Html(out)
} else {
Html("?".to_string())
}
}

55
src/router/records.rs Normal file
View File

@ -0,0 +1,55 @@
use axum::{
extract::State,
response::{Html, IntoResponse},
routing::get,
Json, Router,
};
use crate::{util::ssr, AppState, HtmxPage};
pub fn get_records_router() -> Router<AppState> {
// Records views
Router::new()
.route("/", get(records))
.route("/links", get(records_links))
}
pub(crate) async fn records(State(state): State<AppState>) -> impl IntoResponse {
Json(state.records.lock().await.clone())
}
// This function is to remain ugly until that time in which I properly hide
// this behind some kind of authentication
pub async fn records_links(State(state): State<AppState>) -> impl IntoResponse {
let records = state.records.lock().await.clone();
Html(ssr::render(move || {
leptos::view! {
<HtmxPage>
<div class="form-wrapper">
<div class="column-container">
<ul>
{records
.keys()
.map(|key| {
leptos::view! {
<li class="link-wrapper">
<a href="/link/{key}">{key}</a>
<button
style="margin-left: 1em;"
hx-target="closest .link-wrapper"
hx-swap="outerHTML"
hx-delete="/link/{key}"
>
X
</button>
</li>
}
})
.collect::<Vec<_>>()}
</ul>
</div>
</div>
</HtmxPage>
}
}))
}