From f205147bbe9ea9c194347143640644feba1fee9b Mon Sep 17 00:00:00 2001 From: Aashi Modi Date: Tue, 7 Nov 2023 10:53:58 +0530 Subject: [PATCH 1/7] added rust app and acornfiles --- Acornfile | 50 + Dockerfile.backend | 8 + Dockerfile.frontend | 15 + fullstack-rust-app/.gitignore | 1 + fullstack-rust-app/Acornfile.all | 45 + fullstack-rust-app/Cargo.lock | 2747 +++++++++++++++++ fullstack-rust-app/Cargo.toml | 7 + fullstack-rust-app/Dockerfile.bk | 22 + fullstack-rust-app/README.md | 35 + fullstack-rust-app/backend/.env | 10 + fullstack-rust-app/backend/.gitignore | 1 + fullstack-rust-app/backend/Cargo.lock | 7 + fullstack-rust-app/backend/Cargo.toml | 19 + fullstack-rust-app/backend/Dockerfile | 16 + fullstack-rust-app/backend/Makefile | 29 + fullstack-rust-app/backend/docker-compose.yml | 20 + .../migrations/20230303140608_init.down.sql | 3 + .../migrations/20230303140608_init.up.sql | 16 + fullstack-rust-app/backend/src/handler.rs | 197 ++ fullstack-rust-app/backend/src/main.rs | 60 + fullstack-rust-app/backend/src/model.rs | 15 + fullstack-rust-app/backend/src/schema.rs | 24 + fullstack-rust-app/common/Cargo.lock | 7 + fullstack-rust-app/common/Cargo.toml | 10 + fullstack-rust-app/common/src/lib.rs | 32 + fullstack-rust-app/frontend/.gitignore | 3 + fullstack-rust-app/frontend/Cargo.lock | 1006 ++++++ fullstack-rust-app/frontend/Cargo.toml | 19 + fullstack-rust-app/frontend/Dockerfile.bk | 40 + fullstack-rust-app/frontend/Makefile | 14 + fullstack-rust-app/frontend/Trunk.toml | 6 + fullstack-rust-app/frontend/index.html | 12 + fullstack-rust-app/frontend/logo.svg | 7 + fullstack-rust-app/frontend/src/api.rs | 110 + .../frontend/src/components/alert.rs | 47 + .../frontend/src/components/feedback_form.rs | 126 + .../frontend/src/components/feedback_item.rs | 65 + .../frontend/src/components/feedback_list.rs | 46 + .../frontend/src/components/feedback_stats.rs | 24 + .../frontend/src/components/mod.rs | 6 + .../frontend/src/components/rating.rs | 42 + fullstack-rust-app/frontend/src/main.rs | 55 + fullstack-rust-app/frontend/src/store.rs | 56 + .../frontend/styles/tailwind.css | 13 + .../frontend/tailwind.config.js | 24 + 45 files changed, 5117 insertions(+) create mode 100644 Acornfile create mode 100644 Dockerfile.backend create mode 100644 Dockerfile.frontend create mode 100644 fullstack-rust-app/.gitignore create mode 100644 fullstack-rust-app/Acornfile.all create mode 100644 fullstack-rust-app/Cargo.lock create mode 100644 fullstack-rust-app/Cargo.toml create mode 100644 fullstack-rust-app/Dockerfile.bk create mode 100644 fullstack-rust-app/README.md create mode 100644 fullstack-rust-app/backend/.env create mode 100644 fullstack-rust-app/backend/.gitignore create mode 100644 fullstack-rust-app/backend/Cargo.lock create mode 100644 fullstack-rust-app/backend/Cargo.toml create mode 100644 fullstack-rust-app/backend/Dockerfile create mode 100644 fullstack-rust-app/backend/Makefile create mode 100644 fullstack-rust-app/backend/docker-compose.yml create mode 100644 fullstack-rust-app/backend/migrations/20230303140608_init.down.sql create mode 100644 fullstack-rust-app/backend/migrations/20230303140608_init.up.sql create mode 100644 fullstack-rust-app/backend/src/handler.rs create mode 100644 fullstack-rust-app/backend/src/main.rs create mode 100644 fullstack-rust-app/backend/src/model.rs create mode 100644 fullstack-rust-app/backend/src/schema.rs create mode 100644 fullstack-rust-app/common/Cargo.lock create mode 100644 fullstack-rust-app/common/Cargo.toml create mode 100644 fullstack-rust-app/common/src/lib.rs create mode 100644 fullstack-rust-app/frontend/.gitignore create mode 100644 fullstack-rust-app/frontend/Cargo.lock create mode 100644 fullstack-rust-app/frontend/Cargo.toml create mode 100644 fullstack-rust-app/frontend/Dockerfile.bk create mode 100644 fullstack-rust-app/frontend/Makefile create mode 100644 fullstack-rust-app/frontend/Trunk.toml create mode 100644 fullstack-rust-app/frontend/index.html create mode 100644 fullstack-rust-app/frontend/logo.svg create mode 100644 fullstack-rust-app/frontend/src/api.rs create mode 100644 fullstack-rust-app/frontend/src/components/alert.rs create mode 100644 fullstack-rust-app/frontend/src/components/feedback_form.rs create mode 100644 fullstack-rust-app/frontend/src/components/feedback_item.rs create mode 100644 fullstack-rust-app/frontend/src/components/feedback_list.rs create mode 100644 fullstack-rust-app/frontend/src/components/feedback_stats.rs create mode 100644 fullstack-rust-app/frontend/src/components/mod.rs create mode 100644 fullstack-rust-app/frontend/src/components/rating.rs create mode 100644 fullstack-rust-app/frontend/src/main.rs create mode 100644 fullstack-rust-app/frontend/src/store.rs create mode 100644 fullstack-rust-app/frontend/styles/tailwind.css create mode 100644 fullstack-rust-app/frontend/tailwind.config.js diff --git a/Acornfile b/Acornfile new file mode 100644 index 0000000..927a02a --- /dev/null +++ b/Acornfile @@ -0,0 +1,50 @@ +name: "Rust Sample Acorn" +description: "Acorn running a sample Rust app" +readme: "./README.md" + + +args: { + // Name of the database to create. Defaults to "instance" + dbName: "rust-sqlx" + // Provide user for mysql. If the value is not provided, it will default to + dbUser: "root" +} + +services: db: { + image: "ghcr.io/acorn-io/postgres:v15.#-#" + serviceArgs: { + dbName: args.dbName + dbUser: "root" + } + +} + +containers: { + backend: { + image: "ghcr.io/aashimodi14/rust-backend:v2" + env: { + "POSTGRES_HOST": "@{service.db.address}" + "POSTGRES_PORT": "@{service.db.port.5432}" + "POSTGRES_USER": "@{service.db.secrets.admin.username}" + "POSTGRES_PASSWORD":"@{service.db.secrets.admin.password}" + "POSTGRES_DB":"@{service.db.data.dbName}" + "DATABASE_URL":"postgresql://@{service.db.secrets.admin.username}:@{service.db.secrets.admin.password}@@{service.db.address}:5432?schema=public" + "PGADMIN_DEFAULT_EMAIL":"admin@admin.com" + "PGADMIN_DEFAULT_PASSWORD":"password123" + } + ports: { + expose: "8000/http" + } + cmd: ["/bin/sh", "-c", "cd backend && sqlx migrate run; cargo run"] + consumes: ["db"] + } + frontend: { + image: "ghcr.io/aashimodi14/rust-frontend:v2" + cmd: ["/bin/sh", "-c", "cd frontend && trunk serve --address 0.0.0.0 --port 3000"] + ports: { + publish: "3000/http" + } + dependsOn: ["backend"] + } +} + diff --git a/Dockerfile.backend b/Dockerfile.backend new file mode 100644 index 0000000..e3611ce --- /dev/null +++ b/Dockerfile.backend @@ -0,0 +1,8 @@ +FROM rust:1.73-slim-bullseye + +RUN apt --yes update && apt --yes install git pkg-config curl libssl-dev ca-certificates curl gnupg +RUN cargo install wasm-bindgen-cli +RUN rustup target add wasm32-unknown-unknown + +WORKDIR /usr/src/fullstack-rust-app/ +COPY ./fullstack-rust-app/ . \ No newline at end of file diff --git a/Dockerfile.frontend b/Dockerfile.frontend new file mode 100644 index 0000000..7e4a83f --- /dev/null +++ b/Dockerfile.frontend @@ -0,0 +1,15 @@ +FROM rust:1.73-slim-bullseye + +RUN apt --yes update && apt --yes install git pkg-config curl libssl-dev ca-certificates curl gnupg +RUN cargo install trunk wasm-bindgen-cli +RUN rustup target add wasm32-unknown-unknown + +WORKDIR /usr/src/fullstack-rust-app +COPY . . + +RUN curl -sL https://deb.nodesource.com/setup_18.x | bash - +RUN apt-get install -y nodejs + +RUN npx -v +RUN npx tailwindcss-cli@latest + diff --git a/fullstack-rust-app/.gitignore b/fullstack-rust-app/.gitignore new file mode 100644 index 0000000..c41cc9e --- /dev/null +++ b/fullstack-rust-app/.gitignore @@ -0,0 +1 @@ +/target \ No newline at end of file diff --git a/fullstack-rust-app/Acornfile.all b/fullstack-rust-app/Acornfile.all new file mode 100644 index 0000000..a3acf23 --- /dev/null +++ b/fullstack-rust-app/Acornfile.all @@ -0,0 +1,45 @@ +name: "Spring Boot Sample Acorn" +description: "Acorn running a sample Petclinic Springboot app" +readme: "./README.md" + + +args: { + // Name of the database to create. Defaults to "instance" + dbName: "rust-sqlx" + // Provide user for mysql. If the value is not provided, it will default to + user: "root" +} + +services: db: { + image: "ghcr.io/acorn-io/postgres:v15.#-#" + serviceArgs: { + dbName: args.dbName + memory: 512Mi + } +} + +containers: { + app: { + build: { + context: "." + dockerfile: "Dockerfile.bk" + } + env: { + "POSTGRES_HOST": "@{service.db.address}" + "POSTGRES_PORT": "@{service.db.port.5432}" + "POSTGRES_USER": "@{service.db.secrets.admin.username}" + "POSTGRES_PASSWORD":"@{service.db.secrets.admin.password}" + "POSTGRES_DB":"@{service.db.data.dbName}" + "DATABASE_URL":"postgresql://@{service.db.secrets.admin.username}:@{service.db.secrets.admin.password}@@{service.db.address}:5432?schema=public" + "PGADMIN_DEFAULT_EMAIL":"admin@admin.com" + "PGADMIN_DEFAULT_PASSWORD":"password123" + } + ports: { + expose: "8000/http" + } + cmd: ["/bin/sh", "-c", "cd backend && sqlx migrate run; cargo run"] + consumes: ["db"] + } +} + + diff --git a/fullstack-rust-app/Cargo.lock b/fullstack-rust-app/Cargo.lock new file mode 100644 index 0000000..78c2a00 --- /dev/null +++ b/fullstack-rust-app/Cargo.lock @@ -0,0 +1,2747 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actix-codec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a7559404a7f3573127aab53c08ce37a6c6a315c374a31070f3c91cd1b4a7fe" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-sink", + "log", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "actix-cors" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b340e9cfa5b08690aae90fb61beb44e9b06f44fe3d0f93781aaa58cfba86245e" +dependencies = [ + "actix-utils", + "actix-web", + "derive_more", + "futures-util", + "log", + "once_cell", + "smallvec", +] + +[[package]] +name = "actix-http" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2079246596c18b4a33e274ae10c0e50613f4d32a4198e09c7b93771013fed74" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash 0.8.3", + "base64 0.21.0", + "bitflags", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "futures-core", + "h2", + "http", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-router" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66ff4d247d2b160861fa2866457e85706833527840e4133f8f49aa423a38799" +dependencies = [ + "bytestring", + "http", + "regex", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e8613a75dd50cc45f473cee3c34d59ed677c0f7b44480ce3b8247d7dc519327" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "num_cpus", + "socket2", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +dependencies = [ + "futures-core", + "paste", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3cb42f9566ab176e1ef0b8b3a896529062b4efc6be0123046095914c4c1c96" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash 0.7.6", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2", + "time 0.3.20", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2262160a7ae29e3415554a3f1fc04c764b1540c116aa524683208078b7a75bc9" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anymap" +version = "1.0.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1f8f5a6f3d50d89e3797d7593a50f96bb2aaa20ca0cc7be1fb673232c91d72" + +[[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + +[[package]] +name = "async-trait" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atoi" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c57d12312ff59c811c0643f4d80830505833c9ffaebd193d819392b265be8e" +dependencies = [ + "num-traits", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backend" +version = "0.1.0" +dependencies = [ + "actix-cors", + "actix-web", + "chrono", + "common", + "dotenv", + "env_logger", + "serde", + "serde_json", + "sqlx", + "uuid", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "boolinator" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" + +[[package]] +name = "brotli" +version = "3.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "bytestring" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7f83e57d9154148e355404702e2694463241880b939570d7c97c014da7a69a1" +dependencies = [ + "bytes", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "serde", + "time 0.1.45", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "common" +version = "0.1.0" +dependencies = [ + "serde", + "uuid", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time 0.3.20", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cxx" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "dotenvy" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d8c417d7a8cb362e0c37e5d815f5eb7c37f79ff93707329d5a194e42e54ca0" + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +dependencies = [ + "serde", +] + +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "frontend" +version = "0.1.0" +dependencies = [ + "common", + "gloo", + "reqwasm", + "serde", + "serde_json", + "uuid", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "yew", + "yewdux", +] + +[[package]] +name = "futures" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" + +[[package]] +name = "futures-intrusive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604f7a68fbf8103337523b1fadc8ade7361ee3f112f7c680ad179651616aed5" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot 0.11.2", +] + +[[package]] +name = "futures-io" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" + +[[package]] +name = "futures-macro" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" + +[[package]] +name = "futures-task" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" + +[[package]] +name = "futures-util" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "gloo" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a4bef6b277b3ab073253d4bca60761240cf8d6998f4bd142211957b69a61b20" +dependencies = [ + "gloo-console", + "gloo-dialogs", + "gloo-events", + "gloo-file", + "gloo-history", + "gloo-net 0.2.6", + "gloo-render", + "gloo-storage", + "gloo-timers", + "gloo-utils", + "gloo-worker", +] + +[[package]] +name = "gloo-console" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f" +dependencies = [ + "gloo-utils", + "js-sys", + "serde", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-dialogs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-events" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-file" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7" +dependencies = [ + "gloo-events", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-history" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd451019e0b7a2b8a7a7b23e74916601abf1135c54664e57ff71dcc26dfcdeb7" +dependencies = [ + "gloo-events", + "gloo-utils", + "serde", + "serde-wasm-bindgen", + "serde_urlencoded", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2899cb1a13be9020b010967adc6b2a8a343b6f1428b90238c9d53ca24decc6db" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9902a044653b26b99f7e3693a42f171312d9be8b26b5697bd1e43ad1f8a35e10" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-render" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-storage" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480" +dependencies = [ + "gloo-utils", + "js-sys", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8e8fc851e9c7b9852508bc6e3f690f452f474417e8545ec9857b7f7377036b5" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-worker" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a" +dependencies = [ + "anymap2", + "bincode", + "gloo-console", + "gloo-utils", + "js-sys", + "serde", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "h2" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.6", +] + +[[package]] +name = "hashlink" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "iana-time-zone" +version = "0.1.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "implicit-clone" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40fc102e70475c320b185cd18c1e48bba2d7210b63970a4d581ef903e4368ef7" +dependencies = [ + "indexmap", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" +dependencies = [ + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "is-terminal" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys 0.45.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "jobserver" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + +[[package]] +name = "local-channel" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f303ec0e94c6c54447f84f3b0ef7af769858a9c4ef56ef2a986d3dcd4c3fc9c" +dependencies = [ + "futures-core", + "futures-sink", + "futures-util", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.45.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "openssl" +version = "0.10.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.7", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "paste" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pinned" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a829027bd95e54cfe13e3e258a1ae7b645960553fb82b75ff852c29688ee595b" +dependencies = [ + "futures", + "rustversion", + "thiserror", +] + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "prettyplease" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prokio" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b55e106e5791fa5a13abd13c85d6127312e8e09098059ca2bc9b03ca4cf488" +dependencies = [ + "futures", + "gloo", + "num_cpus", + "once_cell", + "pin-project", + "pinned", + "tokio", + "tokio-stream", + "wasm-bindgen-futures", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] +name = "reqwasm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b89870d729c501fa7a68c43bf4d938bbb3a8c156d333d90faa0e8b3e3212fb" +dependencies = [ + "gloo-net 0.1.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.36.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustversion" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "scratch" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" + +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "sqlformat" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" +dependencies = [ + "itertools", + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9249290c05928352f71c077cc44a464d880c63f26f7534728cca008e135c0428" +dependencies = [ + "sqlx-core", + "sqlx-macros", +] + +[[package]] +name = "sqlx-core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbc16ddba161afc99e14d1713a453747a2b07fc097d2009f4c300ec99286105" +dependencies = [ + "ahash 0.7.6", + "atoi", + "base64 0.13.1", + "bitflags", + "byteorder", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "dirs", + "dotenvy", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-util", + "hashlink", + "hex", + "hkdf", + "hmac", + "indexmap", + "itoa", + "libc", + "log", + "md-5", + "memchr", + "once_cell", + "paste", + "percent-encoding", + "rand", + "serde", + "serde_json", + "sha1", + "sha2", + "smallvec", + "sqlformat", + "sqlx-rt", + "stringprep", + "thiserror", + "tokio-stream", + "url", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-macros" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b850fa514dc11f2ee85be9d055c512aa866746adfacd1cb42d867d68e6a5b0d9" +dependencies = [ + "dotenvy", + "either", + "heck", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-rt", + "syn", + "url", +] + +[[package]] +name = "sqlx-rt" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24c5b2d25fa654cc5f841750b8e1cdedbe21189bf9a9382ee90bfa9dd3562396" +dependencies = [ + "native-tls", + "once_cell", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "stringprep" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.42.0", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "time" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +dependencies = [ + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot 0.12.1", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "windows-sys 0.45.0", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unicode-bidi" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "uuid" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" +dependencies = [ + "getrandom", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "serde", + "serde_json", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "whoami" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45dbc71f0cdca27dc261a9bd37ddec174e4a0af2b900b890f378460f745426e3" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "yew" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dbecfe44343b70cc2932c3eb445425969ae21754a8ab3a0966981c1cf7af1cc" +dependencies = [ + "console_error_panic_hook", + "futures", + "gloo", + "implicit-clone", + "indexmap", + "js-sys", + "prokio", + "rustversion", + "serde", + "slab", + "thiserror", + "tokio", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "yew-macro", +] + +[[package]] +name = "yew-macro" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b64c253c1d401f1ea868ca9988db63958cfa15a69f739101f338d6f05eea8301" +dependencies = [ + "boolinator", + "once_cell", + "prettyplease", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "yewdux" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653ba356bc60d1804c28ec6cc8ddac2741c686bde2a65074d07faba735914464" +dependencies = [ + "anymap", + "async-trait", + "log", + "serde", + "serde_json", + "slab", + "thiserror", + "wasm-bindgen", + "web-sys", + "yew", + "yewdux-macros", +] + +[[package]] +name = "yewdux-macros" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25bcd923aceaa85cb4affad8657cc36e3d6b6932740e711574182f7817492739" +dependencies = [ + "darling", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zstd" +version = "0.12.3+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "6.0.4+zstd.1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7afb4b54b8910cf5447638cb54bf4e8a65cbedd783af98b98c62ffe91f185543" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.7+zstd.1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" +dependencies = [ + "cc", + "libc", + "pkg-config", +] diff --git a/fullstack-rust-app/Cargo.toml b/fullstack-rust-app/Cargo.toml new file mode 100644 index 0000000..5c8c470 --- /dev/null +++ b/fullstack-rust-app/Cargo.toml @@ -0,0 +1,7 @@ +[workspace] +members = [ + "backend", + "common", + "frontend", +] +default-members = ["backend"] \ No newline at end of file diff --git a/fullstack-rust-app/Dockerfile.bk b/fullstack-rust-app/Dockerfile.bk new file mode 100644 index 0000000..1beeed3 --- /dev/null +++ b/fullstack-rust-app/Dockerfile.bk @@ -0,0 +1,22 @@ +FROM rust:1.73-slim-bullseye AS build + +RUN apt --yes update && apt --yes install git pkg-config curl libssl-dev ca-certificates curl gnupg +RUN cargo install trunk wasm-bindgen-cli sqlx-cli +RUN rustup target add wasm32-unknown-unknown + +WORKDIR /usr/src/fullstack-rust-app +COPY . . + +RUN curl -sL https://deb.nodesource.com/setup_18.x | bash - +RUN apt-get install -y nodejs + +RUN cd frontend && trunk serve --port 3000 +RUN cd backend + +#FROM gcr.io/distroless/cc-debian10 + +#COPY --from=build /usr/src/fullstack-rust-app/target/release/backend /usr/local/bin/backend +#COPY --from=build /usr/src/fullstack-rust-app/frontend/dist /usr/local/bin/dist + +#WORKDIR /usr/local/bin +#CMD ["backend"] \ No newline at end of file diff --git a/fullstack-rust-app/README.md b/fullstack-rust-app/README.md new file mode 100644 index 0000000..c23a53c --- /dev/null +++ b/fullstack-rust-app/README.md @@ -0,0 +1,35 @@ +# Build a Full Stack App with Rust, Yew.rs and Actix Web + +In this article, I'll walk you through the process of building a backend API using the Actix web framework, SQLX, PostgreSQL, and Docker. Once we've created a powerful backend, we'll move on to building a single-page app using the Yew.rs framework. By the end of this tutorial, you'll have a fully functional web app that seamlessly integrates the frontend app with the backend API. + +![Build a Full Stack App with Rust, Yew.rs and Actix Web](https://codevoweb.com/wp-content/uploads/2023/03/Build-a-Full-Stack-App-with-Rust-Yew.rs-and-Actix-Web.webp) + +## Topics Covered + +- Run the Full-Stack Rust App Locally +- Setup the Full-Stack Rust App +- Work on the Library Project +- Work on the Backend Project + - Setup the Rust API Project + - Setup PostgreSQL and pgAdmin with Docker + - Database Migration with SQLX + - Create the SQLX Database Model + - Create the Request Validation Structs + - Create the CRUD API Route Handlers + - Register the API Routes and Setup CORS +- Work on the Frontend Project + - Setup the Yew.rs Project + - Setup Tailwind CSS for Styling + - Create the API Request Functions + - State Management with Yewdux + - Create Reusable Components + - Yew Component to Add New Feedback Item + - Yew Component to Display Feedback Statistics + - Yew Component to Display Feedback Information + - Yew Component to Display the Feedback Items + - Export the Component Files as Modules + - Add the Components to the Main File +- Test the Rust Full-Stack App + +Read the entire article here: [https://codevoweb.com/build-full-stack-app-with-rust-yew-and-actix-web/](https://codevoweb.com/build-full-stack-app-with-rust-yew-and-actix-web/) + diff --git a/fullstack-rust-app/backend/.env b/fullstack-rust-app/backend/.env new file mode 100644 index 0000000..db10cfb --- /dev/null +++ b/fullstack-rust-app/backend/.env @@ -0,0 +1,10 @@ +POSTGRES_HOST=127.0.0.1 +POSTGRES_PORT=6500 +POSTGRES_USER=admin +POSTGRES_PASSWORD=password123 +POSTGRES_DB=rust_sqlx + +DATABASE_URL=postgresql://admin:password123@localhost:6500/rust_sqlx?schema=public + +PGADMIN_DEFAULT_EMAIL=admin@admin.com +PGADMIN_DEFAULT_PASSWORD=password123 \ No newline at end of file diff --git a/fullstack-rust-app/backend/.gitignore b/fullstack-rust-app/backend/.gitignore new file mode 100644 index 0000000..c41cc9e --- /dev/null +++ b/fullstack-rust-app/backend/.gitignore @@ -0,0 +1 @@ +/target \ No newline at end of file diff --git a/fullstack-rust-app/backend/Cargo.lock b/fullstack-rust-app/backend/Cargo.lock new file mode 100644 index 0000000..3b3d789 --- /dev/null +++ b/fullstack-rust-app/backend/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "backend" +version = "0.1.0" diff --git a/fullstack-rust-app/backend/Cargo.toml b/fullstack-rust-app/backend/Cargo.toml new file mode 100644 index 0000000..ea77788 --- /dev/null +++ b/fullstack-rust-app/backend/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "backend" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +actix-cors = "0.6.4" +actix-web = "4.3.1" +chrono = { version = "0.4.23", features = ["serde"] } +dotenv = "0.15.0" +env_logger = "0.10.0" +serde = { version = "1.0.152", features = ["derive"] } +serde_json = "1.0.93" +sqlx = { version = "0.6.2", features = ["runtime-async-std-native-tls", "postgres", "chrono", "uuid", "migrate", "offline"] } +#sqlx = { version = "0.6.2", features = ["runtime-tokio-native-tls", "postgres", "chrono", "uuid", "migrate", "offline"] } +uuid = { version = "1.3.0", features = ["serde", "v4"] } +common = { version = "0.1.0", path = "../common" } \ No newline at end of file diff --git a/fullstack-rust-app/backend/Dockerfile b/fullstack-rust-app/backend/Dockerfile new file mode 100644 index 0000000..09d39a6 --- /dev/null +++ b/fullstack-rust-app/backend/Dockerfile @@ -0,0 +1,16 @@ +FROM rust:1.73-slim-bullseye AS build + +RUN apt --yes update && apt --yes install git pkg-config curl libssl-dev ca-certificates curl gnupg +RUN cargo install wasm-bindgen-cli sqlx-cli +RUN rustup target add wasm32-unknown-unknown + +WORKDIR /usr/src/fullstack-rust-app/ +COPY . . +RUN cd backend && cargo build --release + + +FROM alpine AS release + +WORKDIR /app +COPY --from=build /usr/src/fullstack-rust-app/target/release/backend /usr/local/bin/backend + diff --git a/fullstack-rust-app/backend/Makefile b/fullstack-rust-app/backend/Makefile new file mode 100644 index 0000000..2b500a3 --- /dev/null +++ b/fullstack-rust-app/backend/Makefile @@ -0,0 +1,29 @@ +dev: + docker-compose up -d + +dev-down: + docker-compose down + +migrate-up: + sqlx migrate run + +migrate-down: + sqlx migrate revert + +start-server: + cargo watch -q -c -w src/ -x run + +install: + cargo add actix-web + cargo add actix-cors + cargo add serde_json + cargo add serde --features derive + cargo add chrono --features serde + cargo add env_logger + cargo add dotenv + cargo add uuid --features "serde v4" + cargo add sqlx --features "runtime-async-std-native-tls postgres chrono uuid" + # HotReload + cargo install cargo-watch + # SQLX-CLI + cargo install sqlx-cli \ No newline at end of file diff --git a/fullstack-rust-app/backend/docker-compose.yml b/fullstack-rust-app/backend/docker-compose.yml new file mode 100644 index 0000000..d3bbc54 --- /dev/null +++ b/fullstack-rust-app/backend/docker-compose.yml @@ -0,0 +1,20 @@ +version: '3' +services: + postgres: + image: postgres:latest + container_name: postgres + ports: + - '6500:5432' + volumes: + - progresDB:/data/postgres + env_file: + - ./.env + pgAdmin: + image: dpage/pgadmin4 + container_name: pgAdmin + env_file: + - ./.env + ports: + - "5050:80" +volumes: + progresDB: diff --git a/fullstack-rust-app/backend/migrations/20230303140608_init.down.sql b/fullstack-rust-app/backend/migrations/20230303140608_init.down.sql new file mode 100644 index 0000000..82e9623 --- /dev/null +++ b/fullstack-rust-app/backend/migrations/20230303140608_init.down.sql @@ -0,0 +1,3 @@ +-- Add down migration script here + +DROP TABLE IF EXISTS feedbacks; \ No newline at end of file diff --git a/fullstack-rust-app/backend/migrations/20230303140608_init.up.sql b/fullstack-rust-app/backend/migrations/20230303140608_init.up.sql new file mode 100644 index 0000000..485ca7a --- /dev/null +++ b/fullstack-rust-app/backend/migrations/20230303140608_init.up.sql @@ -0,0 +1,16 @@ +-- Add migration script here + +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; + +CREATE TABLE + IF NOT EXISTS feedbacks ( + id UUID PRIMARY KEY NOT NULL DEFAULT (uuid_generate_v4()), + rating INTEGER NOT NULL, + text TEXT NOT NULL UNIQUE, + created_at TIMESTAMP + WITH + TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP + WITH + TIME ZONE DEFAULT NOW() + ); \ No newline at end of file diff --git a/fullstack-rust-app/backend/src/handler.rs b/fullstack-rust-app/backend/src/handler.rs new file mode 100644 index 0000000..f2d32a0 --- /dev/null +++ b/fullstack-rust-app/backend/src/handler.rs @@ -0,0 +1,197 @@ +use crate::{ + model::FeedbackModel, + schema::{CreateFeedbackSchema, FilterOptions, UpdateFeedbackSchema}, + AppState, +}; +use actix_web::{delete, get, patch, post, web, HttpResponse, Responder}; +use chrono::prelude::*; +use serde_json::json; + +#[get("/healthchecker")] +async fn health_checker_handler() -> impl Responder { + const MESSAGE: &str = "Build API with Rust, SQLX, Postgres,and Actix Web"; + + HttpResponse::Ok().json(json!({"status": "success","message": MESSAGE})) +} + +#[get("/feedbacks")] +pub async fn feedback_list_handler( + opts: web::Query, + data: web::Data, +) -> impl Responder { + let limit = opts.limit.unwrap_or(10); + let offset = (opts.page.unwrap_or(1) - 1) * limit; + + let query_result = sqlx::query_as!( + FeedbackModel, + "SELECT * FROM feedbacks ORDER by id LIMIT $1 OFFSET $2", + limit as i32, + offset as i32 + ) + .fetch_all(&data.db) + .await; + + if query_result.is_err() { + let message = "Something bad happened while fetching all feedback items"; + return HttpResponse::InternalServerError() + .json(json!({"status": "error","message": message})); + } + + let feedbacks = query_result.unwrap(); + + let json_response = serde_json::json!({ + "status": "success", + "results": feedbacks.len(), + "feedbacks": feedbacks + }); + HttpResponse::Ok().json(json_response) +} + +#[post("/feedbacks/")] +async fn create_feedback_handler( + body: web::Json, + data: web::Data, +) -> impl Responder { + let query_result = sqlx::query_as!( + FeedbackModel, + "INSERT INTO feedbacks (text,rating) VALUES ($1, $2) RETURNING *", + body.text.to_string(), + body.rating, + ) + .fetch_one(&data.db) + .await; + + match query_result { + Ok(feedback) => { + let feedback_response = serde_json::json!({"status": "success","data": serde_json::json!({ + "feedback": feedback + })}); + + return HttpResponse::Ok().json(feedback_response); + } + Err(e) => { + if e.to_string() + .contains("duplicate key value violates unique constraint") + { + return HttpResponse::BadRequest() + .json(serde_json::json!({"status": "fail","message": "Feedback with that title already exists"})); + } + + return HttpResponse::InternalServerError() + .json(serde_json::json!({"status": "error","message": format!("{:?}", e)})); + } + } +} + +#[get("/feedbacks/{id}")] +async fn get_feedback_handler( + path: web::Path, + data: web::Data, +) -> impl Responder { + let feedback_id = path.into_inner(); + let query_result = sqlx::query_as!( + FeedbackModel, + "SELECT * FROM feedbacks WHERE id = $1", + feedback_id + ) + .fetch_one(&data.db) + .await; + + match query_result { + Ok(feedback) => { + let feedback_response = serde_json::json!({"status": "success","data": serde_json::json!({ + "feedback": feedback + })}); + + return HttpResponse::Ok().json(feedback_response); + } + Err(_) => { + let message = format!("feedback with ID: {} not found", feedback_id); + return HttpResponse::NotFound() + .json(serde_json::json!({"status": "fail","message": message})); + } + } +} + +#[patch("/feedbacks/{id}")] +async fn edit_feedback_handler( + path: web::Path, + body: web::Json, + data: web::Data, +) -> impl Responder { + let feedback_id = path.into_inner(); + let query_result = sqlx::query_as!( + FeedbackModel, + "SELECT * FROM feedbacks WHERE id = $1", + feedback_id + ) + .fetch_one(&data.db) + .await; + + if query_result.is_err() { + let message = format!("Feedback with ID: {} not found", feedback_id); + return HttpResponse::NotFound() + .json(serde_json::json!({"status": "fail","message": message})); + } + + let now = Utc::now(); + let feedback = query_result.unwrap(); + + let query_result = sqlx::query_as!( + FeedbackModel, + "UPDATE feedbacks SET text = $1, rating = $2, updated_at = $3 WHERE id = $4 RETURNING *", + body.text.to_owned().unwrap_or(feedback.text), + body.rating.to_owned().unwrap_or(feedback.rating), + now, + feedback_id + ) + .fetch_one(&data.db) + .await; + + match query_result { + Ok(feedback) => { + let feedback_response = serde_json::json!({"status": "success","data": serde_json::json!({ + "feedback": feedback + })}); + + return HttpResponse::Ok().json(feedback_response); + } + Err(err) => { + let message = format!("Error: {:?}", err); + return HttpResponse::InternalServerError() + .json(serde_json::json!({"status": "error","message": message})); + } + } +} + +#[delete("/feedbacks/{id}")] +async fn delete_feedback_handler( + path: web::Path, + data: web::Data, +) -> impl Responder { + let feedback_id = path.into_inner(); + let rows_affected = sqlx::query!("DELETE FROM feedbacks WHERE id = $1", feedback_id) + .execute(&data.db) + .await + .unwrap() + .rows_affected(); + + if rows_affected == 0 { + let message = format!("Feedback with ID: {} not found", feedback_id); + return HttpResponse::NotFound().json(json!({"status": "fail","message": message})); + } + + HttpResponse::NoContent().finish() +} + +pub fn config(conf: &mut web::ServiceConfig) { + let scope = web::scope("/api") + .service(health_checker_handler) + .service(feedback_list_handler) + .service(create_feedback_handler) + .service(get_feedback_handler) + .service(edit_feedback_handler) + .service(delete_feedback_handler); + + conf.service(scope); +} diff --git a/fullstack-rust-app/backend/src/main.rs b/fullstack-rust-app/backend/src/main.rs new file mode 100644 index 0000000..231f13e --- /dev/null +++ b/fullstack-rust-app/backend/src/main.rs @@ -0,0 +1,60 @@ +mod handler; +mod model; +mod schema; + +use actix_cors::Cors; +use actix_web::middleware::Logger; +use actix_web::{http::header, web, App, HttpServer}; +use dotenv::dotenv; +use sqlx::{postgres::PgPoolOptions, Pool, Postgres}; + +pub struct AppState { + db: Pool, +} + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + if std::env::var_os("RUST_LOG").is_none() { + std::env::set_var("RUST_LOG", "actix_web=info"); + } + dotenv().ok(); + env_logger::init(); + + let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set"); + let pool = match PgPoolOptions::new() + .max_connections(10) + .connect(&database_url) + .await + { + Ok(pool) => { + println!("✅Connection to the database is successful!"); + pool + } + Err(err) => { + println!("🔥 Failed to connect to the database: {:?}", err); + std::process::exit(1); + } + }; + + println!("🚀 Server started successfully"); + + HttpServer::new(move || { + let cors = Cors::default() + .allowed_origin("http://localhost:3000") + .allowed_methods(vec!["GET", "POST", "PATCH", "DELETE"]) + .allowed_headers(vec![ + header::CONTENT_TYPE, + header::AUTHORIZATION, + header::ACCEPT, + ]) + .supports_credentials(); + App::new() + .app_data(web::Data::new(AppState { db: pool.clone() })) + .configure(handler::config) + .wrap(cors) + .wrap(Logger::default()) + }) + .bind(("0.0.0.0", 8000))? + .run() + .await +} diff --git a/fullstack-rust-app/backend/src/model.rs b/fullstack-rust-app/backend/src/model.rs new file mode 100644 index 0000000..65e8400 --- /dev/null +++ b/fullstack-rust-app/backend/src/model.rs @@ -0,0 +1,15 @@ +use serde::{Deserialize, Serialize}; +use sqlx::FromRow; +use uuid::Uuid; + +#[derive(Debug, FromRow, Deserialize, Serialize)] +#[allow(non_snake_case)] +pub struct FeedbackModel { + pub id: Uuid, + pub text: String, + pub rating: i32, + #[serde(rename = "createdAt")] + pub created_at: Option>, + #[serde(rename = "updatedAt")] + pub updated_at: Option>, +} diff --git a/fullstack-rust-app/backend/src/schema.rs b/fullstack-rust-app/backend/src/schema.rs new file mode 100644 index 0000000..0880485 --- /dev/null +++ b/fullstack-rust-app/backend/src/schema.rs @@ -0,0 +1,24 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Debug)] +pub struct FilterOptions { + pub page: Option, + pub limit: Option, +} + +#[derive(Deserialize, Debug)] +pub struct ParamOptions { + pub id: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct CreateFeedbackSchema { + pub rating: i32, + pub text: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct UpdateFeedbackSchema { + pub rating: Option, + pub text: Option, +} diff --git a/fullstack-rust-app/common/Cargo.lock b/fullstack-rust-app/common/Cargo.lock new file mode 100644 index 0000000..db83ec7 --- /dev/null +++ b/fullstack-rust-app/common/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "common" +version = "0.1.0" diff --git a/fullstack-rust-app/common/Cargo.toml b/fullstack-rust-app/common/Cargo.toml new file mode 100644 index 0000000..b1299a8 --- /dev/null +++ b/fullstack-rust-app/common/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "common" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { version = "1.0.152", features = ["derive"] } +uuid = { version = "1.3.0", features = ["serde", "v4", "js"] } diff --git a/fullstack-rust-app/common/src/lib.rs b/fullstack-rust-app/common/src/lib.rs new file mode 100644 index 0000000..a1141d3 --- /dev/null +++ b/fullstack-rust-app/common/src/lib.rs @@ -0,0 +1,32 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, Serialize, PartialEq, Clone)] +pub struct Feedback { + pub id: uuid::Uuid, + pub text: String, + pub rating: u8, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct FeedbackData { + pub feedback: Feedback, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct FeedbackResponse { + pub status: String, + pub data: FeedbackData, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct FeedbackListResponse { + pub status: String, + pub results: i32, + pub feedbacks: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ErrorResponse { + pub status: String, + pub message: String, +} diff --git a/fullstack-rust-app/frontend/.gitignore b/fullstack-rust-app/frontend/.gitignore new file mode 100644 index 0000000..98cbc44 --- /dev/null +++ b/fullstack-rust-app/frontend/.gitignore @@ -0,0 +1,3 @@ +/target + +/dist diff --git a/fullstack-rust-app/frontend/Cargo.lock b/fullstack-rust-app/frontend/Cargo.lock new file mode 100644 index 0000000..3fa5514 --- /dev/null +++ b/fullstack-rust-app/frontend/Cargo.lock @@ -0,0 +1,1006 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anymap" +version = "1.0.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1f8f5a6f3d50d89e3797d7593a50f96bb2aaa20ca0cc7be1fb673232c91d72" + +[[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + +[[package]] +name = "async-trait" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "boolinator" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" + +[[package]] +name = "futures-io" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" + +[[package]] +name = "futures-macro" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" + +[[package]] +name = "futures-task" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" + +[[package]] +name = "futures-util" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gloo" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a4bef6b277b3ab073253d4bca60761240cf8d6998f4bd142211957b69a61b20" +dependencies = [ + "gloo-console", + "gloo-dialogs", + "gloo-events", + "gloo-file", + "gloo-history", + "gloo-net", + "gloo-render", + "gloo-storage", + "gloo-timers", + "gloo-utils", + "gloo-worker", +] + +[[package]] +name = "gloo-console" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b7ce3c05debe147233596904981848862b068862e9ec3e34be446077190d3f" +dependencies = [ + "gloo-utils", + "js-sys", + "serde", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-dialogs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67062364ac72d27f08445a46cab428188e2e224ec9e37efdba48ae8c289002e6" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-events" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b107f8abed8105e4182de63845afcc7b69c098b7852a813ea7462a320992fc" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-file" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d5564e570a38b43d78bdc063374a0c3098c4f0d64005b12f9bbe87e869b6d7" +dependencies = [ + "gloo-events", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-history" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd451019e0b7a2b8a7a7b23e74916601abf1135c54664e57ff71dcc26dfcdeb7" +dependencies = [ + "gloo-events", + "gloo-utils", + "serde", + "serde-wasm-bindgen", + "serde_urlencoded", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-net" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9902a044653b26b99f7e3693a42f171312d9be8b26b5697bd1e43ad1f8a35e10" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-render" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd9306aef67cfd4449823aadcd14e3958e0800aa2183955a309112a84ec7764" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-storage" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6ab60bf5dbfd6f0ed1f7843da31b41010515c745735c970e821945ca91e480" +dependencies = [ + "gloo-utils", + "js-sys", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8e8fc851e9c7b9852508bc6e3f690f452f474417e8545ec9857b7f7377036b5" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gloo-worker" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13471584da78061a28306d1359dd0178d8d6fc1c7c80e5e35d27260346e0516a" +dependencies = [ + "anymap2", + "bincode", + "gloo-console", + "gloo-utils", + "js-sys", + "serde", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "implicit-clone" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40fc102e70475c320b185cd18c1e48bba2d7210b63970a4d581ef903e4368ef7" +dependencies = [ + "indexmap", +] + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pinned" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a829027bd95e54cfe13e3e258a1ae7b645960553fb82b75ff852c29688ee595b" +dependencies = [ + "futures", + "rustversion", + "thiserror", +] + +[[package]] +name = "prettyplease" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e97e3215779627f01ee256d2fad52f3d95e8e1c11e9fc6fd08f7cd455d5d5c78" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prokio" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b55e106e5791fa5a13abd13c85d6127312e8e09098059ca2bc9b03ca4cf488" +dependencies = [ + "futures", + "gloo", + "num_cpus", + "once_cell", + "pin-project", + "pinned", + "tokio", + "tokio-stream", + "wasm-bindgen-futures", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rust-yew-app" +version = "0.1.0" +dependencies = [ + "gloo", + "serde", + "uuid", + "wasm-bindgen", + "web-sys", + "yew", + "yewdux", +] + +[[package]] +name = "rustversion" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "serde" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +dependencies = [ + "autocfg", + "pin-project-lite", + "windows-sys", +] + +[[package]] +name = "tokio-stream" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb52b74f05dbf495a8fba459fdc331812b96aa086d9eb78101fa0d4569c3313" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "uuid" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" +dependencies = [ + "getrandom", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "yew" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dbecfe44343b70cc2932c3eb445425969ae21754a8ab3a0966981c1cf7af1cc" +dependencies = [ + "console_error_panic_hook", + "futures", + "gloo", + "implicit-clone", + "indexmap", + "js-sys", + "prokio", + "rustversion", + "serde", + "slab", + "thiserror", + "tokio", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "yew-macro", +] + +[[package]] +name = "yew-macro" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b64c253c1d401f1ea868ca9988db63958cfa15a69f739101f338d6f05eea8301" +dependencies = [ + "boolinator", + "once_cell", + "prettyplease", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "yewdux" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653ba356bc60d1804c28ec6cc8ddac2741c686bde2a65074d07faba735914464" +dependencies = [ + "anymap", + "async-trait", + "log", + "serde", + "serde_json", + "slab", + "thiserror", + "wasm-bindgen", + "web-sys", + "yew", + "yewdux-macros", +] + +[[package]] +name = "yewdux-macros" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25bcd923aceaa85cb4affad8657cc36e3d6b6932740e711574182f7817492739" +dependencies = [ + "darling", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] diff --git a/fullstack-rust-app/frontend/Cargo.toml b/fullstack-rust-app/frontend/Cargo.toml new file mode 100644 index 0000000..be1910b --- /dev/null +++ b/fullstack-rust-app/frontend/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "frontend" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +gloo = "0.8.0" +serde = { version = "1.0.152", features = ["derive"] } +uuid = { version = "1.3.0", features = ["serde", "v4", "js"] } +wasm-bindgen = "0.2.84" +web-sys = { version = "0.3.61", features = ["HtmlInputElement", "Window"] } +yew = { version = "0.20.0", features = ["csr"] } +yewdux = "0.9.2" +reqwasm = "0.5.0" +wasm-bindgen-futures = "0.4.34" +serde_json = "1.0.93" +common = { version = "0.1.0", path = "../common" } diff --git a/fullstack-rust-app/frontend/Dockerfile.bk b/fullstack-rust-app/frontend/Dockerfile.bk new file mode 100644 index 0000000..78a0876 --- /dev/null +++ b/fullstack-rust-app/frontend/Dockerfile.bk @@ -0,0 +1,40 @@ +FROM rust:1.73-slim-bullseye + +RUN apt --yes update && apt --yes install git pkg-config curl libssl-dev ca-certificates curl gnupg +RUN cargo install trunk wasm-bindgen-cli +RUN rustup target add wasm32-unknown-unknown + +WORKDIR /usr/src/fullstack-rust-app +COPY ../ . + +# ENV NVM_DIR /usr/local/nvm +# ENV NODE_VERSION 18.16.1 + +# RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash - +# RUN source $NVM_DIR/nvm.sh \ +# && nvm install $NODE_VERSION \ +# && nvm alias default $NODE_VERSION \ +# && nvm use default + +RUN curl -sL https://deb.nodesource.com/setup_18.x | bash - +RUN apt-get install -y nodejs + +#ENV NVM_DIR=/root/.nvm + +# RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash +# RUN . ~/.nvm/nvm.sh && nvm install 18.16.1 + +# ENV NODE_VERSION=18.16.1 +# RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash +# ENV NVM_DIR="/root/.nvm" +# RUN . "$NVM_DIR/nvm.sh" && nvm install ${NODE_VERSION} +# RUN . "$NVM_DIR/nvm.sh" && nvm use v${NODE_VERSION} +# RUN . "$NVM_DIR/nvm.sh" && nvm alias default v${NODE_VERSION} +# ENV PATH="/root/.nvm/versions/node/v${NODE_VERSION}/bin/:${PATH}" +# RUN node --version +# RUN npm --version + +RUN npx -v +RUN npx tailwindcss-cli@latest + +RUN cd frontend && trunk build --release \ No newline at end of file diff --git a/fullstack-rust-app/frontend/Makefile b/fullstack-rust-app/frontend/Makefile new file mode 100644 index 0000000..f209bd4 --- /dev/null +++ b/fullstack-rust-app/frontend/Makefile @@ -0,0 +1,14 @@ +install: + cargo add yew --features csr + cargo add serde_json + cargo add serde --features derive + cargo add uuid --features "serde v4 js" + cargo add reqwasm + cargo add gloo + cargo add yewdux + cargo add wasm-bindgen + cargo add web-sys --features "HtmlInputElement Window" + cargo add wasm-bindgen-futures + +start-app: + trunk serve --port 3000 \ No newline at end of file diff --git a/fullstack-rust-app/frontend/Trunk.toml b/fullstack-rust-app/frontend/Trunk.toml new file mode 100644 index 0000000..1c857c5 --- /dev/null +++ b/fullstack-rust-app/frontend/Trunk.toml @@ -0,0 +1,6 @@ +[[hooks]] +stage = "post_build" +command = "sh" +command_arguments = ["-c", "npx tailwindcss -i ./styles/tailwind.css -o ./dist/.stage/index.css"] +[[proxy]] +backend = "http://localhost:80/api" \ No newline at end of file diff --git a/fullstack-rust-app/frontend/index.html b/fullstack-rust-app/frontend/index.html new file mode 100644 index 0000000..eacfa72 --- /dev/null +++ b/fullstack-rust-app/frontend/index.html @@ -0,0 +1,12 @@ + + + + + + + + + My First Yew App + + + diff --git a/fullstack-rust-app/frontend/logo.svg b/fullstack-rust-app/frontend/logo.svg new file mode 100644 index 0000000..e838a2c --- /dev/null +++ b/fullstack-rust-app/frontend/logo.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/fullstack-rust-app/frontend/src/api.rs b/fullstack-rust-app/frontend/src/api.rs new file mode 100644 index 0000000..7b2d06b --- /dev/null +++ b/fullstack-rust-app/frontend/src/api.rs @@ -0,0 +1,110 @@ +use common::{ErrorResponse, Feedback, FeedbackListResponse, FeedbackResponse}; +use reqwasm::http; + +pub async fn api_create_feedback(feedback_data: &str) -> Result { + let response = match http::Request::post("http://localhost:8000/api/feedbacks/") + .header("Content-Type", "application/json") + .body(feedback_data) + .send() + .await + { + Ok(res) => res, + Err(_) => return Err("Failed to make request".to_string()), + }; + + if response.status() != 200 { + let error_response = response.json::().await; + if let Ok(error_response) = error_response { + return Err(error_response.message); + } else { + return Err(format!("API error: {}", response.status())); + } + } + + let res_json = response.json::().await; + match res_json { + Ok(data) => Ok(data.data.feedback), + Err(_) => Err("Failed to parse response".to_string()), + } +} + +pub async fn _api_fetch_single_feedback(feedback_id: &str) -> Result { + let response = match http::Request::get( + format!("http://localhost:8000/api/feedbacks/{}", feedback_id).as_str(), + ) + .send() + .await + { + Ok(res) => res, + Err(_) => return Err("Failed to make request".to_string()), + }; + + if response.status() != 200 { + let error_response = response.json::().await; + if let Ok(error_response) = error_response { + return Err(error_response.message); + } else { + return Err(format!("API error: {}", response.status())); + } + } + + let res_json = response.json::().await; + match res_json { + Ok(data) => Ok(data.data.feedback), + Err(_) => Err("Failed to parse response".to_string()), + } +} + +pub async fn api_fetch_feedbacks((page, limit): (i32, i32)) -> Result, String> { + let response = match http::Request::get( + format!( + "http://localhost:8000/api/feedbacks?page={}&limit={}", + page, limit + ) + .as_str(), + ) + .send() + .await + { + Ok(res) => res, + Err(_) => return Err("Failed to make request".to_string()), + }; + + if response.status() != 200 { + let error_response = response.json::().await; + if let Ok(error_response) = error_response { + return Err(error_response.message); + } else { + return Err(format!("API error: {}", response.status())); + } + } + + let res_json = response.json::().await; + match res_json { + Ok(data) => Ok(data.feedbacks), + Err(_) => Err("Failed to parse response".to_string()), + } +} + +pub async fn api_delete_feedback(feedback_id: &str) -> Result<(), String> { + let response = match http::Request::delete( + format!("http://localhost:8000/api/feedbacks/{}", feedback_id).as_str(), + ) + .send() + .await + { + Ok(res) => res, + Err(_) => return Err("Failed to make request".to_string()), + }; + + if response.status() != 204 { + let error_response = response.json::().await; + if let Ok(error_response) = error_response { + return Err(error_response.message); + } else { + return Err(format!("API error: {}", response.status())); + } + } + + Ok(()) +} diff --git a/fullstack-rust-app/frontend/src/components/alert.rs b/fullstack-rust-app/frontend/src/components/alert.rs new file mode 100644 index 0000000..51c1b1a --- /dev/null +++ b/fullstack-rust-app/frontend/src/components/alert.rs @@ -0,0 +1,47 @@ +use gloo::timers::callback::Timeout; +use yew::prelude::*; +use yewdux::prelude::use_store; + +use crate::store::{set_hide_alert, Store}; + +#[derive(Debug, PartialEq, Properties)] +pub struct Props { + pub message: String, + pub delay_ms: u32, +} + +#[function_component] +pub fn AlertComponent(props: &Props) -> Html { + let (store, dispatch) = use_store::(); + let show_alert = store.alert_input.show_alert; + + use_effect_with_deps( + move |(show_alert, dispatch, delay_ms)| { + let cloned_dispatch = dispatch.clone(); + if *show_alert { + let handle = + Timeout::new(*delay_ms, move || set_hide_alert(cloned_dispatch)).forget(); + let clear_handle = move || { + web_sys::Window::clear_timeout_with_handle( + &web_sys::window().unwrap(), + handle.as_f64().unwrap() as i32, + ); + }; + + Box::new(clear_handle) as Box + } else { + Box::new(|| {}) as Box + } + }, + (show_alert, dispatch.clone(), props.delay_ms), + ); + + html! { +
+

+ {"i"} + {props.message.clone()} +

+
+ } +} diff --git a/fullstack-rust-app/frontend/src/components/feedback_form.rs b/fullstack-rust-app/frontend/src/components/feedback_form.rs new file mode 100644 index 0000000..491535c --- /dev/null +++ b/fullstack-rust-app/frontend/src/components/feedback_form.rs @@ -0,0 +1,126 @@ +use super::rating::Rating; +use crate::{ + api::api_create_feedback, + store::{set_feedback, set_loading, set_show_alert, Store}, +}; + +use wasm_bindgen::JsCast; +use wasm_bindgen_futures::spawn_local; +use web_sys::HtmlInputElement; +use yew::prelude::*; +use yewdux::prelude::*; + +#[function_component] +pub fn FeedbackForm() -> Html { + let (store, dispatch) = use_store::(); + let loading = &store.loading; + let text = use_state(String::new); + let rating = use_state(|| 10_u8); + let min = use_state(|| 10); + let message = use_state(|| Option::::None); + + let text_input_ref = use_node_ref(); + + let handle_select = { + let rating = rating.clone(); + Callback::from(move |value| { + rating.set(value); + }) + }; + + let handle_input = { + let text = text.clone(); + let message = message.clone(); + Callback::from(move |event: InputEvent| { + let target = event.target().unwrap(); + let value = target.unchecked_into::().value(); + message.set(None); + text.set(value); + }) + }; + + let on_submit = { + let cloned_dispatch = dispatch.clone(); + let cloned_text_input_ref = text_input_ref.clone(); + let cloned_rating = rating.clone(); + let cloned_text = text.clone(); + let message = message.clone(); + + Callback::from(move |event: SubmitEvent| { + let text_input_ref = cloned_text_input_ref.clone(); + let text = cloned_text.clone(); + let rating = cloned_rating.clone(); + let dispatch = cloned_dispatch.clone(); + + event.prevent_default(); + set_loading(true, dispatch.clone()); + + if text.trim().len() < *min { + message.set(Some("Text must be at least 10 characters".to_string())); + set_loading(false, dispatch.clone()); + return; + } + + let feedback_data = serde_json::json!({ + "text": text.to_string(), + "rating": *rating + }); + + spawn_local(async move { + set_loading(true, dispatch.clone()); + let text_input = text_input_ref.cast::().unwrap(); + text_input.set_value(""); + text.set(String::new()); + rating.set(10); + + let response = api_create_feedback(feedback_data.to_string().as_str()).await; + + match response { + Ok(feedback) => { + set_loading(false, dispatch.clone()); + set_show_alert("Feeback added successfully".to_string(), dispatch.clone()); + set_feedback(feedback, dispatch); + } + Err(e) => { + set_loading(false, dispatch.clone()); + set_show_alert(e.to_string(), dispatch); + } + } + }); + }) + }; + + html! { +
+
+

