mirror of
https://github.com/kristoferssolo/cipher-workshop.git
synced 2026-01-14 04:36:04 +00:00
refactor(web): use AnyView return type for all components
This commit is contained in:
parent
6acf98bbae
commit
4c3d620250
41
Dockerfile
41
Dockerfile
@ -1,48 +1,51 @@
|
|||||||
|
# Compute recipe
|
||||||
FROM lukemathwalker/cargo-chef:latest-rust-1 AS chef
|
FROM lukemathwalker/cargo-chef:latest-rust-1 AS chef
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN cargo chef prepare --recipe-path recipe.json
|
RUN cargo chef prepare --recipe-path recipe.json
|
||||||
|
|
||||||
|
# Install tools and build dependencies
|
||||||
FROM rustlang/rust:nightly-bookworm AS cacher
|
FROM rustlang/rust:nightly-bookworm AS cacher
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install cargo-binstall
|
||||||
|
RUN curl -L https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz | tar -xz -C /usr/local/bin
|
||||||
|
|
||||||
RUN curl -LO https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz
|
|
||||||
RUN tar -xvf cargo-binstall-x86_64-unknown-linux-musl.tgz
|
|
||||||
RUN cp cargo-binstall /usr/local/cargo/bin
|
|
||||||
RUN cargo binstall cargo-leptos cargo-chef -y
|
RUN cargo binstall cargo-leptos cargo-chef -y
|
||||||
RUN apt-get update -y && apt-get install -y --no-install-recommends clang
|
RUN apt-get update -y && apt-get install -y --no-install-recommends clang
|
||||||
RUN rustup target add wasm32-unknown-unknown
|
RUN rustup target add wasm32-unknown-unknown
|
||||||
|
|
||||||
WORKDIR /app
|
# Cook dependencies
|
||||||
COPY --from=chef /app/recipe.json recipe.json
|
COPY --from=chef /app/recipe.json recipe.json
|
||||||
RUN cargo chef cook --release --recipe-path recipe.json
|
RUN cargo chef cook --release --recipe-path recipe.json
|
||||||
|
|
||||||
|
# Actual build
|
||||||
FROM rustlang/rust:nightly-bookworm AS builder
|
FROM rustlang/rust:nightly-bookworm AS builder
|
||||||
|
|
||||||
RUN wget https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz
|
|
||||||
RUN tar -xvf cargo-binstall-x86_64-unknown-linux-musl.tgz
|
|
||||||
RUN cp cargo-binstall /usr/local/cargo/bin
|
|
||||||
RUN cargo binstall cargo-leptos -y
|
|
||||||
RUN apt-get update -y && apt-get install -y --no-install-recommends clang
|
|
||||||
RUN rustup target add wasm32-unknown-unknown
|
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy the tools from the cacher stage
|
||||||
|
COPY --from=cacher /usr/local/rustup /usr/local/rustup
|
||||||
|
COPY --from=cacher /usr/local/cargo /usr/local/cargo
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends clang
|
||||||
|
|
||||||
|
# Bring in the cooked dependencies
|
||||||
COPY --from=cacher /app/target target
|
COPY --from=cacher /app/target target
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
# Build the Leptos app
|
||||||
RUN cargo leptos build --release -vv
|
RUN cargo leptos build --release -vv
|
||||||
|
|
||||||
|
# Runtime
|
||||||
FROM debian:bookworm-slim AS runtime
|
FROM debian:bookworm-slim AS runtime
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
RUN apt-get update -y \
|
RUN apt-get update && apt-get install -y --no-install-recommends openssl ca-certificates \
|
||||||
&& apt-get install -y --no-install-recommends openssl ca-certificates \
|
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||||
&& apt-get autoremove -y \
|
|
||||||
&& apt-get clean -y \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
|
# Copy binaries and assets
|
||||||
COPY --from=builder /app/target/release/web /app/
|
COPY --from=builder /app/target/release/web /app/
|
||||||
COPY --from=builder /app/target/site /app/site
|
COPY --from=builder /app/target/site /app/site
|
||||||
COPY --from=builder /app/Cargo.toml /app/
|
COPY --from=builder /app/Cargo.toml /app/
|
||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
CMD ["/app/web"]
|
CMD ["/app/web"]
|
||||||
|
|||||||
@ -10,7 +10,7 @@ use leptos::prelude::*;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn CipherForm(algorithm: Algorithm) -> impl IntoView {
|
pub fn CipherForm(algorithm: Algorithm) -> AnyView {
|
||||||
let (mode, set_mode) = signal(OperationMode::Encrypt);
|
let (mode, set_mode) = signal(OperationMode::Encrypt);
|
||||||
let (output_fmt, set_output_fmt) = signal(OutputFormat::Hex);
|
let (output_fmt, set_output_fmt) = signal(OutputFormat::Hex);
|
||||||
|
|
||||||
@ -101,4 +101,5 @@ pub fn CipherForm(algorithm: Algorithm) -> impl IntoView {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
.into_any()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ use wasm_bindgen::JsCast;
|
|||||||
use web_sys::{Blob, Url};
|
use web_sys::{Blob, Url};
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn CipherFormCbc() -> impl IntoView {
|
pub fn CipherFormCbc() -> AnyView {
|
||||||
let (mode, set_mode) = signal(OperationMode::Encrypt);
|
let (mode, set_mode) = signal(OperationMode::Encrypt);
|
||||||
let (output_fmt, set_output_fmt) = signal(OutputFormat::Hex);
|
let (output_fmt, set_output_fmt) = signal(OutputFormat::Hex);
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ pub fn CipherFormCbc() -> impl IntoView {
|
|||||||
let formatted_iv = format!("0x{iv}");
|
let formatted_iv = format!("0x{iv}");
|
||||||
|
|
||||||
// Get input data
|
// Get input data
|
||||||
let input_data: Vec<u8> = match input_mode.get() {
|
let input_data = match input_mode.get() {
|
||||||
InputMode::Text => {
|
InputMode::Text => {
|
||||||
let text = text_content.get();
|
let text = text_content.get();
|
||||||
if text.is_empty() {
|
if text.is_empty() {
|
||||||
@ -192,7 +192,10 @@ pub fn CipherFormCbc() -> impl IntoView {
|
|||||||
<div class="result-toolbar">
|
<div class="result-toolbar">
|
||||||
<strong>"Output ("{output_fmt.get().to_string()}")"</strong>
|
<strong>"Output ("{output_fmt.get().to_string()}")"</strong>
|
||||||
<div class="result-actions">
|
<div class="result-actions">
|
||||||
<button class="btn-copy" on:click=move |_| copy_to_clipboard(output.get())>
|
<button
|
||||||
|
class="btn-copy"
|
||||||
|
on:click=move |_| copy_to_clipboard(output.get())
|
||||||
|
>
|
||||||
{move || if copy_feedback.get() { "Copied" } else { "Copy" }}
|
{move || if copy_feedback.get() { "Copied" } else { "Copy" }}
|
||||||
</button>
|
</button>
|
||||||
{move || {
|
{move || {
|
||||||
@ -201,7 +204,8 @@ pub fn CipherFormCbc() -> impl IntoView {
|
|||||||
<button class="btn-download" on:click=download_output>
|
<button class="btn-download" on:click=download_output>
|
||||||
"Download"
|
"Download"
|
||||||
</button>
|
</button>
|
||||||
}.into_any()
|
}
|
||||||
|
.into_any()
|
||||||
} else {
|
} else {
|
||||||
view! { <span></span> }.into_any()
|
view! { <span></span> }.into_any()
|
||||||
}
|
}
|
||||||
@ -209,21 +213,25 @@ pub fn CipherFormCbc() -> impl IntoView {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="result-content">
|
<div class="result-content">
|
||||||
<code>{move || {
|
<code>
|
||||||
let out = output.get();
|
{move || {
|
||||||
if out.len() > 1000 {
|
let out = output.get();
|
||||||
format!("{}... ({} chars total)", &out[..1000], out.len())
|
if out.len() > 1000 {
|
||||||
} else {
|
format!("{}... ({} chars total)", &out[..1000], out.len())
|
||||||
out
|
} else {
|
||||||
}
|
out
|
||||||
}}</code>
|
}
|
||||||
|
}}
|
||||||
|
</code>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}.into_any()
|
}
|
||||||
|
.into_any()
|
||||||
}}
|
}}
|
||||||
<ErrorBox error_msg=error_msg />
|
<ErrorBox error_msg=error_msg />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
.into_any()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_hex_string(s: &str) -> Result<Vec<u8>, String> {
|
fn parse_hex_string(s: &str) -> Result<Vec<u8>, String> {
|
||||||
@ -234,7 +242,7 @@ fn parse_hex_string(s: &str) -> Result<Vec<u8>, String> {
|
|||||||
.unwrap_or(trimmed);
|
.unwrap_or(trimmed);
|
||||||
|
|
||||||
// Remove whitespace and newlines
|
// Remove whitespace and newlines
|
||||||
let s: String = s.chars().filter(|c| !c.is_whitespace()).collect();
|
let s = s.chars().filter(|c| !c.is_whitespace()).collect::<String>();
|
||||||
|
|
||||||
if !s.len().is_multiple_of(2) {
|
if !s.len().is_multiple_of(2) {
|
||||||
return Err("Hex string must have even length".to_string());
|
return Err("Hex string must have even length".to_string());
|
||||||
@ -282,10 +290,7 @@ fn download_bytes(bytes: &[u8], filename: &str) {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(window) = web_sys::window() else {
|
let Some(document) = window().document() else {
|
||||||
return;
|
|
||||||
};
|
|
||||||
let Some(document) = window.document() else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let Some(a) = document.create_element("a").ok() else {
|
let Some(a) = document.create_element("a").ok() else {
|
||||||
@ -295,7 +300,7 @@ fn download_bytes(bytes: &[u8], filename: &str) {
|
|||||||
let _ = a.set_attribute("href", &url);
|
let _ = a.set_attribute("href", &url);
|
||||||
let _ = a.set_attribute("download", filename);
|
let _ = a.set_attribute("download", filename);
|
||||||
|
|
||||||
let a: web_sys::HtmlElement = a.unchecked_into();
|
let a = a.unchecked_into::<web_sys::HtmlElement>();
|
||||||
a.click();
|
a.click();
|
||||||
|
|
||||||
let _ = Url::revoke_object_url(&url);
|
let _ = Url::revoke_object_url(&url);
|
||||||
|
|||||||
@ -10,7 +10,7 @@ pub fn ConfigurationSection(
|
|||||||
set_mode: WriteSignal<OperationMode>,
|
set_mode: WriteSignal<OperationMode>,
|
||||||
output_fmt: ReadSignal<OutputFormat>,
|
output_fmt: ReadSignal<OutputFormat>,
|
||||||
update_output: impl Fn(OutputFormat) + Copy + Send + 'static,
|
update_output: impl Fn(OutputFormat) + Copy + Send + 'static,
|
||||||
) -> impl IntoView {
|
) -> AnyView {
|
||||||
let handle_format_change = move |ev| {
|
let handle_format_change = move |ev| {
|
||||||
let val = event_target_value(&ev);
|
let val = event_target_value(&ev);
|
||||||
let fmt = OutputFormat::from_str(&val).unwrap_or_default();
|
let fmt = OutputFormat::from_str(&val).unwrap_or_default();
|
||||||
@ -83,4 +83,5 @@ pub fn ConfigurationSection(
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
.into_any()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,7 +41,7 @@ fn read_file(
|
|||||||
fn InputModeToggle(
|
fn InputModeToggle(
|
||||||
input_mode: ReadSignal<InputMode>,
|
input_mode: ReadSignal<InputMode>,
|
||||||
set_input_mode: WriteSignal<InputMode>,
|
set_input_mode: WriteSignal<InputMode>,
|
||||||
) -> impl IntoView {
|
) -> AnyView {
|
||||||
view! {
|
view! {
|
||||||
<div class="input-mode-toggle">
|
<div class="input-mode-toggle">
|
||||||
<button
|
<button
|
||||||
@ -64,6 +64,7 @@ fn InputModeToggle(
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
.into_any()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
@ -71,7 +72,7 @@ fn TextAreaInput(
|
|||||||
text_content: ReadSignal<String>,
|
text_content: ReadSignal<String>,
|
||||||
set_text_content: WriteSignal<String>,
|
set_text_content: WriteSignal<String>,
|
||||||
is_decrypt_mode: Memo<bool>,
|
is_decrypt_mode: Memo<bool>,
|
||||||
) -> impl IntoView {
|
) -> AnyView {
|
||||||
let handle_text_change = move |ev: Event| {
|
let handle_text_change = move |ev: Event| {
|
||||||
if let Some(target) = ev.target() {
|
if let Some(target) = ev.target() {
|
||||||
let textarea: web_sys::HtmlTextAreaElement = target.unchecked_into();
|
let textarea: web_sys::HtmlTextAreaElement = target.unchecked_into();
|
||||||
@ -99,6 +100,7 @@ fn TextAreaInput(
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
.into_any()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
@ -107,7 +109,7 @@ fn FileDropZone(
|
|||||||
set_file_data: WriteSignal<Option<Vec<u8>>>,
|
set_file_data: WriteSignal<Option<Vec<u8>>>,
|
||||||
file_name: ReadSignal<Option<String>>,
|
file_name: ReadSignal<Option<String>>,
|
||||||
set_file_name: WriteSignal<Option<String>>,
|
set_file_name: WriteSignal<Option<String>>,
|
||||||
) -> impl IntoView {
|
) -> AnyView {
|
||||||
let (is_dragging, set_is_dragging) = signal(false);
|
let (is_dragging, set_is_dragging) = signal(false);
|
||||||
|
|
||||||
let handle_file_change = move |ev: Event| {
|
let handle_file_change = move |ev: Event| {
|
||||||
@ -193,6 +195,7 @@ fn FileDropZone(
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
.into_any()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
@ -201,7 +204,7 @@ fn FileSelected(
|
|||||||
file_data: ReadSignal<Option<Vec<u8>>>,
|
file_data: ReadSignal<Option<Vec<u8>>>,
|
||||||
set_file_name: WriteSignal<Option<String>>,
|
set_file_name: WriteSignal<Option<String>>,
|
||||||
set_file_data: WriteSignal<Option<Vec<u8>>>,
|
set_file_data: WriteSignal<Option<Vec<u8>>>,
|
||||||
) -> impl IntoView {
|
) -> AnyView {
|
||||||
let clear_file = move |ev: web_sys::MouseEvent| {
|
let clear_file = move |ev: web_sys::MouseEvent| {
|
||||||
ev.prevent_default();
|
ev.prevent_default();
|
||||||
ev.stop_propagation();
|
ev.stop_propagation();
|
||||||
@ -221,16 +224,18 @@ fn FileSelected(
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
.into_any()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
fn FilePlaceholder() -> impl IntoView {
|
fn FilePlaceholder() -> AnyView {
|
||||||
view! {
|
view! {
|
||||||
<div class="file-placeholder">
|
<div class="file-placeholder">
|
||||||
<span class="upload-icon">"[+]"</span>
|
<span class="upload-icon">"[+]"</span>
|
||||||
<span>"Click to select a file or drag and drop"</span>
|
<span>"Click to select a file or drag and drop"</span>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
.into_any()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
@ -244,7 +249,7 @@ pub fn FileTextInput(
|
|||||||
file_name: ReadSignal<Option<String>>,
|
file_name: ReadSignal<Option<String>>,
|
||||||
set_file_name: WriteSignal<Option<String>>,
|
set_file_name: WriteSignal<Option<String>>,
|
||||||
is_decrypt_mode: Memo<bool>,
|
is_decrypt_mode: Memo<bool>,
|
||||||
) -> impl IntoView {
|
) -> AnyView {
|
||||||
view! {
|
view! {
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="label-header">
|
<div class="label-header">
|
||||||
@ -281,4 +286,5 @@ pub fn FileTextInput(
|
|||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
.into_any()
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user