more htmx utilization

main
Zynh0722 2023-07-31 06:32:17 -07:00
parent e6c996d7d8
commit 6e332d4b3c
6 changed files with 266 additions and 52 deletions

179
Cargo.lock generated
View File

@ -28,6 +28,17 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "ahash"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
]
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "1.0.2" version = "1.0.2"
@ -297,6 +308,42 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
[[package]]
name = "cached"
version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b195e4fbc4b6862bbd065b991a34750399c119797efff72492f28a5864de8700"
dependencies = [
"async-trait",
"cached_proc_macro",
"cached_proc_macro_types",
"futures",
"hashbrown 0.13.2",
"instant",
"once_cell",
"thiserror",
"tokio",
]
[[package]]
name = "cached_proc_macro"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b48814962d2fd604c50d2b9433c2a41a0ab567779ee2c02f7fba6eca1221f082"
dependencies = [
"cached_proc_macro_types",
"darling",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "cached_proc_macro_types"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663"
[[package]] [[package]]
name = "camino" name = "camino"
version = "1.1.6" version = "1.1.6"
@ -364,6 +411,12 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "186dce98367766de751c42c4f03970fc60fc012296e706ccbb9d5df9b6c1e271" checksum = "186dce98367766de751c42c4f03970fc60fc012296e706ccbb9d5df9b6c1e271"
[[package]]
name = "common_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3f6d59c71e7dc3af60f0af9db32364d96a16e9310f3f5db2b55ed642162dd35"
[[package]] [[package]]
name = "config" name = "config"
version = "0.13.3" version = "0.13.3"
@ -456,6 +509,41 @@ dependencies = [
"typenum", "typenum",
] ]
[[package]]
name = "darling"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 1.0.109",
]
[[package]]
name = "darling_macro"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
dependencies = [
"darling_core",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "derive-where" name = "derive-where"
version = "1.2.1" version = "1.2.1"
@ -784,7 +872,16 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [ dependencies = [
"ahash", "ahash 0.7.6",
]
[[package]]
name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
dependencies = [
"ahash 0.8.3",
] ]
[[package]] [[package]]
@ -939,6 +1036,12 @@ dependencies = [
"cc", "cc",
] ]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.4.0" version = "0.4.0"
@ -969,6 +1072,15 @@ dependencies = [
"hashbrown 0.14.0", "hashbrown 0.14.0",
] ]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "interpolator" name = "interpolator"
version = "0.5.0" version = "0.5.0"
@ -1150,6 +1262,36 @@ dependencies = [
"tracing", "tracing",
] ]
[[package]]
name = "leptos_router"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a426605f412e2489b752bd590fad789fa62547e18e00cb4b5bfe53c19a33270d"
dependencies = [
"cached",
"cfg-if",
"common_macros",
"gloo-net",
"js-sys",
"lazy_static",
"leptos",
"linear-map",
"log",
"lru",
"once_cell",
"percent-encoding",
"regex",
"serde",
"serde_json",
"serde_qs",
"thiserror",
"tracing",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]] [[package]]
name = "leptos_server" name = "leptos_server"
version = "0.4.6" version = "0.4.6"
@ -1171,6 +1313,16 @@ version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "linear-map"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfae20f6b19ad527b550c223fddc3077a547fc70cda94b9b566575423fd303ee"
dependencies = [
"serde",
"serde_test",
]
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
version = "0.5.6" version = "0.5.6"
@ -1199,6 +1351,15 @@ version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
name = "lru"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670"
dependencies = [
"hashbrown 0.13.2",
]
[[package]] [[package]]
name = "matchers" name = "matchers"
version = "0.1.0" version = "0.1.0"
@ -1370,6 +1531,7 @@ dependencies = [
"futures", "futures",
"headers", "headers",
"leptos", "leptos",
"leptos_router",
"rand", "rand",
"reqwest", "reqwest",
"sanitize-filename-reader-friendly", "sanitize-filename-reader-friendly",
@ -1998,6 +2160,15 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "serde_test"
version = "1.0.176"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a2f49ace1498612d14f7e0b8245519584db8299541dfe31a06374a828d620ab"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "serde_urlencoded" name = "serde_urlencoded"
version = "0.7.1" version = "0.7.1"
@ -2141,6 +2312,12 @@ version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"

View File

@ -5,6 +5,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
async-bincode = { version = "0.7.0", features = ["tokio"] } async-bincode = { version = "0.7.0", features = ["tokio"] }
async_zip = { version = "0.0.13", features = ["deflate", "tokio", "tokio-fs", "async-compression"] } async_zip = { version = "0.0.13", features = ["deflate", "tokio", "tokio-fs", "async-compression"] }
@ -14,6 +15,7 @@ chrono = { version = "0.4.24", features = ["serde"] }
futures = "0.3.28" futures = "0.3.28"
headers = "0.3.8" headers = "0.3.8"
leptos = { version = "0.4.6", features = ["ssr", "nightly", "tracing", "default-tls"] } leptos = { version = "0.4.6", features = ["ssr", "nightly", "tracing", "default-tls"] }
leptos_router = { version = "0.4.6", features = ["ssr"] }
rand = { version = "0.8.5", features = ["small_rng"] } rand = { version = "0.8.5", features = ["small_rng"] }
reqwest = { version = "0.11.18", features = ["json", "native-tls", "blocking"] } reqwest = { version = "0.11.18", features = ["json", "native-tls", "blocking"] }
sanitize-filename-reader-friendly = "2.2.1" sanitize-filename-reader-friendly = "2.2.1"

2
dist/css/main.css vendored
View File

@ -10,7 +10,7 @@ body {
margin: 0; margin: 0;
} }
.main-form { .column-container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;

3
dist/scripts/loading_progress.js vendored Normal file
View File

@ -0,0 +1,3 @@
htmx.on('#form', 'htmx:xhr:progress', function(evt) {
htmx.find('#progress').setAttribute('value', evt.detail.loaded / evt.detail.total * 100)
});

View File

@ -3,9 +3,9 @@ use async_zip::{tokio::write::ZipFileWriter, Compression, ZipEntryBuilder};
use axum::{ use axum::{
body::StreamBody, body::StreamBody,
extract::{ConnectInfo, DefaultBodyLimit, Multipart, State}, extract::{ConnectInfo, DefaultBodyLimit, Multipart, State},
http::{Request, StatusCode}, http::{Request, Response, StatusCode},
middleware::{self, Next}, middleware::{self, Next},
response::{Html, IntoResponse, Redirect, Response}, response::{Html, IntoResponse, Redirect},
routing::{get, post}, routing::{get, post},
Json, Router, TypedHeader, Json, Router, TypedHeader,
}; };
@ -36,7 +36,7 @@ mod views;
use state::{AppState, UploadRecord}; use state::{AppState, UploadRecord};
use crate::views::{DownloadLink, Welcome}; use crate::views::{DownloadLinkPage, LinkView, Welcome};
pub mod error { pub mod error {
use std::io::{Error, ErrorKind}; use std::io::{Error, ErrorKind};
@ -67,6 +67,7 @@ async fn main() -> io::Result<()> {
let state = state.clone(); let state = state.clone();
async move { async move {
loop { loop {
tokio::time::sleep(Duration::from_secs(15 * 60)).await;
tracing::info!("Cleaning Sweep!"); tracing::info!("Cleaning Sweep!");
let mut records = state.records.lock().await; let mut records = state.records.lock().await;
@ -79,8 +80,6 @@ async fn main() -> io::Result<()> {
cache::write_to_cache(&records).await.unwrap(); cache::write_to_cache(&records).await.unwrap();
} }
} }
tokio::time::sleep(Duration::from_secs(15 * 60)).await
} }
} }
}); });
@ -150,7 +149,7 @@ async fn link(
return Ok(Html(leptos::ssr::render_to_string({ return Ok(Html(leptos::ssr::render_to_string({
let record = record.clone(); let record = record.clone();
|cx| { |cx| {
leptos::view! { cx, <DownloadLink id=id record=record /> } leptos::view! { cx, <DownloadLinkPage id=id record=record /> }
} }
}))); })));
} else { } else {
@ -168,7 +167,7 @@ async fn log_source<B>(
forwarded_for: Option<TypedHeader<ForwardedFor>>, forwarded_for: Option<TypedHeader<ForwardedFor>>,
req: Request<B>, req: Request<B>,
next: Next<B>, next: Next<B>,
) -> Response { ) -> impl IntoResponse {
tracing::info!("{} : {:?}", addr, forwarded_for); tracing::info!("{} : {:?}", addr, forwarded_for);
next.run(req).await next.run(req).await
@ -177,7 +176,7 @@ async fn log_source<B>(
async fn upload_to_zip( async fn upload_to_zip(
State(state): State<AppState>, State(state): State<AppState>,
mut body: Multipart, mut body: Multipart,
) -> Result<Redirect, (StatusCode, String)> { ) -> Result<Response<String>, (StatusCode, String)> {
tracing::debug!("{:?}", *state.records.lock().await); tracing::debug!("{:?}", *state.records.lock().await);
let cache_name = util::get_random_name(10); let cache_name = util::get_random_name(10);
@ -222,7 +221,8 @@ async fn upload_to_zip(
} }
let mut records = state.records.lock().await; let mut records = state.records.lock().await;
records.insert(cache_name.clone(), UploadRecord::new(archive_path)); let record = UploadRecord::new(archive_path);
records.insert(cache_name.clone(), record.clone());
cache::write_to_cache(&records) cache::write_to_cache(&records)
.await .await
@ -230,7 +230,17 @@ async fn upload_to_zip(
writer.close().await.unwrap(); writer.close().await.unwrap();
Ok(Redirect::to(&format!("/link/{}", cache_name))) let id = cache_name;
let response = Response::builder()
.status(200)
.header("Content-Type", "text/html")
.header("HX-Push-Url", format!("/link/{}", &id))
.body(leptos::ssr::render_to_string(|cx| {
leptos::view! { cx, <LinkView id record /> }
}))
.unwrap();
Ok(response)
} }
async fn download( async fn download(

View File

@ -1,5 +1,5 @@
use futures::TryFutureExt; use futures::TryFutureExt;
use leptos::{Children, IntoView}; use leptos::{component, view, Children, IntoAttribute, IntoView, Scope};
use serde::Deserialize; use serde::Deserialize;
use crate::state::UploadRecord; use crate::state::UploadRecord;
@ -19,36 +19,77 @@ pub async fn get_cat_fact() -> String {
// {https://api.thecatapi.com/v1/images/search?size=small&format=src} // {https://api.thecatapi.com/v1/images/search?size=small&format=src}
// {https://cataas.com/cat?width=250&height=250} // {https://cataas.com/cat?width=250&height=250}
#[leptos::component] #[component]
pub fn Welcome(cx: leptos::Scope, fact: String) -> impl IntoView { pub fn Welcome(cx: Scope, fact: String) -> impl IntoView {
leptos::view! { cx, view! { cx,
<HtmxPage> <HtmxPage>
<h1>NyaZoom<sup>2</sup></h1>
<div class="form-wrapper"> <div class="form-wrapper">
<form action="/upload" method="post" enctype="multipart/form-data" class="main-form"> <WelcomeView fact />
<div class="cat-img-wrapper">
<img class="cat-img" src="https://api.thecatapi.com/v1/images/search?size=small&format=src" />
</div>
<input type="file" id="file" name="file" data-multiple-caption="{count} files selected" multiple />
<label for="file">Select Files</label>
<input type="submit" value="Get Link~" />
<p id="cat-fact">{fact}</p>
</form>
</div> </div>
</HtmxPage> </HtmxPage>
} }
} }
#[component]
pub fn WelcomeView(cx: Scope, fact: String) -> impl IntoView {
view! {
cx,
<form id="form" hx-swap="outerHTML" hx-post="/upload" hx-encoding="multipart/form-data" class="column-container">
<div class="cat-img-wrapper">
<img class="cat-img" src="https://api.thecatapi.com/v1/images/search?size=small&format=src" />
</div>
<input type="file" id="file" name="file" data-multiple-caption="{{count}} files selected" multiple />
<label for="file">Select Files</label>
<input type="submit" value="Get Link~" />
<p id="cat-fact">{fact}</p>
<progress id="progress" class="htmx-indicator" value="0" max="100"></progress>
</form>
<script src="/scripts/loading_progress.js" />
}
}
// <link href="../dist/css/link.css" rel="stylesheet" /> // <link href="../dist/css/link.css" rel="stylesheet" />
// #TODO: Handle pushing cleaner // #TODO: Handle pushing cleaner
#[leptos::component] #[component]
pub fn DownloadLink(cx: leptos::Scope, id: String, record: UploadRecord) -> impl IntoView { pub fn DownloadLinkPage(cx: Scope, id: String, record: UploadRecord) -> impl IntoView {
view! { cx,
<HtmxPage>
<div class="form-wrapper">
<LinkView id record />
</div>
</HtmxPage>
}
}
#[component]
pub fn HtmxPage(cx: Scope, children: Children) -> impl IntoView {
view! { cx,
<head>
<title>Nyazoom</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="/css/main.css" rel="stylesheet" />
<link href="/css/link.css" rel="stylesheet" />
<script src="/scripts/file_label.js" />
<script src="/scripts/link.js" />
<script src="https://unpkg.com/htmx.org@1.9.4" integrity="sha384-zUfuhFKKZCbHTY6aRR46gxiqszMk5tcHjsVFxnUo8VMus4kHGVdIYVbOYYNlKmHV" crossorigin="anonymous"></script>
</head>
<body>
<h1>NyaZoom<sup>2</sup></h1>
{children(cx)}
</body>
}
}
#[component]
pub fn LinkView(cx: Scope, id: String, record: UploadRecord) -> impl IntoView {
let downloads_remaining = record.max_downloads - record.downloads; let downloads_remaining = record.max_downloads - record.downloads;
let plural = if downloads_remaining > 1 { "s" } else { "" }; let plural = if downloads_remaining > 1 { "s" } else { "" };
leptos::view! { cx, view! {
<HtmxPage> cx,
<div class="column-container">
<div class="link-wrapper"> <div class="link-wrapper">
<a id="link" href=format!("/download/{id}")>Download Now!</a> <a id="link" href=format!("/download/{id}")>Download Now!</a>
</div> </div>
@ -60,25 +101,6 @@ pub fn DownloadLink(cx: leptos::Scope, id: String, record: UploadRecord) -> impl
<a href="/" class="return-button">Return to home</a> <a href="/" class="return-button">Return to home</a>
</HtmxPage> </div>
}
}
#[leptos::component]
pub fn HtmxPage(cx: leptos::Scope, children: Children) -> impl IntoView {
leptos::view! { cx,
<head>
<title>Nyazoom</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="/css/main.css" rel="stylesheet" />
<link href="/css/link.css" rel="stylesheet" />
<script src="/scripts/file_label.js" />
<script src="/scripts/link.js" />
</head>
<body>
{children(cx)}
</body>
} }
} }