{"How would you rate your service with us?"}

+
+
+ +
+ + +
+ {if let Some(msg) = message.as_ref() { + html! {
{msg.clone()}
} + } else { + html! {} + }} + +
+ } +} diff --git a/fullstack-rust-app/frontend/src/components/feedback_item.rs b/fullstack-rust-app/frontend/src/components/feedback_item.rs new file mode 100644 index 0000000..edaab27 --- /dev/null +++ b/fullstack-rust-app/frontend/src/components/feedback_item.rs @@ -0,0 +1,65 @@ +use wasm_bindgen_futures::spawn_local; +use yew::prelude::*; +use yewdux::prelude::*; + +use crate::{ + api::api_delete_feedback, + store::{delete_feedback, set_loading, set_show_alert, Store}, +}; +use common::Feedback; + +#[derive(Debug, PartialEq, Properties)] +pub struct Props { + pub feedback: Feedback, +} + +fn confirm_delete(message: &str) -> bool { + web_sys::Window::confirm_with_message(&web_sys::window().unwrap(), message).unwrap() +} + +#[function_component] +pub fn FeedbackItem(props: &Props) -> Html { + let (_, dispatch) = use_store::(); + + let on_delete = { + let cloned_dispatch = dispatch.clone(); + let feedback_id = props.feedback.id.clone(); + Callback::from(move |_: MouseEvent| { + let dispatch = cloned_dispatch.clone(); + let confirmed = confirm_delete("Do you really want to delete this item?"); + + if confirmed { + spawn_local(async move { + set_loading(true, dispatch.clone()); + let response = api_delete_feedback(feedback_id.to_string().as_str()).await; + match response { + Ok(_) => { + set_loading(false, dispatch.clone()); + set_show_alert( + "Feedback deleted successfully".to_string(), + dispatch.clone(), + ); + delete_feedback(feedback_id, dispatch); + } + Err(e) => { + set_loading(false, dispatch.clone()); + set_show_alert(e.to_string(), dispatch); + } + } + }); + } + }) + }; + + html! { +
+
+ {props.feedback.rating} +
+ +

