changes
This commit is contained in:
1274
Cargo.lock
generated
1274
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
13
Cargo.toml
13
Cargo.toml
@@ -79,11 +79,15 @@ lettre = { version = "0.11.18", default-features = false, features = [
|
||||
"tokio1-rustls-tls",
|
||||
"tracing",
|
||||
], optional = true }
|
||||
|
||||
aws-config = { version = "1.1", features = [
|
||||
"behavior-version-latest",
|
||||
], optional = true }
|
||||
aws-sdk-s3 = { version = "1.104", optional = true }
|
||||
|
||||
tera = { version = "1.20.0", default-features = false, optional = true }
|
||||
|
||||
[features]
|
||||
default = ["ssr"]
|
||||
hydrate = ["leptos/hydrate", "thaw/csr"]
|
||||
ssr = [
|
||||
"dep:axum",
|
||||
"dep:tower",
|
||||
@@ -102,9 +106,14 @@ ssr = [
|
||||
"dep:leptos_axum",
|
||||
"thaw/ssr",
|
||||
"lettre",
|
||||
"aws-config",
|
||||
"aws-sdk-s3",
|
||||
"tera",
|
||||
]
|
||||
|
||||
default = ["ssr"]
|
||||
hydrate = ["leptos/hydrate", "thaw/csr"]
|
||||
|
||||
[package.metadata.cargo-all-features]
|
||||
denylist = ["axum", "tower", "tower-http", "tokio", "sqlx", "leptos_axum"]
|
||||
skip_feature_sets = [["ssr", "hydrate"]]
|
||||
|
||||
BIN
airsoftfotos.db
BIN
airsoftfotos.db
Binary file not shown.
@@ -9,11 +9,15 @@ CREATE TABLE IF NOT EXISTS albums (
|
||||
FOREIGN KEY(category_id) REFERENCES categories(id)
|
||||
);
|
||||
|
||||
-- s3://airsoftfotos/{category:1}/{event-date:3-9-2025}/{id:1}/airsoftfotos.nl_{uuid}.webp -- processed
|
||||
-- s3://airsoftfotos/{category:1}/{event-date:3-9-2025}/{id:1}/{uuid}_{filename} -- original file
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS photos (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, -- might just store id as the filename in S3... or at least a folder with the og image, and a processed image (lower res and watermarked)
|
||||
album_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
filename TEXT NOT NULL,
|
||||
filename TEXT NOT NULL, -- is this the OG filename, or the filename we need to request from S3
|
||||
description TEXT NULL,
|
||||
visible BOOLEAN NOT NULL DEFAULT TRUE,
|
||||
upvotes INTEGER NOT NULL,
|
||||
|
||||
13
src/app.rs
13
src/app.rs
@@ -10,7 +10,7 @@ use leptos::prelude::*;
|
||||
use leptos_meta::*;
|
||||
use leptos_router::{components::*, path};
|
||||
|
||||
use thaw::{ConfigProvider, Layout, LayoutHeader, LoadingBarProvider, Theme, ToasterProvider};
|
||||
use thaw::{ConfigProvider, LayoutHeader, LoadingBarProvider, Theme, ToasterProvider};
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
pub mod ssr {
|
||||
@@ -28,6 +28,17 @@ pub mod ssr {
|
||||
.ok_or_else(|| ServerFnError::ServerError("Lettre missing".into()))
|
||||
}
|
||||
|
||||
pub fn s3() -> Result<aws_sdk_s3::Client, ServerFnError> {
|
||||
use aws_sdk_s3::Client;
|
||||
let Some(shared_config) =
|
||||
with_context::<AppState, _>(|state| state.aws_shared_config.clone())
|
||||
else {
|
||||
return Err(ServerFnError::ServerError("s3 init error".into()));
|
||||
};
|
||||
|
||||
Ok(Client::new(&shared_config))
|
||||
}
|
||||
|
||||
pub async fn auth() -> Result<AuthSession, ServerFnError> {
|
||||
let auth = leptos_axum::extract().await?;
|
||||
Ok(auth)
|
||||
|
||||
@@ -15,3 +15,6 @@ pub use file_preview::*;
|
||||
|
||||
mod upload;
|
||||
pub use upload::*;
|
||||
|
||||
mod progress_bar;
|
||||
pub use progress_bar::*;
|
||||
|
||||
29
src/app/components/progress-bar.css
Normal file
29
src/app/components/progress-bar.css
Normal file
@@ -0,0 +1,29 @@
|
||||
.thaw-progress-bar {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: var(--colorNeutralBackground6);
|
||||
overflow: hidden;
|
||||
border-radius: var(--borderRadiusMedium);
|
||||
}
|
||||
|
||||
.thaw-progress-bar__bar {
|
||||
transition-timing-function: ease;
|
||||
transition-duration: 0.3s;
|
||||
transition-property: width;
|
||||
height: 100%;
|
||||
background-color: var(--colorCompoundBrandBackground);
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
.thaw-progress-bar--error .thaw-progress-bar__bar {
|
||||
background-color: var(--colorPaletteRedBackground3);
|
||||
}
|
||||
|
||||
.thaw-progress-bar--warning .thaw-progress-bar__bar {
|
||||
background-color: var(--colorPaletteDarkOrangeBackground3);
|
||||
}
|
||||
|
||||
.thaw-progress-bar--success .thaw-progress-bar__bar {
|
||||
background-color: var(--colorPaletteGreenBackground3);
|
||||
}
|
||||
43
src/app/components/progress_bar.rs
Normal file
43
src/app/components/progress_bar.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use leptos::prelude::*;
|
||||
use thaw::ProgressBarColor;
|
||||
use thaw_utils::{class_list, mount_style};
|
||||
|
||||
#[component]
|
||||
pub fn ProgressBar1(
|
||||
#[prop(optional, into)] class: MaybeProp<String>,
|
||||
/// A decimal number between 0 and 1 (or between 0 and max if given),
|
||||
/// which specifies how much of the task has been completed.
|
||||
#[prop(into, optional)]
|
||||
value: Signal<i64>,
|
||||
/// The maximum value, which indicates the task is complete.
|
||||
/// The ProgressBar bar will be full when value equals max.
|
||||
#[prop(default = 100.into(), optional)]
|
||||
max: Signal<i64>,
|
||||
/// ProgressBar color.
|
||||
#[prop(into, optional)]
|
||||
color: Signal<ProgressBarColor>,
|
||||
) -> impl IntoView {
|
||||
mount_style("progress-bar", include_str!("./progress-bar.css"));
|
||||
|
||||
let style = move || {
|
||||
let max = max.get();
|
||||
let value = value.get().max(0).min(max);
|
||||
format!("width: {:.02}%;", (value as f64 / max as f64 * 100.0))
|
||||
};
|
||||
|
||||
view! {
|
||||
<div
|
||||
class=class_list![
|
||||
"thaw-progress-bar",
|
||||
move || format!("thaw-progress-bar--{}", color.get().as_str()),
|
||||
class
|
||||
]
|
||||
role="progressbar"
|
||||
aria_valuemax=move || max.get()
|
||||
aria-valuemin="0"
|
||||
aria-valuenow=move || value.get()
|
||||
>
|
||||
<div class="thaw-progress-bar__bar" style=style></div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::{
|
||||
app::{
|
||||
components::{FilePreview, ImageUpload, Upload1},
|
||||
models::Category,
|
||||
components::{FilePreview, ImageUpload, ProgressBar1, Upload1},
|
||||
pages::get_categories,
|
||||
},
|
||||
auth::{Logout, User},
|
||||
@@ -9,7 +8,10 @@ use crate::{
|
||||
};
|
||||
use chrono::prelude::*;
|
||||
use leptos::prelude::*;
|
||||
use leptos::{logging::log, task::spawn_local};
|
||||
use leptos::{
|
||||
logging::log,
|
||||
task::{spawn, spawn_local},
|
||||
};
|
||||
use thaw::*;
|
||||
use web_sys::js_sys::Uint8Array;
|
||||
|
||||
@@ -19,10 +21,37 @@ pub async fn go_upload(
|
||||
date: NaiveDate,
|
||||
photo: ImageUpload,
|
||||
) -> Result<(), ServerFnError> {
|
||||
// ..
|
||||
log!("{}", category);
|
||||
log!("{}", date);
|
||||
log!("{}", photo.name);
|
||||
use crate::app::ssr::{pool, s3};
|
||||
let client = s3()?;
|
||||
let pool = pool()?;
|
||||
|
||||
spawn(async move {
|
||||
// SHOULD ALL THIS CRAP BE MICROSERVICES?
|
||||
|
||||
// upload the original image
|
||||
// post process the image
|
||||
// upload post processed image
|
||||
|
||||
// store progress, probably in db or something?
|
||||
// so the user can come back to it later
|
||||
// this really need to be a seperate process from this point forward
|
||||
// not a sub-thread that gets aborted on a reload
|
||||
|
||||
let object = match client.list_objects_v2().bucket("airsoftfotos").send().await {
|
||||
Ok(obj) => obj,
|
||||
Err(e) => {
|
||||
log!("{:#?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
dbg!(object);
|
||||
log!("{}", photo.name);
|
||||
log!("{}", category);
|
||||
log!("{}", date);
|
||||
log!("{}", photo.name);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -66,19 +95,26 @@ pub fn Account(
|
||||
}
|
||||
};
|
||||
|
||||
let file_count = Signal::derive(move || files.get().len() as i64);
|
||||
let progress = RwSignal::new(0);
|
||||
let current = RwSignal::new(String::new());
|
||||
let disabled = RwSignal::new(false);
|
||||
|
||||
let upload = move |_| {
|
||||
// todo: rewrite this so it doesn't block frontend... idk why this happens
|
||||
disabled.set(true);
|
||||
|
||||
let category = selected_category.get().parse().unwrap();
|
||||
let date = date.get();
|
||||
let photos = files.get();
|
||||
|
||||
for file in photos {
|
||||
spawn_local(async move {
|
||||
let _ = dbg!(go_upload(category, date, file).await);
|
||||
});
|
||||
}
|
||||
spawn(async move {
|
||||
for file in photos {
|
||||
current.set(file.name.clone());
|
||||
let _ = go_upload(category, date, file).await;
|
||||
progress.update(|f| *f += 1);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
view! {
|
||||
@@ -140,7 +176,7 @@ pub fn Account(
|
||||
}>
|
||||
{ move || if files().is_empty() {
|
||||
view! {
|
||||
<div style="margin: auto;">"Sleep hier de fotos om te uploaden."</div>
|
||||
<div style="margin: auto;">"Sleep naar hier de fotos om deze voor te bereiden om te uploaden."</div>
|
||||
}.into_any()
|
||||
} else {
|
||||
files().into_iter().map(move |file| {
|
||||
@@ -154,6 +190,10 @@ pub fn Account(
|
||||
</Flex>
|
||||
</Upload1>
|
||||
</div>
|
||||
<div>
|
||||
<p>{progress}/{file_count}" - "{current}</p>
|
||||
<ProgressBar1 max=file_count value=progress />
|
||||
</div>
|
||||
|
||||
<Button button_type=ButtonType::Button disabled on_click=upload>
|
||||
"Upload"
|
||||
|
||||
@@ -26,6 +26,7 @@ pub fn Login(action: ServerAction<Login>) -> impl IntoView {
|
||||
name="email"
|
||||
/>
|
||||
<Input input_type=InputType::Password placeholder="Wachtwoord" name="password"/>
|
||||
<input type="checkbox" name="remember" /> Onthouden
|
||||
<Button button_type=ButtonType::Submit class="button">
|
||||
"Inloggen"
|
||||
</Button>
|
||||
|
||||
@@ -103,11 +103,14 @@ async fn main() -> anyhow::Result<()> {
|
||||
let addr = leptos_options.site_addr;
|
||||
let routes = generate_route_list(App);
|
||||
|
||||
let shared_config = aws_config::from_env().load().await;
|
||||
|
||||
let app_state = AppState {
|
||||
leptos_options,
|
||||
pool: pool.clone(),
|
||||
routes: routes.clone(),
|
||||
lettre: dangerous_lettre,
|
||||
aws_shared_config: shared_config,
|
||||
};
|
||||
|
||||
// build our application with a route
|
||||
|
||||
@@ -13,4 +13,5 @@ pub struct AppState {
|
||||
pub pool: SqlitePool,
|
||||
pub routes: Vec<AxumRouteListing>,
|
||||
pub lettre: DangerousLettre,
|
||||
pub aws_shared_config: aws_config::SdkConfig,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user