From a18c4afdb75e82dbef4af4658eadc4a618472c98 Mon Sep 17 00:00:00 2001 From: Miroito Date: Sun, 23 Apr 2023 11:27:50 +0200 Subject: [PATCH] feat/Make API_URL a runtime config in the client --- Cargo.lock | 145 ++++---------- client/Cargo.toml | 6 +- client/Dockerfile | 46 ++--- client/src/api/mod.rs | 15 ++ client/src/api/routes/mod.rs | 1 + client/src/api/routes/transaction.rs | 189 +++++++++--------- client/src/components/base_async_select.rs | 31 ++- client/src/components/paginated_data_table.rs | 35 ++-- client/src/env.rs | 18 +- client/src/global_state.rs | 33 ++- client/src/templates/index.rs | 23 ++- client/src/templates/transactions.rs | 27 ++- docker-compose.yml | 7 +- server/Dockerfile | 15 +- server/src/route/transaction.rs | 2 +- 15 files changed, 264 insertions(+), 329 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 374be4c..86dc8c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,12 +60,6 @@ dependencies = [ "libc", ] -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - [[package]] name = "arrayvec" version = "0.7.2" @@ -253,9 +247,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.6.12" +version = "0.6.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f8ccfd9221ee7d1f3d4b33e1f8319b3a81ed8f61f2ea40b37b859794b4491" +checksum = "113713495a32dd0ab52baf5c10044725aa3aec00b31beda84218e469029b72a3" dependencies = [ "async-trait", "axum-core", @@ -285,9 +279,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2f958c80c248b34b9a877a643811be8dbca03ca5ba827f2b63baf3a81e5fc4e" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" dependencies = [ "async-trait", "bytes", @@ -354,18 +348,6 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" -[[package]] -name = "bitvec" -version = "0.19.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - [[package]] name = "block-buffer" version = "0.10.3" @@ -689,13 +671,13 @@ dependencies = [ [[package]] name = "css-minify" -version = "0.2.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692b185e3b7c9af96b3195f3021f53a931d896968ed2ad3fb1cdb6558b30c9ab" +checksum = "874c6e2d19f8d4a285083b11a3241bfbe01ac3ed85f26e1e6b34888d960552bd" dependencies = [ "derive_more", "indexmap", - "nom 6.1.2", + "nom", ] [[package]] @@ -754,9 +736,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.13.4" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ "darling_core", "darling_macro", @@ -764,9 +746,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.13.4" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" dependencies = [ "fnv", "ident_case", @@ -778,9 +760,9 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.13.4" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ "darling_core", "quote", @@ -1033,12 +1015,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" -[[package]] -name = "funty" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" - [[package]] name = "futures" version = "0.3.26" @@ -1222,9 +1198,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.15" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "17f8a914c2987b688368b5138aa05321db91f4090cf26118185672ad588bce21" dependencies = [ "bytes", "fnv", @@ -1368,9 +1344,9 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.25" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -1523,19 +1499,6 @@ dependencies = [ "spin", ] -[[package]] -name = "lexical-core" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" -dependencies = [ - "arrayvec 0.5.2", - "bitflags", - "cfg-if", - "ryu", - "static_assertions", -] - [[package]] name = "libc" version = "0.2.139" @@ -1648,22 +1611,23 @@ dependencies = [ [[package]] name = "minify-html-onepass" -version = "0.10.1" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d552bca3e100fe7835371eba6b37c83a857a6fe5f400e176dc15a28403e0d1c" +checksum = "c89548d0be6d3c7295335473fbe5c021fde64de738e01312301c90b9f1dd8476" dependencies = [ "aho-corasick", "css-minify", "lazy_static", "memchr", "minify-js", + "rustc-hash", ] [[package]] name = "minify-js" -version = "0.2.9" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe033709f5a1159736cf7e22748518ffb75af26f3a6264d52ecc8bb38c68c36" +checksum = "c300f90ba1138b5c5daf5d9441dc9bdc67b808aac22cf638362a2647bc213be4" dependencies = [ "lazy_static", "parse-js", @@ -1714,19 +1678,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "nom" -version = "6.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7413f999671bd4745a7b624bd370a569fb6bc574b23c83a3c5ed2e453f3d5e2" -dependencies = [ - "bitvec", - "funty", - "lexical-core", - "memchr", - "version_check", -] - [[package]] name = "nom" version = "7.1.3" @@ -1958,9 +1909,9 @@ dependencies = [ [[package]] name = "parse-js" -version = "0.3.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66bb85ec60d22b9e6d4adac1e3dbdaf3903a4485f476c5f4dd7ed1285cbf4dad" +checksum = "30534759e6ad87aa144c396544747e1c25b1020bd133356fd758c8facec764e5" dependencies = [ "aho-corasick", "lazy_static", @@ -1990,9 +1941,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "perseus" -version = "0.4.0-beta.22" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3bf9f8bbc387bcc1b5e69e380b78aa598777e1b44872816ed86fc599aa9729" +checksum = "c692ad07b59511f3f2faac6ae47136b0f7f28fe92a962da60459b659ddf947ca" dependencies = [ "async-trait", "chrono", @@ -2021,9 +1972,9 @@ dependencies = [ [[package]] name = "perseus-axum" -version = "0.4.0-beta.22" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca2d139e1eb507cca8ffcda8878f1da174285058843bf75d4f8c64b7016d8a8" +checksum = "58e7975a5d967d3a1e39ffe479b58588e1db03e8ec03b2666e633cfd07793bc0" dependencies = [ "axum", "perseus", @@ -2032,9 +1983,9 @@ dependencies = [ [[package]] name = "perseus-macro" -version = "0.4.0-beta.22" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5620bc354c480258abe55f2bcdaa5569d584a5b76fb7a1dc4fb34a5d5bd9dcf" +checksum = "868803376c686cfe84dc5a84114ea631bd561f243f1a48115c4418b19c9d199b" dependencies = [ "darling", "proc-macro2", @@ -2221,12 +2172,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "radium" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8" - [[package]] name = "rand" version = "0.8.5" @@ -2441,7 +2386,7 @@ version = "1.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13cf35f7140155d02ba4ec3294373d513a3c7baa8364c162b030e33c61520a8" dependencies = [ - "arrayvec 0.7.2", + "arrayvec", "borsh", "bytecheck", "byteorder", @@ -2453,6 +2398,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc_version" version = "0.2.3" @@ -2951,7 +2902,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" dependencies = [ "itertools", - "nom 7.1.3", + "nom", "unicode_categories", ] @@ -3059,12 +3010,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "stdweb" version = "0.4.20" @@ -3225,7 +3170,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92914a2f809b636d245b28d8a734801ecb8ff9c4996bbe6ea4176582e12503eb" dependencies = [ - "nom 7.1.3", + "nom", "proc-macro2", "quote", "syn 1.0.108", @@ -3276,12 +3221,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - [[package]] name = "tempfile" version = "3.3.0" @@ -4064,12 +4003,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "wyz" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" - [[package]] name = "zeroize" version = "1.5.7" diff --git a/client/Cargo.toml b/client/Cargo.toml index f5a7ccb..4d24c97 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" chrono = { workspace = true, features = ["serde"] } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } -perseus = { version = "0.4.0-beta.22", features = ["hydrate"] } +perseus = { version = "0.4", features = ["hydrate"] } sycamore = { version = "^0.8.1", features = [ "ssr", "serde", @@ -23,9 +23,7 @@ fantoccini = "^0.19.3" [target.'cfg(engine)'.dependencies] tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread"] } reqwest = { version = "0.11", features = ["json", "rustls-tls"] } -perseus-axum = { version = "=0.4.0-beta.22", features = ["dflt-server"] } -# dotenvy = { workspace = true } -# envy = { workspace = true } +perseus-axum = { version = "0.4", features = ["dflt-server"] } [target.'cfg(client)'.dependencies] reqwasm = "0.5.0" diff --git a/client/Dockerfile b/client/Dockerfile index 30d5469..5973db4 100644 --- a/client/Dockerfile +++ b/client/Dockerfile @@ -1,13 +1,13 @@ # get the base image -FROM rust:1.66-slim AS build +FROM rust:1.69 AS build # install build dependencies -RUN apt update \ - && apt install -y --no-install-recommends lsb-release apt-transport-https \ - build-essential curl wget pkg-config libssl-dev +RUN apt-get update \ + && apt-get install -y --no-install-recommends lsb-release apt-transport-https \ + build-essential curl wget pkg-config # vars -ENV PERSEUS_VERSION=0.4.0-beta.22 \ +ENV PERSEUS_VERSION=0.4.0 \ PERSEUS_SIZE_OPT_VERSION=0.1.9 \ ESBUILD_VERSION=0.15.18 \ BINARYEN_VERSION=112 @@ -16,10 +16,8 @@ ENV PERSEUS_VERSION=0.4.0-beta.22 \ WORKDIR /app # download the target for wasm -RUN rustup target add wasm32-unknown-unknown - -# install wasm-pack -RUN cargo install wasm-pack +RUN rustup target add wasm32-unknown-unknown;\ + cargo install wasm-pack # might not be a bad idea to fix the version of this in the future # retrieve the src dir COPY . . @@ -29,25 +27,19 @@ COPY . . WORKDIR /app/client # install perseus-cli -RUN cargo install perseus-cli --version $PERSEUS_VERSION - -# clean and prep app -RUN perseus clean +RUN cargo install perseus-cli --version $PERSEUS_VERSION;\ + perseus clean # specify deps in app config -RUN sed -i s"/perseus = .*/perseus = \"${PERSEUS_VERSION}\"/" ./Cargo.toml \ - && sed -i s"/perseus-size-opt = .*/perseus-size-opt = \"${PERSEUS_SIZE_OPT_VERSION}\"/" ./Cargo.toml \ - && cat ./Cargo.toml - -# modify main.rs -RUN sed -i s'/SizeOpts::default()/SizeOpts { wee_alloc: true, lto: true, opt_level: "s".to_string(), codegen_units: 1, enable_fluent_bundle_patch: false, }/' ./src/main.rs \ - && cat ./src/main.rs - -ARG API_URL -ENV API_URL $API_URL - -# run plugin(s) to adjust app -RUN perseus tinker +# RUN sed -i s"/perseus = .*/perseus = \"${PERSEUS_VERSION}\"/" ./Cargo.toml \ +# && sed -i s"/perseus-size-opt = .*/perseus-size-opt = \"${PERSEUS_SIZE_OPT_VERSION}\"/" ./Cargo.toml \ +# && cat ./Cargo.toml +# +# # modify main.rs +# RUN sed -i s'/SizeOpts::default()/SizeOpts { wee_alloc: true, lto: true, opt_level: "z".to_string(), codegen_units: 1, enable_fluent_bundle_patch: false, }/' ./src/main.rs \ +# && cat ./src/main.rs +# # run plugin(s) to adjust app +# RUN perseus tinker # single-threaded perseus CLI mode required for low memory environments #ENV PERSEUS_CLI_SEQUENTIAL=true @@ -84,4 +76,4 @@ COPY --from=build /app/client/pkg /app/ ENV HOST=0.0.0.0 -CMD ./server +CMD ["./server"] diff --git a/client/src/api/mod.rs b/client/src/api/mod.rs index a061da8..093a69f 100644 --- a/client/src/api/mod.rs +++ b/client/src/api/mod.rs @@ -1,2 +1,17 @@ +use serde::{Deserialize, Serialize}; + pub mod routes; pub mod types; + +#[derive(Clone, Serialize, Deserialize)] +pub struct FastInsidersApi { + url: String, +} + +impl FastInsidersApi { + pub fn new(url: &str) -> Self { + FastInsidersApi { + url: url.to_owned(), + } + } +} diff --git a/client/src/api/routes/mod.rs b/client/src/api/routes/mod.rs index 37f0806..d520402 100644 --- a/client/src/api/routes/mod.rs +++ b/client/src/api/routes/mod.rs @@ -1 +1,2 @@ +pub mod company; pub mod transaction; diff --git a/client/src/api/routes/transaction.rs b/client/src/api/routes/transaction.rs index 0eda605..03bd5ce 100644 --- a/client/src/api/routes/transaction.rs +++ b/client/src/api/routes/transaction.rs @@ -1,109 +1,108 @@ -use crate::api::types::{ - paginated_response::PaginatedResponse, - transaction::{LatestTransaction, TransactionCompany, TransactionsAggregated}, +use crate::api::{ + types::{ + paginated_response::PaginatedResponse, + transaction::{LatestTransaction, TransactionCompany, TransactionsAggregated}, + }, + FastInsidersApi, }; -pub async fn get_transactions( - company_slug: Option, - page: i64, - size: i64, -) -> Result, ()> { - use crate::env::Config; +impl FastInsidersApi { + pub async fn get_transactions( + &self, + company_slug: Option, + page: i64, + size: i64, + ) -> Result, ()> { + let route = &format!( + "{}/transaction?{}&page={}&size={}", + self.url, + company_slug.map_or("".to_string(), |c| format!("company_slug={}", c)), + page, + size, + ); - // TODO: Remove build-time environment variable - let api_url = Config::new().api_url; - let route = &format!( - "{}transaction?{}&page={}&size={}", - api_url, - company_slug.map_or("".to_string(), |c| format!("company_slug={}", c)), - page, - size, - ); + #[cfg(client)] + let res = reqwasm::http::Request::get(route) + .send() + .await + .map_err(|_| ())? + .json::>() + .await + .map_err(|_| ())?; - #[cfg(client)] - let res = reqwasm::http::Request::get(route) - .send() - .await - .map_err(|_| ())? - .json::>() - .await - .map_err(|_| ())?; + #[cfg(engine)] + let res = reqwest::get(route) + .await + .map_err(|_| ())? + .json::>() + .await + .map_err(|_| ())?; - #[cfg(engine)] - let res = reqwest::get(route) - .await - .map_err(|_| ())? - .json::>() - .await - .map_err(|_| ())?; + return Ok(res); + } - return Ok(res); -} - -pub async fn get_aggregated_transactions( - hours: Option, - page: i64, - size: i64, -) -> Result, ()> { - use crate::env::Config; - - // TODO: Remove build-time environment variable - let api_url = Config::new().api_url; - let route = &format!( - "{}transaction/aggregated?{}&page={}&size={}", - api_url, - hours.map_or("".to_string(), |c| format!("hours={}", c)), - page, - size, - ); + pub async fn get_aggregated_transactions( + &self, + hours: Option, + page: i64, + size: i64, + ) -> Result, ()> { + let route = &format!( + "{}/transaction/aggregated?{}&page={}&size={}", + self.url, + hours.map_or("".to_string(), |c| format!("hours={}", c)), + page, + size, + ); - #[cfg(client)] - let res = reqwasm::http::Request::get(route) - .send() - .await - .map_err(|_| ())? - .json::>() - .await - .map_err(|_| ())?; + #[cfg(client)] + let res = reqwasm::http::Request::get(route) + .send() + .await + .map_err(|_| ())? + .json::>() + .await + .map_err(|_| ())?; - #[cfg(engine)] - let res = reqwest::get(route) - .await - .map_err(|_| ())? - .json::>() - .await - .map_err(|_| ())?; - - return Ok(res); -} + #[cfg(engine)] + let res = reqwest::get(route) + .await + .map_err(|_| ())? + .json::>() + .await + .map_err(|_| ())?; -pub async fn get_latest_transactions( - _: Option, - page: i64, - size: i64, -) -> Result, ()> { - use crate::env::Config; + return Ok(res); + } - // TODO: Remove build-time environment variable - let api_url = Config::new().api_url; - let route = &format!("{}transaction/latest?page={}&size={}", api_url, page, size,); + pub async fn get_latest_transactions( + &self, + _: Option, + page: i64, + size: i64, + ) -> Result, ()> { + let route = &format!( + "{}/transaction/latest?page={}&size={}", + self.url, page, size, + ); - #[cfg(client)] - let res = reqwasm::http::Request::get(route) - .send() - .await - .map_err(|_| ())? - .json::>() - .await - .map_err(|_| ())?; + #[cfg(client)] + let res = reqwasm::http::Request::get(route) + .send() + .await + .map_err(|_| ())? + .json::>() + .await + .map_err(|_| ())?; - #[cfg(engine)] - let res = reqwest::get(route) - .await - .map_err(|_| ())? - .json::>() - .await - .map_err(|_| ())?; + #[cfg(engine)] + let res = reqwest::get(route) + .await + .map_err(|_| ())? + .json::>() + .await + .map_err(|_| ())?; - return Ok(res); + return Ok(res); + } } diff --git a/client/src/components/base_async_select.rs b/client/src/components/base_async_select.rs index fab4d1f..03a5529 100644 --- a/client/src/components/base_async_select.rs +++ b/client/src/components/base_async_select.rs @@ -1,3 +1,5 @@ +use std::future::Future; + use perseus::prelude::*; use serde::Deserialize; use sycamore::prelude::*; @@ -7,19 +9,23 @@ pub trait IntoAsyncSelectListItem { } #[derive(Prop)] -pub struct AsyncSelectRx<'a, T> +pub struct AsyncSelectRx<'a, T, F, R> where T: 'static + PartialEq + Clone + IntoAsyncSelectListItem, + F: Future, ()>>, + R: Fn(String, Option) -> F, { - pub remote_list: &'a ReadSignal, + pub route: &'a R, pub selected_item: &'a Signal>, } #[component] -pub fn BaseAsyncSelect<'a, G, T>(cx: Scope<'a>, props: AsyncSelectRx<'a, T>) -> View +pub fn BaseAsyncSelect<'a, G, T, F, R>(cx: Scope<'a>, props: AsyncSelectRx<'a, T, F, R>) -> View where G: Html, - T: 'static + PartialEq + Clone + IntoAsyncSelectListItem, + T: 'a + PartialEq + Clone + IntoAsyncSelectListItem, + F: Future, ()>>, + R: Fn(String, Option) -> F, for<'de> T: Deserialize<'de>, { let input = create_signal(cx, "".to_string()); @@ -47,18 +53,9 @@ where #[cfg(client)] spawn_local_scoped(cx, async move { - let res = reqwasm::http::Request::get(&format!( - "{}/{}?limit={}", - props.remote_list.get(), - input.get(), - 5 - )) - .send() - .await - .unwrap() - .json::>() - .await - .unwrap(); + let res = (props.route)(input.get().to_string(), Some(5)) + .await + .unwrap(); visible.set(!res.is_empty()); item_list.set(res); checkpoint("async_select_item_change"); @@ -76,7 +73,7 @@ where let item = x.clone(); view! {cx, li ( - class="w-full p-2 cursor-pointer dark:hover:bg-slate-900 hover:bg-slate-400", + class="p-2 w-full cursor-pointer dark:hover:bg-slate-900 hover:bg-slate-400", on:mousedown=move |_| { selected.set(true); props.selected_item.set(Some(item.clone())); diff --git a/client/src/components/paginated_data_table.rs b/client/src/components/paginated_data_table.rs index 0f68df9..34ea068 100644 --- a/client/src/components/paginated_data_table.rs +++ b/client/src/components/paginated_data_table.rs @@ -13,28 +13,29 @@ use crate::{ #[derive(Prop)] pub struct PaginatedTableStateRx<'a, M, F, C> where - M: 'static, + M: 'a, C: Fn(Option, i64, i64) -> F, - F: std::future::Future, ()>>, + F: std::future::Future, ()>> + 'a, { pub record_label: String, - pub route: C, + pub route: &'a C, pub filter: Option, pub table_class: &'a String, } impl<'a, M, F, C> PaginatedTableStateRx<'a, M, F, C> where - M: 'static, + M: 'a, C: Fn(Option, i64, i64) -> F, - F: std::future::Future, ()>>, + F: std::future::Future, ()>> + 'a, { async fn get_data(&self, page: i64, size: i64) -> Result, ()> { (self.route)(self.filter.clone(), page, size).await } } -/// This is a generic component that will display a paginated table given a function of the generic signature C and a filter represented by an Option +/// This is a generic component that will display a paginated table given a +/// function of the generic signature C and a filter represented by an Option #[component] pub fn PaginatedTable<'a, G, M, F, C>( cx: Scope<'a>, @@ -42,11 +43,11 @@ pub fn PaginatedTable<'a, G, M, F, C>( ) -> View where G: Html, - M: 'static + Clone, + M: 'a + Clone, PaginatedResponse: IntoTableData, for<'de> M: Deserialize<'de>, - C: Fn(Option, i64, i64) -> F + 'static, - F: std::future::Future, ()>> + 'static, + C: Fn(Option, i64, i64) -> F, + F: std::future::Future, ()>> + 'a, { let paginated_data = create_signal(cx, None); let table_prop: TableContentRx = TableContentRx { @@ -81,11 +82,11 @@ where page.set(0); }); let props_sig = create_signal(cx, props); + #[cfg(client)] create_effect(cx, move || { let page = *page.get(); let page_size_s = page_size_string.get(); let page_size = page_size_s.parse().unwrap_or(20); - #[cfg(client)] spawn_local_scoped(cx, async move { let res = props_sig.get().get_data(page, page_size).await.unwrap(); paginated_data.set(Some(res.clone())); @@ -101,7 +102,7 @@ where (if paginated_data.get().is_some() { if *n_rows.get() == 0 { view!{cx, - div (class="bg-slate-200 dark:bg-slate-800 text-center rounded-md") { + div (class="text-center rounded-md bg-slate-200 dark:bg-slate-800") { (format!("No {}", props_sig.get().record_label)) } } @@ -111,7 +112,7 @@ where p (class="text-right") { (format!("{} {}", n_rows.get(), props_sig.get().record_label)) } div (class="flex flex-row justify-between") { select (bind:value=page_size_string, - class="p-2 justify-end text-slate-700 dark:text-slate-100 bg-slate-200 dark:bg-slate-800 rounded-md", + class="justify-end p-2 rounded-md text-slate-700 bg-slate-200 dark:text-slate-100 dark:bg-slate-800", id="size-select", ) { option (value="10", selected=(*page_size_string.get()).eq("10")) { "10" } @@ -120,11 +121,11 @@ where option (value="40", selected=(*page_size_string.get()).eq("40")) { "40" } option (value="50", selected=(*page_size_string.get()).eq("50")) { "50" } } - div (id="page_buttons", class="flex flex-row p-2 bg-slate-200 dark:bg-slate-800 rounded-md") { + div (id="page_buttons", class="flex flex-row p-2 rounded-md bg-slate-200 dark:bg-slate-800") { button (on:click=page_down,class="m-1 hover:font-bold") { "<<" } - div (class="m-1 align-middle text-center") { + div (class="m-1 text-center align-middle") { (format!("{}/{}",*page.get() + 1, *n_page.get()) ) } button (on:click=page_up, class="m-1 hover:font-bold") { @@ -137,9 +138,9 @@ where } } else { view! {cx, - div (class="flex flex-row justify-center") { - Loading() - } + div (class="flex flex-row justify-center") { + Loading() + } } }) } diff --git a/client/src/env.rs b/client/src/env.rs index 41e3dce..ca12882 100644 --- a/client/src/env.rs +++ b/client/src/env.rs @@ -10,22 +10,8 @@ pub struct Config { #[cfg(engine)] impl Config { pub fn new() -> Self { - let api_url = env!("API_URL").to_string(); + let api_url = + std::env::var("API_URL").expect("The environment variable API_URL should be set"); Config { api_url } } } - -#[cfg(client)] -impl Config { - pub fn new() -> Self { - let api_url = env!("API_URL").to_string(); - Config { api_url } - } -} - -#[cfg(engine)] -impl Default for Config { - fn default() -> Self { - Config::new() - } -} diff --git a/client/src/global_state.rs b/client/src/global_state.rs index 34ae344..6728e5d 100644 --- a/client/src/global_state.rs +++ b/client/src/global_state.rs @@ -1,26 +1,49 @@ use perseus::prelude::*; use serde::{Deserialize, Serialize}; -use crate::env::Config; +use crate::api::FastInsidersApi; use perseus::state::GlobalStateCreator; pub fn get_global_state_creator() -> GlobalStateCreator { - GlobalStateCreator::new().build_state_fn(get_build_state) + GlobalStateCreator::new() + .build_state_fn(get_build_state) + .request_state_fn(get_request_state) + .amalgamate_states_fn(amalgamate_states) } #[derive(Serialize, Deserialize, ReactiveState)] #[rx(alias = "AppStateRx")] pub struct AppState { pub dark_mode: bool, - pub config: Config, + pub api: FastInsidersApi, } #[engine_only_fn] -pub async fn get_build_state(_locale: String) -> AppState { +pub async fn get_build_state() -> AppState { + AppState { + dark_mode: true, + api: FastInsidersApi::new(""), // It's unfortunately not possible to have a different type + // for the build state and the request state, I would rather + // have left this out + // This will also only ever work as long as we don't need the api + // while building the app, so no SSR! + } +} + +#[engine_only_fn] +async fn get_request_state(_req: Request) -> AppState { use crate::env::Config; let config = Config::new(); AppState { - config, dark_mode: true, + api: FastInsidersApi::new(&config.api_url), + } +} + +#[engine_only_fn] +async fn amalgamate_states(build_state: AppState, request_state: AppState) -> AppState { + AppState { + dark_mode: build_state.dark_mode, + api: request_state.api, } } diff --git a/client/src/templates/index.rs b/client/src/templates/index.rs index 909ebe5..6e53c8e 100644 --- a/client/src/templates/index.rs +++ b/client/src/templates/index.rs @@ -2,10 +2,7 @@ use perseus::prelude::*; use sycamore::prelude::*; use crate::{ - api::{ - routes::transaction::{get_aggregated_transactions, get_latest_transactions}, - types::transaction::{LatestTransaction, TransactionsAggregated}, - }, + api::types::transaction::{LatestTransaction, TransactionsAggregated}, components::{ main_content_container::MainContentContainer, paginated_data_table::{PaginatedTable, PaginatedTableStateRx}, @@ -14,23 +11,33 @@ use crate::{ global_state::AppStateRx, }; -fn index_page(cx: Scope) -> View { - let global_state = Reactor::::from_cx(cx).get_global_state::(cx); +fn index_page(cx: BoundedScope) -> View { + let reactor = Reactor::::from_cx(cx); + let global_state = reactor.get_global_state::(cx); + + let api = global_state.api.get(); + let api_scope_ref = create_ref(cx, api); let table_classes = create_ref(cx, "w-full".to_string()); + let route_ref = create_ref(cx, move |c, p, s| { + api_scope_ref.get_latest_transactions(c, p, s) + }); let latest_transactions: PaginatedTableStateRx = PaginatedTableStateRx { record_label: "transactions".to_owned(), - route: get_latest_transactions, + route: route_ref, filter: Some("72".to_string()), table_class: table_classes, }; + let route_ref = create_ref(cx, move |c, p, s| { + api_scope_ref.get_aggregated_transactions(c, p, s) + }); let table_transactions_month: PaginatedTableStateRx = PaginatedTableStateRx { record_label: "companies".to_owned(), - route: get_aggregated_transactions, + route: route_ref, filter: Some((24 * 30).to_string()), table_class: table_classes, }; diff --git a/client/src/templates/transactions.rs b/client/src/templates/transactions.rs index 4ae610b..ff407a9 100644 --- a/client/src/templates/transactions.rs +++ b/client/src/templates/transactions.rs @@ -3,8 +3,6 @@ use serde::{Deserialize, Serialize}; use sycamore::prelude::*; use crate::{ - api::routes::transaction::get_transactions, - api::types::{company::Company, transaction::TransactionCompany}, components::{ base_async_select::{AsyncSelectRx, BaseAsyncSelect}, base_button::{BaseButton, BaseButtonStateRx}, @@ -12,7 +10,6 @@ use crate::{ paginated_data_table::{PaginatedTable, PaginatedTableStateRx}, the_header::TheHeader, }, - env::Config, global_state::AppStateRx, }; @@ -23,8 +20,10 @@ pub struct TransactionsPageState { } #[auto_scope] -fn transactions_page(cx: Scope, state: &TransactionsPageStateRx) -> View { +fn transactions_page<'a, G: Html>(cx: Scope, state: &TransactionsPageStateRx) -> View { let global_state = Reactor::::from_cx(cx).get_global_state::(cx); + let api = global_state.api.get(); + let api_scope_ref = create_ref(cx, api); let expand = create_signal(cx, false); let filter_expand = BaseButtonStateRx { @@ -40,17 +39,17 @@ fn transactions_page(cx: Scope, state: &TransactionsPageStateRx) -> Vie } }); - let paginated_table_state: PaginatedTableStateRx = - PaginatedTableStateRx { - record_label: "transactions".to_owned(), - route: get_transactions, - filter: (*state.company_slug.get()).clone(), - table_class: create_ref(cx, "".to_string()), - }; + let route_ref = create_ref(cx, move |c, p, s| api_scope_ref.get_transactions(c, p, s)); + let paginated_table_state: PaginatedTableStateRx<_, _, _> = PaginatedTableStateRx { + record_label: "transactions".to_owned(), + route: route_ref, + filter: (*state.company_slug.get()).clone(), + table_class: create_ref(cx, "".to_string()), + }; - let api_url = Config::new().api_url; - let async_select_prop: AsyncSelectRx = AsyncSelectRx { - remote_list: create_signal(cx, format!("{}company", api_url)), + let route_ref = create_ref(cx, |n, l| api_scope_ref.get_company_by_name(n, l)); + let async_select_prop: AsyncSelectRx<_, _, _> = AsyncSelectRx { + route: create_ref(cx, |n, l| api_scope_ref.get_company_by_name(n, l)), selected_item: create_signal(cx, None), }; diff --git a/docker-compose.yml b/docker-compose.yml index af547ba..82bad65 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,22 +4,17 @@ services: container_name: fast-insiders-client image: fast-insiders-client:latest build: - context: . - args: - API_URL: http://localhost:8000/ dockerfile: ./client/Dockerfile restart: always ports: - 8080:8080 environment: - PERSEUS_HOST=0.0.0.0 + - API_URL=http://localhost:8000 server: container_name: fast-insiders-server image: fast-insiders-server:latest build: - context: . - args: - API_URL: http://localhost:8000/ dockerfile: ./server/Dockerfile restart: always ports: diff --git a/server/Dockerfile b/server/Dockerfile index 2b27ca4..597cb1d 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1.66-slim as build +FROM rust:1.69-slim as build # Install build dependencies RUN apt-get update \ @@ -10,17 +10,10 @@ WORKDIR /app # retrieve the src dir COPY . . -# RUN curl https://git.albv.org/alban/fast-insiders/archive/master.tar.gz | tar -xz -# go to src dir -# WORKDIR /app - -# Rocket only runs on Nightly +# Build failed when not using nightly though I don't know which crate is responsible RUN rustup default nightly -ARG API_URL -ENV API_URL=$API_URL - # go to src dir WORKDIR /app/server @@ -30,9 +23,6 @@ RUN cargo build --release # prepare deployment image FROM debian:stable-slim -# For tls to work -RUN apt-get update && apt-get -y --no-install-recommends install ca-certificates libssl-dev && rm -rf /var/lib/apt/lists/* - WORKDIR /app COPY --from=build /app/target/release/server /app/ @@ -40,4 +30,3 @@ COPY --from=build /app/target/release/server /app/ ENV HOST=0.0.0.0 CMD ["./server"] - diff --git a/server/src/route/transaction.rs b/server/src/route/transaction.rs index ea3290f..f5cfc29 100644 --- a/server/src/route/transaction.rs +++ b/server/src/route/transaction.rs @@ -1,4 +1,4 @@ -use axum::extract::{Json, Path, Query, State}; +use axum::extract::{Json, Query, State}; use chrono::{NaiveDate, NaiveDateTime}; use sea_orm::{ prelude::*, DbBackend, FromQueryResult, ItemsAndPagesNumber, JoinType, Order, QueryOrder,