+ {&props.feedback.text} +

+
+ } +} diff --git a/fullstack-rust-app/frontend/src/components/feedback_list.rs b/fullstack-rust-app/frontend/src/components/feedback_list.rs new file mode 100644 index 0000000..b48fc42 --- /dev/null +++ b/fullstack-rust-app/frontend/src/components/feedback_list.rs @@ -0,0 +1,46 @@ +use yew::prelude::*; +use yewdux::prelude::*; + +use super::feedback_item::FeedbackItem; +use crate::{ + api::api_fetch_feedbacks, + store::{set_feedback_list, set_loading, set_show_alert, Store}, +}; + +#[function_component] +pub fn FeedbackList() -> Html { + let (store, dispatch) = use_store::(); + let feedback_list = store.feedbacks.clone(); + + use_effect_with_deps( + move |_| { + let dispatch = dispatch.clone(); + wasm_bindgen_futures::spawn_local(async move { + set_loading(true, dispatch.clone()); + let response = api_fetch_feedbacks((1, 10)).await; + match response { + Ok(feedbacks) => { + set_loading(false, dispatch.clone()); + set_feedback_list(feedbacks, dispatch); + } + Err(e) => { + set_loading(false, dispatch.clone()); + set_show_alert(e.to_string(), dispatch); + } + } + }); + }, + (), + ); + + html! { +
+ { + feedback_list.into_iter().map(|feedback|{ + let key = feedback.id.to_string(); + html!{} + }).collect::() + } +
+ } +} diff --git a/fullstack-rust-app/frontend/src/components/feedback_stats.rs b/fullstack-rust-app/frontend/src/components/feedback_stats.rs new file mode 100644 index 0000000..8352c5c --- /dev/null +++ b/fullstack-rust-app/frontend/src/components/feedback_stats.rs @@ -0,0 +1,24 @@ +use yew::prelude::*; +use yewdux::prelude::*; + +use crate::store::Store; + +#[function_component] +pub fn FeedbackStats() -> Html { + let (store, _) = use_store::(); + let count = store.feedbacks.len(); + let sum: u32 = store.feedbacks.iter().map(|f| u32::from(f.rating)).sum(); + + let average = if count > 0 { + format!("{:.2}", sum as f32 / count as f32) + } else { + "0.0".to_string() + }; + + html! { +
+

{count} {" "} {"Reviews"}

+

{"Ratings Average: "} {average}

+
+ } +} diff --git a/fullstack-rust-app/frontend/src/components/mod.rs b/fullstack-rust-app/frontend/src/components/mod.rs new file mode 100644 index 0000000..7b1fd35 --- /dev/null +++ b/fullstack-rust-app/frontend/src/components/mod.rs @@ -0,0 +1,6 @@ +pub mod alert; +pub mod feedback_form; +pub mod feedback_item; +pub mod feedback_list; +pub mod feedback_stats; +pub mod rating; diff --git a/fullstack-rust-app/frontend/src/components/rating.rs b/fullstack-rust-app/frontend/src/components/rating.rs new file mode 100644 index 0000000..b31d61a --- /dev/null +++ b/fullstack-rust-app/frontend/src/components/rating.rs @@ -0,0 +1,42 @@ +use wasm_bindgen::JsCast; +use web_sys::HtmlInputElement; +use yew::prelude::*; + +#[derive(Properties, Clone, PartialEq)] +pub struct Props { + #[prop_or_default] + pub selected: u8, + #[prop_or_default] + pub onchange: Callback, +} + +#[function_component] +pub fn Rating(props: &Props) -> Html { + let selected = props.selected; + + let onchange = props.onchange.clone(); + let on_input_change = Callback::from(move |event: Event| { + let target = event.target().unwrap(); + let value = target.unchecked_into::().value(); + let selected = value.parse::().unwrap(); + onchange.emit(selected) + }); + + html! { +
    + { for (1..=10).map(|i| { + let label = i.to_string(); + let id = format!("num{}", i); + + html! { +
  • + + +
  • + } + })} +
+ } +} diff --git a/fullstack-rust-app/frontend/src/main.rs b/fullstack-rust-app/frontend/src/main.rs new file mode 100644 index 0000000..c5452b1 --- /dev/null +++ b/fullstack-rust-app/frontend/src/main.rs @@ -0,0 +1,55 @@ +mod api; +mod components; +mod store; + +use components::{ + alert::{AlertComponent, Props as AlertProps}, + feedback_form::FeedbackForm, + feedback_list::FeedbackList, + feedback_stats::FeedbackStats, +}; +use store::Store; +use yew::prelude::*; +use yewdux::prelude::*; + +#[function_component] +fn App() -> Html { + let (store, _) = use_store::(); + let message = store.alert_input.alert_message.clone(); + let show_alert = store.alert_input.show_alert; + let loading = &store.loading; + + let alert_props = AlertProps { + message, + delay_ms: 5000, + }; + html! { + <> + if show_alert { + + } +
+ + + +
+ if *loading { +
+ {"Loading..."} +
+ } + + } +} + +fn main() { + yew::Renderer::::new().render(); +} diff --git a/fullstack-rust-app/frontend/src/store.rs b/fullstack-rust-app/frontend/src/store.rs new file mode 100644 index 0000000..b445a2d --- /dev/null +++ b/fullstack-rust-app/frontend/src/store.rs @@ -0,0 +1,56 @@ +use common::Feedback; +use serde::{Deserialize, Serialize}; +use yewdux::prelude::*; + +#[derive(Debug, PartialEq, Serialize, Deserialize, Default, Clone)] +pub struct AlertInput { + pub show_alert: bool, + pub alert_message: String, +} + +#[derive(Default, PartialEq, Serialize, Deserialize, Store, Clone)] +#[store(storage = "local", storage_tab_sync)] +pub struct Store { + pub feedbacks: Vec, + pub loading: bool, + pub alert_input: AlertInput, +} + +pub fn set_feedback(feedback: Feedback, dispatch: Dispatch) { + dispatch.reduce_mut(move |store| { + store.feedbacks.insert(0, feedback); + }) +} + +pub fn set_feedback_list(feedbacks: Vec, dispatch: Dispatch) { + dispatch.reduce_mut(move |store| { + store.feedbacks = feedbacks; + }) +} + +pub fn delete_feedback(id: uuid::Uuid, dispatch: Dispatch) { + dispatch.reduce_mut(move |store| { + store.feedbacks.retain(|f| f.id != id); + }) +} + +pub fn set_loading(loading: bool, dispatch: Dispatch) { + dispatch.reduce_mut(move |store| { + store.loading = loading; + }) +} + +pub fn set_show_alert(message: String, dispatch: Dispatch) { + dispatch.reduce_mut(move |store| { + store.alert_input = AlertInput { + alert_message: message, + show_alert: true, + }; + }) +} + +pub fn set_hide_alert(dispatch: Dispatch) { + dispatch.reduce_mut(move |store| { + store.alert_input.show_alert = false; + }) +} diff --git a/fullstack-rust-app/frontend/styles/tailwind.css b/fullstack-rust-app/frontend/styles/tailwind.css new file mode 100644 index 0000000..f9d3d51 --- /dev/null +++ b/fullstack-rust-app/frontend/styles/tailwind.css @@ -0,0 +1,13 @@ +@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap'); + +@tailwind base; +@tailwind components; +@tailwind utilities; + +html { + font-family: 'Poppins', sans-serif; +} + +body { + background-color: #202142; +} \ No newline at end of file diff --git a/fullstack-rust-app/frontend/tailwind.config.js b/fullstack-rust-app/frontend/tailwind.config.js new file mode 100644 index 0000000..68a1c1a --- /dev/null +++ b/fullstack-rust-app/frontend/tailwind.config.js @@ -0,0 +1,24 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + "./index.html", + "./src/**/*.{rs,html}" + ], + theme: { + extend: { + fontFamily: { + Poppins: ['Poppins, sans-serif'], + }, + container: { + center: true, + padding: '1rem', + screens: { + lg: '1125px', + xl: '1125px', + '2xl': '1125px', + }, + }, + }, + }, + plugins: [], +}; From 9659b50a0f05f8e318b77e99fbe6c8c1e4b9d250 Mon Sep 17 00:00:00 2001 From: Aashi Modi Date: Tue, 7 Nov 2023 11:09:19 +0530 Subject: [PATCH 2/7] updated files --- Acornfile | 3 --- fullstack-rust-app/README.md => README.md | 0 2 files changed, 3 deletions(-) rename fullstack-rust-app/README.md => README.md (100%) diff --git a/Acornfile b/Acornfile index 927a02a..86733fd 100644 --- a/Acornfile +++ b/Acornfile @@ -6,15 +6,12 @@ readme: "./README.md" args: { // Name of the database to create. Defaults to "instance" dbName: "rust-sqlx" - // Provide user for mysql. If the value is not provided, it will default to - dbUser: "root" } services: db: { image: "ghcr.io/acorn-io/postgres:v15.#-#" serviceArgs: { dbName: args.dbName - dbUser: "root" } } diff --git a/fullstack-rust-app/README.md b/README.md similarity index 100% rename from fullstack-rust-app/README.md rename to README.md From 4dbf53f7e3b33b875f050cbaf71ebd5255ad8186 Mon Sep 17 00:00:00 2001 From: Aashi Modi Date: Wed, 8 Nov 2023 20:58:09 +0530 Subject: [PATCH 3/7] added acornfile --- Acornfile | 18 +- Dockerfile.backend | 6 +- Dockerfile.frontend | 2 +- fullstack-rust-app/Cargo.lock | 408 ++++++++++++++++++++++--- fullstack-rust-app/backend/src/main.rs | 2 +- fullstack-rust-app/frontend/Cargo.toml | 2 + fullstack-rust-app/frontend/Trunk.toml | 4 +- fullstack-rust-app/frontend/src/api.rs | 29 +- 8 files changed, 413 insertions(+), 58 deletions(-) diff --git a/Acornfile b/Acornfile index 86733fd..371674d 100644 --- a/Acornfile +++ b/Acornfile @@ -13,12 +13,11 @@ services: db: { serviceArgs: { dbName: args.dbName } - } containers: { backend: { - image: "ghcr.io/aashimodi14/rust-backend:v2" + image: "ghcr.io/aashimodi14/rust-backend:v4" env: { "POSTGRES_HOST": "@{service.db.address}" "POSTGRES_PORT": "@{service.db.port.5432}" @@ -30,17 +29,22 @@ containers: { "PGADMIN_DEFAULT_PASSWORD":"password123" } ports: { - expose: "8000/http" + publish: "8000:8000/http" } - cmd: ["/bin/sh", "-c", "cd backend && sqlx migrate run; cargo run"] + memory: 1500Mi + cmd: ["/bin/sh", "-c", "cd /usr/src/fullstack-rust-app/backend && sqlx migrate run; cargo run --bin backend -- --port 8000"] consumes: ["db"] } frontend: { - image: "ghcr.io/aashimodi14/rust-frontend:v2" - cmd: ["/bin/sh", "-c", "cd frontend && trunk serve --address 0.0.0.0 --port 3000"] + image: "ghcr.io/aashimodi14/rust-frontend:v4" + cmd: ["/bin/sh", "-c", "cd /usr/src/fullstack-rust-app/frontend && BACKEND_SERVER=$BACKEND_SERVER trunk serve --address 0.0.0.0 --port 3000"] ports: { - publish: "3000/http" + publish: "3000:3000/http" + } + env: { + "BACKEND_SERVER": "@{services.backend.endpoint}" } + memory: 2048Mi dependsOn: ["backend"] } } diff --git a/Dockerfile.backend b/Dockerfile.backend index e3611ce..a7d55ec 100644 --- a/Dockerfile.backend +++ b/Dockerfile.backend @@ -1,8 +1,8 @@ FROM rust:1.73-slim-bullseye RUN apt --yes update && apt --yes install git pkg-config curl libssl-dev ca-certificates curl gnupg -RUN cargo install wasm-bindgen-cli +RUN cargo install wasm-bindgen-cli sqlx-cli RUN rustup target add wasm32-unknown-unknown -WORKDIR /usr/src/fullstack-rust-app/ -COPY ./fullstack-rust-app/ . \ No newline at end of file +WORKDIR /usr/src/ +COPY . . \ No newline at end of file diff --git a/Dockerfile.frontend b/Dockerfile.frontend index 7e4a83f..c573656 100644 --- a/Dockerfile.frontend +++ b/Dockerfile.frontend @@ -4,7 +4,7 @@ RUN apt --yes update && apt --yes install git pkg-config curl libssl-dev ca-cert RUN cargo install trunk wasm-bindgen-cli RUN rustup target add wasm32-unknown-unknown -WORKDIR /usr/src/fullstack-rust-app +WORKDIR /usr/src/ COPY . . RUN curl -sL https://deb.nodesource.com/setup_18.x | bash - diff --git a/fullstack-rust-app/Cargo.lock b/fullstack-rust-app/Cargo.lock index 78c2a00..0e9f85b 100644 --- a/fullstack-rust-app/Cargo.lock +++ b/fullstack-rust-app/Cargo.lock @@ -272,6 +272,138 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0c4a4f319e45986f347ee47fef8bf5e81c9abc3f6f58dc2391439f30df65f0" +dependencies = [ + "async-lock", + "async-task", + "concurrent-queue", + "fastrand 2.0.1", + "futures-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite", + "log", + "parking", + "polling", + "rustix 0.37.3", + "slab", + "socket2", + "waker-fn", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-native-tls" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d57d4cec3c647232e1094dc013546c0b33ce785d8aeb251e1f20dfaf8a9a13fe" +dependencies = [ + "futures-util", + "native-tls", + "thiserror", + "url", +] + +[[package]] +name = "async-process" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" +dependencies = [ + "async-io", + "async-lock", + "autocfg", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 0.37.3", + "signal-hook", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "async-process", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1" + [[package]] name = "async-trait" version = "0.1.64" @@ -292,6 +424,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.1.0" @@ -350,6 +488,21 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blocking" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "atomic-waker", + "fastrand 1.9.0", + "futures-lite", + "log", +] + [[package]] name = "boolinator" version = "2.4.0" @@ -453,6 +606,15 @@ dependencies = [ "uuid", ] +[[package]] +name = "concurrent-queue" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -558,6 +720,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "ctor" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "cxx" version = "1.0.91" @@ -735,6 +907,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "errno" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "errno-dragonfly" version = "0.1.2" @@ -760,6 +942,12 @@ dependencies = [ "instant", ] +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "flate2" version = "1.0.25" @@ -805,7 +993,9 @@ name = "frontend" version = "0.1.0" dependencies = [ "common", + "dotenv", "gloo", + "lazy_static", "reqwasm", "serde", "serde_json", @@ -864,6 +1054,21 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + [[package]] name = "futures-macro" version = "0.3.26" @@ -1079,6 +1284,8 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" dependencies = [ + "futures-channel", + "futures-core", "js-sys", "wasm-bindgen", ] @@ -1313,7 +1520,7 @@ checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix", + "rustix 0.36.8", "windows-sys 0.45.0", ] @@ -1350,6 +1557,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "language-tags" version = "0.3.2" @@ -1383,6 +1599,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + [[package]] name = "local-channel" version = "0.1.3" @@ -1418,6 +1640,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", + "value-bag", ] [[package]] @@ -1576,6 +1799,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + [[package]] name = "parking_lot" version = "0.11.2" @@ -1685,6 +1914,22 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1852,10 +2097,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" dependencies = [ "bitflags", - "errno", + "errno 0.2.8", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.1.4", + "windows-sys 0.45.0", +] + +[[package]] +name = "rustix" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b24138615de35e32031d041a09032ef3487a616d901ca4db224e7d557efae2" +dependencies = [ + "bitflags", + "errno 0.3.5", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", "windows-sys 0.45.0", ] @@ -1997,6 +2256,16 @@ dependencies = [ "digest", ] +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -2098,7 +2367,6 @@ dependencies = [ "sqlx-rt", "stringprep", "thiserror", - "tokio-stream", "url", "uuid", "whoami", @@ -2132,10 +2400,9 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24c5b2d25fa654cc5f841750b8e1cdedbe21189bf9a9382ee90bfa9dd3562396" dependencies = [ + "async-native-tls", + "async-std", "native-tls", - "once_cell", - "tokio", - "tokio-native-tls", ] [[package]] @@ -2178,9 +2445,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" dependencies = [ "cfg-if", - "fastrand", + "fastrand 1.9.0", "redox_syscall", - "rustix", + "rustix 0.36.8", "windows-sys 0.42.0", ] @@ -2277,7 +2544,6 @@ dependencies = [ "libc", "memchr", "mio", - "num_cpus", "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", @@ -2285,16 +2551,6 @@ dependencies = [ "windows-sys 0.45.0", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-stream" version = "0.1.12" @@ -2420,6 +2676,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "value-bag" +version = "1.0.0-alpha.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" +dependencies = [ + "ctor", + "version_check", +] + [[package]] name = "vcpkg" version = "0.2.15" @@ -2432,6 +2698,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "waker-fn" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" @@ -2569,13 +2841,13 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.1", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm 0.42.1", + "windows_x86_64_msvc 0.42.1", ] [[package]] @@ -2584,7 +2856,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.1", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -2593,13 +2874,28 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.1", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm 0.42.1", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -2608,42 +2904,84 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_i686_gnu" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_x86_64_gnu" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "yew" version = "0.20.0" diff --git a/fullstack-rust-app/backend/src/main.rs b/fullstack-rust-app/backend/src/main.rs index 231f13e..ea57b2b 100644 --- a/fullstack-rust-app/backend/src/main.rs +++ b/fullstack-rust-app/backend/src/main.rs @@ -40,7 +40,7 @@ async fn main() -> std::io::Result<()> { HttpServer::new(move || { let cors = Cors::default() - .allowed_origin("http://localhost:3000") + .allow_any_origin() .allowed_methods(vec!["GET", "POST", "PATCH", "DELETE"]) .allowed_headers(vec![ header::CONTENT_TYPE, diff --git a/fullstack-rust-app/frontend/Cargo.toml b/fullstack-rust-app/frontend/Cargo.toml index be1910b..c37fcd9 100644 --- a/fullstack-rust-app/frontend/Cargo.toml +++ b/fullstack-rust-app/frontend/Cargo.toml @@ -14,6 +14,8 @@ web-sys = { version = "0.3.61", features = ["HtmlInputElement", "Window"] } yew = { version = "0.20.0", features = ["csr"] } yewdux = "0.9.2" reqwasm = "0.5.0" +dotenv = "0.15.0" +lazy_static = "1.4.0" wasm-bindgen-futures = "0.4.34" serde_json = "1.0.93" common = { version = "0.1.0", path = "../common" } diff --git a/fullstack-rust-app/frontend/Trunk.toml b/fullstack-rust-app/frontend/Trunk.toml index 1c857c5..55fc19e 100644 --- a/fullstack-rust-app/frontend/Trunk.toml +++ b/fullstack-rust-app/frontend/Trunk.toml @@ -1,6 +1,4 @@ [[hooks]] stage = "post_build" command = "sh" -command_arguments = ["-c", "npx tailwindcss -i ./styles/tailwind.css -o ./dist/.stage/index.css"] -[[proxy]] -backend = "http://localhost:80/api" \ No newline at end of file +command_arguments = ["-c", "npx tailwindcss -i ./styles/tailwind.css -o ./dist/.stage/index.css"] \ No newline at end of file diff --git a/fullstack-rust-app/frontend/src/api.rs b/fullstack-rust-app/frontend/src/api.rs index 7b2d06b..50597ca 100644 --- a/fullstack-rust-app/frontend/src/api.rs +++ b/fullstack-rust-app/frontend/src/api.rs @@ -1,8 +1,20 @@ use common::{ErrorResponse, Feedback, FeedbackListResponse, FeedbackResponse}; use reqwasm::http; +//use dotenv::dotenv; +//use std::env; +//use lazy_static::lazy_static; + +// lazy_static! { +// static ref BACKEND_SERVER: String = env::var("BACKEND_SERVER").expect("Error: BACKEND_SERVER not found"); +// } + +//let backend_server: &'static str = std::env!("BACKEND_SERVER"); + +//const backend_server = env::var("BACKEND_SERVER").expect("Error: BACKEND_SERVER not found").to_string(); pub async fn api_create_feedback(feedback_data: &str) -> Result { - let response = match http::Request::post("http://localhost:8000/api/feedbacks/") + let backend_server = std::env!("BACKEND_SERVER"); + let response = match http::Request::post(format!("https://{}/api/feedbacks/", backend_server).as_str()) .header("Content-Type", "application/json") .body(feedback_data) .send() @@ -29,8 +41,9 @@ pub async fn api_create_feedback(feedback_data: &str) -> Result Result { + let backend_server = std::env!("BACKEND_SERVER"); let response = match http::Request::get( - format!("http://localhost:8000/api/feedbacks/{}", feedback_id).as_str(), + format!("https://{}/api/feedbacks/{}", backend_server, feedback_id).as_str(), ) .send() .await @@ -56,12 +69,11 @@ pub async fn _api_fetch_single_feedback(feedback_id: &str) -> Result Result, String> { + //dotenv().ok(); + let backend_server = std::env!("BACKEND_SERVER"); + //let backend_server = env::var("BACKEND_SERVER").is_err(); let response = match http::Request::get( - format!( - "http://localhost:8000/api/feedbacks?page={}&limit={}", - page, limit - ) - .as_str(), + format!("https://{}/api/feedbacks?page={}&limit={}", backend_server, page, limit).as_str(), ) .send() .await @@ -87,8 +99,9 @@ pub async fn api_fetch_feedbacks((page, limit): (i32, i32)) -> Result Result<(), String> { + let backend_server = std::env!("BACKEND_SERVER"); let response = match http::Request::delete( - format!("http://localhost:8000/api/feedbacks/{}", feedback_id).as_str(), + format!("https://{}/api/feedbacks/{}", backend_server, feedback_id).as_str(), ) .send() .await From 0c8ae678b76c041267e74dfa2d807332b9de68ff Mon Sep 17 00:00:00 2001 From: Aashi Modi Date: Thu, 9 Nov 2023 10:59:17 +0530 Subject: [PATCH 4/7] added acornfile for rust --- .github/workflows/publish.yaml | 29 ++++ Acornfile | 8 +- Dockerfile.backend | 2 +- README.md | 34 +--- fullstack-rust-app/Acornfile.all | 45 ------ fullstack-rust-app/Cargo.lock | 213 ++++++++++++++++++++++++- fullstack-rust-app/backend/Cargo.toml | 1 + fullstack-rust-app/backend/dist | 1 + fullstack-rust-app/backend/src/main.rs | 8 + fullstack-rust-app/frontend/Trunk.toml | 5 +- 10 files changed, 258 insertions(+), 88 deletions(-) create mode 100644 .github/workflows/publish.yaml delete mode 100644 fullstack-rust-app/Acornfile.all create mode 120000 fullstack-rust-app/backend/dist diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..bb6e7c7 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,29 @@ +name: Publish Acorn image +on: + workflow_dispatch: + push: + tags: + - "v[0-9]*" + +jobs: + publish: + name: Publish + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - uses: acorn-io/actions-setup@v2 + with: + acorn-version: "main" + - name: Login to GHCR + uses: acorn-io/actions-login@v1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and Publish with signature + run: | + TAG=${GITHUB_REF#refs/*/} + acorn build --platform linux/amd64 --platform linux/arm64 --push -t ghcr.io/infracloudio/rust-acorn:${TAG} . diff --git a/Acornfile b/Acornfile index 371674d..3e200d4 100644 --- a/Acornfile +++ b/Acornfile @@ -17,7 +17,7 @@ services: db: { containers: { backend: { - image: "ghcr.io/aashimodi14/rust-backend:v4" + image: "ghcr.io/infracloudio/rust-backend:v1" env: { "POSTGRES_HOST": "@{service.db.address}" "POSTGRES_PORT": "@{service.db.port.5432}" @@ -31,12 +31,12 @@ containers: { ports: { publish: "8000:8000/http" } - memory: 1500Mi + memory: 1048Mi cmd: ["/bin/sh", "-c", "cd /usr/src/fullstack-rust-app/backend && sqlx migrate run; cargo run --bin backend -- --port 8000"] consumes: ["db"] } frontend: { - image: "ghcr.io/aashimodi14/rust-frontend:v4" + image: "ghcr.io/infracloudio/rust-frontend:v1" cmd: ["/bin/sh", "-c", "cd /usr/src/fullstack-rust-app/frontend && BACKEND_SERVER=$BACKEND_SERVER trunk serve --address 0.0.0.0 --port 3000"] ports: { publish: "3000:3000/http" @@ -44,7 +44,7 @@ containers: { env: { "BACKEND_SERVER": "@{services.backend.endpoint}" } - memory: 2048Mi + memory: 1048Mi dependsOn: ["backend"] } } diff --git a/Dockerfile.backend b/Dockerfile.backend index a7d55ec..60f01aa 100644 --- a/Dockerfile.backend +++ b/Dockerfile.backend @@ -5,4 +5,4 @@ RUN cargo install wasm-bindgen-cli sqlx-cli RUN rustup target add wasm32-unknown-unknown WORKDIR /usr/src/ -COPY . . \ No newline at end of file +COPY . . diff --git a/README.md b/README.md index c23a53c..ce89578 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,9 @@ -# Build a Full Stack App with Rust, Yew.rs and Actix Web +# Acorn for a Rust sample app - Feedback App -In this article, I'll walk you through the process of building a backend API using the Actix web framework, SQLX, PostgreSQL, and Docker. Once we've created a powerful backend, we'll move on to building a single-page app using the Yew.rs framework. By the end of this tutorial, you'll have a fully functional web app that seamlessly integrates the frontend app with the backend API. +This is a Fullstack Rust app created in using Actix, SQLX and Postgres as Backend and Yew-TailwindCSS as its frontend. The user can give feedback and comments using this SPA web app. -![Build a Full Stack App with Rust, Yew.rs and Actix Web](https://codevoweb.com/wp-content/uploads/2023/03/Build-a-Full-Stack-App-with-Rust-Yew.rs-and-Actix-Web.webp) +## What is Rust? -## Topics Covered +Rust has been Stack Overflow's most loved language for four years in a row and rightfully so. Rust is a blazing fast and memory-efficient static compiled language with a rich type system and ownership model. It can be used to power performance-critical services while guaranteeing memory-safety and thread-safety, empowering developers to debug at compile-time. -- Run the Full-Stack Rust App Locally -- Setup the Full-Stack Rust App -- Work on the Library Project -- Work on the Backend Project - - Setup the Rust API Project - - Setup PostgreSQL and pgAdmin with Docker - - Database Migration with SQLX - - Create the SQLX Database Model - - Create the Request Validation Structs - - Create the CRUD API Route Handlers - - Register the API Routes and Setup CORS -- Work on the Frontend Project - - Setup the Yew.rs Project - - Setup Tailwind CSS for Styling - - Create the API Request Functions - - State Management with Yewdux - - Create Reusable Components - - Yew Component to Add New Feedback Item - - Yew Component to Display Feedback Statistics - - Yew Component to Display Feedback Information - - Yew Component to Display the Feedback Items - - Export the Component Files as Modules - - Add the Components to the Main File -- Test the Rust Full-Stack App - -Read the entire article here: [https://codevoweb.com/build-full-stack-app-with-rust-yew-and-actix-web/](https://codevoweb.com/build-full-stack-app-with-rust-yew-and-actix-web/) diff --git a/fullstack-rust-app/Acornfile.all b/fullstack-rust-app/Acornfile.all deleted file mode 100644 index a3acf23..0000000 --- a/fullstack-rust-app/Acornfile.all +++ /dev/null @@ -1,45 +0,0 @@ -name: "Spring Boot Sample Acorn" -description: "Acorn running a sample Petclinic Springboot app" -readme: "./README.md" - - -args: { - // Name of the database to create. Defaults to "instance" - dbName: "rust-sqlx" - // Provide user for mysql. If the value is not provided, it will default to - user: "root" -} - -services: db: { - image: "ghcr.io/acorn-io/postgres:v15.#-#" - serviceArgs: { - dbName: args.dbName - memory: 512Mi - } -} - -containers: { - app: { - build: { - context: "." - dockerfile: "Dockerfile.bk" - } - env: { - "POSTGRES_HOST": "@{service.db.address}" - "POSTGRES_PORT": "@{service.db.port.5432}" - "POSTGRES_USER": "@{service.db.secrets.admin.username}" - "POSTGRES_PASSWORD":"@{service.db.secrets.admin.password}" - "POSTGRES_DB":"@{service.db.data.dbName}" - "DATABASE_URL":"postgresql://@{service.db.secrets.admin.username}:@{service.db.secrets.admin.password}@@{service.db.address}:5432?schema=public" - "PGADMIN_DEFAULT_EMAIL":"admin@admin.com" - "PGADMIN_DEFAULT_PASSWORD":"password123" - } - ports: { - expose: "8000/http" - } - cmd: ["/bin/sh", "-c", "cd backend && sqlx migrate run; cargo run"] - consumes: ["db"] - } -} - - diff --git a/fullstack-rust-app/Cargo.lock b/fullstack-rust-app/Cargo.lock index 0e9f85b..c9e3765 100644 --- a/fullstack-rust-app/Cargo.lock +++ b/fullstack-rust-app/Cargo.lock @@ -34,6 +34,29 @@ dependencies = [ "smallvec", ] +[[package]] +name = "actix-files" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d832782fac6ca7369a70c9ee9a20554623c5e51c76e190ad151780ebea1cf689" +dependencies = [ + "actix-http", + "actix-service", + "actix-utils", + "actix-web", + "askama_escape", + "bitflags", + "bytes", + "derive_more", + "futures-core", + "http-range", + "log", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", +] + [[package]] name = "actix-http" version = "3.3.1" @@ -198,6 +221,55 @@ dependencies = [ "syn", ] +[[package]] +name = "actix-web-lab" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9b7c50a90657ef1868db9dd85f74e82c4d9858ce583d4639ae3583f0bb97775" +dependencies = [ + "actix-files", + "actix-http", + "actix-router", + "actix-service", + "actix-utils", + "actix-web", + "actix-web-lab-derive", + "ahash 0.8.3", + "arc-swap", + "async-trait", + "bytes", + "bytestring", + "csv", + "derive_more", + "futures-core", + "futures-util", + "http", + "impl-more", + "itertools 0.11.0", + "local-channel", + "mediatype", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "serde", + "serde_html_form", + "serde_json", + "tokio", + "tracing", +] + +[[package]] +name = "actix-web-lab-derive" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16294584c7794939b1e5711f28e7cae84ef30e62a520db3f9af425f85269bcd2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "adler" version = "1.0.2" @@ -272,6 +344,18 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + [[package]] name = "async-channel" version = "1.9.0" @@ -442,6 +526,7 @@ version = "0.1.0" dependencies = [ "actix-cors", "actix-web", + "actix-web-lab", "chrono", "common", "dotenv", @@ -720,6 +805,27 @@ dependencies = [ "typenum", ] +[[package]] +name = "csv" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" +dependencies = [ + "memchr", +] + [[package]] name = "ctor" version = "0.1.26" @@ -896,6 +1002,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.2.8" @@ -1332,7 +1444,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.2", "slab", "tokio", "tokio-util", @@ -1348,13 +1460,19 @@ dependencies = [ "ahash 0.7.6", ] +[[package]] +name = "hashbrown" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" + [[package]] name = "hashlink" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa" dependencies = [ - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -1416,6 +1534,12 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-range" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" + [[package]] name = "httparse" version = "1.8.0" @@ -1474,13 +1598,19 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "impl-more" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d" + [[package]] name = "implicit-clone" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40fc102e70475c320b185cd18c1e48bba2d7210b63970a4d581ef903e4368ef7" dependencies = [ - "indexmap", + "indexmap 1.9.2", ] [[package]] @@ -1490,7 +1620,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.2", ] [[package]] @@ -1533,6 +1673,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.5" @@ -1652,6 +1801,12 @@ dependencies = [ "digest", ] +[[package]] +name = "mediatype" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c408dc227d302f1496c84d9dc68c00fec6f56f9228a18f3023f976f3ca7c945" + [[package]] name = "memchr" version = "2.5.0" @@ -1664,6 +1819,16 @@ version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2211,6 +2376,19 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_html_form" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cde65b75f2603066b78d6fa239b2c07b43e06ead09435f60554d3912962b4a3c" +dependencies = [ + "form_urlencoded", + "indexmap 2.1.0", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_json" version = "1.0.93" @@ -2306,7 +2484,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c12bc9199d1db8234678b7051747c07f517cdcf019262d1847b94ec8b1aee3e" dependencies = [ - "itertools", + "itertools 0.10.5", "nom", "unicode_categories", ] @@ -2348,7 +2526,7 @@ dependencies = [ "hex", "hkdf", "hmac", - "indexmap", + "indexmap 1.9.2", "itoa", "libc", "log", @@ -2548,9 +2726,21 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "socket2", + "tokio-macros", "windows-sys 0.45.0", ] +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio-stream" version = "0.1.12" @@ -2615,6 +2805,15 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.10" @@ -2992,7 +3191,7 @@ dependencies = [ "futures", "gloo", "implicit-clone", - "indexmap", + "indexmap 1.9.2", "js-sys", "prokio", "rustversion", diff --git a/fullstack-rust-app/backend/Cargo.toml b/fullstack-rust-app/backend/Cargo.toml index ea77788..8d1197e 100644 --- a/fullstack-rust-app/backend/Cargo.toml +++ b/fullstack-rust-app/backend/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] actix-cors = "0.6.4" actix-web = "4.3.1" +actix-web-lab = { version = "0.19.1", features = ["spa"] } chrono = { version = "0.4.23", features = ["serde"] } dotenv = "0.15.0" env_logger = "0.10.0" diff --git a/fullstack-rust-app/backend/dist b/fullstack-rust-app/backend/dist new file mode 120000 index 0000000..f640917 --- /dev/null +++ b/fullstack-rust-app/backend/dist @@ -0,0 +1 @@ +../frontend/dist \ No newline at end of file diff --git a/fullstack-rust-app/backend/src/main.rs b/fullstack-rust-app/backend/src/main.rs index ea57b2b..959c337 100644 --- a/fullstack-rust-app/backend/src/main.rs +++ b/fullstack-rust-app/backend/src/main.rs @@ -4,6 +4,7 @@ mod schema; use actix_cors::Cors; use actix_web::middleware::Logger; +use actix_web_lab::web::spa; use actix_web::{http::header, web, App, HttpServer}; use dotenv::dotenv; use sqlx::{postgres::PgPoolOptions, Pool, Postgres}; @@ -53,6 +54,13 @@ async fn main() -> std::io::Result<()> { .configure(handler::config) .wrap(cors) .wrap(Logger::default()) + .service( + spa() + .index_file("./dist/index.html") + .static_resources_mount("/") + .static_resources_location("./dist") + .finish(), + ) }) .bind(("0.0.0.0", 8000))? .run() diff --git a/fullstack-rust-app/frontend/Trunk.toml b/fullstack-rust-app/frontend/Trunk.toml index 55fc19e..d7de0cb 100644 --- a/fullstack-rust-app/frontend/Trunk.toml +++ b/fullstack-rust-app/frontend/Trunk.toml @@ -1,4 +1,7 @@ [[hooks]] stage = "post_build" command = "sh" -command_arguments = ["-c", "npx tailwindcss -i ./styles/tailwind.css -o ./dist/.stage/index.css"] \ No newline at end of file +command_arguments = ["-c", "npx tailwindcss -i ./styles/tailwind.css -o ./dist/.stage/index.css"] +[build] +target = "index.html" +dist = "../dist" \ No newline at end of file From d2be2f2fa158eaccd453b5fcecdad5132621f740 Mon Sep 17 00:00:00 2001 From: Aashi Modi Date: Thu, 9 Nov 2023 11:02:53 +0530 Subject: [PATCH 5/7] updated files --- fullstack-rust-app/Dockerfile.bk | 22 ------------- fullstack-rust-app/backend/Dockerfile | 16 --------- fullstack-rust-app/frontend/Dockerfile.bk | 40 ----------------------- 3 files changed, 78 deletions(-) delete mode 100644 fullstack-rust-app/Dockerfile.bk delete mode 100644 fullstack-rust-app/backend/Dockerfile delete mode 100644 fullstack-rust-app/frontend/Dockerfile.bk diff --git a/fullstack-rust-app/Dockerfile.bk b/fullstack-rust-app/Dockerfile.bk deleted file mode 100644 index 1beeed3..0000000 --- a/fullstack-rust-app/Dockerfile.bk +++ /dev/null @@ -1,22 +0,0 @@ -FROM rust:1.73-slim-bullseye AS build - -RUN apt --yes update && apt --yes install git pkg-config curl libssl-dev ca-certificates curl gnupg -RUN cargo install trunk wasm-bindgen-cli sqlx-cli -RUN rustup target add wasm32-unknown-unknown - -WORKDIR /usr/src/fullstack-rust-app -COPY . . - -RUN curl -sL https://deb.nodesource.com/setup_18.x | bash - -RUN apt-get install -y nodejs - -RUN cd frontend && trunk serve --port 3000 -RUN cd backend - -#FROM gcr.io/distroless/cc-debian10 - -#COPY --from=build /usr/src/fullstack-rust-app/target/release/backend /usr/local/bin/backend -#COPY --from=build /usr/src/fullstack-rust-app/frontend/dist /usr/local/bin/dist - -#WORKDIR /usr/local/bin -#CMD ["backend"] \ No newline at end of file diff --git a/fullstack-rust-app/backend/Dockerfile b/fullstack-rust-app/backend/Dockerfile deleted file mode 100644 index 09d39a6..0000000 --- a/fullstack-rust-app/backend/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM rust:1.73-slim-bullseye AS build - -RUN apt --yes update && apt --yes install git pkg-config curl libssl-dev ca-certificates curl gnupg -RUN cargo install wasm-bindgen-cli sqlx-cli -RUN rustup target add wasm32-unknown-unknown - -WORKDIR /usr/src/fullstack-rust-app/ -COPY . . -RUN cd backend && cargo build --release - - -FROM alpine AS release - -WORKDIR /app -COPY --from=build /usr/src/fullstack-rust-app/target/release/backend /usr/local/bin/backend - diff --git a/fullstack-rust-app/frontend/Dockerfile.bk b/fullstack-rust-app/frontend/Dockerfile.bk deleted file mode 100644 index 78a0876..0000000 --- a/fullstack-rust-app/frontend/Dockerfile.bk +++ /dev/null @@ -1,40 +0,0 @@ -FROM rust:1.73-slim-bullseye - -RUN apt --yes update && apt --yes install git pkg-config curl libssl-dev ca-certificates curl gnupg -RUN cargo install trunk wasm-bindgen-cli -RUN rustup target add wasm32-unknown-unknown - -WORKDIR /usr/src/fullstack-rust-app -COPY ../ . - -# ENV NVM_DIR /usr/local/nvm -# ENV NODE_VERSION 18.16.1 - -# RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash - -# RUN source $NVM_DIR/nvm.sh \ -# && nvm install $NODE_VERSION \ -# && nvm alias default $NODE_VERSION \ -# && nvm use default - -RUN curl -sL https://deb.nodesource.com/setup_18.x | bash - -RUN apt-get install -y nodejs - -#ENV NVM_DIR=/root/.nvm - -# RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash -# RUN . ~/.nvm/nvm.sh && nvm install 18.16.1 - -# ENV NODE_VERSION=18.16.1 -# RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash -# ENV NVM_DIR="/root/.nvm" -# RUN . "$NVM_DIR/nvm.sh" && nvm install ${NODE_VERSION} -# RUN . "$NVM_DIR/nvm.sh" && nvm use v${NODE_VERSION} -# RUN . "$NVM_DIR/nvm.sh" && nvm alias default v${NODE_VERSION} -# ENV PATH="/root/.nvm/versions/node/v${NODE_VERSION}/bin/:${PATH}" -# RUN node --version -# RUN npm --version - -RUN npx -v -RUN npx tailwindcss-cli@latest - -RUN cd frontend && trunk build --release \ No newline at end of file From f424ae109f83543cff9bdd02e231ed9957745c29 Mon Sep 17 00:00:00 2001 From: Aashi Modi Date: Tue, 28 Nov 2023 08:49:00 +0530 Subject: [PATCH 6/7] updated dockerfile --- Acornfile | 7 ++++--- Dockerfile.backend | 2 ++ Dockerfile.frontend | 4 +--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Acornfile b/Acornfile index 3e200d4..6a58ad8 100644 --- a/Acornfile +++ b/Acornfile @@ -6,25 +6,27 @@ readme: "./README.md" args: { // Name of the database to create. Defaults to "instance" dbName: "rust-sqlx" + dbUser: "rust" } services: db: { image: "ghcr.io/acorn-io/postgres:v15.#-#" serviceArgs: { dbName: args.dbName + dbUser: args.dbUser } } containers: { backend: { - image: "ghcr.io/infracloudio/rust-backend:v1" + image: "ghcr.io/infracloudio/rust-backend:v2" env: { "POSTGRES_HOST": "@{service.db.address}" "POSTGRES_PORT": "@{service.db.port.5432}" "POSTGRES_USER": "@{service.db.secrets.admin.username}" "POSTGRES_PASSWORD":"@{service.db.secrets.admin.password}" "POSTGRES_DB":"@{service.db.data.dbName}" - "DATABASE_URL":"postgresql://@{service.db.secrets.admin.username}:@{service.db.secrets.admin.password}@@{service.db.address}:5432?schema=public" + "DATABASE_URL":"postgresql://@{service.db.secrets.admin.username}:@{service.db.secrets.admin.password}@@{service.db.address}:5432/@{service.db.data.dbName}?schema=public" "PGADMIN_DEFAULT_EMAIL":"admin@admin.com" "PGADMIN_DEFAULT_PASSWORD":"password123" } @@ -32,7 +34,6 @@ containers: { publish: "8000:8000/http" } memory: 1048Mi - cmd: ["/bin/sh", "-c", "cd /usr/src/fullstack-rust-app/backend && sqlx migrate run; cargo run --bin backend -- --port 8000"] consumes: ["db"] } frontend: { diff --git a/Dockerfile.backend b/Dockerfile.backend index 60f01aa..8672781 100644 --- a/Dockerfile.backend +++ b/Dockerfile.backend @@ -6,3 +6,5 @@ RUN rustup target add wasm32-unknown-unknown WORKDIR /usr/src/ COPY . . + +ENTRYPOINT ["/bin/sh", "-c", "cd /usr/src/fullstack-rust-app/backend && sqlx migrate run; cargo run --bin backend -- --port 8000"] \ No newline at end of file diff --git a/Dockerfile.frontend b/Dockerfile.frontend index c573656..c4d460b 100644 --- a/Dockerfile.frontend +++ b/Dockerfile.frontend @@ -10,6 +10,4 @@ COPY . . RUN curl -sL https://deb.nodesource.com/setup_18.x | bash - RUN apt-get install -y nodejs -RUN npx -v -RUN npx tailwindcss-cli@latest - +RUN npx tailwindcss-cli@latest \ No newline at end of file From f44b8e10155fc277a6a2172771ce69399a8b11c5 Mon Sep 17 00:00:00 2001 From: Aashi Modi Date: Tue, 28 Nov 2023 09:13:02 +0530 Subject: [PATCH 7/7] added icon --- Acornfile | 5 ++++- README.md | 3 ++- rust_icon.png | Bin 0 -> 34593 bytes 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 rust_icon.png diff --git a/Acornfile b/Acornfile index 6a58ad8..87a9470 100644 --- a/Acornfile +++ b/Acornfile @@ -1,11 +1,13 @@ name: "Rust Sample Acorn" description: "Acorn running a sample Rust app" readme: "./README.md" +icon: "./rust_icon.png" args: { - // Name of the database to create. Defaults to "instance" + // Name of the database to create. Defaults to "rust-sqlx" dbName: "rust-sqlx" + // Name of the database user to create. Defaults to "rust" dbUser: "rust" } @@ -43,6 +45,7 @@ containers: { publish: "3000:3000/http" } env: { + // Frontend forwards the requests to the Backend Container Service Endpoint "BACKEND_SERVER": "@{services.backend.endpoint}" } memory: 1048Mi diff --git a/README.md b/README.md index ce89578..25d794a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Acorn for a Rust sample app - Feedback App -This is a Fullstack Rust app created in using Actix, SQLX and Postgres as Backend and Yew-TailwindCSS as its frontend. The user can give feedback and comments using this SPA web app. +This is a Fullstack Rust app created in using Actix, SQLX and Postgres as Backend and Yew-TailwindCSS as its frontend. The user can provide feedback ratings and comments using this SPA web app. The application utilises the Acorn Postgres DB service to store the feedback data where the dbName and dbUser are customisable. Once deployed, the SPA web app is available on the Frontend Service Endpoint. + ## What is Rust? diff --git a/rust_icon.png b/rust_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d69df799c9f2a7538e21a9c58056be613c6b99f9 GIT binary patch literal 34593 zcmXtf1yoy26X?Z@7A;y_io0v^;KkkDrNxQ_FAl-oDYUq|yIb+%QYaL6$;V z$?oW#**i14JBd_Nk;6nIK?48)6C^LK4giR-pNPPl|Gs|V@4oqOWb;w^BLLLKqd%J= z!N!yp^6JU};7bnx;7|Z~giV3}0KgXx05~=U0HJgMAau=c`y>Jbz?&<|NdwS`_=#>9 z766j|sOi0W`rE5TYjCCJ;)%>RC5{qJo#2F$@5E?!`K!wS&xFn84^WJ4CVi%^xp{0< zB(MDmZ*5RiFu{?jl*CbI-qi~g)7%u>&+4b<>c5>EwAr23;@l1gdiBY7YhDi!YASFTNcB?C2BJgm27 z{LzbT#<@ek93dl@8~SrMfxN%gAtRghs5;zEm33@8&c15`bf9h9et*qMuO7obf(2FG z$fugRJw#JFVTO~Xg~O!AC%rqt zD3ha~6{Ir^DsP9n)y26L^(BZpRFv3SCZbpRk(ohgk{)8L_RC~>-_rhW=--~_C6|CJ ziHtNe#J`usYC|-;)}qPzB4z~+xYFvM+4U6RNYiQ{+n%JAwB?4PdY}4w9x%R`vv{gLAGIWfHKO$RD@ zQq>DlBe0P%-pPU2f4%!%0v48e6;@g4sfrHsBd@(sfGGUx=s6AKYanA?jkOCld?h?o z?~&3owvJ4DOwRt-osOf<>h;R+ZPqT-WES80XO0IGm=!$lG>$OLlPCZJP?OoqRr5 zk}1BloInQ}EMwWhydLzc!YJ=(mgRbmXvIJOp{5{Ax2Qu5f~X&YXOL&mr9L<GmSKwAs~0_*8R zA{Mll9Z4j?iYoOBIi}MLq*$_AzW==hVV#4iRjmDkiLN4?mu0|6iD)k}D~M?Vui$OL z{0en`-3H0SImMoOPn8mSXGQ#4J{>5`_Wg14^-|vSG7q>;w)L6)z65;w@FBR4?R#ac zzP^T&R6bGTcM5}A#@O^>8^4~PN~+SWao<%C<&JvhPo_@lx{4sA32)Hl?pHHhjKe|lgL+x7&{u#aQewEtPDn-*X zfMxGWm-u%e**G*jGUu{!)j<*eh73sK`CmN)#8p7;AW}0@DMN}Qd7VS;+ z-taPexUv=ty3c}C@vY;eJI<}?FF#AQo69$5i?$RR5Kj5v3zFw@^$b-hS6rkY(Tg%@ zKgQv~1X~dJp9?AT&kB}=%3fTIY>`?;>#w+zg@G+kk`YJ^bWXE_PReEdi>seJhb^G_0Q zuVeSUH_%IBobE-a+Uvx}W*bTo-Y>`8Ix0Evm9kdS1k%>wkEc%&Dp?4boD1f0WsLfb z`jcgSo2x8Fn3sGwVD+KQ&(a*X9YAZx0O`&0_WW#-$tF}FW|yD2xsH-H-nZFaGQ?I` z6O5OEORfF+KD==2-+Yv8LbbVF&!mXbRnNG$d!e;b9WOMAi~;F2R=Fi2%MN>s-?P3y z0TrwSEFQ{l6sqQLF0*c(X>gs6ybB62q1f0S0FN*C+e7|`QH@^B{h{ZbUuH@&UhgEF zCPzX6)rsa228`MgzUE_NYYcRTE5GWQmYI=31 z+U&(7qJpW^tlR3*0{YPDD)?;2%(OpEk;%>%LQ7dyJ@llrNHTzt|8$mF4e=6e80i)i znlV~WM#dCq8|hw-zgrd$v58Rviq&LVYeCH9hpIBa(aw=2$O$z5e!0h+p5;qotaqRI z4&VzdTzq@8%yRsYd+|Z%=@n>W&_;ufUeY}b@o0q$iRuCb-A$r%o^sS4B zG!W5t9M4aGu^_$UKT{JISBuApu%GESK^K!Of8ZcilqxX`RWL*OQRk_j04@w ziaV2qT_JMR?am~60OG#m#o6`ZPusc3q-vnD3wOZq=lxB<&NIt_4|3g+Q23UYIm<7X zQvuqN&5&Bpv#V~OgZAK@d)70dy$8L+L#F_#O_xQP_x;(ridW$G5q$^L5@BbeO_8mL z&^@->-E?f@^&b@vG2eaSI`2Q7*i3qAhbsZew|q@KI$82E`bri*FhctFU__JrS)f7m zE6hyT@}i#Y$Cl3(ve7KwWLw&`g8Cd9o|rs7oL;=g0AN0#;;(_44Tp!HqYjAM z0uq$~h1qPA3(lolJ)A&|toA<^`^9-@@bcZh9m>;?S9dt*R=gj%`N~FJR>GK2$Yc2Z zsnb0if}Qr z66QntqE*1K0{NE0#@i0Z({ytrx;b@vjub%_M3UJU&(B3wW}sqECG=H)++Ar>X zb1&InJ%ZoY;D1L!ICV7MP5^AerfQ#5CT#cuEz*MRophYdn{X5-4zq#7uWz?gB*t&^ z|HCd1@)9_6mt08-F?lHZ?L{qS^3MNzGpG2r2Tj_yX$k~ELI*eaGi2kYa?c)hKmBk% zEs>fYew}{b6DcO+Q8C=+BKsb{s zi}8y}I6q6fc&rK$(tp{pCX5+;6{Kru@+_T=^*1|2_-lxx)@#=BZX8|)#FY4QpqaA425NoajJ7o+?oZ$pqpKKXY}wjp;vOO$NqE=-P7*`a63sn5pdhY)f+&2y~% z@`QL-%4wnl>GVGA$62w14I2mMK17KNpQ4_Whq0~*0s7#g=ZG_6`^sZ~PtG@X+IDZ$ zox7;8fnOL=djj40W2WO9!-x{O!3t3k#Kfa+vKKAA?*Md+AXKmj5<#~EweVaXM$?JE z60nB>hM0-CNPAWk#Bgub-EqPt2)01B*nZH6tcoHV-ipx!m-w2vA|yw%6#T&inKRHpq#}hXIFq1LgpUX>F#G(hH)}90f3QzoD(b2=T(#&yB?phGY z0Mh@`v_A||0`RMuR3&FwP~%>E#UW&>CCCm|4fHT&@IL7$3vIo5f3v9k0yL}=62C&?*9$qe`{$(X@nQG z9bJ0%v1nmW_Ev`rBj!Y&kjQc63ijB<0Nr4QJU?UnTpCXyYhACvrF zoO!iTPHz6a55sPO(<_jRI5yd+i9*ZrBtk_pw=5Ebpaa6nMQj<8x0;A&J!g z5Yv4L!YOZ#YX>N4*F`qW1zfJM@XF!0Fr43hd38cVx%eot+4x>{mTh4h>gSA{525wO?O+31{ldNFA(Wm^!p`0i5%B+ zppMPsY}W7PsT7pRhIPyWCE&91-OF-Xo+z~18(vQrJsQ_;C=&B{ zh)H}-qpE1&!f&KE9JBnfbU{41DHs$>!!rK;FH1yTnYf2fZuB3W0lx|23XE1I4Z+4e zo^J8HFvB(`Wc)7LCoIF+P_xGw>XEDY$g7hBc&C&Rkc=v@vc(iTOTT<;s zy5jRKX%sU*i-Y){@$)nJ1u91Bw9Z90FggtfF`z%woR|Yf{E@PQ0{Wg~VtlqtUapWB z1U@j5G`2RD;n6)yn=0PGJ1>%&b9XaQ%ppOQ*(Zg;JGi!hf^k(3d>`s|)7``>5c7I+Kn`D!H8$H}eBIN{ z@Uk2WJFW;Soo}o_F)6ue<5@6lYvF}Q&^^A9E6XbpnQTR3%l3FjiK+wrOJX~6k%Sbm51d+bk+(Y&8 z;tn@ino=UJeazplT}ZnPcHi2(MPWudi%M6im|btsr=AIFK6RAzVR3qMNN0}Zqpbn< zj(}D>4cLkNQw~xSqnNR441$)9puAR#>j>x)jLCq*zJ}76i@wr^bA{F0S!p3U;)k>{ zhkFTBh$D?`xcAtmz7qVPlk*^HUxgw$zFV5*N*?qdyjhUbBcKO9lJ;UDQ&|kT@!xSU zy6unYqa{>qLmi9+6E%(w>=HO#=GQ3G0Ys9=qNS z7INsX4BBQR40IFj>#S*zU ze(0{vV(@V^S4ff#p2bkOlqht8xR-T(at9w8+`xvSw0qv&=1Vr^b8^U-ER?mZkp+aB zVTfW#+Or(wt@of1-_zMC!Oe+12Ra!9OpM}mebC##n-fD&<5&XlcV(m!aOYdHCk5E? z`U6Rp;4Q80BklwuCR@^{MatE8=%h^HvE$M(KcaO^0(>^u++5dLb1Cqauw^jzxvG$P znBZm3U`3H3yBUbuW&&Fp^!6JLI}pEov1`RiXoN+87ow5GE+@8GL%95znA;SJNPmV0 z(>9xqo1Bc`CB(|TiI}Cn1H)m->6Xv{Sz&pCegi;-y<}6Q%Em**!f+_?@O@-Aj@sng z7Mk70a~y=Df&Gcg0mb~Z|B~M%vv}8*yAW4GdFy@Ss4;rN{wVY;bBa>P9WF@}u2Whg zprL5Yt_e~3E(i2`y|I@Y*Ow+g3a<5Mv~PC5!1MrOW3MNcI$F|C0CGV*XxC~KlQMu$ z8Q%|GsxKQ}Wp06|tc=we^clq-aK`^$&l}>=Zt?Zf8FA0%GSEp&M4mm)z2PeG5Z;%ZSBNtN6RwJEn1a6Dj0KzLa%gkATW!N~-pJO5zwjJxGSMEes8%@0yImDjW%Scey3Ha_xsw|pEjUMa_@gx5BV zfyL#@%-f9M{y?8h57u$fH6yrup7v19 zz*Ga24?f@SOCfw}a$SP=J`*Rh5d79}v_tYi2lyaa1^?!_{u{HMV~9d83s?u*Yd=Ey zZVjOYUc!!ND2WX`P2=dSfkuV&=C$=ICELyvVJU3*6v+?{o5BSy`It^ht#n#2oaSLf z0`Z@U3i^E~Dzjj;Foxq6y*p9FRe4Yizjj-;?S|2ijiAo_h%9~~jULff>VzEe3^wn! z{vFyiE1-a<1DE_t{E>oG?aw2JV8UvqqmzuO@J5Y|D-w9FUkfPlJK#vr=gXDn+1&uN z?HYIz63g~P4pyaLN*-+)1A~F&*Re)8!WoQoON;m_eBi|6p1>2jbp#2!Ehg}en2Bpl zEIqRFgEFd4s>eg@6W7p=ZdXGyj5}mX3mEhx&reaNAIgGDDYwgZW@Bv4R>Y#?&Y zh{Q4i$~Z5u??%v*8ScSkff0h=lKY%=irMeg0^dUnCTc}9{qoCj+8GO~{z0}DFHOUW zvTGX+zyqguTE5ZFPzgk?n~;#F(wm~hGS3XEx#NYCM?U-LzwguJRsg~8%~;HWk|2LI zp$l?BEVC92QKBmufgS0U+=_G|NnDISk;2i>Ey+^KINFF^vI0!;OHTV=8JD_W-5$?N zV{d}(nIhxOw5wzr-@tn;BKqP>5mqOzIJzV zdaf-tv&RadsIt>flZzIrwD0SVShdF`p@K1jttatSjmHELXNRBXDzmiM>1N&u`qMrb z{xtry#+yd!CbBKc&sdvzE|2xK#OlQP7qi0Yy-1qZtG}bBLLD00*tR{m{BzB*q63@W zZ1C-Z1tWS!e{e!xY~}QL>)MMm-1n;*hL~jUmj^;+kK3$GQ})zw-%uf*QsUUMVf9ju zaYRaD5?4gIS(n&|xzySOK^9A@tBhgEcU)-?Eap`8dLz^a+-YGfmd2prj_7%JPhaJ& zH)0JoDe7hp=3N$3OILfy2G@0|?!gf!J+GoIKHb)HF`}JKRj6= z8B0`Z)=5z>LV{YKWxpIyxb50oNJ+s2$imdew3F7zoJ)}9DZ1bf@ukU3{xWc6*xyJs zEs_pY$Wb8ITUp8(^!gptebfIuJrkSM$MyG0&pMu|M8U}UAja@z$9_m%MvAWx6xxmu zbHEe6wqEGT67Bci8WA0eys}W7Op-o-8dUhgvZI@&-wBy1%D0^ZfoZoh<^?eH^~K1~g3MuYKsc0|P(2Eb;0PM9s-p$F$LWXZ8~dt^60W{l7xjZ8f7Z%3;Fy%C&sOQPbKM-;jG5spyn zr3L~4CkNoT?n78J>ztX;i_biVd`1&@{QMY4s5u?xe4~T?sx_c5@Eozned~Ik#&w2y zyXVo59i`tOuM;$%3M@3nhMD7j!1FNyB^=(pBoW^a~7tYOtrdFES3t03Pf|8y&&;_QrmI1vjX z_?v)L@&c7j3-I*H82o1nxqID&WX0%v^0y@6BJnCmYiKPfMH-J-{d=ux^Xo*?W3~G0 zQ%B)c*uNZ)RKRAW<5x`$66;fpupm=axySNqMg)K1w;CR({-F87qf%d)a;lB21{1e# z(k!N;_(<}t4s(kp1Urns{8nS}P{^f7UcV%m^n_six!9ew@$&OPYP?t;xP`+09;pi>0MP<{SB(Qdp?Rc; zAP1jWJuu`Y+iT8;HAm$Ok*b`5(QMV1OOrWYqfr^;0^7VU_ksOJ5=M+>OSq{|+XbI;hvDTJ6@#)YbPa`eL(Ph@v=hRP27MHfr04 zbY=UVSgbKGzsG_?(9ZEs|2Pq$dD20ZU7FI2Cm%xTo4SJ62lbqvXv!&5)(08G3n;IS z(wn@1D&yHB-mEM~f27v<`uDGN1Z7d&_swP>MwE}kRaK3gmJMw?_DZw2<68kk}lAIe7rf@JikI6Lf#>|NzDBLdaCY5Z)4)Yr6I zjL+(IG8xKf2W@GLEx-2H7&ET~quRZ~r7%{5K_a%yhSjKKf6m#gE;Q>d)ir*-Ym<37FL$<1(ovrqKl8&t%(IV&Nwd#}Vh$MWChkmnJa10fq*w01SIe2ZlpY9CJ z_p2|-*KD?bGwFcMzn^ecDH&Tew#q;&x5-%lWPEWpA0W*>Bn(|Hg z=a>wfJ3c3zI2LHcgBqIoA^2IN>%&Gd6W1<|(;Py3ZK>aD1r_TqG?=(G7!@C)U*e02 zzHGrRp&hb6_D{SHG5W=awN~x_tmk@8Dp&qYdJ*}g1yTUFK1X+7n8^nI{v?uK5Iwvz zQHXa{e`#TFRc4+#pDudpLpxX{ChRWO&KVL({QRXzueVFEaz*^Y(KA4)$+&195^v|Y zNyErFG9CPo*#SpXm<6U)$pKyw5f+$bc^gQ}bU9I=bIH0sWOM(<9Y_kO(e;s0srD z1DjJBwMH%-5cf(d>!}qjow^0qK5f^;2u-j>+4w)f^45q}P6u(u=+?E{m=R?aCdQnN z9fpp?pv_OCMc30Zv8U-Nm|&gijuW2<=xV#X!o4e3@|tbd|Z#mPSGJM<2epgNG#E%_oJ~*71L#Y{&Hugj|6dzpf$>S{_ zhyG@rBoF3kF&F*fO~O0Tp)Kj5&z52iFZM0UujR39##owtOZ*o@A~Rs=$nvss) z9a&1<@OAY$O_WQYzi1`u2g_?sg&3#FWZuaq+7ekpv!li`>OZP@RSzo{QUlm?zk41W?{T+TTJM(fE{vT-Y7Q!3)ru!3Q&$Qm?RUt2 zVN!&0GIQT&Zkbq{cZX+B0uugW<`!S^L5x?_-#O??XvX@<63g4}Au7av8COht8@z$2 zQl}u=lBtc3Nw{(rA6}a)Q0ei`{s|UzMFk+KsTYkb#~7?pv#YLi%%ox?Yki#0nI?pg!1kLcrT6d?-M%Vs z_s(58^9_|qV#bA)paB-PZk!rnzrD?&L4N3K(YfIfRXQ#Lwos7s=vy;qD`jkQD{Q|v;4`r2EKP*~XX`<&U{-PJw%APuj84wHc7++BU>}O-Um*wQgDuzTxUm2Os zERn70Zv%@AnsT!hp{(H8qmTS;^ReWg3#wj1ien7MYDIDjFE@rSG9q8N=($oaVk|@~ z=DVa!N$|j>pNoWl>tH@BpJLMPPjJQU&MqW3(NWB>CEakiXi;;-!2)P5sszz-4P(xc z61(qQ#-#sZa0PuOeYgk#0IWgo9f4ehu}-cYY&7 zf+~wc{`JnLWZbY8K#~fQlWvlG179&kbsXy&h9Y(K#eR(jdn!Y29Rq$2wyEE^MXJE+ z{8iS|Lk`!okU0o#^YEn4hLQ2_3!`77+r4!lj1|5`k^jDiCaMG;);^_Ye#F~jkUChl zPS`{J)10=SJSnpyLMmr!vrjlfMqUbHJ};q1T>M{<`iI;i@o|k$b9k-8$4osU&btbw z5&r4tUc5pf49gL|i)FN7h3HwOd#{i3Fs20BMZPC}f_}a2b}E3q8@f(Qt_ei4@jbc^ zD*@g6F+pSOcg1jWGeO_`{JCLJGJZHzQ@NQv4{o#+Ig7X$G&>#e>iR+tN_?VeFUe27BDU2c*nZmfa}w}6TH{`M zfhM<) zV(w1+{d;~g-b1t&!C4uJ3jEB*Dx`S%Ii-s611zEs{D|_<#AJYTEs_I8sapJ#?@hXm zZW;N1v%2<9AoZBX67!swmkwL4LH-}%Nr0asjfu=xiX2UiR`=69clo#>e+A_y(Pf#Yl9Jr|#i2y5jJ%pZlU+;dzA9Q{v1 z5d8L^b~*Q4QwPo3nwYPIL+2D5+CBVw77+ZWUpFZ*HDhDyfAIY|aP8u&5SC7fF%@=D zhL~#&Ix~8U&LoOBYE>?^Ut=9Qfwx(4R)YA;EGA+4ZIrSzy%SjS8mX8+YZb1j#^T;;e4uDI_zJ=QY4_RUYp4FjllH62|;ZLCy#LEqY4-Me3$O1x4$L!-P2i;huS++sI0 z%~jm9DRWV6azzR23NY(tGly4R8T3sqCi+!ErApTp>NPiOuiu~7O0|xlDLr#;TvE-; z2;$UN@e)&UJi^)r{j|k&sS_UW6P3Ovy=B|gwbo06;1>xQLbm3NtdT0s*cd`ZpYcZH zUh=qlR+Ec$b3oriQ%}B5;a2aHiJ?I$8>t@{i)_c^VDHLc>AM?T!CB+p&PbC&qQ-8w zFFXbDvZBT|Umdg{=Jm-{+f_{LqnskPzx;;fAL|675VT|$&*N;mZkRx|<-_rKI;9G1 zjpkhzRKqgd&vI!%kM~r)HLR8Ea8|&KPAJZ)BK7PZuBb>zv+_;gqCDRnW)Q46di29t zjJ8rB3;OrH@mF1@a4U6Nh|JSvm~6*j>S7J;pe3~^?zMA|2C-vGg|$d$QHP!}@lm|N zh#N~?933OQkpR!fC1`hpq;`GiZlMvg4)MiXvQcO~tc8qkR4?|6kZ48RFO0q4wr`1s zpVFX%pv4B-!NRC1KM2@;!3-m(GR{S0#O)JB9angsAlPlO5e9{D<9&*&CwtZ5PV^`e zx0PWLZ{(LMw~19$$Dp5QybS|?AS5D~7A5Uw9fxw0$7xJA$qKF&LZklV8 zRr9J3^(i%DmQ5DcA`w+Fr*hK=h^Yp55d79sGThx+(sS;grYQv3*h}XWHt;|{k>x3j zu}aLvvUq7HLCsh-G0A2xv-!(WipV5f699@@mejAMO{L+trgo|T0Aow2K;Cd!%+0Wl z8FV69Zx8iZnOmUnN{ z&5VcrEhT#ZW9>g^_elIbcSImy?Pibb9H2u_O_b+B@~a6I6g9JfLs3m9&iO}z*XGg= z7foH53eG3f0k?^FfQOzlgkXILcq@9qVKv6f+=xObM%cRy{Ob*?KnC}6 zZW?OS-Ts1Q&cD%wl9;SNYlLosJ9aLfZ6w$-vQ1&U?|QqFL$u>>0SSi||5y1L z+qA!lZw`%a4f+9rcJU#!QWtl?8sQehKsaP2qIkC0=U!}k>Q(H!W^D>=TXzf59E)dl zib`iD@`D%v%eL-;jAujQ$;cNg3}{WT!*b-sEj2{l?z?=PyWLhlRM&Hl-AYu^WH9e& zyljNJ6O5O{`^>-&2S&{%oK4KN){#X{mKCuRr)CT*)SX&StcQOc8vZl>2k1KV_uOv% zM*V^?L|b{ujJHgAb~yDW&Yd)ZTRi_Pa~ub@X`p8O;w$6+V!}Uj)}B>_kj0ogT#y(e z;N|Ld$<)o2sC@Z46Yolhpo_D0SGlzyPm)NBRBrP}?oXc`oJmIxj_a8}9~46rEwV#e z$r|2Oeqw>eXJBMS$dneV)7i`}%ZXce#4`kT`W7YDO z8DeJh*xgVmPvc>+z>l_LYE5-=uo_$V!us3hM(iiJe_eOvFOtWdSsXE}QZfC*pH((U zT_6GoGyDtNf#h;w3t&_7;riuQY4xbW(k{Yt6S|a70Bm^!B$)jltGk{9?=>=40$Wo@3-zsrkgiH?O`I3#-q$9 z?^c`R^W(Vz-j(D^si%KT$e|+7QoQ1ajXk(OEtXU7P70FkwUn%my{td3&PP`+2X+22 z_2v>ms3DL;TCT^!cp>*C5Vw}LO&b)hLCs@eM^-Fo98?jnO7?d46|a8~u^<)I0~8*-c=#!O>Su*opv zenwu|eL8*`Jc~IKZ0mW4e z34~j;({>Xy%iT>$jKh7P`;==K#mbPk`btA+E9(Z^yE-dHS#~K_DfVcWc-)j7+1>Ds?|47Np|6Nn*y+ zN6JW2+l<;1e*N||i}2hvw0n0}V)Kz_gj}JOGydR?yl{ zE&AezoS|L6zA!5C-9Q{@sSd(RlRw1sOg?H)|AhYap8G!5Ox|7?*ng#cNkzY&QU_$k`YVjOMv3TY643JWF|96dT)tpoeq*GX8scGeDuW zA)9$Q;s}|VjvGKh?69^||MS#b^?PS1A|M8tY}i6+Th=q$Me)@e7tGocjEb?1lBgV_ z^le*51>^oHAZ>)7U;dMKijf~1C5%BDieVdwQ`*u`df0CCEm0q0m1(pHEx;-mPZhJH zE+pr>uE#mxY2UNiY%yiRSmc*A;*7zIQSj#S*5UjqRVkW^x}HW<--gYKhr%7_v@R3i zInc~KKzEL_>a*~~P_}j3+Vl?d0LE$s$@;RwsSwBEpQ}tzjN8I$8yf%I>?w%t+NqSd zrQ(A1PlioPxza<-zp{Ii#p5F70oMnvPfYvEB3Py^W^W8#6*uDYUA9ULaHXVE2Q={- z1-|tv9lFJps666wOpSG{q`5HJH_TED+7mOF;Z{loax;Y=8l`$krMlv`ThOcSE>Kl4Jx!K&UKBaX< z5}hiyiQ=bF#tLDbVWqH zszn=J`G_ynVjHynGGsIuPfvWXqa`ogwb4SSwqTPRebzsbY6sYMnu3Ie`L@zpLtyqB?QY2p50 zpX}P<+?8#9(YJSSrwQmmSvS7lw<$pFA@7*h_#l_oY*_f_y&%sX&F7DE7U7gs zs1nQZlQ%Z=-wJ0lXw;3eQnCl1!m7+aILeDd4sG)ut&OwLsfFg)CA%$t;eu$Sy!W+V z{m6Q}LOVT--?K%2AR!#M9c$ZaIBe}BA7i0~TMqeJo)#G{MhQ86FC@cZ^lQOb(Hx0_ zD6}qCK_>n)UluG6uw|!33RMojgt16h3&Pn>A`lb57KrJ{xF&Z?pLYZqz=6Q15-UH5 z-;WUZ-urLx;z;jiPQ-YAX=y$ny!e$3tYZ%`zcKev`r~yNAGL=CG02w(<}p8Jh*<;x zn{_O~5c2+QDvE8_uC#(or@_X3dZ1H~&0_M!b2Sv!xlFT~zaX54i&!7&)a6e!(##T7 zToH(V(^*C8VK8)jE8v@!1{fx^GEA+x>)1w%*n5~rXHf?_chh~O)Tw@WH=&%&n3((p z?;m>YT^Doe(ojTsVQJVIDeQ2~?K3jz^G9CQnB+gIiB1BJ@RV>oe27*g??#%H;>sGH z&W97a9+0s{Oi#%>w|~R&X9FiHAxeA?6c_c_T{laKqSh^x2@GB(*ACWia1#MPk`==V z=x!R7-(Nan|4H8v%x+|tepHFP3BE^hq*u)&Izw{3qs@}B07ZCxqTo8ecQcOW1ZQ&E z2b;(NaCgiBc`GCvwbzLG9^s^~XRS;%IhYO?OY)>Mq_+mRKqtmW$w$Z%O((dWuci@b zyvOSvb;a~z6NR&d9F;-?MTkdkq#Vc(BN9`e^`2N!%Pb8-)KZt=pGps@S??rX@kRp5 zD!ql0N&7};i?0zW|DI(Fe`_8$zIVeulY1;>N*~6CQT@rih)zE5#9rp_9P5x*+{n8d z|JV8U!0C3{8&fAa7x2q?IM__IX>H%DjN{uU&;8-kTmlvy!G)2k-)`LQWsuCU0f6GY z*FcHOH21o41TNIq;&o6XvD5s6mIKS>t2stJt=EbR2q3d~ zGj=KR3oc+Shtu$PZl7*|$+e8GL6sRb!+v2L1B`ahs#Z^mZI9-k`E+pjmBQ_oirB$U z!X~)W`OF(Z0zRL{A5$^|RU*OI%A2M0j?QmFe7Q4U<~PB~sFB|QG`i$2t{++M0<*ix zlSqSRb%hNT)MJ-DBq!33-Z4;dvlrW5%P&_4irIfQ$`@=;HWnX!W-4*jLdphuQW7_j z?-fL+_LS(a-mr6i7ERhWiz$_ni~7a*&TNVk z>D#RIEsNs-+qJ)UUX25|I#$h12P(ct-%km5JFBq{e*MxWIRA9vgNy z)cHmniZiRGZ5A=Eb1MB&9}K3~X(mOX<=r6@}aqqo7Q9&zk*;bg^L>lD~2>!_R2Iy z&SZ%7vo)Ym@vTS6s(SA5Gt;=eLVJs-6Q#AgpixWIV7Mf_sa)>9k0%EL_dsPmz_?Dkk4mRL#lx)d<68sF8lCdMmUkbOJ;F)EcF1vTous`fM z6j^%-*}+}Hm3{x`d(+;3+vwy4k-QDLy-}TvFCNeFD=SNQSq{b6Y|M|x{E^c1ebkR9 zO~*A)*V~8*xxl{K>m=50MAb+SVIMaeDW=#M$^P^;KzbcV?PO)tl>$=hV%Nm2C_9Nx zNI!M$;(20v-ZO++eZ^bJ@v3A7{@Vg&|J!0`B(69bS&eF!OWMRsF}lAG-eU?LDpk|6%GY!>Ve& zw+|tbO2eU~TPZ04C8SeQy1PTVq+2*N97?*oQ@XpmySsTe&+q@?y)Hj^nAv;Qtcg9d z;=X6oYF~Y?hezqD_*n7V+NfmN0DamA8`6%$UzAksU19x`C*bbihy7Uf z`K@N2-CerNkBDv;%Y@uf+1z<~WQRiEw5kA~E&7P95Q5j#0a5qYdS|pJd70HDoA(&c z$4~EVm@im0r8|HeHR4Aqs$Qw#{=@cqyt$Mm7w(CxTJID8m+S6+G5SJnqnp+j?~Pd? z0??9@N(}G{y5xARpxeK{`5g(3Y#wqT%gNPXq2Xkwn6Q#)v}_< zZO68)&Hl|>Xz$+TI>}ov@!+bMjfB)jZOtyeMCX(K)=bUM+F;uWUY4%Jy&h->ZcB!Q zyiaMs$kr9~`8pQW2xWL}0|*z6{_A1U!n*!pHbgDID4|cIEpBlb*$rPj_)7hF`07Oa z-;u-4Wz459rClOpB+UEpIuqE+{SKdU2xrPWmxbkho{|L+es7@~Ng`yOv-AduieY)9 zC3kc=Y|;ZVA#wUlXr@&4vsz`a!L_>tYB`GZ&SShZy`w9PIJ)h$7nffT-;u)w2e3oD zcW!Bw43`-f*g(ly1+2JSkCGC<>4Oxa(y5OkkaU`?sJ44rB;g7g(an%i?f3lLK6~k zwKWD=e1Rygo_aBk5C#kmg1++#iacQRzbOXN73t7J?@~Lwa$L`(R$xs8h?65I-{@cj z@;n+B;g!lx98!ZUEdj?%@2M9)6Mn$p)p}F}WQ-cLB(IDU&F4l*HOhFs;}t;Wi^P9p zK@Ez9cCRWbb!y-~Q~>vwb!Dy0+9450|0S+lgQ#gY)0XQ6LG3vG6B0jFE=!0# z`HFQL7$iP-0q@4P#BA^^Ujnhm$9o>~wt2f<@ci=Qg9cvDM)(qt<0*}dWmH7{ zAeo!aRwVB%18`qNlL5Y6B8WeT6V68lq-H}bq`-G(4X9cC*bwh4D=PO%pWDS3UtdoZ ze#7*YUj+KM;tt99eii}75m`TrSM_A{e*dNwQcg|ETUG1^#cgX0s2@U#;M3pCoKf=_7tIY`4Mt%n8_%WAp!p)T5!4u)$Z^8a z!`%j0NCOe=Um0Fspa;gY4XP!BE03W9KM4O`LZ#(|>0R!8nb~$;77OXZcU=3U2u9oi zO*2F$!)#H4=AgGPNt__ct~IOs(;D4VyAlz@71TV&yORp;v$P}V5BUm@e;Lqi$5v|4 z8~O_)ep4iVN1Er0Q3FbJM%C2GN;mBTEnqf zoyjn(gKtqy0;5J_n`Vg=M~X< zW1Miv(x7wJvzOwoRCb794b*%##2C%5*@Zu(0W7CF+IshO!~V5c__g>7bK!!`>7Ek? zQSnVKYtjiGYs1W=tfLOCSFXRb2n3cbHhM1bJ(^#b#F2lXq;8UT0LcOs%cvWe^vL2M`g-3Lg&yh+RSG(T z>N*G^tnRFo<(|kHvro*a88c z2BeG+D;~v_nrotmFIYK_4r2DM?Nh8oHC~YRfhLFO|rs6^z1*162JqZ4yy9NB>a=w(2I_Wi6q%gvoOX#9D?Hv1p}3t z+j{}|slE$63z>JId(Q{xuy?dRd8x>PQdwDnhA=ZJ-am+?H@mc@ssZBvDQ&Cn6O)Ma z)UZSc-cc3vFr5OD+R+y2p{Ae{@Eqhu$dCd$JN@lWZwU$XnDnL_NX(P>d5ld#^=Wn? z#XUWkdf9No@}|?!@f!qFQHk-9*kp2GdGn(h^xtEF>Qa)!fxO5p__?ruR_`pVwQ7>@ zdYoQDtDRsUVVuA;r&GOvMBjD@{UGej@NJrpIzumjPSQiz?%oX8e<%v5!4nTYGp>CD zL}bj_dHzdO1nd_4>|Q##i`FOxK=+BRoV}@E7Z_%k4$brF5g~{J3Ia6=Y0DTl0Mi;X zgK*|mW#mKKu#W2~zh3?vcoFL0_|Ewp*gud98-pl;I6WeLpzA2gLrWI_ejR-1;(YwF zs*0M$WSzh!ArJeQ!h!H_xry|oj4=0XE8tn&FkJhbqtW(jB`_VZE0r~jfrrgMhhFS; z9I!uC_QSRmge33AH6{bukC8FFG&WTNWt=PCX2(X7R%&iTUZzNGDtF(Ptq@)}-oLF6 z5FNLRD|(&v)?0W`O9MDEjRwwrRYV@MdYcI$gErj$oX6N6hG&}hOj`{$vXllc$z@th zA}gAEoD`evy|sLvZI{;_5@>VB$Qw{|Rs$KQO7-=&Kdsq%D-<4dZp#K_x5~>~wwI#% zuz~oIuwDQwj!PX_2UrsIp8dE88T^EX*YUBR&$K;ZL8`6)E7-U56`++14SO!Zq!@?Up@hASY}L)U#4Q`$~c;+}P4Fe(_mTQq86)(Km0GcTyq+rE3uDSjM!8}%UHQK+q)aRlxbX@Y> z6DgCF!+Kex0)aYqlY^6CUYAn|ZfoCYscD->OQ7M$%6jhqrgdW4DS1!NcE z3?y&L*fp_xqwQLvVQS@Fg3LWjhxd-_-wxqHItV)z<^|UO4N(iSXMEyHMfKDjF~w}G zeImy6j7osot-}f`klPxA@KA0o;wkRIa6y%6j;3ARKP_a1h9Z1XFJRe1h@5 zT>h`}JC8kUbXJPZ;1?4t(CxiZ8!r|}9Qoe}ngBH;q8IRetEPBRRYEm-_a`ThJZUuH zO^s-*B|&LU&Eb^^;^*9t#cZ{it~H0l!-*c8=Bd!O=rQ6V$EA#xS+zFqo zvG#Y)4>(T)bor0#3!Bs+YLL8KfDMtlZ7C1KD;pqk_5^!2d5)>D;BXpJKq(? zA=8_E3PtK+ltMzkz})gG#f~M!)rjR&%8rMA zYal1Ja~q!`%~I~9FqlMYGX@To!$9Hp(7GTGd8grAO~6kJAe$+)B~pa0snYNj-yWv zb~JWo_G|3a8a;@P_QVz)1yuZ}HlbNE2BfxS)C>=^LOn_KOzd>yyI_GJ{x4V{YZ&+Jk4*MDxR~BR zaL6QjU22UmogAlarU0U*HiwLEe)F5t>TX_wXNib1Ebk8@Pd|N;ykng4PAdcuo2|cf zg1r?BdptA}w&7RFp9$`ig#-@PYLevJlx^%LPxuBhp6M9v)e9}zaWsZv6|-1i^16yO z5`WX@9oU29SHJE!)V_T;2a~TZuL53zv&px`sP(J$(E5qQ&v(254O8T2T1dyP%e^ZU zKvbf*&o1h;%rmbp-XbD-cQ7r+g~ZR6D!E56`O6l^8#_QU!315fTDHHxF<+@hCh+2$ zS`HPANl)luRL1H9X*za*KU6wa7xBmZwbqeNY;n(U6Q+oRk1VL+lzzdG{u0=<+s{~Q z3H{Ko#OEkxhe1W!vH_zMdr15rv_=;H)_-hfqVnIwYaylmUai1^cW^X~w%2QZm;}Ja zX+Mh|4Ei^h49O$+p?aF43&xZfdh`6$C7krx;o{0iY7^&`j+P4Wa8pmkhN!!YkopR!Q&-nzH|f-e#&*K5(kx1+gdr@!^CL^*0Q=_XrTCbL5uXQ+9E_Pg;Y&Pl()AJ0 zh`;B10dR1*cn44r$f_8+E#+g1^2*Mx3(~t6NL#XSYi(3>;94{Tq6z6dTbbcA=DOfX zeLdS#1P1Hkzm>6Xjtw5()qV}%GYgFtyuckH^ou-`Z{)c*V0Qf!DBTzqF#ygd^1LVY9au@Dd{>NZkm%v_%`wZ9qI3G62a zjXOoDA!fTqnA@FN$3^f@11Z|z%xv4K6JdeqENbN~(FI2#AE+DLchrOp3s)=FZ{9K< zr@d`JvF*Pc+zpM$+Dzz`1SG{9r!6-$9|~5MBMtGSkYz#Q*H1+}=lf2l5K3&RE#GU! z22X}o(U$^ON9)AU-)@ij2M=qg9m^Y+H~FHNKof!1|A@>8-%su_NhX+4g=e_d4%UH` zY5dDBd^$yGx%n_$W1*!a@|^rJ3u1;zXLvB<43mk;7JR|`(g~_w`1H3zdr=Q%*Y<3r zbPlzIK9NN2QNaufoOqysbQC5$4m~^w$YE2MgC)lz#^>mBR+U@Y4+JvGTB<`1eyZ9Z zBW6hdsGG!kNHM+dc?Eek0Jl~EKk|}q04e3iI?f+F$7p4;Xwp0+8J>4YZA|W1sYu=i zu#reS_P_M{EAp9szni+&khg*@6Sqox(2nP`uYUuBbCFOn_Y33!-*$7i&EL%u!aCK|Sjl^H9*d)d(u?!z61sX5* zS>U4IW`z#i6bB(fMUKX?yj!Z+eO7db>HSBNL2Cs=Pf+5Gv-g{&%7TUg{l_5)5(k@-95duF% zi!OC2c&yy)0`K^|z9L8|J{2^`>1sZgf`A}^yfhI&iWz?UmdV2TFBdxD1u7o?aqkcl zA54P5<+Oa7=WCTpMX+Dc;lRV=mrluKtTiYtr5()jJ5?XC4G^Z&Qfths*NA@b@YlFvl7vLznOk$a!)cddYX<_MQVWiniY{DHM+Q!EQxlVw^YG029`ip|;_V1uSn{5Za4x5{rG9?JkHbNiu*Q%l{+_BA6=C{P)=UPJV^1_v z@6zG)+3>v^X&)(P2S=YV?$HS3T zy}%cFOz+Y`UFL_0L~WM&TS?@hg8+WD+_A@yp}me@QcOoE^aEcWdmbZFFSnVbsy|Ws zuc8T&7VFY`WpMJ{5r2rkV8?;M$%SZg$6__9(_}Bg5vApwe>OJKd5Vc~40Z8obVje{w^kVwbmzR=nm(Ime=LrHrmkBjLj0n0ydA`Y@>S#(yd-hk{zp5U6M#^7_=W*P4OIDS?8}l>l4kDI6Pek2)VUL`P{`97W~|HW?A;*UkSAH ztg7ld8(w^w0A$iVh-l1^oj17YdAwF#XVkT^tXuf$tsQdUSX&%vBB*F4@UqG*hAh+t z>UGXIu+I4XD8aWZtj7h4aJlpOT}bn12xRXU7Rc~BPAOZ$Ljs=H4+u+fO~PC5um}yy zg~+OREtq0+r<@I=f7Bt&kI`lE7K*2I#1>@oVGtJ-9m3^Gvl!P8ROV-H6}Z!i7k(eV zkE?w($Pkd*=SwYGr?fOYimpKyH&^XjKoH1}FT?l*Tq?bMJ!Yb^!kj12X}54sD}Ycd z#%-?}OmTBt9-ZtZdq#GcS!Uo=2tdJIQ(8GlYQdf+SR+SP#feLCB1)9gr?SH2^SWbr_oo>lFF#cz7$fQb@22*m2EhXT4c?Czrm z`E#eTy*{$;*d|d}$u$L^Zlqb?RYPt{98ftQ;fgKYldokj17=E?!-L~L@ ze-^>3;7(+L$sl)gECdDJ!bnAmco_O4`a0hsDNu`isGEsla-^a+C*)QhI3IniOI8D5 zp5w~{j|Aiej98~hX|JwVXQ)ACc&%k0#pq|zh0;-PaUOPn$bb$$@dU)E7=ls)SVMj) zr%KyYEN_Y9oRlMe0JP68*K!fB?Ti|PXYXyvFaP7~I-760)NJz0fH`a#mT!@l_%IA@ zZHZ1TfSNQ^|L4ne%jT*mZ9eiooP(0YAMF`QFZHHMZ3QX79n*)mnE0rmu% z59@}UCnOg>ol`|^2O^<3^n&Wcx+!}IjfYQXxse+oh*zxGLHo^`1teN!x>JHUCzkHx z0Q*_-xWD>EBnWkt@<2^q^{Zi%fLspIfQzausAhd+x-zSR&loWV0KU_V!}1p4Bk~z@!y2q27or_ zgC6pdpW#>EUsivj@=s5q0W?#<)GF%+Ys37lnglrFlL@5{-g~(-4I%NrJB)+1O8;XkU_KOX29$~HmtlHm&JMdqWB+z@XDMT~qE%TRfN)|76O&`#6j0#haY_*mqaIo@P(Ho1#6Tbp}d+FO5uI|V`jwu zEVF!c8R?tN*$-)GjIzd#`= zJEjya2w__ckxD!RwIZGjFbMHs$gXo7$jWDl*vAUUQ7?9jD`vC7c*jBq5(jH=sf2fL zT1bB8b0)KK$H(fD{AIoTU`xYy%XR_G0u0suU`AF{iDH+>M3P1DwJtsRJ-0MhjPLAk zlF4eTY+=gR|hSx(NDIbZU(3cjxvW~Kiw{w@!Wv%$6=|Hn`37^;GHt&R4` z7A;>C;ulcpBfTfX={lNn>=4HNBkG)jrxpaN*%D4t&ktEd{;Er~fj{8feWuFl)Lkql zOnXqa$9aJ8rP4)0;$tGH)7m&Sw|uGqanNX-^X5V{ca1y7K|__v7&E%%PF5O7MH@}Y zsre#)aD@(IKGGamjcdtL1G4BscBxODiN}zd9}d2Z0VCtUYw4%VRutl zyGkjORXA1pD?ygCrFUDlFm_HtUF_p^`H_ThJ!QQ4rtv8`=c%kndBXJP3a|-#>vnz7 za}{H&N-4~SJ6@e)S+GWbbHHy$i;kMhEV!dRek?eMdD^%ymFkmiHJgTQsEe;Y>!n+c zO-OPAZD--CEGjHWA+f)5H3B9+HaX#3uh?b1za_`vtLUIkz4W1G7x~!+HVpJz^4hlH8p7t$7#@(6eAr*jgHm!!=ltNL-LS1DBj@N&%C|k zbLC1DcH=g0s_#vShJ|o$$2B-D?Ed~jD#yt-GA1Ilm&SfhRcuIQ^aKbwH%WUfcKm?R{+McWtA2Eb)+^KseC_fa9^{u%!T4 zF%OKn37<3A-tQc9N`|QOk@U@-zW;TbkCUeHF)=Oq;YXp@an(MRaGuY<`a^TP@h_57 z3B|dfPr5G}sbLG{F=W@`z*f)jsdww7q(e`uU!EkMIQ-Txj+>9TkURL{FqTqy75{i- zOz+?%NtkBac&o=CA9G~n==43#qi@P#Jf(jLr_+|>Z6Gdm2bKSPQXlik-a}2skmhTp z69oIOG=4XAeMH-54&z_|bH~ngIMB#uIBa^-oy$2X&}0Rq=*?t zECQ=;Mj5HC87lQluzx(__&_Y#H9D03vu6T13qWPNfrMiHniPjf0z$#0_-C0u?mw5OukF_-uMt#&b)k5ICs~l5BMQdB|U>R~O zS#e$b6FJYkd^c+xOPC&`{!fkVTFb71sxssm08)YzCTM}9DLIo<9#0?n={CjnlArQ+l z@PH+13B}bfkB{%aOzKcaB)SKpd$vh>MTEoNiQu^$_v?ZM_-N#&AESWYtZ+(tnxO`; z=()pT*)>Nt&0lqxjEXl}OM7rHbtG%D-tw75_Es<%d|L+kV8?R-8;+@3(G_OMm!`fuT5NzqodD|Gp+=ZR<}|^7DA$?lTQr;^Z_&CG>k3&;JUl)INmN^* zW=PXZeE9{?O$3G+5P;BJB9@BU2}IH_0IZ+!xIdV#S4oumw@iGyQajjj9^@Vg`Z)C2 zNvF7c@;{H~r1VgEzT9d5A8JV2F%1uszO1@VxY}6!aG@iCY%=cUDomjqU2{_dXEsmF z=XAb>&D*Pe|3Yb;5gs`7z-{;Sg%i6~spHRx5{3g$fdaivcMb-QYA9*jGcYfsYv1rn zGKzs)T-BDDZzP=bt*$Fd+A@4P*|+keEkP6nFnsnfDd$LEa9WmAeCH)KXlHajE*{Hx zVv3Yjsll#Bpf`AWulS&f8uaVdtEO==uj2)Hr2pkd59wz2h@>Y<0TREsOoXMrbeF

%0FVQv!Y7oxjAV)j{8g~~dExXJa5ZPq^<4eytP#`)!fE&-&1BRVb zi~LSlbx%w=Av0>zz{FL8iQ(Y~Tyg ze3XYY=4wUJdoO8YAT^-{YD4^!L?v%G(nkcD+d6UNABNrO~vAQzeJ5Z z)eMJea<-H@>?kzc!mz&Dp9}zK$1J29k_NR;&T<&d&?n~fW6*m6tTO5enuP{Q^rCr> zRG`YAri6gm46`ththYFJ5HN0VwkGK?3>0&;V96oPKsXS{YP*5KtC_Jd9EK6Ak)s+u zFlDg0$MhDpH00iXQkoOo+KpL&U!|j77`7XDC~$L*&}oJL1_->0v+HeM0~sg9DjVIzs}`1#bv;$$X64tHZ6BsWZRpt~e{r>#D+ zw?1ITH|gxeyZjyuWC#pgR4GsLYH!B^xq};_Z-Pb*QdfU1>oF>sYhGRc<$S2rraUp7 z9f#)+#u>tFt8Z$v4_g(AD~ct((Ghjb{+9d`jG^_EHrx?FX6At}{L3J1YjB<7s{u9x z1+N+A*UCG#W|TTr#bjTXAVaeFZC1h-Omn&3VlVK^b{p)c1^V5N>cL@ES*0$MubUJ` z<-F~g5r1V}2`z*RWNTeD+Wf0N9q?>dM*0uxlA)-!5#d$@xd4!Gp$Q$ZxD}CejB4yd z2&WWQz0!f#;MNT*T%0pJ!VCN1_qJBsUu7JiKLnS0&d9WkmwVZz7}l8xZ0TFGgslF4 z^Hx_TBqSJziU8o;L$4rT<1qoB|ANC!zf&ZQ0dQP&2SF3{0p==%gi;{#8E*=%5KoK8 zlo!W2PTzZX0Nl%e(C};MPXOGjBIs_=kPIbJWt_!&3rYl)vA^S+DP`~b86;f?trRp< zKPgYy2*cdhtU=pyE36L}kfO`?dPHhXH+`q)g|!oZ=_C!b$?+Enfd8^57>lH~_zpmg z#Za$MqWo&TP?^AQ?|AH?>JuOHDc&7Ywve&k_j51$(v)7T#+@kJ}*9+fX1UCU92Q z{wMr9w&a7hK~;~Mlr~}7Uq>ry*_@GGT$7jxw8#Dp!Q@oTz4kWa^$6rD_a(M3)-E~H zvbJ@f>~qJf(63ofKAdXwedOS!8ZZ7`bCFF1c1CJEbJ@NxGPiIJ5jE__of$;hQ=-m& ztJbe#=`4{&2Gmjzq|=w(HYyl#rEm8T%Vz());tK6VA%Vh1}%A424%Bwl*j_Z)SRyb zD))D=R>jWsYVfY+!5{R-gSitwy=RfAc4ldV)YQz;+0Mhywx_+n(w!M#8`t;vv7Om3 z5AsY5j^0~}bdufeY-;Oo2n?4At`yZda{H9*uQoo9I`Td3ih)qGeo-;oUA0#*-rlwM zq~bXt6z-wqqlJS6bai^<@k3gG+Bgn$AO!V$lW+JiJgKLf;VS21r(4E{4I`065N)L^ z{3YK98}(g74;rkI@k7&0g~fS89eT?j-yckfF=qR<=}c2)rbJYZekS}*9e8?y!kpf> zT6lfjW2fkPcz^QtoKJoJFox@2RX+wscCA7^9UO}jdn6M7;P^K*rJfvMEbj-?#N3M-zA_~5b6z-R z%Jq#Q-J(~F^PhoZgMaEK_q@udwK({&yz2~f-?X_!eK;EewPYrrfE0Wt|EdcPhpjiI z-S;*kx52j^R-%6R?%7Ft4Oi5&&GC(@dwUYa0HF;&-TA-f#c_|nd0^gQe0xuO5f33x zP?t{=(K;SE^p^(3tbo0@2E!i{qOm5oB>Kcfzx`=i1om_$3)pn|*LE2TD86k2aO>P~ zW$!L1m~JGt4M^|oaDZr-L}T#+eJ&&B8KUDic|}8@BIZacz(tJ2k6K}KlJ97S<(!y!P`-u+p2G%@Otq%Cz8p7d(oP!F?#g;3aY zkORi!4`5(M6{zlltGJ){IgGZ>{%?V}6rV^5kgQR#z_MJ_vLsloX$DliG4 z?~DvLDGdOUdoe&EfW#kUEV9;Mn1!J6M(4F%@q0i!mMJiDIq@kIx3=&9c92!7mFmEen6_7{QEio(=x+_z$=ZrEA&H>bMe<(; zemw<4<-Z-96jekR^e>XKUsJq;8mi=gQw9UH+p0jJSZ4HwKP^ z!HFTFU-4tz>o6a@V18Z=1F|VdN3Aae_`8^9m2aPV`#od=tU%tc(-5%le`fw$qSNDj zVw(h_HAmw2aut=|4|tr_Ql$oE&obz@0x5J~d{+?aG?3HHskIFMelC=b=Wi|bVIY^d zujDXni4Xtr!$cHk3j8pbrL=3yL+NMdRLf1T%qM6vwN?seGITh2rK|MYlJywXn}cgg zF{*xL(uYv+tDu4v@|t=xQo6JM7_4=McyD$KOC0C>fWr13ys|e6br*lMT?qucMs}kygP4V zX5arY`SXwK%0@&^_X}Aiib>0ZYB%YA98P`>FQ&LS9!t#$5}Yc!IH6Y%hhc2h zr}#=n8on7M4k`bg94J&|O7TLV?*dfK6}pEJ=Rh*^<~~j!Kj+d>F6;e-M_(dUE3LDW z$adb$ePg)78aO3WAwS71RVR>~La{H(9bZU1o^{spH}>yO8hG>i#f0}^n{L7@6-Pq~ z+y~`=r9Zf;0eGJR`B2OBTALg_As*lW;|}oXPe&<@ffD^Gb*T}rNiZOSvkGjFYY)_%&``g zQ&zn3z%K`MrlP~^Fx#6orFJ;{81$$D7|+8u_9VaOnnnN%tz_IpB=Y=ts*HvDDyo1( z%c@L?afq}sVCGMJZ6jk5u6kW%W zagC&8hYv1;)neoiH=Qj~3CE;lBWDm2KjOTLaH1eZoxfK_FMpoc6&Lhkc7#7`nEHCs z-;N@^y}pSt+|`~

7V%SJBT3$c-kkxs}acLi>%xe|k^g6Eh!laqGPXty%V07*{c~ z#UEjd3l&`HkiVX!2GOmv8_e&(92t@4FAg<_u@lu-QPJ2Ur=a@rRkMK#`Y4Dn4$f#;Vn(==rg7gk=oqzx$xyXecT=FDoPIQkDQg1uLd{tZNtSfetXu`;n(hQG8R z-Pq+#VT5G!cKGuC(-4aKm+@i~vBtko5F*Fh-N*QIqje$S71^I$i?5od^d~WZ8eiCz zWzD+CEUiZ?nEt4YrjoHg!XzUhqT+9cPcy)D*su4`h=abr3G>gxWs>-_p^8Qds8!x_>J>zRS9{X zI$W(Q0T72*GSAz&M)kCHuY47M!25js-MEwb43_D%C^{TzG*WZ!&y$|#HLq7eyT$S(y5sI|vrKJ_{fa#j1t^7EftQyWT?K`oZAM2+#cYkjAEa2S5r zyYd-2DvY}D3%>Wj-O?6zW{mp!_X>awY;?+6ddm7i&XAcLAAtXqz1ERcBM8Iy!2hRy zgDef-Vvv6G3_LOEhl1ZmUH7IOIh03>51CGu05 zY$>f5D~tvN+RPr+FFd|;gTaTUk@uf7xrl|Zz@&)xyMx?db_MxKr6hr_?N5^j^_*KKp3nP2wW?U_YIXHdS2RmkSEAT(| zw!URRUBK_xcY@vwiF1ixe`SSXejESayApgQ{}UH@(|;AWR|OJ=eckog#W0cw;iPO4 z_+R5)&u;xtb~|kyc3`*FaOA(^+Hfy%r4B=fov}c%1?}iri&AQxc8f@l!8|{ohrFx? z4532A4BwHbSgqEOb4ndCoIb~oG2|@g#^UI~rr$DVTW$8Z2b$mEG0fbb@SLLz=fuU5 z`~M9Itn&*#*zI|+M{4XGeVDA32MBfI8yLfHgM{isdN@y8Zw%0TW=pc#n%hR4t>IqW zzI_6aphlWq87uY10pRvM+%5V=`cxg`drOBf}pP31~T=U6fzMZM;gy(0s zFW^{mWnsP4XWZl{S*!f}Qes|o5S?ALt1QUcOowOpE?kglLD}8p4Zs`dJPDkUjL!(a z*a{$Zd~?UXyN9Q9i#z{;R5MKFND?{tZ(msHKyYz1G8$Kl|Ad^DF|h^e`=8FK)w65T6f&l%td|D5O?#S_1vhT z51^-L)mK8dm5uKWw#120KTa|flPt~V~{Z*Kf&8N13>0ur}`8nesr?q!A0I3{<+r;sngI*L4z`(U$ z8D!pn|B$^X^WkaFLKSL8f)r20Q9Zl&1S{sc-+I_v`66 z)YvfRUS6lIS(k|uRW2x34vPVDG<3L~DsV*4l+j*}g&g0VuIY=z`ATRPri^It=}gEy+8#=quJ+`LjECMzNJN>DS$^f=SE&j|q=vcZ*!4BU2Si^`4Up{b!5l}lo? z-X{V3r1FHhmU*6J_hftJk7$N)QS0G=tyAKw+d9X>%#%T4C@A*olhpYkKuw6v{Q*9P z*A0l}DAlFJ8SF8^p~r)v*Z8HnD}j{{&tTHY+5xa~#V8Nlc9M!w-FB{N1IpqAB8IY0 z5r9nycy23F5@ZMq%&$H+QDC!MO4v-K05(S}seVe};$rX>A@6iy7M6ejA)hwXR4P_C zk_{wLX-3_t70@^rHK3`O4eQ>2b=ZSe9!whW(LrTs5~qYXr(ZuthyLoRHNG+4P?HJ1OMKa{SzC~GnQFGV!UeSESlQ)9Qy z^k5YP`_pSpqwkBcO7`rHKu&50wpK-m-EJ3bpx13ob6wKDm?Y=Ks}}nMXg{Yh zZV5kjwHBol(dSIZU)aCXQS-~Te_#YY2o!%0hP8BgQW?*!N%YE3!moBCaeiBei}7YT1&m+4&cf@)7t9u|hyat9lWL zyT=~vH$TO>&q}>Ht`Y}qQFZcPzjawAja9*i?Mg7mBOJ35{lTOP&FZ@W8Ygx3F~ zyFIvI_P{5x$>muz>;GMgkF|S)X6$y1Ru`sUObyINcpXh#Ht)8{E_Bb-RO zr_0-;k}{^(W=%1b+QRy(&&pQ+M;{CF^B1D2?90dcv|_ls9K+8Niv5)0hOXZtYqM4a zGSLTarO1~RUe;aT-sfDdqHk0GjWPG!o=7A4tSQ@faIQfv0p7;_8eT^c$=YIVOh%`E zjH(mF&A!Q+n&eHk?~ybm*MCC83Ulj;mOaTbc>w|;FpGZ|P_*g~f{!_m8C4&BtQc_4 zeWhmR&KR^h6RJUO4hx6fZ5eowGT0!HRP8J4M0=sgQ_`dRmQ}Tj4_H^Ra^@ZO<{KiLOE-*Hj;8~@@USR8ZaPsE>c*yh5)tx%n5Gskq zgCeeKFCDD8P81Pb6c*UY#XF=Q=riDA{uk3*9r@D@$6!P`^sVT0M1-zPuwheogrNL& zm4OHn@w&Q0a9LP&BJRX$RSaARtuaatjnQqI@Bz6*3#X)BvUcz}_|FhNfh!{w9s4kC z1y9j>k~awnEVAJlB(i}q<~q_;Evd%{zttXnsn7L^-fUUaccb;mZw8y7hM6|^Zn~*%^&jI(Ph|W zqg!6)_tKkG#2+SVjESAI+c1~4+oG`{F?z8YsvDiVcAi)psqXPqj!2r^7j)tb4`>+i zmow$nx$}oIzC=_N zE$Le-@L1Y7d6ysR=aSMfnb{)?O%s@?037I)t7tf-q8yCPG948X1Sj%sC-F+x_f15q z;@Y9j(n>a{OhvyYazeW5C^9S3%3DG05z)`+HT<^x^Qdg@gj`IhX?6j@<|&E)g-!7`j?; z$;N?}US#|64dbu7l_a`VQH+>h+?JM;0Hr)H#(nzdl#j6V2gP*i&o`Qy6tk{-wuDub z`OGu{&uU4UumHQdB#iH3d5P?9sm_=GxaOsVw*9$CKW?<~OS*aX$GsHqwaHo64>H(i zWK+_q93xxu7pz};4-j0T_=v){7!C!Kl^E1bbTf+6FT!M?iM=rSjf>P{Gbg+bjjqYo z(#mVmc7dxMZO7OLLHd*8eD&wr90nn}BW%vuSW29o5i(R!r@)#U5k*Ou4mVxPH?@Is z%jOonkgWAX^jn?F?|E)5DQWFnUMosGemzW1Wn_|&5O&tlj9oMWf3ZpO1=C8nS|E6&#d z%92lQb>3@OSy9AM#*!x>?QvUR{c!2fK*nDNrtX!25h;~t7Xr|SA#Xp+2ldJeh5AQp z>BcfcPq)6 z&Sc0K-)Qv7M7=~r4`}>83kp|(C^qO15coYa#j&m=jRP8M$SY#~puZ7I8C&wzUV_Bu znMfhgR|^?`2@vI}KFmM<%CZTPWw-cq9sP1-wC1UJ?k-;w=)?b;=%39;jP7R~25MZc zuI(k!FmIt>Pby!L=7Wxi_~pUv>0VJ)H!rtoduoM$)L$v-_ISo=nJeoW0^j`Gp4jW{ zk(RgpqSvHT*XQ_mOVu5_Y{s~BpXt%l|M#sHUEHnp8F;(6h9}oCIUT*E+xPuCHHud= ze7ii`bG6H#X?{L>JI-|9Jj|x;$#rTXYaXXb>$^_P&p$QefN}iJM{~syZ;MOEv^=>k z#nnVU{_wfXMgOm+&rWZ~$FV>KeS2b)JNKvPORrMV%*fr5&%FDx?!&Glw$*Xxf)4zc z{(tRCyYQR@xn*6yGm@33sHM$bfmz?(9{E|@RR3EHcmZZMx0$aohmNw&- zRx@wUNnz!6r>6h+oU}wJ#AwfphxHay+59FgDY-o_)s^@E=^OVJcuty9H(g{ZLrl^E z70**mT<>%$e(b+|^x!0wR997VQIkLSpWLnYoV4kw&a?c25A)AQvH=ZylX*V(^-b@5 z&g~cPF#t^n&6PZ{dG%h+0=4r-27>wq(?k@-e|uD3?Mgc!13WmwbCL=iX|DZeKTqJ& z{GSI60vH$=R7+eVN>UO_QmvAUQh^kMk%5u1u7SC(fpLg|p_Q?Lm65Twfq|8QLE`GL fd=w42`6-!cmAEwse0z5psDZ)L)z4*}Q$iB}?cCtO literal 0 HcmV?d00001