diff --git a/.github/workflows/demo-ci.yml b/.github/workflows/demo-ci.yml
deleted file mode 100644
index e2d9097b9..000000000
--- a/.github/workflows/demo-ci.yml
+++ /dev/null
@@ -1,82 +0,0 @@
-name: "[demo:ci]"
-
-on:
- push:
- branches:
- - master
- pull_request:
-
-env:
- RUST_TOOLCHAIN: stable
- TOOLCHAIN_PROFILE: minimal
-
-jobs:
- style:
- runs-on: ubuntu-latest
-
- permissions:
- contents: read
-
- steps:
- - name: Checkout the code
- uses: actions/checkout@v4
- - uses: dtolnay/rust-toolchain@stable
- with:
- toolchain: ${{ env.RUST_TOOLCHAIN }}
- components: rustfmt
- - name: Setup Rust cache
- uses: Swatinem/rust-cache@v2
- - run: cargo fmt --all -- --check
- working-directory: ./examples/demo
- - name: Run cargo clippy
- run: cargo clippy -- -W clippy::nursery -W clippy::pedantic -W rust-2018-idioms -W rust-2021-compatibility
- working-directory: ./examples/demo
-
- test:
- needs: [style]
- runs-on: ubuntu-latest
-
- permissions:
- contents: read
-
- services:
- redis:
- image: redis
- options: >-
- --health-cmd "redis-cli ping"
- --health-interval 10s
- --health-timeout 5s
- --health-retries 5
- ports:
- - "6379:6379"
- postgres:
- image: postgres
- env:
- POSTGRES_DB: postgres_test
- POSTGRES_USER: postgres
- POSTGRES_PASSWORD: postgres
- ports:
- - "5432:5432"
- # Set health checks to wait until postgres has started
- options: --health-cmd pg_isready
- --health-interval 10s
- --health-timeout 5s
- --health-retries 5
-
- steps:
- - name: Checkout the code
- uses: actions/checkout@v4
- - uses: dtolnay/rust-toolchain@stable
- with:
- toolchain: ${{ env.RUST_TOOLCHAIN }}
- - name: Setup Rust cache
- uses: Swatinem/rust-cache@v2
- - name: Install seaorm cli
- run: cargo install sea-orm-cli
- - name: Run cargo test
- run: cargo test --all-features --all
- working-directory: ./examples/demo
- env:
- LOCO_CI_MODE: 1
- REDIS_URL: redis://localhost:${{job.services.redis.ports[6379]}}
- DATABASE_URL: postgres://postgres:postgres@localhost:5432/postgres_test
diff --git a/examples/demo/.cargo/config.toml b/examples/demo/.cargo/config.toml
deleted file mode 100644
index fb921ea85..000000000
--- a/examples/demo/.cargo/config.toml
+++ /dev/null
@@ -1,4 +0,0 @@
-[alias]
-loco = "run --"
-loco-tool = "run --bin tool --"
-playground = "run --example playground"
diff --git a/examples/demo/.rustfmt.toml b/examples/demo/.rustfmt.toml
deleted file mode 100644
index 777ca6821..000000000
--- a/examples/demo/.rustfmt.toml
+++ /dev/null
@@ -1,8 +0,0 @@
-max_width = 100
-comment_width = 80
-wrap_comments = true
-imports_granularity = "Crate"
-use_small_heuristics = "Default"
-group_imports = "StdExternalCrate"
-format_strings = true
-edition = "2021"
diff --git a/examples/demo/.vscode/settings.json b/examples/demo/.vscode/settings.json
deleted file mode 100644
index b48948abd..000000000
--- a/examples/demo/.vscode/settings.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "rust-analyzer.showUnlinkedFileNotification": false
-}
diff --git a/examples/demo/Cargo.lock b/examples/demo/Cargo.lock
deleted file mode 100644
index e91a236a9..000000000
--- a/examples/demo/Cargo.lock
+++ /dev/null
@@ -1,6527 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 4
-
-[[package]]
-name = "addr2line"
-version = "0.24.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
-dependencies = [
- "gimli",
-]
-
-[[package]]
-name = "adler2"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
-
-[[package]]
-name = "aead"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
-dependencies = [
- "crypto-common",
- "generic-array",
-]
-
-[[package]]
-name = "aes"
-version = "0.8.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
-dependencies = [
- "cfg-if",
- "cipher",
- "cpufeatures",
-]
-
-[[package]]
-name = "aes-gcm"
-version = "0.10.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
-dependencies = [
- "aead",
- "aes",
- "cipher",
- "ctr",
- "ghash",
- "subtle",
-]
-
-[[package]]
-name = "ahash"
-version = "0.7.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
-dependencies = [
- "getrandom",
- "once_cell",
- "version_check",
-]
-
-[[package]]
-name = "ahash"
-version = "0.8.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
-dependencies = [
- "cfg-if",
- "getrandom",
- "once_cell",
- "version_check",
- "zerocopy",
-]
-
-[[package]]
-name = "aho-corasick"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "aliasable"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
-
-[[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 = "allocator-api2"
-version = "0.2.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
-
-[[package]]
-name = "android-tzdata"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
-
-[[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 = "anstream"
-version = "0.6.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
-dependencies = [
- "anstyle",
- "anstyle-parse",
- "anstyle-query",
- "anstyle-wincon",
- "colorchoice",
- "is_terminal_polyfill",
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle"
-version = "1.0.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
-
-[[package]]
-name = "anstyle-parse"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
-dependencies = [
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle-query"
-version = "1.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
-dependencies = [
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "anstyle-wincon"
-version = "3.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
-dependencies = [
- "anstyle",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "anyhow"
-version = "1.0.95"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
-
-[[package]]
-name = "arc-swap"
-version = "1.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
-
-[[package]]
-name = "argon2"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
-dependencies = [
- "base64ct",
- "blake2",
- "cpufeatures",
- "password-hash",
-]
-
-[[package]]
-name = "arrayvec"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
-
-[[package]]
-name = "assert-json-diff"
-version = "2.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12"
-dependencies = [
- "serde",
- "serde_json",
-]
-
-[[package]]
-name = "async-attributes"
-version = "1.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5"
-dependencies = [
- "quote",
- "syn 1.0.109",
-]
-
-[[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 2.5.3",
- "futures-core",
-]
-
-[[package]]
-name = "async-channel"
-version = "2.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a"
-dependencies = [
- "concurrent-queue",
- "event-listener-strategy",
- "futures-core",
- "pin-project-lite",
-]
-
-[[package]]
-name = "async-compression"
-version = "0.4.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522"
-dependencies = [
- "brotli",
- "flate2",
- "futures-core",
- "memchr",
- "pin-project-lite",
- "tokio",
- "zstd",
- "zstd-safe",
-]
-
-[[package]]
-name = "async-executor"
-version = "1.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec"
-dependencies = [
- "async-task",
- "concurrent-queue",
- "fastrand",
- "futures-lite",
- "slab",
-]
-
-[[package]]
-name = "async-global-executor"
-version = "2.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c"
-dependencies = [
- "async-channel 2.3.1",
- "async-executor",
- "async-io",
- "async-lock",
- "blocking",
- "futures-lite",
- "once_cell",
- "tokio",
-]
-
-[[package]]
-name = "async-io"
-version = "2.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059"
-dependencies = [
- "async-lock",
- "cfg-if",
- "concurrent-queue",
- "futures-io",
- "futures-lite",
- "parking",
- "polling",
- "rustix",
- "slab",
- "tracing",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "async-lock"
-version = "3.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
-dependencies = [
- "event-listener 5.3.1",
- "event-listener-strategy",
- "pin-project-lite",
-]
-
-[[package]]
-name = "async-std"
-version = "1.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c634475f29802fde2b8f0b505b1bd00dfe4df7d4a000f0b36f7671197d5c3615"
-dependencies = [
- "async-attributes",
- "async-channel 1.9.0",
- "async-global-executor",
- "async-io",
- "async-lock",
- "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-stream"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
-dependencies = [
- "async-stream-impl",
- "futures-core",
- "pin-project-lite",
-]
-
-[[package]]
-name = "async-stream-impl"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "async-task"
-version = "4.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
-
-[[package]]
-name = "async-trait"
-version = "0.1.84"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b1244b10dcd56c92219da4e14caa97e312079e185f04ba3eea25061561dc0a0"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "atoi"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528"
-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 = "auto-future"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c1e7e457ea78e524f48639f551fd79703ac3f2237f5ecccdf4708f8a75ad373"
-
-[[package]]
-name = "autocfg"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
-
-[[package]]
-name = "axum"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8"
-dependencies = [
- "axum-core",
- "axum-macros",
- "bytes",
- "form_urlencoded",
- "futures-util",
- "http 1.2.0",
- "http-body",
- "http-body-util",
- "hyper",
- "hyper-util",
- "itoa",
- "matchit",
- "memchr",
- "mime",
- "multer",
- "percent-encoding",
- "pin-project-lite",
- "rustversion",
- "serde",
- "serde_json",
- "serde_path_to_error",
- "serde_urlencoded",
- "sync_wrapper",
- "tokio",
- "tower 0.5.2",
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "axum-core"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733"
-dependencies = [
- "bytes",
- "futures-util",
- "http 1.2.0",
- "http-body",
- "http-body-util",
- "mime",
- "pin-project-lite",
- "rustversion",
- "sync_wrapper",
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "axum-extra"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "460fc6f625a1f7705c6cf62d0d070794e94668988b1c38111baeec177c715f7b"
-dependencies = [
- "axum",
- "axum-core",
- "bytes",
- "cookie",
- "futures-util",
- "http 1.2.0",
- "http-body",
- "http-body-util",
- "mime",
- "pin-project-lite",
- "serde",
- "tower 0.5.2",
- "tower-layer",
- "tower-service",
-]
-
-[[package]]
-name = "axum-macros"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "axum-test"
-version = "17.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53f1009889890a439cbf67a4071a2593d027c65209da4faeac5582f28ca9e6c3"
-dependencies = [
- "anyhow",
- "assert-json-diff",
- "auto-future",
- "axum",
- "bytes",
- "bytesize",
- "cookie",
- "http 1.2.0",
- "http-body-util",
- "hyper",
- "hyper-util",
- "mime",
- "pretty_assertions",
- "reserve-port",
- "rust-multipart-rfc7578_2",
- "serde",
- "serde_json",
- "serde_urlencoded",
- "smallvec",
- "tokio",
- "tower 0.5.2",
- "url",
-]
-
-[[package]]
-name = "axum_session"
-version = "0.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bcd5d9395b53d6a7054f599f4fe6f519d374a5b84fa758d1163af0d278b9213"
-dependencies = [
- "aes-gcm",
- "async-trait",
- "axum",
- "base64 0.22.1",
- "bytes",
- "chrono",
- "cookie",
- "dashmap",
- "forwarded-header-value",
- "futures",
- "hmac",
- "http 1.2.0",
- "http-body",
- "rand",
- "serde",
- "serde_json",
- "sha2",
- "thiserror 2.0.9",
- "tokio",
- "tower-layer",
- "tower-service",
- "tracing",
- "uuid",
-]
-
-[[package]]
-name = "backon"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba5289ec98f68f28dd809fd601059e6aa908bb8f6108620930828283d4ee23d7"
-dependencies = [
- "fastrand",
- "gloo-timers",
- "tokio",
-]
-
-[[package]]
-name = "backtrace"
-version = "0.3.74"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
-dependencies = [
- "addr2line",
- "cfg-if",
- "libc",
- "miniz_oxide",
- "object",
- "rustc-demangle",
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "backtrace_printer"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8d28de81c708c843640982b66573df0f0168d87e42854b563971f326745aab7"
-dependencies = [
- "btparse-stable",
- "colored",
- "regex",
- "thiserror 1.0.69",
-]
-
-[[package]]
-name = "base64"
-version = "0.21.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
-
-[[package]]
-name = "base64"
-version = "0.22.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
-
-[[package]]
-name = "base64ct"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
-
-[[package]]
-name = "bb8"
-version = "0.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d89aabfae550a5c44b43ab941844ffcd2e993cb6900b342debf59e9ea74acdb8"
-dependencies = [
- "async-trait",
- "futures-util",
- "parking_lot",
- "tokio",
-]
-
-[[package]]
-name = "bigdecimal"
-version = "0.4.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f31f3af01c5c65a07985c804d3366560e6fa7883d640a122819b14ec327482c"
-dependencies = [
- "autocfg",
- "libm",
- "num-bigint",
- "num-integer",
- "num-traits",
- "serde",
-]
-
-[[package]]
-name = "bitflags"
-version = "2.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "bitvec"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
-dependencies = [
- "funty",
- "radium",
- "tap",
- "wyz",
-]
-
-[[package]]
-name = "blake2"
-version = "0.10.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
-dependencies = [
- "digest",
-]
-
-[[package]]
-name = "block-buffer"
-version = "0.10.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
-dependencies = [
- "generic-array",
-]
-
-[[package]]
-name = "blocking"
-version = "1.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea"
-dependencies = [
- "async-channel 2.3.1",
- "async-task",
- "futures-io",
- "futures-lite",
- "piper",
-]
-
-[[package]]
-name = "borsh"
-version = "1.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2506947f73ad44e344215ccd6403ac2ae18cd8e046e581a441bf8d199f257f03"
-dependencies = [
- "borsh-derive",
- "cfg_aliases",
-]
-
-[[package]]
-name = "borsh-derive"
-version = "1.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2593a3b8b938bd68373196c9832f516be11fa487ef4ae745eb282e6a56a7244"
-dependencies = [
- "once_cell",
- "proc-macro-crate",
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "brotli"
-version = "7.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd"
-dependencies = [
- "alloc-no-stdlib",
- "alloc-stdlib",
- "brotli-decompressor",
-]
-
-[[package]]
-name = "brotli-decompressor"
-version = "4.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362"
-dependencies = [
- "alloc-no-stdlib",
- "alloc-stdlib",
-]
-
-[[package]]
-name = "bstr"
-version = "1.11.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0"
-dependencies = [
- "memchr",
- "serde",
-]
-
-[[package]]
-name = "btparse-stable"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d75b8252ed252f881d1dc4482ae3c3854df6ee8183c1906bac50ff358f4f89f"
-
-[[package]]
-name = "bumpalo"
-version = "3.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
-
-[[package]]
-name = "byte-unit"
-version = "4.0.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da78b32057b8fdfc352504708feeba7216dcd65a2c9ab02978cbd288d1279b6c"
-dependencies = [
- "serde",
- "utf8-width",
-]
-
-[[package]]
-name = "bytecheck"
-version = "0.6.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2"
-dependencies = [
- "bytecheck_derive",
- "ptr_meta",
- "simdutf8",
-]
-
-[[package]]
-name = "bytecheck_derive"
-version = "0.6.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 1.0.109",
-]
-
-[[package]]
-name = "byteorder"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
-
-[[package]]
-name = "bytes"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
-
-[[package]]
-name = "bytesize"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc"
-
-[[package]]
-name = "cc"
-version = "1.2.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7"
-dependencies = [
- "jobserver",
- "libc",
- "shlex",
-]
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "cfg_aliases"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
-
-[[package]]
-name = "chrono"
-version = "0.4.39"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825"
-dependencies = [
- "android-tzdata",
- "iana-time-zone",
- "js-sys",
- "num-traits",
- "serde",
- "wasm-bindgen",
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "chrono-tz"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb"
-dependencies = [
- "chrono",
- "chrono-tz-build",
- "phf",
-]
-
-[[package]]
-name = "chrono-tz-build"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1"
-dependencies = [
- "parse-zoneinfo",
- "phf",
- "phf_codegen",
-]
-
-[[package]]
-name = "chumsky"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8eebd66744a15ded14960ab4ccdbfb51ad3b81f51f3f04a80adac98c985396c9"
-dependencies = [
- "hashbrown 0.14.5",
- "stacker",
-]
-
-[[package]]
-name = "cipher"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
-dependencies = [
- "crypto-common",
- "inout",
-]
-
-[[package]]
-name = "clap"
-version = "4.5.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84"
-dependencies = [
- "clap_builder",
- "clap_derive",
-]
-
-[[package]]
-name = "clap_builder"
-version = "4.5.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838"
-dependencies = [
- "anstream",
- "anstyle",
- "clap_lex",
- "strsim",
-]
-
-[[package]]
-name = "clap_derive"
-version = "4.5.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
-dependencies = [
- "heck 0.5.0",
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "clap_lex"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
-
-[[package]]
-name = "colorchoice"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
-
-[[package]]
-name = "colored"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
-dependencies = [
- "lazy_static",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "combine"
-version = "4.6.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
-dependencies = [
- "bytes",
- "futures-core",
- "memchr",
- "pin-project-lite",
- "tokio",
- "tokio-util",
-]
-
-[[package]]
-name = "concurrent-queue"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
-dependencies = [
- "crossbeam-utils",
-]
-
-[[package]]
-name = "console"
-version = "0.15.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b"
-dependencies = [
- "encode_unicode",
- "libc",
- "once_cell",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "const-oid"
-version = "0.9.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
-
-[[package]]
-name = "content_inspector"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7bda66e858c683005a53a9a60c69a4aca7eeaa45d124526e389f7aec8e62f38"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "convert_case"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
-dependencies = [
- "unicode-segmentation",
-]
-
-[[package]]
-name = "cookie"
-version = "0.18.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
-dependencies = [
- "aes-gcm",
- "base64 0.22.1",
- "percent-encoding",
- "rand",
- "subtle",
- "time",
- "version_check",
-]
-
-[[package]]
-name = "core-foundation"
-version = "0.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
-dependencies = [
- "core-foundation-sys",
- "libc",
-]
-
-[[package]]
-name = "core-foundation-sys"
-version = "0.8.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
-
-[[package]]
-name = "cpufeatures"
-version = "0.2.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "crc"
-version = "3.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
-dependencies = [
- "crc-catalog",
-]
-
-[[package]]
-name = "crc-catalog"
-version = "2.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
-
-[[package]]
-name = "crc32fast"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "cron"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f8c3e73077b4b4a6ab1ea5047c37c57aee77657bc8ecd6f29b0af082d0b0c07"
-dependencies = [
- "chrono",
- "nom",
- "once_cell",
-]
-
-[[package]]
-name = "cron_clock"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a8699d8ed16e3db689f8ae04d8dc3c6666a4ba7e724e5a157884b7cc385d16b"
-dependencies = [
- "chrono",
- "nom",
- "once_cell",
-]
-
-[[package]]
-name = "crossbeam-channel"
-version = "0.5.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471"
-dependencies = [
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-deque"
-version = "0.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
-dependencies = [
- "crossbeam-epoch",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-epoch"
-version = "0.9.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
-dependencies = [
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-queue"
-version = "0.3.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
-dependencies = [
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-utils"
-version = "0.8.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
-
-[[package]]
-name = "cruet"
-version = "0.13.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "113a9e83d8f614be76de8df1f25bf9d0ea6e85ea573710a3d3f3abe1438ae49c"
-dependencies = [
- "once_cell",
- "regex",
-]
-
-[[package]]
-name = "cruet"
-version = "0.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6132609543972496bc97b1e01f1ce6586768870aeb4cabeb3385f4e05b5caead"
-dependencies = [
- "once_cell",
- "regex",
-]
-
-[[package]]
-name = "crypto-common"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
-dependencies = [
- "generic-array",
- "rand_core",
- "typenum",
-]
-
-[[package]]
-name = "cssparser"
-version = "0.34.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7c66d1cd8ed61bf80b38432613a7a2f09401ab8d0501110655f8b341484a3e3"
-dependencies = [
- "cssparser-macros",
- "dtoa-short",
- "itoa",
- "phf",
- "smallvec",
-]
-
-[[package]]
-name = "cssparser-macros"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
-dependencies = [
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "ctr"
-version = "0.9.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
-dependencies = [
- "cipher",
-]
-
-[[package]]
-name = "darling"
-version = "0.20.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989"
-dependencies = [
- "darling_core",
- "darling_macro",
-]
-
-[[package]]
-name = "darling_core"
-version = "0.20.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5"
-dependencies = [
- "fnv",
- "ident_case",
- "proc-macro2",
- "quote",
- "strsim",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "darling_macro"
-version = "0.20.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
-dependencies = [
- "darling_core",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "dashmap"
-version = "6.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
-dependencies = [
- "cfg-if",
- "crossbeam-utils",
- "hashbrown 0.14.5",
- "lock_api",
- "once_cell",
- "parking_lot_core",
-]
-
-[[package]]
-name = "demo_app"
-version = "0.1.0"
-dependencies = [
- "async-trait",
- "axum",
- "axum-extra",
- "axum-test",
- "axum_session",
- "chrono",
- "fluent-templates",
- "futures-util",
- "include_dir",
- "insta",
- "loco-rs",
- "migration",
- "rstest",
- "sea-orm",
- "serde",
- "serde_json",
- "serial_test",
- "tera",
- "tokio",
- "tower 0.4.13",
- "tracing",
- "tracing-subscriber",
- "trycmd",
- "unic-langid",
- "utoipa",
- "uuid",
- "validator",
-]
-
-[[package]]
-name = "der"
-version = "0.7.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0"
-dependencies = [
- "const-oid",
- "pem-rfc7468",
- "zeroize",
-]
-
-[[package]]
-name = "deranged"
-version = "0.3.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
-dependencies = [
- "powerfmt",
- "serde",
-]
-
-[[package]]
-name = "derive_more"
-version = "0.99.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "deunicode"
-version = "1.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00"
-
-[[package]]
-name = "diff"
-version = "0.1.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
-
-[[package]]
-name = "digest"
-version = "0.10.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
-dependencies = [
- "block-buffer",
- "const-oid",
- "crypto-common",
- "subtle",
-]
-
-[[package]]
-name = "dirs-next"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
-dependencies = [
- "cfg-if",
- "dirs-sys-next",
-]
-
-[[package]]
-name = "dirs-sys-next"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
-dependencies = [
- "libc",
- "redox_users",
- "winapi",
-]
-
-[[package]]
-name = "displaydoc"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "doc-comment"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
-
-[[package]]
-name = "dotenvy"
-version = "0.15.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
-
-[[package]]
-name = "dtoa"
-version = "1.0.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653"
-
-[[package]]
-name = "dtoa-short"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87"
-dependencies = [
- "dtoa",
-]
-
-[[package]]
-name = "duct"
-version = "0.13.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4ab5718d1224b63252cd0c6f74f6480f9ffeb117438a2e0f5cf6d9a4798929c"
-dependencies = [
- "libc",
- "once_cell",
- "os_pipe",
- "shared_child",
-]
-
-[[package]]
-name = "duct_sh"
-version = "0.13.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a6633cadba557545fbbe0299a2f9adc4bb2fc5fb238773f5e841e0c23d62146"
-dependencies = [
- "duct",
-]
-
-[[package]]
-name = "dunce"
-version = "1.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
-
-[[package]]
-name = "ego-tree"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c6ba7d4eec39eaa9ab24d44a0e73a7949a1095a8b3f3abb11eddf27dbb56a53"
-
-[[package]]
-name = "either"
-version = "1.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "email-encoding"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea3d894bbbab314476b265f9b2d46bf24b123a36dd0e96b06a1b49545b9d9dcc"
-dependencies = [
- "base64 0.22.1",
- "memchr",
-]
-
-[[package]]
-name = "email_address"
-version = "0.2.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449"
-
-[[package]]
-name = "encode_unicode"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
-
-[[package]]
-name = "encoding_rs"
-version = "0.8.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "english-to-cron"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c16423ac933fee80f05a52b435a912d5b08edbbbfe936e0042ebb3accdf303da"
-dependencies = [
- "lazy_static",
- "regex",
-]
-
-[[package]]
-name = "equivalent"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
-
-[[package]]
-name = "errno"
-version = "0.3.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
-dependencies = [
- "libc",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "etcetera"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
-dependencies = [
- "cfg-if",
- "home",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "event-listener"
-version = "2.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
-
-[[package]]
-name = "event-listener"
-version = "5.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba"
-dependencies = [
- "concurrent-queue",
- "parking",
- "pin-project-lite",
-]
-
-[[package]]
-name = "event-listener-strategy"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2"
-dependencies = [
- "event-listener 5.3.1",
- "pin-project-lite",
-]
-
-[[package]]
-name = "fastrand"
-version = "2.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
-
-[[package]]
-name = "filetime"
-version = "0.2.25"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
-dependencies = [
- "cfg-if",
- "libc",
- "libredox",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "flagset"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec"
-
-[[package]]
-name = "flate2"
-version = "1.0.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
-dependencies = [
- "crc32fast",
- "miniz_oxide",
-]
-
-[[package]]
-name = "fluent"
-version = "0.16.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb74634707bebd0ce645a981148e8fb8c7bccd4c33c652aeffd28bf2f96d555a"
-dependencies = [
- "fluent-bundle",
- "unic-langid",
-]
-
-[[package]]
-name = "fluent-bundle"
-version = "0.15.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fe0a21ee80050c678013f82edf4b705fe2f26f1f9877593d13198612503f493"
-dependencies = [
- "fluent-langneg",
- "fluent-syntax",
- "intl-memoizer",
- "intl_pluralrules",
- "rustc-hash 1.1.0",
- "self_cell 0.10.3",
- "smallvec",
- "unic-langid",
-]
-
-[[package]]
-name = "fluent-langneg"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94"
-dependencies = [
- "unic-langid",
-]
-
-[[package]]
-name = "fluent-syntax"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a530c4694a6a8d528794ee9bbd8ba0122e779629ac908d15ad5a7ae7763a33d"
-dependencies = [
- "thiserror 1.0.69",
-]
-
-[[package]]
-name = "fluent-template-macros"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dec7592cd1f45c1afe9084ce59c62a3a7c266c125c4c2ec97e95b0563c4aa914"
-dependencies = [
- "flume 0.10.14",
- "ignore",
- "once_cell",
- "proc-macro2",
- "quote",
- "syn 1.0.109",
- "unic-langid",
-]
-
-[[package]]
-name = "fluent-templates"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c3ef2c2152757885365abce32ddf682746062f1b6b3c0824a29fbed6ee4d080"
-dependencies = [
- "arc-swap",
- "fluent",
- "fluent-bundle",
- "fluent-langneg",
- "fluent-syntax",
- "fluent-template-macros",
- "flume 0.10.14",
- "heck 0.4.1",
- "ignore",
- "intl-memoizer",
- "lazy_static",
- "log",
- "once_cell",
- "serde_json",
- "snafu",
- "tera",
- "unic-langid",
-]
-
-[[package]]
-name = "flume"
-version = "0.10.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
-dependencies = [
- "spin",
-]
-
-[[package]]
-name = "flume"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
-dependencies = [
- "futures-core",
- "futures-sink",
- "spin",
-]
-
-[[package]]
-name = "fnv"
-version = "1.0.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
-
-[[package]]
-name = "foldhash"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f"
-
-[[package]]
-name = "form_urlencoded"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
-dependencies = [
- "percent-encoding",
-]
-
-[[package]]
-name = "forwarded-header-value"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8835f84f38484cc86f110a805655697908257fb9a7af005234060891557198e9"
-dependencies = [
- "nonempty",
- "thiserror 1.0.69",
-]
-
-[[package]]
-name = "fs-err"
-version = "2.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88a41f105fe1d5b6b34b2055e3dc59bb79b46b48b2040b9e6c7b4b5de097aa41"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "funty"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
-
-[[package]]
-name = "futf"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843"
-dependencies = [
- "mac",
- "new_debug_unreachable",
-]
-
-[[package]]
-name = "futures"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-executor",
- "futures-io",
- "futures-sink",
- "futures-task",
- "futures-util",
-]
-
-[[package]]
-name = "futures-channel"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
-dependencies = [
- "futures-core",
- "futures-sink",
-]
-
-[[package]]
-name = "futures-core"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
-
-[[package]]
-name = "futures-executor"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
-dependencies = [
- "futures-core",
- "futures-task",
- "futures-util",
-]
-
-[[package]]
-name = "futures-intrusive"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
-dependencies = [
- "futures-core",
- "lock_api",
- "parking_lot",
-]
-
-[[package]]
-name = "futures-io"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
-
-[[package]]
-name = "futures-lite"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cef40d21ae2c515b51041df9ed313ed21e572df340ea58a922a0aefe7e8891a1"
-dependencies = [
- "fastrand",
- "futures-core",
- "futures-io",
- "parking",
- "pin-project-lite",
-]
-
-[[package]]
-name = "futures-macro"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "futures-sink"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
-
-[[package]]
-name = "futures-task"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
-
-[[package]]
-name = "futures-timer"
-version = "3.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
-
-[[package]]
-name = "futures-util"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
-dependencies = [
- "futures-channel",
- "futures-core",
- "futures-io",
- "futures-macro",
- "futures-sink",
- "futures-task",
- "memchr",
- "pin-project-lite",
- "pin-utils",
- "slab",
-]
-
-[[package]]
-name = "fxhash"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
-dependencies = [
- "byteorder",
-]
-
-[[package]]
-name = "generator"
-version = "0.8.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd"
-dependencies = [
- "cfg-if",
- "libc",
- "log",
- "rustversion",
- "windows 0.58.0",
-]
-
-[[package]]
-name = "generic-array"
-version = "0.14.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
-dependencies = [
- "typenum",
- "version_check",
-]
-
-[[package]]
-name = "gethostname"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e"
-dependencies = [
- "libc",
- "winapi",
-]
-
-[[package]]
-name = "getopts"
-version = "0.2.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
-dependencies = [
- "unicode-width",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
-dependencies = [
- "cfg-if",
- "js-sys",
- "libc",
- "wasi",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "ghash"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
-dependencies = [
- "opaque-debug",
- "polyval",
-]
-
-[[package]]
-name = "gimli"
-version = "0.31.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
-
-[[package]]
-name = "glob"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
-
-[[package]]
-name = "globset"
-version = "0.4.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19"
-dependencies = [
- "aho-corasick",
- "bstr",
- "log",
- "regex-automata 0.4.9",
- "regex-syntax 0.8.5",
-]
-
-[[package]]
-name = "globwalk"
-version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757"
-dependencies = [
- "bitflags",
- "ignore",
- "walkdir",
-]
-
-[[package]]
-name = "gloo-timers"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
-dependencies = [
- "futures-channel",
- "futures-core",
- "js-sys",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "h2"
-version = "0.4.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e"
-dependencies = [
- "atomic-waker",
- "bytes",
- "fnv",
- "futures-core",
- "futures-sink",
- "http 1.2.0",
- "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.8",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.14.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
-dependencies = [
- "ahash 0.8.11",
- "allocator-api2",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.15.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
-dependencies = [
- "allocator-api2",
- "equivalent",
- "foldhash",
-]
-
-[[package]]
-name = "hashlink"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
-dependencies = [
- "hashbrown 0.15.2",
-]
-
-[[package]]
-name = "heck"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
-
-[[package]]
-name = "heck"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
-
-[[package]]
-name = "hermit-abi"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
-
-[[package]]
-name = "hermit-abi"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
-
-[[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.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
-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 = "home"
-version = "0.5.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf"
-dependencies = [
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "hostname"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba"
-dependencies = [
- "cfg-if",
- "libc",
- "windows 0.52.0",
-]
-
-[[package]]
-name = "html5ever"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e15626aaf9c351bc696217cbe29cb9b5e86c43f8a46b5e2f5c6c5cf7cb904ce"
-dependencies = [
- "log",
- "mac",
- "markup5ever",
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "http"
-version = "0.2.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
-dependencies = [
- "bytes",
- "fnv",
- "itoa",
-]
-
-[[package]]
-name = "http"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
-dependencies = [
- "bytes",
- "fnv",
- "itoa",
-]
-
-[[package]]
-name = "http-body"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
-dependencies = [
- "bytes",
- "http 1.2.0",
-]
-
-[[package]]
-name = "http-body-util"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
-dependencies = [
- "bytes",
- "futures-util",
- "http 1.2.0",
- "http-body",
- "pin-project-lite",
-]
-
-[[package]]
-name = "http-range-header"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c"
-
-[[package]]
-name = "httparse"
-version = "1.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946"
-
-[[package]]
-name = "httpdate"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
-
-[[package]]
-name = "humansize"
-version = "2.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
-dependencies = [
- "libm",
-]
-
-[[package]]
-name = "humantime"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
-
-[[package]]
-name = "humantime-serde"
-version = "1.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c"
-dependencies = [
- "humantime",
- "serde",
-]
-
-[[package]]
-name = "hyper"
-version = "1.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0"
-dependencies = [
- "bytes",
- "futures-channel",
- "futures-util",
- "h2",
- "http 1.2.0",
- "http-body",
- "httparse",
- "httpdate",
- "itoa",
- "pin-project-lite",
- "smallvec",
- "tokio",
- "want",
-]
-
-[[package]]
-name = "hyper-rustls"
-version = "0.27.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
-dependencies = [
- "futures-util",
- "http 1.2.0",
- "hyper",
- "hyper-util",
- "rustls",
- "rustls-pki-types",
- "tokio",
- "tokio-rustls",
- "tower-service",
- "webpki-roots",
-]
-
-[[package]]
-name = "hyper-util"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
-dependencies = [
- "bytes",
- "futures-channel",
- "futures-util",
- "http 1.2.0",
- "http-body",
- "hyper",
- "pin-project-lite",
- "socket2",
- "tokio",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "iana-time-zone"
-version = "0.1.61"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
-dependencies = [
- "android_system_properties",
- "core-foundation-sys",
- "iana-time-zone-haiku",
- "js-sys",
- "wasm-bindgen",
- "windows-core 0.52.0",
-]
-
-[[package]]
-name = "iana-time-zone-haiku"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
-dependencies = [
- "cc",
-]
-
-[[package]]
-name = "icu_collections"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
-dependencies = [
- "displaydoc",
- "yoke",
- "zerofrom",
- "zerovec",
-]
-
-[[package]]
-name = "icu_locid"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
-dependencies = [
- "displaydoc",
- "litemap",
- "tinystr",
- "writeable",
- "zerovec",
-]
-
-[[package]]
-name = "icu_locid_transform"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
-dependencies = [
- "displaydoc",
- "icu_locid",
- "icu_locid_transform_data",
- "icu_provider",
- "tinystr",
- "zerovec",
-]
-
-[[package]]
-name = "icu_locid_transform_data"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
-
-[[package]]
-name = "icu_normalizer"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
-dependencies = [
- "displaydoc",
- "icu_collections",
- "icu_normalizer_data",
- "icu_properties",
- "icu_provider",
- "smallvec",
- "utf16_iter",
- "utf8_iter",
- "write16",
- "zerovec",
-]
-
-[[package]]
-name = "icu_normalizer_data"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
-
-[[package]]
-name = "icu_properties"
-version = "1.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
-dependencies = [
- "displaydoc",
- "icu_collections",
- "icu_locid_transform",
- "icu_properties_data",
- "icu_provider",
- "tinystr",
- "zerovec",
-]
-
-[[package]]
-name = "icu_properties_data"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
-
-[[package]]
-name = "icu_provider"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
-dependencies = [
- "displaydoc",
- "icu_locid",
- "icu_provider_macros",
- "stable_deref_trait",
- "tinystr",
- "writeable",
- "yoke",
- "zerofrom",
- "zerovec",
-]
-
-[[package]]
-name = "icu_provider_macros"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "ident_case"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
-
-[[package]]
-name = "idna"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
-dependencies = [
- "idna_adapter",
- "smallvec",
- "utf8_iter",
-]
-
-[[package]]
-name = "idna_adapter"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
-dependencies = [
- "icu_normalizer",
- "icu_properties",
-]
-
-[[package]]
-name = "ignore"
-version = "0.4.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
-dependencies = [
- "crossbeam-deque",
- "globset",
- "log",
- "memchr",
- "regex-automata 0.4.9",
- "same-file",
- "walkdir",
- "winapi-util",
-]
-
-[[package]]
-name = "include_dir"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd"
-dependencies = [
- "include_dir_macros",
-]
-
-[[package]]
-name = "include_dir_macros"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75"
-dependencies = [
- "proc-macro2",
- "quote",
-]
-
-[[package]]
-name = "indexmap"
-version = "2.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
-dependencies = [
- "equivalent",
- "hashbrown 0.15.2",
- "serde",
-]
-
-[[package]]
-name = "inherent"
-version = "1.0.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0122b7114117e64a63ac49f752a5ca4624d534c7b1c7de796ac196381cd2d947"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "inout"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
-dependencies = [
- "generic-array",
-]
-
-[[package]]
-name = "insta"
-version = "1.42.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6513e4067e16e69ed1db5ab56048ed65db32d10ba5fc1217f5393f8f17d8b5a5"
-dependencies = [
- "console",
- "linked-hash-map",
- "once_cell",
- "pest",
- "pest_derive",
- "regex",
- "serde",
- "similar",
-]
-
-[[package]]
-name = "intl-memoizer"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe22e020fce238ae18a6d5d8c502ee76a52a6e880d99477657e6acc30ec57bda"
-dependencies = [
- "type-map",
- "unic-langid",
-]
-
-[[package]]
-name = "intl_pluralrules"
-version = "7.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "078ea7b7c29a2b4df841a7f6ac8775ff6074020c6776d48491ce2268e068f972"
-dependencies = [
- "unic-langid",
-]
-
-[[package]]
-name = "ipnet"
-version = "2.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708"
-
-[[package]]
-name = "ipnetwork"
-version = "0.20.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "is-terminal"
-version = "0.4.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
-dependencies = [
- "hermit-abi 0.4.0",
- "libc",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "is_terminal_polyfill"
-version = "1.70.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
-
-[[package]]
-name = "itertools"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
-dependencies = [
- "either",
-]
-
-[[package]]
-name = "itoa"
-version = "1.0.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
-
-[[package]]
-name = "jobserver"
-version = "0.1.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "js-sys"
-version = "0.3.76"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7"
-dependencies = [
- "once_cell",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "jsonwebtoken"
-version = "9.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f"
-dependencies = [
- "base64 0.21.7",
- "js-sys",
- "pem",
- "ring",
- "serde",
- "serde_json",
- "simple_asn1",
-]
-
-[[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 = "lazy_static"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
-dependencies = [
- "spin",
-]
-
-[[package]]
-name = "lettre"
-version = "0.11.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab4c9a167ff73df98a5ecc07e8bf5ce90b583665da3d1762eb1f775ad4d0d6f5"
-dependencies = [
- "async-trait",
- "base64 0.22.1",
- "chumsky",
- "email-encoding",
- "email_address",
- "fastrand",
- "futures-io",
- "futures-util",
- "hostname",
- "httpdate",
- "idna",
- "mime",
- "nom",
- "percent-encoding",
- "quoted_printable",
- "rustls",
- "rustls-pemfile",
- "rustls-pki-types",
- "socket2",
- "tokio",
- "tokio-rustls",
- "url",
- "webpki-roots",
-]
-
-[[package]]
-name = "libc"
-version = "0.2.169"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
-
-[[package]]
-name = "libm"
-version = "0.2.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
-
-[[package]]
-name = "libredox"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
-dependencies = [
- "bitflags",
- "libc",
- "redox_syscall",
-]
-
-[[package]]
-name = "libsqlite3-sys"
-version = "0.30.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
-dependencies = [
- "cc",
- "pkg-config",
- "vcpkg",
-]
-
-[[package]]
-name = "linked-hash-map"
-version = "0.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
-
-[[package]]
-name = "linux-raw-sys"
-version = "0.4.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
-
-[[package]]
-name = "litemap"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
-
-[[package]]
-name = "lock_api"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
-dependencies = [
- "autocfg",
- "scopeguard",
-]
-
-[[package]]
-name = "loco-gen"
-version = "0.14.0"
-dependencies = [
- "chrono",
- "clap",
- "colored",
- "cruet 0.14.0",
- "duct",
- "include_dir",
- "regex",
- "rrgen",
- "serde",
- "serde_json",
- "thiserror 1.0.69",
- "tracing",
-]
-
-[[package]]
-name = "loco-rs"
-version = "0.14.0"
-dependencies = [
- "argon2",
- "async-trait",
- "axum",
- "axum-extra",
- "axum-test",
- "backtrace_printer",
- "bb8",
- "byte-unit",
- "bytes",
- "cfg-if",
- "chrono",
- "clap",
- "colored",
- "cruet 0.13.3",
- "duct",
- "duct_sh",
- "english-to-cron",
- "fs-err",
- "futures-util",
- "heck 0.4.1",
- "hyper",
- "include_dir",
- "ipnetwork",
- "jsonwebtoken",
- "lettre",
- "loco-gen",
- "mime",
- "moka",
- "opendal",
- "rand",
- "regex",
- "reqwest",
- "rusty-sidekiq",
- "scraper",
- "sea-orm",
- "sea-orm-migration",
- "semver",
- "serde",
- "serde_json",
- "serde_variant",
- "serde_yaml",
- "sqlx",
- "tera",
- "thiserror 1.0.69",
- "thousands",
- "tokio",
- "tokio-cron-scheduler",
- "tokio-util",
- "toml",
- "tower 0.4.13",
- "tower-http",
- "tracing",
- "tracing-appender",
- "tracing-subscriber",
- "ulid",
- "uuid",
- "validator",
-]
-
-[[package]]
-name = "log"
-version = "0.4.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
-dependencies = [
- "value-bag",
-]
-
-[[package]]
-name = "loom"
-version = "0.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca"
-dependencies = [
- "cfg-if",
- "generator",
- "scoped-tls",
- "tracing",
- "tracing-subscriber",
-]
-
-[[package]]
-name = "mac"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
-
-[[package]]
-name = "markup5ever"
-version = "0.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82c88c6129bd24319e62a0359cb6b958fa7e8be6e19bb1663bc396b90883aca5"
-dependencies = [
- "log",
- "phf",
- "phf_codegen",
- "string_cache",
- "string_cache_codegen",
- "tendril",
-]
-
-[[package]]
-name = "matchers"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
-dependencies = [
- "regex-automata 0.1.10",
-]
-
-[[package]]
-name = "matchit"
-version = "0.8.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
-
-[[package]]
-name = "md-5"
-version = "0.10.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
-dependencies = [
- "cfg-if",
- "digest",
-]
-
-[[package]]
-name = "memchr"
-version = "2.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
-
-[[package]]
-name = "migration"
-version = "0.1.0"
-dependencies = [
- "async-std",
- "loco-rs",
- "sea-orm-migration",
-]
-
-[[package]]
-name = "mime"
-version = "0.3.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
-
-[[package]]
-name = "mime_guess"
-version = "2.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
-dependencies = [
- "mime",
- "unicase",
-]
-
-[[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.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394"
-dependencies = [
- "adler2",
-]
-
-[[package]]
-name = "mio"
-version = "1.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
-dependencies = [
- "libc",
- "wasi",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "moka"
-version = "0.12.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23db87a7f248211f6a7c8644a1b750541f8a4c68ae7de0f908860e44c0c201f6"
-dependencies = [
- "crossbeam-channel",
- "crossbeam-epoch",
- "crossbeam-utils",
- "loom",
- "parking_lot",
- "quanta",
- "rustc_version",
- "smallvec",
- "tagptr",
- "thiserror 1.0.69",
- "uuid",
-]
-
-[[package]]
-name = "multer"
-version = "3.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b"
-dependencies = [
- "bytes",
- "encoding_rs",
- "futures-util",
- "http 1.2.0",
- "httparse",
- "memchr",
- "mime",
- "spin",
- "version_check",
-]
-
-[[package]]
-name = "new_debug_unreachable"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
-
-[[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 = "nonempty"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9e591e719385e6ebaeb5ce5d3887f7d5676fceca6411d1925ccc95745f3d6f7"
-
-[[package]]
-name = "normalize-line-endings"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
-
-[[package]]
-name = "nu-ansi-term"
-version = "0.46.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
-dependencies = [
- "overload",
- "winapi",
-]
-
-[[package]]
-name = "num-bigint"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
-dependencies = [
- "num-integer",
- "num-traits",
-]
-
-[[package]]
-name = "num-bigint-dig"
-version = "0.8.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151"
-dependencies = [
- "byteorder",
- "lazy_static",
- "libm",
- "num-integer",
- "num-iter",
- "num-traits",
- "rand",
- "smallvec",
- "zeroize",
-]
-
-[[package]]
-name = "num-conv"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
-
-[[package]]
-name = "num-derive"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "num-integer"
-version = "0.1.46"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
-dependencies = [
- "num-traits",
-]
-
-[[package]]
-name = "num-iter"
-version = "0.1.45"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
-dependencies = [
- "autocfg",
- "num-integer",
- "num-traits",
-]
-
-[[package]]
-name = "num-traits"
-version = "0.2.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
-dependencies = [
- "autocfg",
- "libm",
-]
-
-[[package]]
-name = "num_cpus"
-version = "1.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
-dependencies = [
- "hermit-abi 0.3.9",
- "libc",
-]
-
-[[package]]
-name = "object"
-version = "0.36.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "once_cell"
-version = "1.20.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
-
-[[package]]
-name = "opaque-debug"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
-
-[[package]]
-name = "opendal"
-version = "0.50.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb28bb6c64e116ceaf8dd4e87099d3cfea4a58e85e62b104fef74c91afba0f44"
-dependencies = [
- "anyhow",
- "async-trait",
- "backon",
- "base64 0.22.1",
- "bytes",
- "chrono",
- "flagset",
- "futures",
- "getrandom",
- "http 1.2.0",
- "log",
- "md-5",
- "once_cell",
- "percent-encoding",
- "quick-xml",
- "reqwest",
- "serde",
- "serde_json",
- "tokio",
- "uuid",
-]
-
-[[package]]
-name = "ordered-float"
-version = "3.9.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc"
-dependencies = [
- "num-traits",
-]
-
-[[package]]
-name = "os_pipe"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
-dependencies = [
- "libc",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "ouroboros"
-version = "0.18.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "944fa20996a25aded6b4795c6d63f10014a7a83f8be9828a11860b08c5fc4a67"
-dependencies = [
- "aliasable",
- "ouroboros_macro",
- "static_assertions",
-]
-
-[[package]]
-name = "ouroboros_macro"
-version = "0.18.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39b0deead1528fd0e5947a8546a9642a9777c25f6e1e26f34c97b204bbb465bd"
-dependencies = [
- "heck 0.4.1",
- "itertools",
- "proc-macro2",
- "proc-macro2-diagnostics",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "overload"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
-
-[[package]]
-name = "parking"
-version = "2.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
-
-[[package]]
-name = "parking_lot"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
-dependencies = [
- "lock_api",
- "parking_lot_core",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.9.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
-dependencies = [
- "cfg-if",
- "libc",
- "redox_syscall",
- "smallvec",
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "parse-zoneinfo"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24"
-dependencies = [
- "regex",
-]
-
-[[package]]
-name = "password-hash"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
-dependencies = [
- "base64ct",
- "rand_core",
- "subtle",
-]
-
-[[package]]
-name = "pem"
-version = "3.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae"
-dependencies = [
- "base64 0.22.1",
- "serde",
-]
-
-[[package]]
-name = "pem-rfc7468"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
-dependencies = [
- "base64ct",
-]
-
-[[package]]
-name = "percent-encoding"
-version = "2.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
-
-[[package]]
-name = "pest"
-version = "2.7.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b7cafe60d6cf8e62e1b9b2ea516a089c008945bb5a275416789e7db0bc199dc"
-dependencies = [
- "memchr",
- "thiserror 2.0.9",
- "ucd-trie",
-]
-
-[[package]]
-name = "pest_derive"
-version = "2.7.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "816518421cfc6887a0d62bf441b6ffb4536fcc926395a69e1a85852d4363f57e"
-dependencies = [
- "pest",
- "pest_generator",
-]
-
-[[package]]
-name = "pest_generator"
-version = "2.7.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d1396fd3a870fc7838768d171b4616d5c91f6cc25e377b673d714567d99377b"
-dependencies = [
- "pest",
- "pest_meta",
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "pest_meta"
-version = "2.7.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1e58089ea25d717bfd31fb534e4f3afcc2cc569c70de3e239778991ea3b7dea"
-dependencies = [
- "once_cell",
- "pest",
- "sha2",
-]
-
-[[package]]
-name = "phf"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
-dependencies = [
- "phf_macros",
- "phf_shared 0.11.2",
-]
-
-[[package]]
-name = "phf_codegen"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a"
-dependencies = [
- "phf_generator 0.11.2",
- "phf_shared 0.11.2",
-]
-
-[[package]]
-name = "phf_generator"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
-dependencies = [
- "phf_shared 0.10.0",
- "rand",
-]
-
-[[package]]
-name = "phf_generator"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
-dependencies = [
- "phf_shared 0.11.2",
- "rand",
-]
-
-[[package]]
-name = "phf_macros"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
-dependencies = [
- "phf_generator 0.11.2",
- "phf_shared 0.11.2",
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "phf_shared"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
-dependencies = [
- "siphasher",
-]
-
-[[package]]
-name = "phf_shared"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
-dependencies = [
- "siphasher",
-]
-
-[[package]]
-name = "pin-project-lite"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff"
-
-[[package]]
-name = "pin-utils"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
-
-[[package]]
-name = "piper"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
-dependencies = [
- "atomic-waker",
- "fastrand",
- "futures-io",
-]
-
-[[package]]
-name = "pkcs1"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
-dependencies = [
- "der",
- "pkcs8",
- "spki",
-]
-
-[[package]]
-name = "pkcs8"
-version = "0.10.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
-dependencies = [
- "der",
- "spki",
-]
-
-[[package]]
-name = "pkg-config"
-version = "0.3.31"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
-
-[[package]]
-name = "polling"
-version = "3.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f"
-dependencies = [
- "cfg-if",
- "concurrent-queue",
- "hermit-abi 0.4.0",
- "pin-project-lite",
- "rustix",
- "tracing",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "polyval"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
-dependencies = [
- "cfg-if",
- "cpufeatures",
- "opaque-debug",
- "universal-hash",
-]
-
-[[package]]
-name = "powerfmt"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
-
-[[package]]
-name = "ppv-lite86"
-version = "0.2.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
-dependencies = [
- "zerocopy",
-]
-
-[[package]]
-name = "precomputed-hash"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
-
-[[package]]
-name = "pretty_assertions"
-version = "1.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
-dependencies = [
- "diff",
- "yansi",
-]
-
-[[package]]
-name = "proc-macro-crate"
-version = "3.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
-dependencies = [
- "toml_edit",
-]
-
-[[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 1.0.109",
- "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-macro-error-attr2"
-version = "2.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
-dependencies = [
- "proc-macro2",
- "quote",
-]
-
-[[package]]
-name = "proc-macro-error2"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
-dependencies = [
- "proc-macro-error-attr2",
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "proc-macro-hack"
-version = "0.5.20+deprecated"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.92"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "proc-macro2-diagnostics"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
- "version_check",
- "yansi",
-]
-
-[[package]]
-name = "psm"
-version = "0.1.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810"
-dependencies = [
- "cc",
-]
-
-[[package]]
-name = "ptr_meta"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1"
-dependencies = [
- "ptr_meta_derive",
-]
-
-[[package]]
-name = "ptr_meta_derive"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 1.0.109",
-]
-
-[[package]]
-name = "quanta"
-version = "0.12.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e"
-dependencies = [
- "crossbeam-utils",
- "libc",
- "once_cell",
- "raw-cpuid",
- "wasi",
- "web-sys",
- "winapi",
-]
-
-[[package]]
-name = "quick-xml"
-version = "0.36.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe"
-dependencies = [
- "memchr",
- "serde",
-]
-
-[[package]]
-name = "quinn"
-version = "0.11.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef"
-dependencies = [
- "bytes",
- "pin-project-lite",
- "quinn-proto",
- "quinn-udp",
- "rustc-hash 2.1.0",
- "rustls",
- "socket2",
- "thiserror 2.0.9",
- "tokio",
- "tracing",
-]
-
-[[package]]
-name = "quinn-proto"
-version = "0.11.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d"
-dependencies = [
- "bytes",
- "getrandom",
- "rand",
- "ring",
- "rustc-hash 2.1.0",
- "rustls",
- "rustls-pki-types",
- "slab",
- "thiserror 2.0.9",
- "tinyvec",
- "tracing",
- "web-time",
-]
-
-[[package]]
-name = "quinn-udp"
-version = "0.5.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904"
-dependencies = [
- "cfg_aliases",
- "libc",
- "once_cell",
- "socket2",
- "tracing",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.38"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "quoted_printable"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "640c9bd8497b02465aeef5375144c26062e0dcd5939dfcbb0f5db76cb8c17c73"
-
-[[package]]
-name = "radium"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
-
-[[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 = "raw-cpuid"
-version = "11.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "rayon"
-version = "1.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
-dependencies = [
- "either",
- "rayon-core",
-]
-
-[[package]]
-name = "rayon-core"
-version = "1.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
-dependencies = [
- "crossbeam-deque",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "redis"
-version = "0.22.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa8455fa3621f6b41c514946de66ea0531f57ca017b2e6c7cc368035ea5b46df"
-dependencies = [
- "async-trait",
- "bytes",
- "combine",
- "futures-util",
- "itoa",
- "percent-encoding",
- "pin-project-lite",
- "ryu",
- "sha1_smol",
- "tokio",
- "tokio-util",
- "url",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.5.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "redox_users"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
-dependencies = [
- "getrandom",
- "libredox",
- "thiserror 1.0.69",
-]
-
-[[package]]
-name = "regex"
-version = "1.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-automata 0.4.9",
- "regex-syntax 0.8.5",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
-dependencies = [
- "regex-syntax 0.6.29",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.4.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
-dependencies = [
- "aho-corasick",
- "memchr",
- "regex-syntax 0.8.5",
-]
-
-[[package]]
-name = "regex-syntax"
-version = "0.6.29"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
-
-[[package]]
-name = "regex-syntax"
-version = "0.8.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
-
-[[package]]
-name = "relative-path"
-version = "1.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
-
-[[package]]
-name = "rend"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c"
-dependencies = [
- "bytecheck",
-]
-
-[[package]]
-name = "reqwest"
-version = "0.12.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da"
-dependencies = [
- "base64 0.22.1",
- "bytes",
- "encoding_rs",
- "futures-core",
- "futures-util",
- "h2",
- "http 1.2.0",
- "http-body",
- "http-body-util",
- "hyper",
- "hyper-rustls",
- "hyper-util",
- "ipnet",
- "js-sys",
- "log",
- "mime",
- "once_cell",
- "percent-encoding",
- "pin-project-lite",
- "quinn",
- "rustls",
- "rustls-pemfile",
- "rustls-pki-types",
- "serde",
- "serde_json",
- "serde_urlencoded",
- "sync_wrapper",
- "system-configuration",
- "tokio",
- "tokio-rustls",
- "tokio-util",
- "tower 0.5.2",
- "tower-service",
- "url",
- "wasm-bindgen",
- "wasm-bindgen-futures",
- "wasm-streams",
- "web-sys",
- "webpki-roots",
- "windows-registry",
-]
-
-[[package]]
-name = "reserve-port"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9838134a2bfaa8e1f40738fcc972ac799de6e0e06b5157acb95fc2b05a0ea283"
-dependencies = [
- "lazy_static",
- "thiserror 1.0.69",
-]
-
-[[package]]
-name = "ring"
-version = "0.17.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
-dependencies = [
- "cc",
- "cfg-if",
- "getrandom",
- "libc",
- "spin",
- "untrusted",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "rkyv"
-version = "0.7.45"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b"
-dependencies = [
- "bitvec",
- "bytecheck",
- "bytes",
- "hashbrown 0.12.3",
- "ptr_meta",
- "rend",
- "rkyv_derive",
- "seahash",
- "tinyvec",
- "uuid",
-]
-
-[[package]]
-name = "rkyv_derive"
-version = "0.7.45"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 1.0.109",
-]
-
-[[package]]
-name = "rrgen"
-version = "0.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18e27f5f254d89b0b5b76445442e5c935b63a566ee5a735c9d877ca2029b4ce9"
-dependencies = [
- "cruet 0.13.3",
- "fs-err",
- "glob",
- "heck 0.4.1",
- "regex",
- "serde",
- "serde_json",
- "serde_regex",
- "serde_yaml",
- "tera",
- "thiserror 1.0.69",
-]
-
-[[package]]
-name = "rsa"
-version = "0.9.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519"
-dependencies = [
- "const-oid",
- "digest",
- "num-bigint-dig",
- "num-integer",
- "num-traits",
- "pkcs1",
- "pkcs8",
- "rand_core",
- "signature",
- "spki",
- "subtle",
- "zeroize",
-]
-
-[[package]]
-name = "rstest"
-version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9afd55a67069d6e434a95161415f5beeada95a01c7b815508a82dcb0e1593682"
-dependencies = [
- "futures",
- "futures-timer",
- "rstest_macros",
- "rustc_version",
-]
-
-[[package]]
-name = "rstest_macros"
-version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4165dfae59a39dd41d8dec720d3cbfbc71f69744efb480a3920f5d4e0cc6798d"
-dependencies = [
- "cfg-if",
- "glob",
- "proc-macro-crate",
- "proc-macro2",
- "quote",
- "regex",
- "relative-path",
- "rustc_version",
- "syn 2.0.95",
- "unicode-ident",
-]
-
-[[package]]
-name = "rust-multipart-rfc7578_2"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03b748410c0afdef2ebbe3685a6a862e2ee937127cdaae623336a459451c8d57"
-dependencies = [
- "bytes",
- "futures-core",
- "futures-util",
- "http 0.2.12",
- "mime",
- "mime_guess",
- "rand",
- "thiserror 1.0.69",
-]
-
-[[package]]
-name = "rust_decimal"
-version = "1.36.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555"
-dependencies = [
- "arrayvec",
- "borsh",
- "bytes",
- "num-traits",
- "rand",
- "rkyv",
- "serde",
- "serde_json",
-]
-
-[[package]]
-name = "rustc-demangle"
-version = "0.1.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
-
-[[package]]
-name = "rustc-hash"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
-
-[[package]]
-name = "rustc-hash"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
-
-[[package]]
-name = "rustc_version"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
-dependencies = [
- "semver",
-]
-
-[[package]]
-name = "rustix"
-version = "0.38.42"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85"
-dependencies = [
- "bitflags",
- "errno",
- "libc",
- "linux-raw-sys",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "rustls"
-version = "0.23.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b"
-dependencies = [
- "log",
- "once_cell",
- "ring",
- "rustls-pki-types",
- "rustls-webpki",
- "subtle",
- "zeroize",
-]
-
-[[package]]
-name = "rustls-pemfile"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
-dependencies = [
- "rustls-pki-types",
-]
-
-[[package]]
-name = "rustls-pki-types"
-version = "1.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37"
-dependencies = [
- "web-time",
-]
-
-[[package]]
-name = "rustls-webpki"
-version = "0.102.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
-dependencies = [
- "ring",
- "rustls-pki-types",
- "untrusted",
-]
-
-[[package]]
-name = "rustversion"
-version = "1.0.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
-
-[[package]]
-name = "rusty-sidekiq"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15544f047600b602c7b11ff7ee0882f9034f9cbe2c205693edd5615e2a6c03ee"
-dependencies = [
- "async-trait",
- "bb8",
- "chrono",
- "convert_case",
- "cron_clock",
- "gethostname",
- "hex",
- "num_cpus",
- "rand",
- "redis",
- "serde",
- "serde_json",
- "serial_test",
- "sha2",
- "slog-term",
- "thiserror 1.0.69",
- "tokio",
- "tokio-util",
- "tracing",
- "tracing-subscriber",
-]
-
-[[package]]
-name = "ryu"
-version = "1.0.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
-
-[[package]]
-name = "same-file"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
-name = "scc"
-version = "2.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28e1c91382686d21b5ac7959341fcb9780fa7c03773646995a87c950fa7be640"
-dependencies = [
- "sdd",
-]
-
-[[package]]
-name = "scoped-tls"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
-
-[[package]]
-name = "scopeguard"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
-
-[[package]]
-name = "scraper"
-version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0e749d29b2064585327af5038a5a8eb73aeebad4a3472e83531a436563f7208"
-dependencies = [
- "ahash 0.8.11",
- "cssparser",
- "ego-tree",
- "getopts",
- "html5ever",
- "indexmap",
- "precomputed-hash",
- "selectors",
- "tendril",
-]
-
-[[package]]
-name = "sdd"
-version = "3.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "478f121bb72bbf63c52c93011ea1791dca40140dfe13f8336c4c5ac952c33aa9"
-
-[[package]]
-name = "sea-bae"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f694a6ab48f14bc063cfadff30ab551d3c7e46d8f81836c51989d548f44a2a25"
-dependencies = [
- "heck 0.4.1",
- "proc-macro-error2",
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "sea-orm"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0dbcf83248860dc632c46c7e81a221e041b50d0006191756cb001d9e8afc60a9"
-dependencies = [
- "async-stream",
- "async-trait",
- "bigdecimal",
- "chrono",
- "futures",
- "log",
- "ouroboros",
- "rust_decimal",
- "sea-orm-macros",
- "sea-query",
- "sea-query-binder",
- "serde",
- "serde_json",
- "sqlx",
- "strum",
- "thiserror 1.0.69",
- "time",
- "tracing",
- "url",
- "uuid",
-]
-
-[[package]]
-name = "sea-orm-cli"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a8dbef29c7e534a8e9afb49abfa946c7a9df3d85f610dfed9140215ff8bff17"
-dependencies = [
- "chrono",
- "clap",
- "dotenvy",
- "glob",
- "regex",
- "sea-schema",
- "tracing",
- "tracing-subscriber",
- "url",
-]
-
-[[package]]
-name = "sea-orm-macros"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49ce6f08134f3681b1ca92185b96fac898f26d9b4f5538d13f7032ef243d14b2"
-dependencies = [
- "heck 0.4.1",
- "proc-macro2",
- "quote",
- "sea-bae",
- "syn 2.0.95",
- "unicode-ident",
-]
-
-[[package]]
-name = "sea-orm-migration"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e53f46fe9874161ba57b15ff45d2589d754d7cbbbaab9470cb79cbada149ca7"
-dependencies = [
- "async-trait",
- "clap",
- "dotenvy",
- "futures",
- "sea-orm",
- "sea-orm-cli",
- "sea-schema",
- "tracing",
- "tracing-subscriber",
-]
-
-[[package]]
-name = "sea-query"
-version = "0.32.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "085e94f7d7271c0393ac2d164a39994b1dff1b06bc40cd9a0da04f3d672b0fee"
-dependencies = [
- "bigdecimal",
- "chrono",
- "inherent",
- "ordered-float",
- "rust_decimal",
- "sea-query-derive",
- "serde_json",
- "time",
- "uuid",
-]
-
-[[package]]
-name = "sea-query-binder"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0019f47430f7995af63deda77e238c17323359af241233ec768aba1faea7608"
-dependencies = [
- "bigdecimal",
- "chrono",
- "rust_decimal",
- "sea-query",
- "serde_json",
- "sqlx",
- "time",
- "uuid",
-]
-
-[[package]]
-name = "sea-query-derive"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9834af2c4bd8c5162f00c89f1701fb6886119a88062cf76fe842ea9e232b9839"
-dependencies = [
- "darling",
- "heck 0.4.1",
- "proc-macro2",
- "quote",
- "syn 2.0.95",
- "thiserror 1.0.69",
-]
-
-[[package]]
-name = "sea-schema"
-version = "0.16.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ef5dd7848c993f3789d09a2616484c72c9330cae2b048df59d8c9b8c0343e95"
-dependencies = [
- "futures",
- "sea-query",
- "sea-schema-derive",
-]
-
-[[package]]
-name = "sea-schema-derive"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "debdc8729c37fdbf88472f97fd470393089f997a909e535ff67c544d18cfccf0"
-dependencies = [
- "heck 0.4.1",
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "seahash"
-version = "4.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
-
-[[package]]
-name = "selectors"
-version = "0.26.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd568a4c9bb598e291a08244a5c1f5a8a6650bee243b5b0f8dbb3d9cc1d87fe8"
-dependencies = [
- "bitflags",
- "cssparser",
- "derive_more",
- "fxhash",
- "log",
- "new_debug_unreachable",
- "phf",
- "phf_codegen",
- "precomputed-hash",
- "servo_arc",
- "smallvec",
-]
-
-[[package]]
-name = "self_cell"
-version = "0.10.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e14e4d63b804dc0c7ec4a1e52bcb63f02c7ac94476755aa579edac21e01f915d"
-dependencies = [
- "self_cell 1.1.0",
-]
-
-[[package]]
-name = "self_cell"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe"
-
-[[package]]
-name = "semver"
-version = "1.0.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba"
-
-[[package]]
-name = "serde"
-version = "1.0.217"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.217"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "serde_json"
-version = "1.0.134"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d"
-dependencies = [
- "itoa",
- "memchr",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "serde_path_to_error"
-version = "0.1.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
-dependencies = [
- "itoa",
- "serde",
-]
-
-[[package]]
-name = "serde_regex"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8136f1a4ea815d7eac4101cfd0b16dc0cb5e1fe1b8609dfd728058656b7badf"
-dependencies = [
- "regex",
- "serde",
-]
-
-[[package]]
-name = "serde_spanned"
-version = "0.6.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
-dependencies = [
- "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 = "serde_variant"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a0068df419f9d9b6488fdded3f1c818522cdea328e02ce9d9f147380265a432"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "serde_yaml"
-version = "0.9.34+deprecated"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
-dependencies = [
- "indexmap",
- "itoa",
- "ryu",
- "serde",
- "unsafe-libyaml",
-]
-
-[[package]]
-name = "serial_test"
-version = "3.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9"
-dependencies = [
- "futures",
- "log",
- "once_cell",
- "parking_lot",
- "scc",
- "serial_test_derive",
-]
-
-[[package]]
-name = "serial_test_derive"
-version = "3.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "servo_arc"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae65c4249478a2647db249fb43e23cec56a2c8974a427e7bd8cb5a1d0964921a"
-dependencies = [
- "stable_deref_trait",
-]
-
-[[package]]
-name = "sha1"
-version = "0.10.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
-dependencies = [
- "cfg-if",
- "cpufeatures",
- "digest",
-]
-
-[[package]]
-name = "sha1_smol"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
-
-[[package]]
-name = "sha2"
-version = "0.10.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
-dependencies = [
- "cfg-if",
- "cpufeatures",
- "digest",
-]
-
-[[package]]
-name = "sharded-slab"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
-dependencies = [
- "lazy_static",
-]
-
-[[package]]
-name = "shared_child"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09fa9338aed9a1df411814a5b2252f7cd206c55ae9bf2fa763f8de84603aa60c"
-dependencies = [
- "libc",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "shlex"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
-
-[[package]]
-name = "signal-hook-registry"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "signature"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
-dependencies = [
- "digest",
- "rand_core",
-]
-
-[[package]]
-name = "simdutf8"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
-
-[[package]]
-name = "similar"
-version = "2.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
-
-[[package]]
-name = "simple_asn1"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085"
-dependencies = [
- "num-bigint",
- "num-traits",
- "thiserror 1.0.69",
- "time",
-]
-
-[[package]]
-name = "siphasher"
-version = "0.3.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
-
-[[package]]
-name = "slab"
-version = "0.4.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
-dependencies = [
- "autocfg",
-]
-
-[[package]]
-name = "slog"
-version = "2.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06"
-
-[[package]]
-name = "slog-term"
-version = "2.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6e022d0b998abfe5c3782c1f03551a596269450ccd677ea51c56f8b214610e8"
-dependencies = [
- "is-terminal",
- "slog",
- "term",
- "thread_local",
- "time",
-]
-
-[[package]]
-name = "slug"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724"
-dependencies = [
- "deunicode",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "smallvec"
-version = "1.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "snafu"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6"
-dependencies = [
- "doc-comment",
- "snafu-derive",
-]
-
-[[package]]
-name = "snafu-derive"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf"
-dependencies = [
- "heck 0.4.1",
- "proc-macro2",
- "quote",
- "syn 1.0.109",
-]
-
-[[package]]
-name = "snapbox"
-version = "0.4.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b831b6e80fbcd2889efa75b185d24005f85981431495f995292b25836519d84"
-dependencies = [
- "anstream",
- "anstyle",
- "content_inspector",
- "dunce",
- "filetime",
- "libc",
- "normalize-line-endings",
- "os_pipe",
- "similar",
- "snapbox-macros",
- "tempfile",
- "wait-timeout",
- "walkdir",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "snapbox-macros"
-version = "0.3.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16569f53ca23a41bb6f62e0a5084aa1661f4814a67fa33696a79073e03a664af"
-dependencies = [
- "anstream",
-]
-
-[[package]]
-name = "socket2"
-version = "0.5.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
-dependencies = [
- "libc",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "spin"
-version = "0.9.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
-dependencies = [
- "lock_api",
-]
-
-[[package]]
-name = "spki"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
-dependencies = [
- "base64ct",
- "der",
-]
-
-[[package]]
-name = "sqlx"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4410e73b3c0d8442c5f99b425d7a435b5ee0ae4167b3196771dd3f7a01be745f"
-dependencies = [
- "sqlx-core",
- "sqlx-macros",
- "sqlx-mysql",
- "sqlx-postgres",
- "sqlx-sqlite",
-]
-
-[[package]]
-name = "sqlx-core"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a007b6936676aa9ab40207cde35daab0a04b823be8ae004368c0793b96a61e0"
-dependencies = [
- "bigdecimal",
- "bytes",
- "chrono",
- "crc",
- "crossbeam-queue",
- "either",
- "event-listener 5.3.1",
- "futures-core",
- "futures-intrusive",
- "futures-io",
- "futures-util",
- "hashbrown 0.15.2",
- "hashlink",
- "indexmap",
- "log",
- "memchr",
- "once_cell",
- "percent-encoding",
- "rust_decimal",
- "rustls",
- "rustls-pemfile",
- "serde",
- "serde_json",
- "sha2",
- "smallvec",
- "thiserror 2.0.9",
- "time",
- "tokio",
- "tokio-stream",
- "tracing",
- "url",
- "uuid",
- "webpki-roots",
-]
-
-[[package]]
-name = "sqlx-macros"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3112e2ad78643fef903618d78cf0aec1cb3134b019730edb039b69eaf531f310"
-dependencies = [
- "proc-macro2",
- "quote",
- "sqlx-core",
- "sqlx-macros-core",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "sqlx-macros-core"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e9f90acc5ab146a99bf5061a7eb4976b573f560bc898ef3bf8435448dd5e7ad"
-dependencies = [
- "dotenvy",
- "either",
- "heck 0.5.0",
- "hex",
- "once_cell",
- "proc-macro2",
- "quote",
- "serde",
- "serde_json",
- "sha2",
- "sqlx-core",
- "sqlx-mysql",
- "sqlx-postgres",
- "sqlx-sqlite",
- "syn 2.0.95",
- "tempfile",
- "tokio",
- "url",
-]
-
-[[package]]
-name = "sqlx-mysql"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4560278f0e00ce64938540546f59f590d60beee33fffbd3b9cd47851e5fff233"
-dependencies = [
- "atoi",
- "base64 0.22.1",
- "bigdecimal",
- "bitflags",
- "byteorder",
- "bytes",
- "chrono",
- "crc",
- "digest",
- "dotenvy",
- "either",
- "futures-channel",
- "futures-core",
- "futures-io",
- "futures-util",
- "generic-array",
- "hex",
- "hkdf",
- "hmac",
- "itoa",
- "log",
- "md-5",
- "memchr",
- "once_cell",
- "percent-encoding",
- "rand",
- "rsa",
- "rust_decimal",
- "serde",
- "sha1",
- "sha2",
- "smallvec",
- "sqlx-core",
- "stringprep",
- "thiserror 2.0.9",
- "time",
- "tracing",
- "uuid",
- "whoami",
-]
-
-[[package]]
-name = "sqlx-postgres"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5b98a57f363ed6764d5b3a12bfedf62f07aa16e1856a7ddc2a0bb190a959613"
-dependencies = [
- "atoi",
- "base64 0.22.1",
- "bigdecimal",
- "bitflags",
- "byteorder",
- "chrono",
- "crc",
- "dotenvy",
- "etcetera",
- "futures-channel",
- "futures-core",
- "futures-util",
- "hex",
- "hkdf",
- "hmac",
- "home",
- "itoa",
- "log",
- "md-5",
- "memchr",
- "num-bigint",
- "once_cell",
- "rand",
- "rust_decimal",
- "serde",
- "serde_json",
- "sha2",
- "smallvec",
- "sqlx-core",
- "stringprep",
- "thiserror 2.0.9",
- "time",
- "tracing",
- "uuid",
- "whoami",
-]
-
-[[package]]
-name = "sqlx-sqlite"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f85ca71d3a5b24e64e1d08dd8fe36c6c95c339a896cc33068148906784620540"
-dependencies = [
- "atoi",
- "chrono",
- "flume 0.11.1",
- "futures-channel",
- "futures-core",
- "futures-executor",
- "futures-intrusive",
- "futures-util",
- "libsqlite3-sys",
- "log",
- "percent-encoding",
- "serde",
- "serde_urlencoded",
- "sqlx-core",
- "time",
- "tracing",
- "url",
- "uuid",
-]
-
-[[package]]
-name = "stable_deref_trait"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
-
-[[package]]
-name = "stacker"
-version = "0.1.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "799c883d55abdb5e98af1a7b3f23b9b6de8ecada0ecac058672d7635eb48ca7b"
-dependencies = [
- "cc",
- "cfg-if",
- "libc",
- "psm",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "static_assertions"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-
-[[package]]
-name = "string_cache"
-version = "0.8.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
-dependencies = [
- "new_debug_unreachable",
- "once_cell",
- "parking_lot",
- "phf_shared 0.10.0",
- "precomputed-hash",
- "serde",
-]
-
-[[package]]
-name = "string_cache_codegen"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
-dependencies = [
- "phf_generator 0.10.0",
- "phf_shared 0.10.0",
- "proc-macro2",
- "quote",
-]
-
-[[package]]
-name = "stringprep"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1"
-dependencies = [
- "unicode-bidi",
- "unicode-normalization",
- "unicode-properties",
-]
-
-[[package]]
-name = "strsim"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
-
-[[package]]
-name = "strum"
-version = "0.26.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06"
-
-[[package]]
-name = "subtle"
-version = "2.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
-
-[[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 = "syn"
-version = "2.0.95"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "sync_wrapper"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
-dependencies = [
- "futures-core",
-]
-
-[[package]]
-name = "synstructure"
-version = "0.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "system-configuration"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
-dependencies = [
- "bitflags",
- "core-foundation",
- "system-configuration-sys",
-]
-
-[[package]]
-name = "system-configuration-sys"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
-dependencies = [
- "core-foundation-sys",
- "libc",
-]
-
-[[package]]
-name = "tagptr"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
-
-[[package]]
-name = "tap"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
-
-[[package]]
-name = "tempfile"
-version = "3.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
-dependencies = [
- "cfg-if",
- "fastrand",
- "getrandom",
- "once_cell",
- "rustix",
- "windows-sys 0.59.0",
-]
-
-[[package]]
-name = "tendril"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0"
-dependencies = [
- "futf",
- "mac",
- "utf-8",
-]
-
-[[package]]
-name = "tera"
-version = "1.20.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab9d851b45e865f178319da0abdbfe6acbc4328759ff18dafc3a41c16b4cd2ee"
-dependencies = [
- "chrono",
- "chrono-tz",
- "globwalk",
- "humansize",
- "lazy_static",
- "percent-encoding",
- "pest",
- "pest_derive",
- "rand",
- "regex",
- "serde",
- "serde_json",
- "slug",
- "unic-segment",
-]
-
-[[package]]
-name = "term"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
-dependencies = [
- "dirs-next",
- "rustversion",
- "winapi",
-]
-
-[[package]]
-name = "thiserror"
-version = "1.0.69"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
-dependencies = [
- "thiserror-impl 1.0.69",
-]
-
-[[package]]
-name = "thiserror"
-version = "2.0.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
-dependencies = [
- "thiserror-impl 2.0.9",
-]
-
-[[package]]
-name = "thiserror-impl"
-version = "1.0.69"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "thiserror-impl"
-version = "2.0.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "thousands"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820"
-
-[[package]]
-name = "thread_local"
-version = "1.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
-dependencies = [
- "cfg-if",
- "once_cell",
-]
-
-[[package]]
-name = "time"
-version = "0.3.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
-dependencies = [
- "deranged",
- "itoa",
- "num-conv",
- "powerfmt",
- "serde",
- "time-core",
- "time-macros",
-]
-
-[[package]]
-name = "time-core"
-version = "0.1.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
-
-[[package]]
-name = "time-macros"
-version = "0.2.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de"
-dependencies = [
- "num-conv",
- "time-core",
-]
-
-[[package]]
-name = "tinystr"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
-dependencies = [
- "displaydoc",
- "zerovec",
-]
-
-[[package]]
-name = "tinyvec"
-version = "1.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8"
-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.42.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
-dependencies = [
- "backtrace",
- "bytes",
- "libc",
- "mio",
- "parking_lot",
- "pin-project-lite",
- "signal-hook-registry",
- "socket2",
- "tokio-macros",
- "windows-sys 0.52.0",
-]
-
-[[package]]
-name = "tokio-cron-scheduler"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2594dd7c2abbbafbb1c78d167fd10860dc7bd75f814cb051a1e0d3e796b9702"
-dependencies = [
- "chrono",
- "cron",
- "num-derive",
- "num-traits",
- "tokio",
- "tracing",
- "uuid",
-]
-
-[[package]]
-name = "tokio-macros"
-version = "2.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "tokio-rustls"
-version = "0.26.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37"
-dependencies = [
- "rustls",
- "tokio",
-]
-
-[[package]]
-name = "tokio-stream"
-version = "0.1.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
-dependencies = [
- "futures-core",
- "pin-project-lite",
- "tokio",
-]
-
-[[package]]
-name = "tokio-util"
-version = "0.7.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078"
-dependencies = [
- "bytes",
- "futures-core",
- "futures-sink",
- "pin-project-lite",
- "tokio",
-]
-
-[[package]]
-name = "toml"
-version = "0.8.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
-dependencies = [
- "serde",
- "serde_spanned",
- "toml_datetime",
- "toml_edit",
-]
-
-[[package]]
-name = "toml_datetime"
-version = "0.6.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
-dependencies = [
- "serde",
-]
-
-[[package]]
-name = "toml_edit"
-version = "0.22.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
-dependencies = [
- "indexmap",
- "serde",
- "serde_spanned",
- "toml_datetime",
- "winnow",
-]
-
-[[package]]
-name = "tower"
-version = "0.4.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
-dependencies = [
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "tower"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
-dependencies = [
- "futures-core",
- "futures-util",
- "pin-project-lite",
- "sync_wrapper",
- "tokio",
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "tower-http"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697"
-dependencies = [
- "async-compression",
- "bitflags",
- "bytes",
- "futures-core",
- "futures-util",
- "http 1.2.0",
- "http-body",
- "http-body-util",
- "http-range-header",
- "httpdate",
- "mime",
- "mime_guess",
- "percent-encoding",
- "pin-project-lite",
- "tokio",
- "tokio-util",
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
-[[package]]
-name = "tower-layer"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
-
-[[package]]
-name = "tower-service"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
-
-[[package]]
-name = "tracing"
-version = "0.1.41"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
-dependencies = [
- "log",
- "pin-project-lite",
- "tracing-attributes",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-appender"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf"
-dependencies = [
- "crossbeam-channel",
- "thiserror 1.0.69",
- "time",
- "tracing-subscriber",
-]
-
-[[package]]
-name = "tracing-attributes"
-version = "0.1.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "tracing-core"
-version = "0.1.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
-dependencies = [
- "once_cell",
- "valuable",
-]
-
-[[package]]
-name = "tracing-log"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
-dependencies = [
- "log",
- "once_cell",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-serde"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1"
-dependencies = [
- "serde",
- "tracing-core",
-]
-
-[[package]]
-name = "tracing-subscriber"
-version = "0.3.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
-dependencies = [
- "matchers",
- "nu-ansi-term",
- "once_cell",
- "regex",
- "serde",
- "serde_json",
- "sharded-slab",
- "smallvec",
- "thread_local",
- "tracing",
- "tracing-core",
- "tracing-log",
- "tracing-serde",
-]
-
-[[package]]
-name = "try-lock"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
-
-[[package]]
-name = "trycmd"
-version = "0.14.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d41014f614932fff67cd3b780e0eb0ecb14e698a831a0e555ef2a5137be968d5"
-dependencies = [
- "glob",
- "humantime",
- "humantime-serde",
- "rayon",
- "serde",
- "shlex",
- "snapbox",
- "toml_edit",
-]
-
-[[package]]
-name = "type-map"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "deb68604048ff8fa93347f02441e4487594adc20bb8a084f9e564d2b827a0a9f"
-dependencies = [
- "rustc-hash 1.1.0",
-]
-
-[[package]]
-name = "typenum"
-version = "1.17.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
-
-[[package]]
-name = "ucd-trie"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
-
-[[package]]
-name = "ulid"
-version = "1.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04f903f293d11f31c0c29e4148f6dc0d033a7f80cebc0282bea147611667d289"
-dependencies = [
- "getrandom",
- "rand",
- "web-time",
-]
-
-[[package]]
-name = "unic-char-property"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
-dependencies = [
- "unic-char-range",
-]
-
-[[package]]
-name = "unic-char-range"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
-
-[[package]]
-name = "unic-common"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
-
-[[package]]
-name = "unic-langid"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23dd9d1e72a73b25e07123a80776aae3e7b0ec461ef94f9151eed6ec88005a44"
-dependencies = [
- "unic-langid-impl",
- "unic-langid-macros",
-]
-
-[[package]]
-name = "unic-langid-impl"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a5422c1f65949306c99240b81de9f3f15929f5a8bfe05bb44b034cc8bf593e5"
-dependencies = [
- "tinystr",
-]
-
-[[package]]
-name = "unic-langid-macros"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0da1cd2c042d3c7569a1008806b02039e7a4a2bdf8f8e96bd3c792434a0e275e"
-dependencies = [
- "proc-macro-hack",
- "tinystr",
- "unic-langid-impl",
- "unic-langid-macros-impl",
-]
-
-[[package]]
-name = "unic-langid-macros-impl"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ed7f4237ba393424195053097c1516bd4590dc82b84f2f97c5c69e12704555b"
-dependencies = [
- "proc-macro-hack",
- "quote",
- "syn 2.0.95",
- "unic-langid-impl",
-]
-
-[[package]]
-name = "unic-segment"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4ed5d26be57f84f176157270c112ef57b86debac9cd21daaabbe56db0f88f23"
-dependencies = [
- "unic-ucd-segment",
-]
-
-[[package]]
-name = "unic-ucd-segment"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700"
-dependencies = [
- "unic-char-property",
- "unic-char-range",
- "unic-ucd-version",
-]
-
-[[package]]
-name = "unic-ucd-version"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
-dependencies = [
- "unic-common",
-]
-
-[[package]]
-name = "unicase"
-version = "2.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
-
-[[package]]
-name = "unicode-bidi"
-version = "0.3.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
-
-[[package]]
-name = "unicode-normalization"
-version = "0.1.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
-dependencies = [
- "tinyvec",
-]
-
-[[package]]
-name = "unicode-properties"
-version = "0.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
-
-[[package]]
-name = "unicode-segmentation"
-version = "1.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
-
-[[package]]
-name = "unicode-width"
-version = "0.1.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
-
-[[package]]
-name = "universal-hash"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
-dependencies = [
- "crypto-common",
- "subtle",
-]
-
-[[package]]
-name = "unsafe-libyaml"
-version = "0.2.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
-
-[[package]]
-name = "untrusted"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
-
-[[package]]
-name = "url"
-version = "2.5.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
-dependencies = [
- "form_urlencoded",
- "idna",
- "percent-encoding",
-]
-
-[[package]]
-name = "utf-8"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
-
-[[package]]
-name = "utf16_iter"
-version = "1.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
-
-[[package]]
-name = "utf8-width"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3"
-
-[[package]]
-name = "utf8_iter"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
-
-[[package]]
-name = "utf8parse"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
-
-[[package]]
-name = "utoipa"
-version = "4.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5afb1a60e207dca502682537fefcfd9921e71d0b83e9576060f09abc6efab23"
-dependencies = [
- "indexmap",
- "serde",
- "serde_json",
- "utoipa-gen",
-]
-
-[[package]]
-name = "utoipa-gen"
-version = "4.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20c24e8ab68ff9ee746aad22d39b5535601e6416d1b0feeabf78be986a5c4392"
-dependencies = [
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "uuid"
-version = "1.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
-dependencies = [
- "getrandom",
- "rand",
- "serde",
-]
-
-[[package]]
-name = "validator"
-version = "0.20.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43fb22e1a008ece370ce08a3e9e4447a910e92621bb49b85d6e48a45397e7cfa"
-dependencies = [
- "idna",
- "once_cell",
- "regex",
- "serde",
- "serde_derive",
- "serde_json",
- "url",
- "validator_derive",
-]
-
-[[package]]
-name = "validator_derive"
-version = "0.20.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7df16e474ef958526d1205f6dda359fdfab79d9aa6d54bafcb92dcd07673dca"
-dependencies = [
- "darling",
- "once_cell",
- "proc-macro-error2",
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "valuable"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
-
-[[package]]
-name = "value-bag"
-version = "1.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2"
-
-[[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.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
-
-[[package]]
-name = "wait-timeout"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "walkdir"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
-dependencies = [
- "same-file",
- "winapi-util",
-]
-
-[[package]]
-name = "want"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
-dependencies = [
- "try-lock",
-]
-
-[[package]]
-name = "wasi"
-version = "0.11.0+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
-
-[[package]]
-name = "wasite"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
-
-[[package]]
-name = "wasm-bindgen"
-version = "0.2.99"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396"
-dependencies = [
- "cfg-if",
- "once_cell",
- "wasm-bindgen-macro",
-]
-
-[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.99"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79"
-dependencies = [
- "bumpalo",
- "log",
- "proc-macro2",
- "quote",
- "syn 2.0.95",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-futures"
-version = "0.4.49"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2"
-dependencies = [
- "cfg-if",
- "js-sys",
- "once_cell",
- "wasm-bindgen",
- "web-sys",
-]
-
-[[package]]
-name = "wasm-bindgen-macro"
-version = "0.2.99"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe"
-dependencies = [
- "quote",
- "wasm-bindgen-macro-support",
-]
-
-[[package]]
-name = "wasm-bindgen-macro-support"
-version = "0.2.99"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
- "wasm-bindgen-backend",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-shared"
-version = "0.2.99"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6"
-
-[[package]]
-name = "wasm-streams"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
-dependencies = [
- "futures-util",
- "js-sys",
- "wasm-bindgen",
- "wasm-bindgen-futures",
- "web-sys",
-]
-
-[[package]]
-name = "web-sys"
-version = "0.3.76"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc"
-dependencies = [
- "js-sys",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "web-time"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
-dependencies = [
- "js-sys",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "webpki-roots"
-version = "0.26.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e"
-dependencies = [
- "rustls-pki-types",
-]
-
-[[package]]
-name = "whoami"
-version = "1.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d"
-dependencies = [
- "redox_syscall",
- "wasite",
-]
-
-[[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.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
-dependencies = [
- "windows-sys 0.59.0",
-]
-
-[[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"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
-dependencies = [
- "windows-core 0.52.0",
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows"
-version = "0.58.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
-dependencies = [
- "windows-core 0.58.0",
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-core"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
-dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-core"
-version = "0.58.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
-dependencies = [
- "windows-implement",
- "windows-interface",
- "windows-result",
- "windows-strings",
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-implement"
-version = "0.58.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "windows-interface"
-version = "0.58.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "windows-registry"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
-dependencies = [
- "windows-result",
- "windows-strings",
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-result"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
-dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-strings"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
-dependencies = [
- "windows-result",
- "windows-targets 0.52.6",
-]
-
-[[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]]
-name = "windows-sys"
-version = "0.52.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
-dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.59.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
-dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[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]]
-name = "windows-targets"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
-dependencies = [
- "windows_aarch64_gnullvm 0.52.6",
- "windows_aarch64_msvc 0.52.6",
- "windows_i686_gnu 0.52.6",
- "windows_i686_gnullvm",
- "windows_i686_msvc 0.52.6",
- "windows_x86_64_gnu 0.52.6",
- "windows_x86_64_gnullvm 0.52.6",
- "windows_x86_64_msvc 0.52.6",
-]
-
-[[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_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
-
-[[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_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
-
-[[package]]
-name = "windows_i686_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
-
-[[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_gnu"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
-
-[[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_gnullvm"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
-
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.52.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
-
-[[package]]
-name = "winnow"
-version = "0.6.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39281189af81c07ec09db316b302a3e67bf9bd7cbf6c820b50e35fee9c2fa980"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "write16"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
-
-[[package]]
-name = "writeable"
-version = "0.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
-
-[[package]]
-name = "wyz"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
-dependencies = [
- "tap",
-]
-
-[[package]]
-name = "yansi"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
-
-[[package]]
-name = "yoke"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
-dependencies = [
- "serde",
- "stable_deref_trait",
- "yoke-derive",
- "zerofrom",
-]
-
-[[package]]
-name = "yoke-derive"
-version = "0.7.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
- "synstructure",
-]
-
-[[package]]
-name = "zerocopy"
-version = "0.7.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
-dependencies = [
- "byteorder",
- "zerocopy-derive",
-]
-
-[[package]]
-name = "zerocopy-derive"
-version = "0.7.35"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "zerofrom"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
-dependencies = [
- "zerofrom-derive",
-]
-
-[[package]]
-name = "zerofrom-derive"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
- "synstructure",
-]
-
-[[package]]
-name = "zeroize"
-version = "1.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
-
-[[package]]
-name = "zerovec"
-version = "0.10.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
-dependencies = [
- "yoke",
- "zerofrom",
- "zerovec-derive",
-]
-
-[[package]]
-name = "zerovec-derive"
-version = "0.10.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.95",
-]
-
-[[package]]
-name = "zstd"
-version = "0.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9"
-dependencies = [
- "zstd-safe",
-]
-
-[[package]]
-name = "zstd-safe"
-version = "7.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059"
-dependencies = [
- "zstd-sys",
-]
-
-[[package]]
-name = "zstd-sys"
-version = "2.0.13+zstd.1.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa"
-dependencies = [
- "cc",
- "pkg-config",
-]
diff --git a/examples/demo/Cargo.toml b/examples/demo/Cargo.toml
deleted file mode 100644
index d48b8f62d..000000000
--- a/examples/demo/Cargo.toml
+++ /dev/null
@@ -1,62 +0,0 @@
-[workspace]
-# this empty pragma excludes this project from workspaces
-
-[package]
-name = "demo_app"
-version = "0.1.0"
-edition = "2021"
-default-run = "demo_app-cli"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-
-loco-rs = { path = "../../", version = "*" }
-migration = { path = "migration" }
-
-serde = { version = "1", features = ["derive"] }
-serde_json = "1"
-tokio = { version = "1.33.0", features = ["full"] }
-async-trait = "0.1.74"
-tracing = "0.1.40"
-chrono = "0.4"
-validator = { version = "0.20" }
-sea-orm = { version = "1.1.0", features = [
- "sqlx-sqlite",
- "sqlx-postgres",
- "runtime-tokio-rustls",
- "macros",
-] }
-
-axum = { version = "0.8.1", features = ["multipart"] }
-axum_session = { version = "0.15.0", default-features = false }
-axum-extra = { version = "0.10", features = ["cookie"] }
-
-include_dir = "0.7"
-uuid = { version = "1.6.0", features = ["v4"] }
-tracing-subscriber = { version = "0.3.17", features = ["env-filter", "json"] }
-
-fluent-templates = { version = "0.8.0", features = ["tera"] }
-unic-langid = "0.9.4"
-tera = "1.19.1"
-tower = "0.4.13"
-futures-util = "0.3.30"
-utoipa = "4.2.3"
-
-[[bin]]
-name = "demo_app-cli"
-path = "src/bin/main.rs"
-required-features = []
-
-[[bin]]
-name = "tool"
-path = "src/bin/tool.rs"
-required-features = []
-
-[dev-dependencies]
-serial_test = "3.1.1"
-rstest = "0.21.0"
-loco-rs = { path = "../../", version = "*", features = ["testing"] }
-trycmd = "0.14.19"
-insta = { version = "1.34.0", features = ["redactions", "yaml", "filters"] }
-axum-test = { version = "17.0.1" }
diff --git a/examples/demo/README.md b/examples/demo/README.md
deleted file mode 100644
index 52561c753..000000000
--- a/examples/demo/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Demo
-
-This app is a kitchensink for various capabilities and examples of the [Loco](https://loco.rs) project.
-
-# Example Listings
-
-## OpenAPI with `utoipa`
-
-See [src/controllers/responses.rs](src/controllers/responses.rs)
diff --git a/examples/demo/assets/i18n/de-DE/main.ftl b/examples/demo/assets/i18n/de-DE/main.ftl
deleted file mode 100644
index ced609fe4..000000000
--- a/examples/demo/assets/i18n/de-DE/main.ftl
+++ /dev/null
@@ -1,4 +0,0 @@
-hello-world = Hallo Welt!
-greeting = Hallochen { $name }!
- .placeholder = Hallo Freund!
-about = Uber
diff --git a/examples/demo/assets/i18n/en-US/main.ftl b/examples/demo/assets/i18n/en-US/main.ftl
deleted file mode 100644
index 9d4d5e7c4..000000000
--- a/examples/demo/assets/i18n/en-US/main.ftl
+++ /dev/null
@@ -1,10 +0,0 @@
-hello-world = Hello World!
-greeting = Hello { $name }!
- .placeholder = Hello Friend!
-about = About
-simple = simple text
-reference = simple text with a reference: { -something }
-parameter = text with a { $param }
-parameter2 = text one { $param } second { $multi-word-param }
-email = text with an EMAIL("example@example.org")
-fallback = this should fall back
diff --git a/examples/demo/assets/i18n/shared.ftl b/examples/demo/assets/i18n/shared.ftl
deleted file mode 100644
index f169eca9d..000000000
--- a/examples/demo/assets/i18n/shared.ftl
+++ /dev/null
@@ -1 +0,0 @@
--something = foo
diff --git a/examples/demo/assets/static/404.html b/examples/demo/assets/static/404.html
deleted file mode 100644
index 66e78fb22..000000000
--- a/examples/demo/assets/static/404.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
-not found :-(
-
diff --git a/examples/demo/assets/static/image.png b/examples/demo/assets/static/image.png
deleted file mode 100644
index fa5a09508..000000000
Binary files a/examples/demo/assets/static/image.png and /dev/null differ
diff --git a/examples/demo/assets/views/home/hello.html b/examples/demo/assets/views/home/hello.html
deleted file mode 100644
index 6b97c398e..000000000
--- a/examples/demo/assets/views/home/hello.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
- find this tera template at assets/views/home/hello.html
:
-
-
- {{ t(key="hello-world", lang="en-US") }},
-
- {{ t(key="hello-world", lang="de-DE") }}
-
-
-
\ No newline at end of file
diff --git a/examples/demo/config/development.yaml b/examples/demo/config/development.yaml
deleted file mode 100644
index ac3c78131..000000000
--- a/examples/demo/config/development.yaml
+++ /dev/null
@@ -1,139 +0,0 @@
-# Loco configuration file documentation
-
-#
-settings:
- allow_list:
- - google.com
- - apple.com
-#
-
-#
-# Application logging configuration
-logger:
- # Enable or disable logging.
- enable: true
- # Enable pretty backtrace (sets RUST_BACKTRACE=1)
- pretty_backtrace: true
- # Log level, options: trace, debug, info, warn or error.
- level: debug
- # Define the logging format. options: compact, pretty or json
- format: compact
- # By default the logger has filtering only logs that came from your code or logs that came from `loco` framework. to see all third party libraries
- # Uncomment the line below to override to see all third party libraries you can enable this config and override the logger filters.
- # override_filter: trace
-#
-
-# Web server configuration
-#
-server:
- # Port on which the server will listen. the server binding is 0.0.0.0:{PORT}
- port: {{ get_env(name="NODE_PORT", default=5150) }}
- # The UI hostname or IP address that mailers will point to.
- host: http://localhost
-#
-
-# Worker Configuration
-workers:
- # specifies the worker mode. Options:
- # - BackgroundQueue - Workers operate asynchronously in the background, processing queued.
- # - ForegroundBlocking - Workers operate in the foreground and block until tasks are completed.
- # - BackgroundAsync - Workers operate asynchronously in the background, processing tasks with async capabilities.
- mode: BackgroundQueue
-
-# Mailer Configuration.
-#
-mailer:
- # SMTP mailer configuration.
- smtp:
- # Enable/Disable smtp mailer.
- enable: true
- # SMTP server host. e.x localhost, smtp.gmail.com
- host: {{ get_env(name="MAILER_HOST", default="localhost") }}
- # SMTP server port
- port: 1025
- # Use secure connection (SSL/TLS).
- secure: false
- # auth:
- # user:
- # password:
-#
-
-# Initializers Configuration
-# initializers:
-# oauth2:
-# authorization_code: # Authorization code grant type
-# - client_identifier: google # Identifier for the OAuth2 provider. Replace 'google' with your provider's name if different, must be unique within the oauth2 config.
-# ... other fields
-
-# Database Configuration
-#
-database:
- # Database connection URI
- uri: {{get_env(name="DATABASE_URL", default="postgres://loco:loco@localhost:5432/loco_app")}}
- # When enabled, the sql query will be logged.
- enable_logging: false
- # Set the timeout duration when acquiring a connection.
- connect_timeout: 500
- # Set the idle duration before closing a connection.
- idle_timeout: 500
- # Minimum number of connections for a pool.
- min_connections: 1
- # Maximum number of connections for a pool.
- max_connections: 1
- # Run migration up when application loaded
- auto_migrate: true
- # Truncate database when application loaded. This is a dangerous operation, make sure that you using this flag only on dev environments or test mode
- dangerously_truncate: false
- # Recreating schema when application loaded. This is a dangerous operation, make sure that you using this flag only on dev environments or test mode
- dangerously_recreate: false
-#
-
-# Queue Configuration
-#
-queue:
- kind: Redis
- # Redis connection URI
- uri: {{ get_env(name="REDIS_URL", default="redis://127.0.0.1") }}
- # Dangerously flush all data in Redis on startup. dangerous operation, make sure that you using this flag only on dev environments or test mode
- dangerously_flush: false
-#
-
-# Authentication Configuration
-#
-auth:
- # JWT authentication
- jwt:
- # Secret key for token generation and verification
- secret: PqRwLF2rhHe8J22oBeHy
- # Token expiration time in seconds
- expiration: 604800 # 7 days
-#
-
-# Scheduler Jobs Configuration
-#
-scheduler:
- # Location of shipping the command stdout and stderr.
- output: stdout
- # A list of jobs to be scheduled.
- jobs:
- # The name of the job.
- write_content:
- # by default false meaning executing the the run value as a task. if true execute the run value as shell command
- shell: true
- # command to run
- run: "echo loco >> ./scheduler.txt"
- # The cron expression that defines the job's schedule.
- schedule: run every 1 second
- output: silent
- tags: ['base', 'infra']
-
- run_task:
- run: "foo"
- schedule: "at 10:00 am"
-
- list_if_users:
- run: "user_report"
- shell: true
- schedule: "* 2 * * * *"
- tags: ['base', 'users']
-#
\ No newline at end of file
diff --git a/examples/demo/config/production.yaml b/examples/demo/config/production.yaml
deleted file mode 100644
index e69de29bb..000000000
diff --git a/examples/demo/config/scheduler.yaml b/examples/demo/config/scheduler.yaml
deleted file mode 100644
index 48421987a..000000000
--- a/examples/demo/config/scheduler.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-output: stdout
-jobs:
- write_content:
- shell: true
- run: "echo loco >> ./scheduler.txt"
- schedule: run every 1 second
- output: silent
- tags: ['base', 'infra']
-
- run_task:
- run: "foo"
- schedule: "at 10:00 am"
-
- list_if_users:
- run: "user_report"
- shell: true
- schedule: "* 2 * * * *"
- tags: ['base', 'users']
\ No newline at end of file
diff --git a/examples/demo/config/test.yaml b/examples/demo/config/test.yaml
deleted file mode 100644
index 6d3121a51..000000000
--- a/examples/demo/config/test.yaml
+++ /dev/null
@@ -1,154 +0,0 @@
-# Loco configuration file documentation
-
-# Application logging configuration
-logger:
- # Enable or disable logging.
- enable: false
- # Log level, options: trace, debug, info, warn or error.
- level: error
- # Define the logging format. options: compact, pretty or json
- format: compact
- # By default the logger has filtering only logs that came from your code or logs that came from `loco` framework. to see all third party libraries
- # Uncomment the line below to override to see all third party libraries you can enable this config and override the logger filters.
- # override_filter: trace
-
-# Web server configuration
-server:
- # Port on which the server will listen. the server binding is 0.0.0.0:{PORT}
- port: 5150
- # The UI hostname or IP address that mailers will point to.
- host: http://localhost
- # Out of the box middleware configuration. to disable middleware you can changed the `enable` field to `false` of comment the middleware block
- middlewares:
- # Allows to limit the payload size request. payload that bigger than this file will blocked the request.
- limit_payload:
- # Enable/Disable the middleware.
- enable: true
- # the limit size. can be b,kb,kib,mb,mib,gb,gib
- body_limit: 5mb
- # Generating a unique request ID and enhancing logging with additional information such as the start and completion of request processing, latency, status code, and other request details.
- logger:
- # Enable/Disable the middleware.
- enable: true
- # when your code is panicked, the request still returns 500 status code.
- catch_panic:
- # Enable/Disable the middleware.
- enable: true
- # Timeout for incoming requests middleware. requests that take more time from the configuration will cute and 408 status code will returned.
- timeout_request:
- # Enable/Disable the middleware.
- enable: true
- # Duration time in milliseconds.
- timeout: 5000
- static_assets:
- enable: true
- must_exist: true
- precompressed: true
- folder:
- path: assets
- fallback: index.html
- compression:
- enable: true
- cors:
- enable: true
- # Set the value of the [`Access-Control-Allow-Origin`][mdn] header
- # allow_origins:
- # - https://loco.rs
- # Set the value of the [`Access-Control-Allow-Headers`][mdn] header
- # allow_headers:
- # - Content-Type
- # Set the value of the [`Access-Control-Allow-Methods`][mdn] header
- # allow_methods:
- # - POST
- # Set the value of the [`Access-Control-Max-Age`][mdn] header in seconds
- # max_age: 3600
-
-# Worker Configuration
-workers:
- # specifies the worker mode. Options:
- # - BackgroundQueue - Workers operate asynchronously in the background, processing queued.
- # - ForegroundBlocking - Workers operate in the foreground and block until tasks are completed.
- # - BackgroundAsync - Workers operate asynchronously in the background, processing tasks with async capabilities.
- mode: ForegroundBlocking
-
-# Mailer Configuration.
-mailer:
- # SMTP mailer configuration.
- smtp:
- # Enable/Disable smtp mailer.
- enable: true
- # SMTP server host. e.x localhost, smtp.gmail.com
- host: localhost
- # SMTP server port
- port: 1025
- # Use secure connection (SSL/TLS).
- secure: false
- # auth:
- # user:
- # password:
- stub: true
-
-# Initializers Configuration
-# initializers:
-# oauth2:
-# authorization_code: # Authorization code grant type
-# - client_identifier: google # Identifier for the OAuth2 provider. Replace 'google' with your provider's name if different, must be unique within the oauth2 config.
-# ... other fields
-
-# Database Configuration
-database:
- # Database connection URI
- uri: {{get_env(name="DATABASE_URL", default="postgres://loco:loco@localhost:5432/loco_app")}}
- # When enabled, the sql query will be logged.
- enable_logging: false
- # Set the timeout duration when acquiring a connection.
- connect_timeout: 500
- # Set the idle duration before closing a connection.
- idle_timeout: 500
- # Minimum number of connections for a pool.
- min_connections: 1
- # Maximum number of connections for a pool.
- max_connections: 1
- # Run migration up when application loaded
- auto_migrate: true
- # Truncate database when application loaded. This is a dangerous operation, make sure that you using this flag only on dev environments or test mode
- dangerously_truncate: true
- # Recreating schema when application loaded. This is a dangerous operation, make sure that you using this flag only on dev environments or test mode
- dangerously_recreate: true
-
-# Queue Configuration
-queue:
- kind: Redis
- # Redis connection URI
- uri: {{get_env(name="REDIS_URL", default="redis://127.0.0.1")}}
- # Dangerously flush all data in Redis on startup. dangerous operation, make sure that you using this flag only on dev environments or test mode
- dangerously_flush: false
-
-# Authentication Configuration
-auth:
- # JWT authentication
- jwt:
- # Secret key for token generation and verification
- secret: PqRwLF2rhHe8J22oBeHy
- # Token expiration time in seconds
- expiration: 604800 # 7 days
-
-scheduler:
- output: stdout
- jobs:
- write_content:
- shell: true
- run: "echo loco >> ./scheduler.txt"
- schedule: run every 1 second
- output: silent
- tags: ['base', 'infra']
-
- run_task:
- run: "foo"
- schedule: "at 10:00 am"
-
- list_if_users:
- run: "user_report"
- shell: true
- schedule: "* 2 * * * *"
- tags: ['base', 'users']
\ No newline at end of file
diff --git a/examples/demo/config/teste2e.yaml b/examples/demo/config/teste2e.yaml
deleted file mode 100644
index 68d3e40de..000000000
--- a/examples/demo/config/teste2e.yaml
+++ /dev/null
@@ -1,113 +0,0 @@
-# Loco configuration file documentation
-
-# Application logging configuration
-logger:
- # Enable or disable logging.
- enable: true
- # Log level, options: trace, debug, info, warn or error.
- level: debug
- # Define the logging format. options: compact, pretty or json
- format: compact
- # By default the logger has filtering only logs that came from your code or logs that came from `loco` framework. to see all third party libraries
- # Uncomment the line below to override to see all third party libraries you can enable this config and override the logger filters.
- # override_filter: trace
-
-# Web server configuration
-server:
- # Port on which the server will listen. the server binding is 0.0.0.0:{PORT}
- port: 5150
- # The UI hostname or IP address that mailers will point to.
- host: http://localhost
- # Out of the box middleware configuration. to disable middleware you can changed the `enable` field to `false` of comment the middleware block
- middlewares:
- # Allows to limit the payload size request. payload that bigger than this file will blocked the request.
- limit_payload:
- # Enable/Disable the middleware.
- enable: true
- # the limit size. can be b,kb,kib,mb,mib,gb,gib
- body_limit: 5mb
- # Generating a unique request ID and enhancing logging with additional information such as the start and completion of request processing, latency, status code, and other request details.
- logger:
- # Enable/Disable the middleware.
- enable: true
- # when your code is panicked, the request still returns 500 status code.
- catch_panic:
- # Enable/Disable the middleware.
- enable: true
- # Timeout for incoming requests middleware. requests that take more time from the configuration will cute and 408 status code will returned.
- timeout_request:
- # Enable/Disable the middleware.
- enable: true
- # Duration time in milliseconds.
- timeout: 5000
-
-# Worker Configuration
-workers:
- # specifies the worker mode. Options:
- # - BackgroundQueue - Workers operate asynchronously in the background, processing queued.
- # - ForegroundBlocking - Workers operate in the foreground and block until tasks are completed.
- # - BackgroundAsync - Workers operate asynchronously in the background, processing tasks with async capabilities.
- mode: ForegroundBlocking
-
-# Mailer Configuration.
-mailer:
- # SMTP mailer configuration.
- smtp:
- # Enable/Disable smtp mailer.
- enable: true
- # SMTP server host. e.x localhost, smtp.gmail.com
- host: localhost
- # SMTP server port
- port: 1025
- # Use secure connection (SSL/TLS).
- secure: false
- # auth:
- # user:
- # password:
- stub: true
-
-# Initializers Configuration
-# initializers:
-# oauth2:
-# authorization_code: # Authorization code grant type
-# - client_identifier: google # Identifier for the OAuth2 provider. Replace 'google' with your provider's name if different, must be unique within the oauth2 config.
-# ... other fields
-
-
-# Database Configuration
-database:
- # Database connection URI
- uri: {{get_env(name="DATABASE_URL", default="postgres://localhost:5432/loco_app")}}
- # When enabled, the sql query will be logged.
- enable_logging: false
- # Set the timeout duration when acquiring a connection.
- connect_timeout: 500
- # Set the idle duration before closing a connection.
- idle_timeout: 500
- # Minimum number of connections for a pool.
- min_connections: 1
- # Maximum number of connections for a pool.
- max_connections: 1
- # Run migration up when application loaded
- auto_migrate: true
- # Truncate database when application loaded. This is a dangerous operation, make sure that you using this flag only on dev environments or test mode
- dangerously_truncate: true
- # Recreating schema when application loaded. This is a dangerous operation, make sure that you using this flag only on dev environments or test mode
- dangerously_recreate: true
-
-# Queue Configuration
-queue:
- kind: Redis
- # Redis connection URI
- uri: {{get_env(name="APP_REDIS_URI", default="redis://127.0.0.1")}}
- # Dangerously flush all data in Redis on startup. dangerous operation, make sure that you using this flag only on dev environments or test mode
- dangerously_flush: false
-
-# Authentication Configuration
-auth:
- # JWT authentication
- jwt:
- # Secret key for token generation and verification
- secret: PqRwLF2rhHe8J22oBeHy
- # Token expiration time in seconds
- expiration: 604800 # 7 days
diff --git a/examples/demo/examples/playground.rs b/examples/demo/examples/playground.rs
deleted file mode 100644
index c9501a325..000000000
--- a/examples/demo/examples/playground.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use demo_app::app::App;
-#[allow(unused_imports)]
-use loco_rs::{cli::playground, prelude::*};
-
-#[tokio::main]
-async fn main() -> loco_rs::Result<()> {
- let _ctx = playground::().await?;
-
- // let active_model: articles::ActiveModel = ActiveModel {
- // title: Set(Some("how to build apps in 3 steps".to_string())),
- // content: Set(Some("use Loco: https://loco.rs".to_string())),
- // ..Default::default()
- // };
- // active_model.insert(&ctx.db).await.unwrap();
-
- // let res = articles::Entity::find().all(&ctx.db).await.unwrap();
- // println!("{:?}", res);
- println!("welcome to playground. edit me at `examples/playground.rs`");
-
- Ok(())
-}
diff --git a/examples/demo/examples/start.rs b/examples/demo/examples/start.rs
deleted file mode 100644
index 0f398b08c..000000000
--- a/examples/demo/examples/start.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use demo_app::app::App;
-use loco_rs::{
- boot::{create_app, start, ServeParams, StartMode},
- environment::{resolve_from_env, Environment},
-};
-use migration::Migrator;
-
-#[tokio::main]
-async fn main() -> loco_rs::Result<()> {
- let environment: Environment = resolve_from_env().into();
- let config = environment.load()?;
-
- let boot_result =
- create_app::(StartMode::ServerAndWorker, &environment, config).await?;
- let serve_params = ServeParams {
- port: boot_result.app_context.config.server.port,
- binding: boot_result.app_context.config.server.binding.to_string(),
- };
- start::(boot_result, serve_params, false).await?;
- Ok(())
-}
diff --git a/examples/demo/examples/task.rs b/examples/demo/examples/task.rs
deleted file mode 100644
index 3807cea5a..000000000
--- a/examples/demo/examples/task.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use std::env;
-
-use demo_app::app::App;
-use loco_rs::{
- boot::{create_context, run_task},
- environment::{resolve_from_env, Environment},
- task,
-};
-
-#[tokio::main]
-async fn main() -> loco_rs::Result<()> {
- let environment: Environment = resolve_from_env().into();
- let config = environment.load()?;
-
- let args = env::args().collect::>();
- let cmd = args.get(1);
- let app_context = create_context::(&environment, config).await?;
- run_task::(&app_context, cmd, &task::Vars::default()).await?;
-
- Ok(())
-}
diff --git a/examples/demo/examples/workers.rs b/examples/demo/examples/workers.rs
deleted file mode 100644
index b36f4c676..000000000
--- a/examples/demo/examples/workers.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use demo_app::app::App;
-use loco_rs::{
- boot::{create_app, start, ServeParams, StartMode},
- environment::{resolve_from_env, Environment},
-};
-use migration::Migrator;
-
-#[tokio::main]
-async fn main() -> loco_rs::Result<()> {
- let environment: Environment = resolve_from_env().into();
- let config = environment.load()?;
-
- let boot_result =
- create_app::(StartMode::WorkerOnly, &environment, config).await?;
- let serve_params = ServeParams {
- port: boot_result.app_context.config.server.port,
- binding: boot_result.app_context.config.server.binding.to_string(),
- };
- start::(boot_result, serve_params, false).await?;
- Ok(())
-}
diff --git a/examples/demo/migration/Cargo.toml b/examples/demo/migration/Cargo.toml
deleted file mode 100644
index fd31e14f5..000000000
--- a/examples/demo/migration/Cargo.toml
+++ /dev/null
@@ -1,22 +0,0 @@
-[package]
-name = "migration"
-version = "0.1.0"
-edition = "2021"
-publish = false
-
-[lib]
-name = "migration"
-path = "src/lib.rs"
-
-[dependencies]
-async-std = { version = "1", features = ["attributes", "tokio1"] }
-loco-rs = { path = "../../../", version = "*" }
-
-[dependencies.sea-orm-migration]
-version = "1.1.0"
-features = [
- # Enable at least one `ASYNC_RUNTIME` and `DATABASE_DRIVER` feature if you want to run migration via CLI.
- # View the list of supported features at https://www.sea-ql.org/SeaORM/docs/install-and-config/database-and-async-runtime.
- # e.g.
- "runtime-tokio-rustls", # `ASYNC_RUNTIME` feature
-]
diff --git a/examples/demo/migration/src/lib.rs b/examples/demo/migration/src/lib.rs
deleted file mode 100644
index f7072b696..000000000
--- a/examples/demo/migration/src/lib.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-pub use sea_orm_migration::prelude::*;
-
-mod m20220101_000001_users;
-mod m20231103_114510_notes;
-mod m20240416_071825_roles;
-mod m20240416_082115_users_roles;
-pub struct Migrator;
-
-#[async_trait::async_trait]
-impl MigratorTrait for Migrator {
- fn migrations() -> Vec> {
- vec![
- Box::new(m20220101_000001_users::Migration),
- Box::new(m20231103_114510_notes::Migration),
- Box::new(m20240416_071825_roles::Migration),
- Box::new(m20240416_082115_users_roles::Migration),
- // inject-above (do not remove this comment)
- ]
- }
-}
diff --git a/examples/demo/migration/src/m20220101_000001_users.rs b/examples/demo/migration/src/m20220101_000001_users.rs
deleted file mode 100644
index 888ca8434..000000000
--- a/examples/demo/migration/src/m20220101_000001_users.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-use sea_orm_migration::{prelude::*, schema::*};
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
- async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
- let table = table_auto(Users::Table)
- .col(pk_auto(Users::Id))
- .col(uuid(Users::Pid))
- .col(string_uniq(Users::Email))
- .col(string(Users::Password))
- .col(string(Users::ApiKey).unique_key())
- .col(string(Users::Name))
- .col(string_null(Users::ResetToken))
- .col(timestamp_null(Users::ResetSentAt))
- .col(string_null(Users::EmailVerificationToken))
- .col(timestamp_null(Users::EmailVerificationSentAt))
- .col(timestamp_null(Users::EmailVerifiedAt))
- .to_owned();
- manager.create_table(table).await?;
- Ok(())
- }
-
- async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
- manager
- .drop_table(Table::drop().table(Users::Table).to_owned())
- .await
- }
-}
-
-/// Learn more at https://docs.rs/sea-query#iden
-#[derive(Iden)]
-pub enum Users {
- Table,
- Id,
- Pid,
- Email,
- Name,
- Password,
- ApiKey,
- ResetToken,
- ResetSentAt,
- EmailVerificationToken,
- EmailVerificationSentAt,
- EmailVerifiedAt,
-}
diff --git a/examples/demo/migration/src/m20231103_114510_notes.rs b/examples/demo/migration/src/m20231103_114510_notes.rs
deleted file mode 100644
index d99a30875..000000000
--- a/examples/demo/migration/src/m20231103_114510_notes.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-use sea_orm_migration::{prelude::*, schema::*};
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
- async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
- manager
- .create_table(
- table_auto(Notes::Table)
- .col(pk_auto(Notes::Id))
- .col(string_null(Notes::Title))
- .col(string_null(Notes::Content))
- .to_owned(),
- )
- .await
- }
-
- async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
- manager
- .drop_table(Table::drop().table(Notes::Table).to_owned())
- .await
- }
-}
-
-#[derive(DeriveIden)]
-enum Notes {
- Table,
- Id,
- Title,
- Content,
-}
diff --git a/examples/demo/migration/src/m20240416_071825_roles.rs b/examples/demo/migration/src/m20240416_071825_roles.rs
deleted file mode 100644
index f1cd57e79..000000000
--- a/examples/demo/migration/src/m20240416_071825_roles.rs
+++ /dev/null
@@ -1,74 +0,0 @@
-use sea_orm_migration::{prelude::*, schema::*};
-
-use crate::{
- extension::postgres::Type,
- sea_orm::{DbBackend, DeriveActiveEnum, EnumIter, Schema},
-};
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
- async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
- // Create a new enum type `roles_name` with the values `Admin` and `User`
- let schema = Schema::new(DbBackend::Postgres);
- manager
- .create_type(schema.create_enum_from_active_enum::())
- .await?;
- // Create a new table `roles` with the columns `id`, `pid`, and `name`
- manager
- .create_table(
- table_auto(Roles::Table)
- .col(pk_auto(Roles::Id))
- .col(uuid_uniq(Roles::Pid))
- .col(
- ColumnDef::new(Roles::Name)
- .custom(Alias::new("roles_name")) // Use the enum type name
- .not_null()
- .to_owned(),
- )
- .to_owned(),
- )
- .await?;
-
- Ok(())
- }
-
- async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
- // Drop the table `roles`
- manager
- .drop_table(Table::drop().table(Roles::Table).to_owned())
- .await?;
-
- // Drop the enum type `roles_name`
- manager
- .drop_type(
- Type::drop()
- .if_exists()
- .name(RolesNameEnum)
- .restrict()
- .to_owned(),
- )
- .await?;
- Ok(())
- }
-}
-
-#[derive(DeriveIden)]
-enum Roles {
- Table,
- Id,
- Pid,
- Name,
-}
-
-// Create a new enum for the roles_name
-#[derive(EnumIter, DeriveActiveEnum)]
-#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "roles_name")]
-pub enum RolesName {
- #[sea_orm(string_value = "Admin")]
- Admin,
- #[sea_orm(string_value = "User")]
- User,
-}
diff --git a/examples/demo/migration/src/m20240416_082115_users_roles.rs b/examples/demo/migration/src/m20240416_082115_users_roles.rs
deleted file mode 100644
index 2592e2f8e..000000000
--- a/examples/demo/migration/src/m20240416_082115_users_roles.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-use sea_orm_migration::{prelude::*, schema::*};
-
-#[derive(DeriveMigrationName)]
-pub struct Migration;
-
-#[async_trait::async_trait]
-impl MigrationTrait for Migration {
- async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
- manager
- .create_table(
- table_auto(UsersRoles::Table)
- .primary_key(
- Index::create()
- .name("idx-users_roles-refs-pk")
- .table(UsersRoles::Table)
- .col(UsersRoles::UsersId)
- .col(UsersRoles::RolesId),
- )
- .col(integer(UsersRoles::UsersId))
- .col(integer(UsersRoles::RolesId))
- .foreign_key(
- ForeignKey::create()
- .name("fk-users_roles-users")
- .from(UsersRoles::Table, UsersRoles::UsersId)
- .to(Users::Table, Users::Id)
- .on_delete(ForeignKeyAction::Cascade)
- .on_update(ForeignKeyAction::Cascade),
- )
- .foreign_key(
- ForeignKey::create()
- .name("fk-users_roles-roles")
- .from(UsersRoles::Table, UsersRoles::RolesId)
- .to(Roles::Table, Roles::Id)
- .on_delete(ForeignKeyAction::Cascade)
- .on_update(ForeignKeyAction::Cascade),
- )
- .to_owned(),
- )
- .await
- }
-
- async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
- manager
- .drop_table(Table::drop().table(UsersRoles::Table).to_owned())
- .await
- }
-}
-
-#[derive(DeriveIden)]
-enum UsersRoles {
- Table,
- UsersId,
- RolesId,
-}
-
-#[derive(DeriveIden)]
-enum Users {
- Table,
- Id,
-}
-#[derive(DeriveIden)]
-enum Roles {
- Table,
- Id,
-}
diff --git a/examples/demo/src/app.rs b/examples/demo/src/app.rs
deleted file mode 100644
index 724c54d53..000000000
--- a/examples/demo/src/app.rs
+++ /dev/null
@@ -1,123 +0,0 @@
-use std::path::Path;
-
-use async_trait::async_trait;
-use loco_rs::{
- app::{AppContext, Hooks, Initializer},
- boot::{create_app, BootResult, StartMode},
- cache,
- config::Config,
- controller::AppRoutes,
- db::{self, truncate_table},
- environment::Environment,
- prelude::*,
- storage::{self, Storage},
- task::Tasks,
- Result,
-};
-use migration::Migrator;
-
-use crate::{
- controllers::{self, middlewares},
- initializers,
- models::_entities::{notes, roles, users, users_roles},
- tasks,
- workers::downloader::DownloadWorker,
-};
-
-pub struct App;
-#[async_trait]
-impl Hooks for App {
- fn app_version() -> String {
- format!(
- "{} ({})",
- env!("CARGO_PKG_VERSION"),
- option_env!("BUILD_SHA")
- .or(option_env!("GITHUB_SHA"))
- .unwrap_or("dev")
- )
- }
-
- fn app_name() -> &'static str {
- env!("CARGO_CRATE_NAME")
- }
-
- //
- async fn initializers(_ctx: &AppContext) -> Result>> {
- let initializers: Vec> = vec![
- Box::new(initializers::axum_session::AxumSessionInitializer),
- Box::new(initializers::view_engine::ViewEngineInitializer),
- Box::new(initializers::hello_view_engine::HelloViewEngineInitializer),
- ];
-
- Ok(initializers)
- }
- //
-
- fn routes(ctx: &AppContext) -> AppRoutes {
- AppRoutes::with_default_routes()
- .add_route(
- controllers::mylayer::routes(ctx.clone())
- .layer(middlewares::routes::role::RoleRouteLayer::new(ctx.clone())),
- )
- .add_route(controllers::notes::routes())
- .add_route(controllers::auth::routes())
- .add_route(controllers::mysession::routes())
- .add_route(controllers::view_engine::routes())
- .add_route(controllers::user::routes())
- .add_route(controllers::upload::routes())
- .add_route(controllers::responses::routes())
- .add_route(controllers::cache::routes())
- }
-
- async fn boot(
- mode: StartMode,
- environment: &Environment,
- config: Config,
- ) -> Result {
- create_app::(mode, environment, config).await
- }
-
- async fn after_context(ctx: AppContext) -> Result {
- let store = if ctx.environment == Environment::Test {
- storage::drivers::mem::new()
- } else {
- storage::drivers::local::new_with_prefix("storage-uploads").map_err(Box::from)?
- };
-
- Ok(AppContext {
- storage: Storage::single(store).into(),
- cache: cache::Cache::new(cache::drivers::inmem::new()).into(),
- ..ctx
- })
-
- // Ok(ctx)
- }
-
- async fn connect_workers(ctx: &AppContext, queue: &Queue) -> Result<()> {
- queue.register(DownloadWorker::build(ctx)).await?;
- Ok(())
- }
-
- fn register_tasks(tasks: &mut Tasks) {
- tasks.register(tasks::user_report::UserReport);
- tasks.register(tasks::seed::SeedData);
- tasks.register(tasks::foo::Foo);
- // tasks-inject (do not remove)
- }
-
- async fn truncate(ctx: &AppContext) -> Result<()> {
- truncate_table(&ctx.db, users_roles::Entity).await?;
- truncate_table(&ctx.db, roles::Entity).await?;
- truncate_table(&ctx.db, users::Entity).await?;
- truncate_table(&ctx.db, notes::Entity).await?;
- Ok(())
- }
-
- async fn seed(ctx: &AppContext, base: &Path) -> Result<()> {
- db::seed::(&ctx.db, &base.join("users.yaml").display().to_string())
- .await?;
- db::seed::(&ctx.db, &base.join("notes.yaml").display().to_string())
- .await?;
- Ok(())
- }
-}
diff --git a/examples/demo/src/bin/main.rs b/examples/demo/src/bin/main.rs
deleted file mode 100644
index 8eb2a0306..000000000
--- a/examples/demo/src/bin/main.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-use demo_app::app::App;
-use loco_rs::cli;
-use migration::Migrator;
-
-#[tokio::main]
-async fn main() -> loco_rs::Result<()> {
- cli::main::().await
-}
diff --git a/examples/demo/src/bin/tool.rs b/examples/demo/src/bin/tool.rs
deleted file mode 100644
index 8eb2a0306..000000000
--- a/examples/demo/src/bin/tool.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-use demo_app::app::App;
-use loco_rs::cli;
-use migration::Migrator;
-
-#[tokio::main]
-async fn main() -> loco_rs::Result<()> {
- cli::main::().await
-}
diff --git a/examples/demo/src/common/mod.rs b/examples/demo/src/common/mod.rs
deleted file mode 100644
index 6e98cefd0..000000000
--- a/examples/demo/src/common/mod.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod settings;
diff --git a/examples/demo/src/common/settings.rs b/examples/demo/src/common/settings.rs
deleted file mode 100644
index 79db3d10b..000000000
--- a/examples/demo/src/common/settings.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use loco_rs::Result;
-use serde::{Deserialize, Serialize};
-
-#[derive(Serialize, Deserialize, Default, Debug)]
-pub struct Settings {
- pub allow_list: Option>,
-}
-
-impl Settings {
- /// Deserialize a strongly typed settings
- ///
- /// # Errors
- ///
- /// This function will return an error if deserialization fails
- pub fn from_json(value: &serde_json::Value) -> Result {
- Ok(serde_json::from_value(value.clone())?)
- }
-}
diff --git a/examples/demo/src/controllers/auth.rs b/examples/demo/src/controllers/auth.rs
deleted file mode 100644
index d6de58b40..000000000
--- a/examples/demo/src/controllers/auth.rs
+++ /dev/null
@@ -1,147 +0,0 @@
-use loco_rs::{controller::bad_request, prelude::*};
-use serde::{Deserialize, Serialize};
-
-use crate::{
- mailers::auth::AuthMailer,
- models::{
- _entities::users,
- users::{LoginParams, RegisterParams},
- },
- views::auth::UserSession,
-};
-#[derive(Debug, Deserialize, Serialize)]
-pub struct VerifyParams {
- pub token: String,
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct ForgotParams {
- pub email: String,
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct ResetParams {
- pub token: String,
- pub password: String,
-}
-
-/// Register function creates a new user with the given parameters and sends a
-/// welcome email to the user
-async fn register(
- State(ctx): State,
- Json(params): Json,
-) -> Result {
- let res = users::Model::create_with_password(&ctx.db, ¶ms).await;
-
- let user = match res {
- Ok(user) => user,
- Err(err) => {
- let msg = "could not register user";
-
- tracing::info!(message = err.to_string(), user_email = ¶ms.email, msg,);
- return bad_request(msg);
- }
- };
-
- let user = user
- .into_active_model()
- .set_email_verification_sent(&ctx.db)
- .await?;
-
- AuthMailer::send_welcome(&ctx, &user).await?;
-
- let jwt_secret = ctx.config.get_jwt_config()?;
-
- let token = user
- .generate_jwt(&jwt_secret.secret, jwt_secret.expiration)
- .or_else(|_| unauthorized("unauthorized!"))?;
- format::json(UserSession::new(&user, &token))
-}
-
-/// Verify register user. if the user not verified his email, he can't login to
-/// the system.
-async fn verify(
- State(ctx): State,
- Json(params): Json,
-) -> Result {
- let user = users::Model::find_by_verification_token(&ctx.db, ¶ms.token).await?;
-
- if user.email_verified_at.is_some() {
- tracing::info!(pid = user.pid.to_string(), "user already verified");
- } else {
- let active_model = user.into_active_model();
- let user = active_model.verified(&ctx.db).await?;
- tracing::info!(pid = user.pid.to_string(), "user verified");
- }
-
- format::empty_json()
-}
-
-/// In case the user forgot his password this endpoints generate a forgot token
-/// and send email to the user. In case the email not found in our DB, we are
-/// returning a valid request for for security reasons (not exposing users DB
-/// list).
-async fn forgot(
- State(ctx): State,
- Json(params): Json,
-) -> Result {
- let Ok(user) = users::Model::find_by_email(&ctx.db, ¶ms.email).await else {
- // we don't want to expose our users email. if the email is invalid we still
- // returning success to the caller
- return format::empty_json();
- };
-
- let user = user
- .into_active_model()
- .set_forgot_password_sent(&ctx.db)
- .await?;
-
- AuthMailer::forgot_password(&ctx, &user).await?;
-
- format::empty_json()
-}
-
-/// reset user password by the given parameters
-async fn reset(State(ctx): State, Json(params): Json) -> Result {
- let Ok(user) = users::Model::find_by_reset_token(&ctx.db, ¶ms.token).await else {
- // we don't want to expose our users email. if the email is invalid we still
- // returning success to the caller
- tracing::info!("reset token not found");
-
- return format::empty_json();
- };
- user.into_active_model()
- .reset_password(&ctx.db, ¶ms.password)
- .await?;
-
- format::empty_json()
-}
-
-/// Creates a user login and returns a token
-async fn login(State(ctx): State, Json(params): Json) -> Result {
- let user = users::Model::find_by_email(&ctx.db, ¶ms.email).await?;
-
- let valid = user.verify_password(¶ms.password);
-
- if !valid {
- return unauthorized("unauthorized!");
- }
-
- let jwt_secret = ctx.config.get_jwt_config()?;
-
- let token = user
- .generate_jwt(&jwt_secret.secret, jwt_secret.expiration)
- .or_else(|_| unauthorized("unauthorized!"))?;
-
- format::json(UserSession::new(&user, &token))
-}
-
-pub fn routes() -> Routes {
- Routes::new()
- .prefix("auth")
- .add("/register", post(register))
- .add("/verify", post(verify))
- .add("/login", post(login))
- .add("/forgot", post(forgot))
- .add("/reset", post(reset))
-}
diff --git a/examples/demo/src/controllers/cache.rs b/examples/demo/src/controllers/cache.rs
deleted file mode 100644
index 90c95fe4c..000000000
--- a/examples/demo/src/controllers/cache.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-use loco_rs::prelude::*;
-use serde::{Deserialize, Serialize};
-
-use crate::models::users;
-#[derive(Serialize, Deserialize)]
-pub struct CacheResponse {
- value: Option,
-}
-
-async fn get_cache(State(ctx): State) -> Result {
- format::json(CacheResponse {
- value: ctx.cache.get("value").await.unwrap(),
- })
-}
-
-async fn insert(State(ctx): State) -> Result {
- ctx.cache.insert("value", "loco cache value").await.unwrap();
- format::empty()
-}
-
-async fn get_or_insert(State(ctx): State) -> Result {
- let res = ctx
- .cache
- .get_or_insert("user", async {
- let user = users::Model::find_by_email(&ctx.db, "user1@example.com").await?;
- Ok(user.name)
- })
- .await;
-
- match res {
- Ok(username) => format::text(&username),
- Err(_e) => format::text("not found"),
- }
-}
-
-pub fn routes() -> Routes {
- Routes::new()
- .prefix("cache")
- .add("/", get(get_cache))
- .add("/insert", post(insert))
- .add("/get_or_insert", get(get_or_insert))
-}
diff --git a/examples/demo/src/controllers/middlewares/handlers/admin.rs b/examples/demo/src/controllers/middlewares/handlers/admin.rs
deleted file mode 100644
index 489ff1272..000000000
--- a/examples/demo/src/controllers/middlewares/handlers/admin.rs
+++ /dev/null
@@ -1,109 +0,0 @@
-use std::{
- convert::Infallible,
- task::{Context, Poll},
-};
-
-use axum::{
- body::Body,
- extract::{FromRequestParts, Request},
- response::Response,
-};
-use futures_util::future::BoxFuture;
-use loco_rs::prelude::{auth::JWTWithUser, *};
-use tower::{Layer, Service};
-
-use crate::models::{roles, sea_orm_active_enums::RolesName, users};
-
-#[derive(Clone)]
-pub struct AdminHandlerLayer {
- state: AppContext,
-}
-
-impl AdminHandlerLayer {
- pub fn new(state: AppContext) -> Self {
- Self { state }
- }
-}
-
-impl Layer for AdminHandlerLayer {
- type Service = AdminHandlerService;
-
- fn layer(&self, inner: S) -> Self::Service {
- Self::Service {
- inner,
- state: self.state.clone(),
- }
- }
-}
-
-#[derive(Clone)]
-pub struct AdminHandlerService {
- inner: S,
- state: AppContext,
-}
-
-impl Service> for AdminHandlerService
-where
- S: Service, Response = Response, Error = Infallible> + Clone + Send + 'static, /* Inner Service must return Response and never error */
- S::Future: Send + 'static,
- B: Send + 'static,
-{
- // Response type is the same as the inner service / handler
- type Response = S::Response;
- // Error type is the same as the inner service / handler
- type Error = S::Error;
- // Future type is the same as the inner service / handler
- type Future = BoxFuture<'static, Result>;
- fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> {
- self.inner.poll_ready(cx)
- }
-
- fn call(&mut self, req: Request) -> Self::Future {
- let state = self.state.clone();
- let clone = self.inner.clone();
- // take the service that was ready
- let mut inner = std::mem::replace(&mut self.inner, clone);
- Box::pin(async move {
- // Example of extracting JWT and checking roles
- let (mut parts, body) = req.into_parts();
- let auth = JWTWithUser::::from_request_parts(&mut parts, &state).await;
-
- match auth {
- Ok(auth) => {
- // Check user roles here
- // If the user has the required role, proceed with the inner service
- let _admin = match roles::Model::find_by_user(&state.db, &auth.user).await {
- Ok(role) => match role.name {
- RolesName::User => {
- return Ok(Response::builder()
- .status(401)
- .body(Body::empty())
- .unwrap()
- .into_response())
- }
- RolesName::Admin => role,
- },
- Err(_) => {
- return Ok(Response::builder()
- .status(401)
- .body(Body::empty())
- .unwrap()
- .into_response())
- }
- };
-
- let req = Request::from_parts(parts, body);
- inner.call(req).await
- }
- Err(_) => {
- // Handle error, e.g., return an unauthorized response
- Ok(Response::builder()
- .status(401)
- .body(Body::empty())
- .unwrap()
- .into_response())
- }
- }
- })
- }
-}
diff --git a/examples/demo/src/controllers/middlewares/handlers/mod.rs b/examples/demo/src/controllers/middlewares/handlers/mod.rs
deleted file mode 100644
index 1e6bf65ba..000000000
--- a/examples/demo/src/controllers/middlewares/handlers/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub mod admin;
-pub mod user;
diff --git a/examples/demo/src/controllers/middlewares/handlers/user.rs b/examples/demo/src/controllers/middlewares/handlers/user.rs
deleted file mode 100644
index e6c458c84..000000000
--- a/examples/demo/src/controllers/middlewares/handlers/user.rs
+++ /dev/null
@@ -1,112 +0,0 @@
-use std::{
- convert::Infallible,
- task::{Context, Poll},
-};
-
-use axum::{
- body::Body,
- extract::{FromRequestParts, Request},
- response::Response,
-};
-use futures_util::future::BoxFuture;
-use loco_rs::prelude::{auth::JWTWithUser, *};
-use tower::{Layer, Service};
-
-use crate::models::{roles, sea_orm_active_enums::RolesName, users};
-
-#[derive(Clone)]
-pub struct UserHandlerLayer {
- state: AppContext,
-}
-
-impl UserHandlerLayer {
- pub fn new(state: AppContext) -> Self {
- Self { state }
- }
-}
-
-impl Layer for UserHandlerLayer {
- type Service = UserHandlerService;
-
- fn layer(&self, inner: S) -> Self::Service {
- Self::Service {
- inner,
- state: self.state.clone(),
- }
- }
-}
-
-#[derive(Clone)]
-pub struct UserHandlerService {
- inner: S,
- state: AppContext,
-}
-
-/// Service that checks if the user has the required user role before calling
-/// the inner service If the user has the required role, the inner service is
-/// called Otherwise, an unauthorized response is returned
-impl Service> for UserHandlerService
-where
- S: Service, Response = Response, Error = Infallible> + Clone + Send + 'static,
- S::Future: Send + 'static,
- B: Send + 'static,
-{
- // Response type is the same as the inner service / handler
- type Response = S::Response;
- // Error type is the same as the inner service / handler
- type Error = S::Error;
- // Future type is the same as the inner service / handler
- type Future = BoxFuture<'static, Result>;
- fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> {
- self.inner.poll_ready(cx)
- }
-
- fn call(&mut self, req: Request) -> Self::Future {
- let state = self.state.clone();
- let clone = self.inner.clone();
- // take the service that was ready
- let mut inner = std::mem::replace(&mut self.inner, clone);
- Box::pin(async move {
- // Example of extracting JWT and checking roles
- let (mut parts, body) = req.into_parts();
- let auth = JWTWithUser::::from_request_parts(&mut parts, &state).await;
-
- match auth {
- Ok(auth) => {
- // Check user roles here
- // If the user has the required role, proceed with the inner service
- let _user = match roles::Model::find_by_user(&state.db, &auth.user).await {
- Ok(role) => match role.name {
- RolesName::Admin => {
- return Ok(Response::builder()
- .status(401)
- .body(Body::empty())
- .unwrap()
- .into_response())
- }
- RolesName::User => role,
- },
- Err(_) => {
- return Ok(Response::builder()
- .status(401)
- .body(Body::empty())
- .unwrap()
- .into_response())
- }
- };
-
- let req = Request::from_parts(parts, body);
- inner.call(req).await
- }
- Err(_) => {
- // Return an unauthorized response
- Ok(Response::builder()
- .status(401)
- .body(Body::empty())
- .unwrap()
- .into_response())
- }
- }
- })
- }
-}
diff --git a/examples/demo/src/controllers/middlewares/mod.rs b/examples/demo/src/controllers/middlewares/mod.rs
deleted file mode 100644
index c0c696a55..000000000
--- a/examples/demo/src/controllers/middlewares/mod.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub mod handlers;
-pub mod routes;
diff --git a/examples/demo/src/controllers/middlewares/routes/mod.rs b/examples/demo/src/controllers/middlewares/routes/mod.rs
deleted file mode 100644
index f5b70d333..000000000
--- a/examples/demo/src/controllers/middlewares/routes/mod.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod role;
diff --git a/examples/demo/src/controllers/middlewares/routes/role.rs b/examples/demo/src/controllers/middlewares/routes/role.rs
deleted file mode 100644
index 8403547aa..000000000
--- a/examples/demo/src/controllers/middlewares/routes/role.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-use std::{
- convert::Infallible,
- task::{Context, Poll},
-};
-
-use axum::{
- body::Body,
- extract::{FromRequestParts, Request},
- response::Response,
-};
-use futures_util::future::BoxFuture;
-use loco_rs::prelude::{auth::JWTWithUser, *};
-use tower::{Layer, Service};
-
-use crate::models::{roles, users};
-
-#[derive(Clone)]
-pub struct RoleRouteLayer {
- state: AppContext,
-}
-
-impl RoleRouteLayer {
- pub fn new(state: AppContext) -> Self {
- Self { state }
- }
-}
-
-impl Layer for RoleRouteLayer {
- type Service = RoleRouteService;
-
- fn layer(&self, inner: S) -> Self::Service {
- Self::Service {
- inner,
- state: self.state.clone(),
- }
- }
-}
-#[derive(Clone)]
-pub struct RoleRouteService {
- inner: S,
- state: AppContext,
-}
-
-impl Service> for RoleRouteService
-where
- S: Service, Response = Response, Error = Infallible> + Clone + Send + 'static, /* Inner Service must return Response and never error */
- S::Future: Send + 'static,
- B: Send + 'static,
-{
- // Response type is the same as the inner service / handler
- type Response = S::Response;
- // Error type is the same as the inner service / handler
- type Error = S::Error;
- // Future type is the same as the inner service / handler
- type Future = BoxFuture<'static, Result>;
-
- fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> {
- self.inner.poll_ready(cx)
- }
-
- fn call(&mut self, req: Request) -> Self::Future {
- let state = self.state.clone();
- let clone = self.inner.clone();
- // take the service that was ready
- let mut inner = std::mem::replace(&mut self.inner, clone);
- Box::pin(async move {
- // Example of extracting JWT and checking roles
- let (mut parts, body) = req.into_parts();
- let auth = JWTWithUser::::from_request_parts(&mut parts, &state).await;
-
- match auth {
- Ok(auth) => {
- // Check user roles here
- // If the user has the required role, proceed with the inner service
- let _role = match roles::Model::find_by_user(&state.db, &auth.user).await {
- Ok(role) => role,
- Err(_) => {
- return Ok(Response::builder()
- .status(401)
- .body(Body::empty())
- .unwrap()
- .into_response())
- }
- };
- let req = Request::from_parts(parts, body);
- inner.call(req).await
- }
- Err(_) => {
- // Handle error, e.g., return an unauthorized response
- Ok(Response::builder()
- .status(401)
- .body(Body::empty())
- .unwrap()
- .into_response())
- }
- }
- })
- }
-}
diff --git a/examples/demo/src/controllers/mod.rs b/examples/demo/src/controllers/mod.rs
deleted file mode 100644
index 40e04b872..000000000
--- a/examples/demo/src/controllers/mod.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-pub mod auth;
-pub mod cache;
-pub mod middlewares;
-pub mod mylayer;
-pub mod mysession;
-pub mod notes;
-pub mod responses;
-pub mod upload;
-pub mod user;
-pub mod view_engine;
diff --git a/examples/demo/src/controllers/mylayer.rs b/examples/demo/src/controllers/mylayer.rs
deleted file mode 100644
index b9428a4dc..000000000
--- a/examples/demo/src/controllers/mylayer.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-#![allow(clippy::unused_async)]
-use loco_rs::prelude::*;
-
-use crate::controllers::middlewares::handlers;
-
-async fn user() -> Result {
- format::json("Hello, user!")
-}
-
-async fn admin() -> Result {
- format::json("Hello, admin!")
-}
-
-async fn echo() -> Result {
- format::json("Hello, World!")
-}
-
-pub fn routes(ctx: AppContext) -> Routes {
- Routes::new()
- .prefix("mylayer")
- // Only users with the RoleName::Admin can access this route
- .add(
- "/admin",
- get(admin).layer(handlers::admin::AdminHandlerLayer::new(ctx.clone())),
- )
- // Only users with the RoleName::User can access this route
- .add(
- "/user",
- get(user).layer(handlers::user::UserHandlerLayer::new(ctx.clone())),
- )
- .add("/echo", get(echo))
-}
diff --git a/examples/demo/src/controllers/mysession.rs b/examples/demo/src/controllers/mysession.rs
deleted file mode 100644
index f8b948ebb..000000000
--- a/examples/demo/src/controllers/mysession.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-#![allow(clippy::unused_async)]
-use axum_session::{Session, SessionNullPool};
-use loco_rs::prelude::*;
-
-/// Get a session
-///
-/// # Errors
-///
-/// This function will return an error if result fails
-pub async fn get_session(_session: Session) -> Result {
- format::empty()
-}
-
-pub fn routes() -> Routes {
- Routes::new().prefix("mysession").add("/", get(get_session))
-}
diff --git a/examples/demo/src/controllers/notes.rs b/examples/demo/src/controllers/notes.rs
deleted file mode 100644
index 9dd9e6977..000000000
--- a/examples/demo/src/controllers/notes.rs
+++ /dev/null
@@ -1,161 +0,0 @@
-#![allow(clippy::missing_errors_doc)]
-#![allow(clippy::unnecessary_struct_initialization)]
-#![allow(clippy::unused_async)]
-use axum::extract::Query;
-use loco_rs::{controller::bad_request, model::ModelError, prelude::*};
-use sea_orm::Condition;
-use serde::{Deserialize, Serialize};
-
-use crate::{
- models::_entities::notes::{ActiveModel, Column, Entity, Model},
- views::notes::PaginationResponse,
-};
-
-#[derive(Clone, Debug, Serialize, Deserialize)]
-pub struct Params {
- pub title: Option,
- pub content: Option,
-}
-
-#[derive(Debug, Deserialize)]
-pub struct ListQueryParams {
- pub title: Option,
- pub content: Option,
- #[serde(flatten)]
- pub pagination: query::PaginationQuery,
-}
-
-impl Params {
- fn update(&self, item: &mut ActiveModel) {
- item.title = Set(self.title.clone());
- item.content = Set(self.content.clone());
- }
-}
-
-async fn load_item(ctx: &AppContext, id: i32) -> Result {
- let item = Entity::find_by_id(id).one(&ctx.db).await?;
- item.ok_or_else(|| Error::NotFound)
-}
-
-pub async fn list(
- State(ctx): State,
- Query(params): Query,
-) -> Result {
- let pagination_query = query::PaginationQuery {
- page_size: params.pagination.page_size,
- page: params.pagination.page,
- };
-
- let paginated_notes = query::paginate(
- &ctx.db,
- Entity::find(),
- Some(query::with(params.into_query()).build()),
- &pagination_query,
- )
- .await?;
-
- /*
- if let Some(settings) = &ctx.config.settings {
- let settings = common::settings::Settings::from_json(settings)?;
- println!("allow list: {:?}", settings.allow_list);
- }*/
-
- format::render()
- .cookies(&[
- cookie::Cookie::new("foo", "bar"),
- cookie::Cookie::new("baz", "qux"),
- ])?
- .etag("foobar")?
- .json(PaginationResponse::response(
- paginated_notes,
- &pagination_query,
- ))
-}
-
-pub async fn add(State(ctx): State, Json(params): Json) -> Result {
- let mut item = ActiveModel {
- ..Default::default()
- };
- params.update(&mut item);
- let item = item.insert(&ctx.db).await?;
- format::json(item)
-}
-
-pub async fn update(
- Path(id): Path,
- State(ctx): State,
- Json(params): Json,
-) -> Result {
- let item = load_item(&ctx, id).await?;
- let mut item = item.into_active_model();
- params.update(&mut item);
- let item = item.update(&ctx.db).await?;
- format::json(item)
-}
-
-pub async fn remove(Path(id): Path, State(ctx): State) -> Result {
- load_item(&ctx, id).await?.delete(&ctx.db).await?;
- format::empty()
-}
-
-pub async fn get_one(
- Format(respond_to): Format,
- Path(id): Path,
- State(ctx): State,
-) -> Result {
- // having `load_item` is useful because inside the function you can call and use
- // '?' to bubble up errors, then, in here, we centralize handling of errors.
- // if you want to freely use code statements with no wrapping function, you can
- // use the experimental `try` feature in Rust where you can do:
- // ```
- // let res = try {
- // ...
- // ...
- // }
- //
- // match res { ..}
- // ```
- let res = load_item(&ctx, id).await;
-
- match res {
- // we're good, let's render the item based on content type
- Ok(item) => match respond_to {
- RespondTo::Html => format::html(&format!("{:?}", item.title)),
- _ => format::json(item),
- },
- // we have an opinion how to render out validation errors, only in HTML content
- Err(Error::Model(ModelError::ModelValidation { errors })) => match respond_to {
- RespondTo::Html => {
- format::html(&format!("errors: {errors:?}"))
- }
- _ => bad_request("opaque message: cannot respond!"),
- },
- // we have no clue what this is, let the framework render default errors
- Err(err) => Err(err),
- }
-}
-
-impl ListQueryParams {
- #[must_use]
- pub fn into_query(&self) -> Condition {
- let mut condition = query::condition();
-
- if let Some(title) = &self.title {
- condition = condition.like(Column::Title, title);
- }
- if let Some(content) = &self.content {
- condition = condition.like(Column::Content, content);
- }
- condition.build()
- }
-}
-
-pub fn routes() -> Routes {
- Routes::new()
- .prefix("notes")
- .add("/", get(list))
- .add("/", post(add))
- .add("/{id}", get(get_one))
- .add("/{id}", delete(remove))
- .add("/{id}", post(update))
-}
diff --git a/examples/demo/src/controllers/responses.rs b/examples/demo/src/controllers/responses.rs
deleted file mode 100644
index 43e0ea505..000000000
--- a/examples/demo/src/controllers/responses.rs
+++ /dev/null
@@ -1,147 +0,0 @@
-#![allow(clippy::unused_async)]
-use axum_extra::extract::cookie::Cookie;
-use loco_rs::prelude::*;
-use serde::Serialize;
-use utoipa::{openapi, OpenApi, ToSchema};
-
-#[derive(Serialize)]
-pub struct Health {
- pub ok: bool,
-}
-
-/// return an empty response
-///
-/// # Errors
-///
-/// This function will return an error if result fails
-pub async fn empty() -> Result {
- format::empty()
-}
-
-/// return an text response
-///
-/// # Errors
-///
-/// This function will return an error if result fails
-pub async fn text() -> Result {
- format::text("Loco")
-}
-
-/// return an JSON response
-///
-/// # Errors
-///
-/// This function will return an error if result fails
-pub async fn json() -> Result {
- format::json(Health { ok: true })
-}
-
-/// return an empty JSON response
-///
-/// # Errors
-///
-/// This function will return an error if result fails
-pub async fn empty_json() -> Result {
- format::empty_json()
-}
-
-/// return an HTML response
-///
-/// # Errors
-///
-/// This function will return an error if result fails
-pub async fn html() -> Result {
- format::html("hello, world")
-}
-
-/// return an redirect response
-///
-/// # Errors
-///
-/// This function will return an error if result fails
-pub async fn redirect() -> Result {
- format::redirect("/dashboard")
-}
-
-/// return an custom status code response
-///
-/// # Errors
-///
-/// This function will return an error if result fails
-pub async fn render_with_status_code() -> Result {
- format::render().status(201).empty()
-}
-
-/// return response with ETag header
-///
-/// # Errors
-///
-/// This function will return an error if result fails
-pub async fn etag() -> Result {
- format::render().etag("loco-etag")?.empty()
-}
-
-/// return response with cookie
-///
-/// # Errors
-///
-/// This function will return an error if result fails
-pub async fn set_cookie() -> Result {
- let cookie = Cookie::build(("loco-cookie-name", "loco-cookie-value"))
- // .domain("localhost:5173")
- .path("/")
- .same_site(cookie::SameSite::Strict)
- .secure(true)
- .http_only(true)
- .build();
-
- format::render().cookies(&[cookie])?.json(())
-}
-
-#[derive(Serialize, Debug, ToSchema)]
-pub struct Album {
- title: String,
- rating: u32,
-}
-
-//
-// OpenAPI spec with `utoipa`
-//
-#[derive(OpenApi)]
-#[openapi(paths(album))]
-struct Spec;
-
-/// Return an OpenAPI-spec'd response
-///
-/// # Errors
-///
-/// This function will return an error if it fails
-#[utoipa::path(
- get,
- path = "/response/album",
- responses(
- (status = 200, description = "Album found", body = Album),
- ),
-)]
-pub async fn album() -> Result {
- println!("{}", Spec::openapi().to_pretty_json().unwrap());
-
- format::json(Album {
- title: "VH II".to_string(),
- rating: 10,
- })
-}
-pub fn routes() -> Routes {
- Routes::new()
- .prefix("response")
- .add("/empty", get(empty))
- .add("/text", get(text))
- .add("/json", get(json))
- .add("/empty_json", get(empty_json))
- .add("/html", get(html))
- .add("/redirect", get(redirect))
- .add("/render_with_status_code", get(render_with_status_code))
- .add("/etag", get(etag))
- .add("/album", get(album))
- .add("/set_cookie", get(set_cookie))
-}
diff --git a/examples/demo/src/controllers/upload.rs b/examples/demo/src/controllers/upload.rs
deleted file mode 100644
index b04d0cd89..000000000
--- a/examples/demo/src/controllers/upload.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-use std::path::PathBuf;
-
-use axum::extract::Multipart;
-use loco_rs::prelude::*;
-
-use crate::views;
-
-/// File upload example
-///
-/// ## Request Example
-///
-/// curl -H "Content-Type: multipart/form-data" -F "file=@./test-2.json"
-/// 127.0.0.1:5150/upload/file
-async fn upload_file(State(ctx): State, mut multipart: Multipart) -> Result {
- let mut file = None;
- while let Some(field) = multipart.next_field().await.map_err(|err| {
- tracing::error!(error = ?err,"could not readd multipart");
- Error::BadRequest("could not readd multipart".into())
- })? {
- let file_name = match field.file_name() {
- Some(file_name) => file_name.to_string(),
- _ => return Err(Error::BadRequest("file name not found".into())),
- };
-
- let content = field.bytes().await.map_err(|err| {
- tracing::error!(error = ?err,"could not readd bytes");
- Error::BadRequest("could not readd bytes".into())
- })?;
-
- let path = PathBuf::from("folder").join(file_name);
- ctx.storage
- .as_ref()
- .upload(path.as_path(), &content)
- .await?;
-
- file = Some(path);
- }
-
- file.map_or_else(not_found, |path| {
- format::json(views::upload::Response::new(path.as_path()))
- })
-}
-
-pub fn routes() -> Routes {
- Routes::new()
- .prefix("upload")
- .add("/file", post(upload_file))
-}
diff --git a/examples/demo/src/controllers/user.rs b/examples/demo/src/controllers/user.rs
deleted file mode 100644
index 48365d48c..000000000
--- a/examples/demo/src/controllers/user.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-use loco_rs::prelude::*;
-
-use crate::{
- models::{_entities::users, roles},
- views::user::{CurrentResponse, UserResponse},
-};
-
-async fn current(
- auth: auth::JWTWithUser,
- State(_ctx): State,
-) -> Result {
- format::json(CurrentResponse::new(&auth.user))
-}
-
-async fn current_by_api_key(
- auth: auth::ApiToken,
- State(_ctx): State,
-) -> Result {
- format::json(CurrentResponse::new(&auth.user))
-}
-
-async fn convert_to_admin(
- auth: auth::JWTWithUser,
- State(ctx): State,
-) -> Result {
- let roles = roles::Model::add_user_to_admin_role(&ctx.db, &auth.user).await?;
- format::json(UserResponse::new(&auth.user, &roles))
-}
-
-async fn convert_to_user(
- auth: auth::JWTWithUser,
- State(ctx): State,
-) -> Result {
- let roles = roles::Model::add_user_to_user_role(&ctx.db, &auth.user).await?;
- format::json(UserResponse::new(&auth.user, &roles))
-}
-
-pub fn routes() -> Routes {
- Routes::new()
- .prefix("user")
- .add("/current", get(current))
- .add("/current_api_key", get(current_by_api_key))
- .add("/convert/admin", post(convert_to_admin))
- .add("/convert/user", post(convert_to_user))
-}
diff --git a/examples/demo/src/controllers/view_engine.rs b/examples/demo/src/controllers/view_engine.rs
deleted file mode 100644
index fc89d2b75..000000000
--- a/examples/demo/src/controllers/view_engine.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-#![allow(clippy::unused_async)]
-use loco_rs::prelude::*;
-use serde_json::json;
-
-use crate::{initializers::hello_view_engine::HelloView, views};
-
-/// Renders the dashboard home page
-///
-/// # Errors
-///
-/// This function will return an error if render fails
-pub async fn render_home(ViewEngine(v): ViewEngine) -> Result {
- views::engine::home(&v)
-}
-
-/// Hello
-///
-/// # Errors
-///
-/// This function will return an error if render fails
-pub async fn render_hello(ViewEngine(v): ViewEngine) -> Result {
- // NOTE: v is a hello engine, which always returns 'hello', params dont matter.
- // it's a funky behavior that we use for demonstrating how easy it is
- // to build a custom view engine.
- format::render().view(&v, "foobar", ())
-}
-
-pub async fn render_simple() -> Result {
- format::render().template("{{name}} website", json!({"name": "Loco"}))
-}
-
-pub fn routes() -> Routes {
- Routes::new()
- .prefix("view-engine")
- .add("/home", get(render_home))
- .add("/hello", get(render_hello))
- .add("/simple", get(render_simple))
-}
diff --git a/examples/demo/src/fixtures/notes.yaml b/examples/demo/src/fixtures/notes.yaml
deleted file mode 100644
index 25cd13444..000000000
--- a/examples/demo/src/fixtures/notes.yaml
+++ /dev/null
@@ -1,54 +0,0 @@
----
-- id: 1
- content: content 1
- created_at: "2023-11-12T12:34:56.789"
- updated_at: "2023-11-12T12:34:56.789"
-- id: 2
- title: Loco note 2
- created_at: "2023-11-12T12:34:56.789"
- updated_at: "2023-11-12T12:34:56.789"
-- id: 3
- title: Loco note 3
- content: content 3
- created_at: "2023-11-12T12:34:56.789"
- updated_at: "2023-11-12T12:34:56.789"
-- id: 4
- title: Loco note 4
- content: content 4
- created_at: "2023-11-12T12:34:56.789"
- updated_at: "2023-11-12T12:34:56.789"
-- id: 5
- title: Loco note 5
- content: content 5
- created_at: "2023-11-12T12:34:56.789"
- updated_at: "2023-11-12T12:34:56.789"
-- id: 6
- title: Loco 6
- content: content 6
- created_at: "2023-11-12T12:34:56.789"
- updated_at: "2023-11-12T12:34:56.789"
-- id: 7
- title: Loco 7
- content: content 7
- created_at: "2023-11-12T12:34:56.789"
- updated_at: "2023-11-12T12:34:56.789"
-- id: 8
- title: Loco 8
- content: content 8
- created_at: "2023-11-12T12:34:56.789"
- updated_at: "2023-11-12T12:34:56.789"
-- id: 9
- title: Loco 9
- content: content 9
- created_at: "2023-11-12T12:34:56.789"
- updated_at: "2023-11-12T12:34:56.789"
-- id: 10
- title: Loco 10
- content: content 10
- created_at: "2023-11-12T12:34:56.789"
- updated_at: "2023-11-12T12:34:56.789"
-- id: 11
- title: Loco 11
- content: content 11
- created_at: "2023-11-12T12:34:56.789"
- updated_at: "2023-11-12T12:34:56.789"
diff --git a/examples/demo/src/fixtures/users.yaml b/examples/demo/src/fixtures/users.yaml
deleted file mode 100644
index 32ebc95ca..000000000
--- a/examples/demo/src/fixtures/users.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
----
-- id: 1
- pid: 11111111-1111-1111-1111-111111111111
- email: user1@example.com
- password: "$argon2id$v=19$m=19456,t=2,p=1$JkBeJfBWoNlj4D684c568g$bKRQ+Ud8qwYGIaAu+x+4flGHPS4WJh3ylUUV4sEtDBY"
- api_key: lo-95ec80d7-cb60-4b70-9b4b-9ef74cb88758
- name: user1
- created_at: "2023-11-12T12:34:56.789"
- updated_at: "2023-11-12T12:34:56.789"
-- id: 2
- pid: 22222222-2222-2222-2222-222222222222
- email: user2@example.com
- password: "$argon2id$v=19$m=19456,t=2,p=1$JkBeJfBWoNlj4D684c568g$bKRQ+Ud8qwYGIaAu+x+4flGHPS4WJh3ylUUV4sEtDBY"
- api_key: lo-153561ca-fa84-4e1b-813a-c62526d0a77e
- name: user2
- created_at: "2023-11-12T12:34:56.789"
- updated_at: "2023-11-12T12:34:56.789"
diff --git a/examples/demo/src/initializers/axum_session.rs b/examples/demo/src/initializers/axum_session.rs
deleted file mode 100644
index fb01bbee5..000000000
--- a/examples/demo/src/initializers/axum_session.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use async_trait::async_trait;
-use axum::Router as AxumRouter;
-use loco_rs::prelude::*;
-
-pub struct AxumSessionInitializer;
-
-#[async_trait]
-impl Initializer for AxumSessionInitializer {
- fn name(&self) -> String {
- "axum-session".to_string()
- }
-
- async fn after_routes(&self, router: AxumRouter, _ctx: &AppContext) -> Result {
- let session_config =
- axum_session::SessionConfig::default().with_table_name("sessions_table");
-
- let session_store =
- axum_session::SessionStore::::new(None, session_config)
- .await
- .unwrap();
-
- let router = router.layer(axum_session::SessionLayer::new(session_store));
-
- Ok(router)
- }
-}
diff --git a/examples/demo/src/initializers/hello_view_engine.rs b/examples/demo/src/initializers/hello_view_engine.rs
deleted file mode 100644
index 19161f4ad..000000000
--- a/examples/demo/src/initializers/hello_view_engine.rs
+++ /dev/null
@@ -1,28 +0,0 @@
-use async_trait::async_trait;
-use axum::{Extension, Router as AxumRouter};
-use loco_rs::{
- app::{AppContext, Initializer},
- controller::views::{ViewEngine, ViewRenderer},
- Result,
-};
-use serde::Serialize;
-
-#[derive(Clone)]
-pub struct HelloView;
-impl ViewRenderer for HelloView {
- fn render(&self, _key: &str, _data: S) -> Result {
- Ok("hello".to_string())
- }
-}
-
-pub struct HelloViewEngineInitializer;
-#[async_trait]
-impl Initializer for HelloViewEngineInitializer {
- fn name(&self) -> String {
- "custom-view-engine".to_string()
- }
-
- async fn after_routes(&self, router: AxumRouter, _ctx: &AppContext) -> Result {
- Ok(router.layer(Extension(ViewEngine::from(HelloView))))
- }
-}
diff --git a/examples/demo/src/initializers/mod.rs b/examples/demo/src/initializers/mod.rs
deleted file mode 100644
index c289cb97d..000000000
--- a/examples/demo/src/initializers/mod.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![allow(clippy::module_name_repetitions)]
-pub mod axum_session;
-pub mod hello_view_engine;
-pub mod view_engine;
diff --git a/examples/demo/src/initializers/view_engine.rs b/examples/demo/src/initializers/view_engine.rs
deleted file mode 100644
index f3ba9e6a9..000000000
--- a/examples/demo/src/initializers/view_engine.rs
+++ /dev/null
@@ -1,46 +0,0 @@
-use async_trait::async_trait;
-use axum::{Extension, Router as AxumRouter};
-use fluent_templates::{ArcLoader, FluentLoader};
-use loco_rs::{
- app::{AppContext, Initializer},
- controller::views::{engines, ViewEngine},
- Error, Result,
-};
-use tracing::info;
-
-const I18N_DIR: &str = "assets/i18n";
-const I18N_SHARED: &str = "assets/i18n/shared.ftl";
-
-pub struct ViewEngineInitializer;
-#[async_trait]
-impl Initializer for ViewEngineInitializer {
- fn name(&self) -> String {
- "view-engine".to_string()
- }
-
- async fn after_routes(&self, router: AxumRouter, _ctx: &AppContext) -> Result {
- #[allow(unused_mut)]
- let mut tera_engine = engines::TeraView::build()?;
- if std::path::Path::new(I18N_DIR).exists() {
- let arc = ArcLoader::builder(&I18N_DIR, unic_langid::langid!("en-US"))
- .shared_resources(Some(&[I18N_SHARED.into()]))
- .customize(|bundle| bundle.set_use_isolating(false))
- .build()
- .map_err(|e| Error::string(&e.to_string()))?;
- #[cfg(debug_assertions)]
- tera_engine
- .tera
- .lock()
- .expect("lock")
- .register_function("t", FluentLoader::new(arc));
-
- #[cfg(not(debug_assertions))]
- tera_engine
- .tera
- .register_function("t", FluentLoader::new(arc));
- info!("locales loaded");
- }
-
- Ok(router.layer(Extension(ViewEngine::from(tera_engine))))
- }
-}
diff --git a/examples/demo/src/lib.rs b/examples/demo/src/lib.rs
deleted file mode 100644
index d0eea6a3b..000000000
--- a/examples/demo/src/lib.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-pub mod app;
-pub mod common;
-pub mod controllers;
-pub mod initializers;
-pub mod mailers;
-pub mod models;
-pub mod tasks;
-pub mod views;
-pub mod workers;
diff --git a/examples/demo/src/mailers/auth.rs b/examples/demo/src/mailers/auth.rs
deleted file mode 100644
index 28a8991aa..000000000
--- a/examples/demo/src/mailers/auth.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-// auth mailer
-#![allow(non_upper_case_globals)]
-
-use loco_rs::prelude::*;
-use serde_json::json;
-
-use crate::models::users;
-
-static welcome: Dir<'_> = include_dir!("src/mailers/auth/welcome");
-static forgot: Dir<'_> = include_dir!("src/mailers/auth/forgot");
-// #[derive(Mailer)] // -- disabled for faster build speed. it works. but lets
-// move on for now.
-
-#[allow(clippy::module_name_repetitions)]
-pub struct AuthMailer {}
-impl Mailer for AuthMailer {}
-impl AuthMailer {
- /// Sending welcome email the the given user
- ///
- /// # Errors
- ///
- /// When email sending is failed
- pub async fn send_welcome(ctx: &AppContext, user: &users::Model) -> Result<()> {
- Self::mail_template(
- ctx,
- &welcome,
- mailer::Args {
- to: user.email.to_string(),
- locals: json!({
- "name": user.name,
- "verifyToken": user.email_verification_token,
- "host": ctx.config.server.host
- }),
- ..Default::default()
- },
- )
- .await?;
-
- Ok(())
- }
-
- /// Sending forgot password email
- ///
- /// # Errors
- ///
- /// When email sending is failed
- pub async fn forgot_password(ctx: &AppContext, user: &users::Model) -> Result<()> {
- Self::mail_template(
- ctx,
- &forgot,
- mailer::Args {
- to: user.email.to_string(),
- locals: json!({
- "name": user.name,
- "resetToken": user.reset_token,
- "host": ctx.config.server.host
- }),
- ..Default::default()
- },
- )
- .await?;
-
- Ok(())
- }
-}
diff --git a/examples/demo/src/mailers/auth/forgot/html.t b/examples/demo/src/mailers/auth/forgot/html.t
deleted file mode 100644
index 4662b57c8..000000000
--- a/examples/demo/src/mailers/auth/forgot/html.t
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
- Hey {{name}},
- Forgot your password? No worries! You can reset it by clicking the link below:
- Reset Your Password
- If you didn't request a password reset, please ignore this email.
- Best regards,
The Loco Team
-
-
-
diff --git a/examples/demo/src/mailers/auth/forgot/subject.t b/examples/demo/src/mailers/auth/forgot/subject.t
deleted file mode 100644
index 4938df1e3..000000000
--- a/examples/demo/src/mailers/auth/forgot/subject.t
+++ /dev/null
@@ -1 +0,0 @@
-Your reset password link
diff --git a/examples/demo/src/mailers/auth/forgot/text.t b/examples/demo/src/mailers/auth/forgot/text.t
deleted file mode 100644
index a6ab4968b..000000000
--- a/examples/demo/src/mailers/auth/forgot/text.t
+++ /dev/null
@@ -1,3 +0,0 @@
-Reset your password with this link:
-
-{{host}}/reset/{{resetToken}}
diff --git a/examples/demo/src/mailers/auth/welcome/html.t b/examples/demo/src/mailers/auth/welcome/html.t
deleted file mode 100644
index 33eca5261..000000000
--- a/examples/demo/src/mailers/auth/welcome/html.t
+++ /dev/null
@@ -1,13 +0,0 @@
-;
-
-
- Dear {{name}},
- Welcome to Loco! You can now log in to your account.
- Before you get started, please verify your account by clicking the link below:
-
- Verify Your Account
-
- Best regards,
The Loco Team
-
-
-
diff --git a/examples/demo/src/mailers/auth/welcome/subject.t b/examples/demo/src/mailers/auth/welcome/subject.t
deleted file mode 100644
index 82cc6fbf7..000000000
--- a/examples/demo/src/mailers/auth/welcome/subject.t
+++ /dev/null
@@ -1 +0,0 @@
-Welcome {{name}}
diff --git a/examples/demo/src/mailers/auth/welcome/text.t b/examples/demo/src/mailers/auth/welcome/text.t
deleted file mode 100644
index 291ba0009..000000000
--- a/examples/demo/src/mailers/auth/welcome/text.t
+++ /dev/null
@@ -1,4 +0,0 @@
-Welcome {{name}}, you can now log in.
- Verify your account with the link below:
-
-{{host}}/verify/{{verifyToken}}
diff --git a/examples/demo/src/mailers/mod.rs b/examples/demo/src/mailers/mod.rs
deleted file mode 100644
index 0e4a05d59..000000000
--- a/examples/demo/src/mailers/mod.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod auth;
diff --git a/examples/demo/src/models/_entities/mod.rs b/examples/demo/src/models/_entities/mod.rs
deleted file mode 100644
index 2a1e4f725..000000000
--- a/examples/demo/src/models/_entities/mod.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
-
-pub mod prelude;
-
-pub mod notes;
-pub mod roles;
-pub mod sea_orm_active_enums;
-pub mod users;
-pub mod users_roles;
diff --git a/examples/demo/src/models/_entities/notes.rs b/examples/demo/src/models/_entities/notes.rs
deleted file mode 100644
index 3fa02a044..000000000
--- a/examples/demo/src/models/_entities/notes.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
-
-use sea_orm::entity::prelude::*;
-use serde::{Deserialize, Serialize};
-
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
-#[sea_orm(table_name = "notes")]
-pub struct Model {
- pub created_at: DateTime,
- pub updated_at: DateTime,
- #[sea_orm(primary_key)]
- pub id: i32,
- pub title: Option,
- pub content: Option,
-}
-
-#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
-pub enum Relation {}
diff --git a/examples/demo/src/models/_entities/prelude.rs b/examples/demo/src/models/_entities/prelude.rs
deleted file mode 100644
index 202b05606..000000000
--- a/examples/demo/src/models/_entities/prelude.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
-
-pub use super::{
- notes::Entity as Notes, roles::Entity as Roles, users::Entity as Users,
- users_roles::Entity as UsersRoles,
-};
diff --git a/examples/demo/src/models/_entities/roles.rs b/examples/demo/src/models/_entities/roles.rs
deleted file mode 100644
index b3475c7ea..000000000
--- a/examples/demo/src/models/_entities/roles.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
-
-use sea_orm::entity::prelude::*;
-use serde::{Deserialize, Serialize};
-
-use super::sea_orm_active_enums::RolesName;
-
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
-#[sea_orm(table_name = "roles")]
-pub struct Model {
- pub created_at: DateTime,
- pub updated_at: DateTime,
- #[sea_orm(primary_key)]
- pub id: i32,
- #[sea_orm(unique)]
- pub pid: Uuid,
- pub name: RolesName,
-}
-
-#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
-pub enum Relation {
- #[sea_orm(has_many = "super::users_roles::Entity")]
- UsersRoles,
-}
-
-impl Related for Entity {
- fn to() -> RelationDef {
- Relation::UsersRoles.def()
- }
-}
-
-impl Related for Entity {
- fn to() -> RelationDef {
- super::users_roles::Relation::Users.def()
- }
- fn via() -> Option {
- Some(super::users_roles::Relation::Roles.def().rev())
- }
-}
diff --git a/examples/demo/src/models/_entities/sea_orm_active_enums.rs b/examples/demo/src/models/_entities/sea_orm_active_enums.rs
deleted file mode 100644
index ae91241b3..000000000
--- a/examples/demo/src/models/_entities/sea_orm_active_enums.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
-
-use sea_orm::entity::prelude::*;
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Serialize, Deserialize)]
-#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "roles_name")]
-pub enum RolesName {
- #[sea_orm(string_value = "Admin")]
- Admin,
- #[sea_orm(string_value = "User")]
- User,
-}
diff --git a/examples/demo/src/models/_entities/users.rs b/examples/demo/src/models/_entities/users.rs
deleted file mode 100644
index 3b7f1cce4..000000000
--- a/examples/demo/src/models/_entities/users.rs
+++ /dev/null
@@ -1,46 +0,0 @@
-//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
-
-use sea_orm::entity::prelude::*;
-use serde::{Deserialize, Serialize};
-
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
-#[sea_orm(table_name = "users")]
-pub struct Model {
- pub created_at: DateTime,
- pub updated_at: DateTime,
- #[sea_orm(primary_key)]
- pub id: i32,
- pub pid: Uuid,
- #[sea_orm(unique)]
- pub email: String,
- pub password: String,
- #[sea_orm(unique)]
- pub api_key: String,
- pub name: String,
- pub reset_token: Option,
- pub reset_sent_at: Option,
- pub email_verification_token: Option,
- pub email_verification_sent_at: Option,
- pub email_verified_at: Option,
-}
-
-#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
-pub enum Relation {
- #[sea_orm(has_many = "super::users_roles::Entity")]
- UsersRoles,
-}
-
-impl Related for Entity {
- fn to() -> RelationDef {
- Relation::UsersRoles.def()
- }
-}
-
-impl Related for Entity {
- fn to() -> RelationDef {
- super::users_roles::Relation::Roles.def()
- }
- fn via() -> Option {
- Some(super::users_roles::Relation::Users.def().rev())
- }
-}
diff --git a/examples/demo/src/models/_entities/users_roles.rs b/examples/demo/src/models/_entities/users_roles.rs
deleted file mode 100644
index 8efb42283..000000000
--- a/examples/demo/src/models/_entities/users_roles.rs
+++ /dev/null
@@ -1,47 +0,0 @@
-//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.15
-
-use sea_orm::entity::prelude::*;
-use serde::{Deserialize, Serialize};
-
-#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
-#[sea_orm(table_name = "users_roles")]
-pub struct Model {
- pub created_at: DateTime,
- pub updated_at: DateTime,
- #[sea_orm(primary_key, auto_increment = false)]
- pub users_id: i32,
- #[sea_orm(primary_key, auto_increment = false)]
- pub roles_id: i32,
-}
-
-#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
-pub enum Relation {
- #[sea_orm(
- belongs_to = "super::roles::Entity",
- from = "Column::RolesId",
- to = "super::roles::Column::Id",
- on_update = "Cascade",
- on_delete = "Cascade"
- )]
- Roles,
- #[sea_orm(
- belongs_to = "super::users::Entity",
- from = "Column::UsersId",
- to = "super::users::Column::Id",
- on_update = "Cascade",
- on_delete = "Cascade"
- )]
- Users,
-}
-
-impl Related for Entity {
- fn to() -> RelationDef {
- Relation::Roles.def()
- }
-}
-
-impl Related for Entity {
- fn to() -> RelationDef {
- Relation::Users.def()
- }
-}
diff --git a/examples/demo/src/models/mod.rs b/examples/demo/src/models/mod.rs
deleted file mode 100644
index 7eaaf3b59..000000000
--- a/examples/demo/src/models/mod.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-pub mod _entities;
-pub mod notes;
-pub mod roles;
-pub mod sea_orm_active_enums;
-pub mod users;
-pub mod users_roles;
diff --git a/examples/demo/src/models/notes.rs b/examples/demo/src/models/notes.rs
deleted file mode 100644
index 110259823..000000000
--- a/examples/demo/src/models/notes.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-use sea_orm::entity::prelude::*;
-
-use super::_entities::notes::ActiveModel;
-
-impl ActiveModelBehavior for ActiveModel {
- // extend activemodel below (keep comment for generators)
-}
diff --git a/examples/demo/src/models/roles.rs b/examples/demo/src/models/roles.rs
deleted file mode 100644
index 8c45bf9aa..000000000
--- a/examples/demo/src/models/roles.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-use loco_rs::prelude::*;
-
-pub use super::_entities::roles::{self, ActiveModel, Entity, Model};
-use crate::models::{_entities::sea_orm_active_enums::RolesName, users, users_roles};
-
-#[async_trait]
-impl ActiveModelBehavior for ActiveModel {
- // extend activemodel below (keep comment for generators)
- async fn before_save(self, _db: &C, insert: bool) -> std::result::Result
- where
- C: ConnectionTrait,
- {
- if insert {
- let mut this = self;
- this.pid = ActiveValue::Set(Uuid::new_v4());
- Ok(this)
- } else {
- Ok(self)
- }
- }
-}
-
-impl super::_entities::roles::Model {
- pub async fn add_user_to_admin_role(
- db: &DatabaseConnection,
- user: &users::Model,
- ) -> ModelResult {
- // Find the admin role
- let role = Self::upsert_by_name(db, RolesName::Admin).await?;
- // Connect the user to the admin role
- users_roles::Model::connect_user_to_role(db, user, &role).await?;
- Ok(role)
- }
-
- pub async fn add_user_to_user_role(
- db: &DatabaseConnection,
- user: &users::Model,
- ) -> ModelResult {
- // Find the user role
- let role = Self::upsert_by_name(db, RolesName::User).await?;
- // Connect the user to the user role
- users_roles::Model::connect_user_to_role(db, user, &role).await?;
- Ok(role)
- }
-
- pub async fn upsert_by_name(db: &DatabaseConnection, name: RolesName) -> ModelResult {
- let role = roles::Entity::find()
- .filter(roles::Column::Name.eq(name.clone()))
- .one(db)
- .await?;
- match role {
- Some(role) => Ok(role),
- None => {
- let role = roles::ActiveModel {
- name: Set(name),
- ..Default::default()
- }
- .insert(db)
- .await?;
- Ok(role)
- }
- }
- }
-
- pub async fn find_by_user(db: &DatabaseConnection, user: &users::Model) -> ModelResult {
- let role = roles::Entity::find()
- .inner_join(users_roles::Entity)
- .filter(users_roles::Column::UsersId.eq(user.id.clone()))
- .one(db)
- .await?;
- role.ok_or_else(|| ModelError::EntityNotFound)
- }
-}
diff --git a/examples/demo/src/models/sea_orm_active_enums.rs b/examples/demo/src/models/sea_orm_active_enums.rs
deleted file mode 100644
index f761755f5..000000000
--- a/examples/demo/src/models/sea_orm_active_enums.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-use std::fmt::Display;
-
-pub use crate::models::_entities::sea_orm_active_enums::RolesName;
-
-impl Display for RolesName {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- let str = match self {
- Self::Admin => "Admin".to_string(),
- Self::User => "User".to_string(),
- };
- write!(f, "{}", str)
- }
-}
diff --git a/examples/demo/src/models/users.rs b/examples/demo/src/models/users.rs
deleted file mode 100644
index d9762d3a8..000000000
--- a/examples/demo/src/models/users.rs
+++ /dev/null
@@ -1,303 +0,0 @@
-use async_trait::async_trait;
-use chrono::offset::Local;
-use loco_rs::{auth::jwt, hash, prelude::*};
-use serde::{Deserialize, Serialize};
-use serde_json::Map;
-use uuid::Uuid;
-
-pub use super::_entities::users::{self, ActiveModel, Entity, Model};
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct LoginParams {
- pub email: String,
- pub password: String,
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct RegisterParams {
- pub email: String,
- pub password: String,
- pub name: String,
-}
-
-//
-#[derive(Debug, Validate, Deserialize)]
-pub struct Validator {
- #[validate(length(min = 2, message = "Name must be at least 2 characters long."))]
- pub name: String,
- #[validate(custom(function = "validation::is_valid_email"))]
- pub email: String,
-}
-
-impl Validatable for super::_entities::users::ActiveModel {
- fn validator(&self) -> Box {
- Box::new(Validator {
- name: self.name.as_ref().to_owned(),
- email: self.email.as_ref().to_owned(),
- })
- }
-}
-//
-
-#[async_trait::async_trait]
-impl ActiveModelBehavior for super::_entities::users::ActiveModel {
- async fn before_save(self, _db: &C, insert: bool) -> Result
- where
- C: ConnectionTrait,
- {
- {
- self.validate()?;
- if insert {
- let mut this = self;
- this.pid = ActiveValue::Set(Uuid::new_v4());
- this.api_key = ActiveValue::Set(format!("lo-{}", Uuid::new_v4()));
- Ok(this)
- } else {
- Ok(self)
- }
- }
- }
-}
-
-#[async_trait]
-impl Authenticable for super::_entities::users::Model {
- async fn find_by_api_key(db: &DatabaseConnection, api_key: &str) -> ModelResult {
- let user = users::Entity::find()
- .filter(
- query::condition()
- .eq(users::Column::ApiKey, api_key)
- .build(),
- )
- .one(db)
- .await?;
- user.ok_or_else(|| ModelError::EntityNotFound)
- }
-
- async fn find_by_claims_key(db: &DatabaseConnection, claims_key: &str) -> ModelResult {
- Self::find_by_pid(db, claims_key).await
- }
-}
-
-impl super::_entities::users::Model {
- /// finds a user by the provided email
- ///
- /// # Errors
- ///
- /// When could not find user by the given token or DB query error
- pub async fn find_by_email(db: &DatabaseConnection, email: &str) -> ModelResult {
- let user = users::Entity::find()
- .filter(query::condition().eq(users::Column::Email, email).build())
- .one(db)
- .await?;
- user.ok_or_else(|| ModelError::EntityNotFound)
- }
-
- /// finds a user by the provided verification token
- ///
- /// # Errors
- ///
- /// When could not find user by the given token or DB query error
- pub async fn find_by_verification_token(
- db: &DatabaseConnection,
- token: &str,
- ) -> ModelResult {
- let user = users::Entity::find()
- .filter(
- query::condition()
- .eq(users::Column::EmailVerificationToken, token)
- .build(),
- )
- .one(db)
- .await?;
- user.ok_or_else(|| ModelError::EntityNotFound)
- }
-
- /// finds a user by the provided reset token
- ///
- /// # Errors
- ///
- /// When could not find user by the given token or DB query error
- pub async fn find_by_reset_token(db: &DatabaseConnection, token: &str) -> ModelResult {
- let user = users::Entity::find()
- .filter(
- query::condition()
- .eq(users::Column::ResetToken, token)
- .build(),
- )
- .one(db)
- .await?;
- user.ok_or_else(|| ModelError::EntityNotFound)
- }
-
- /// finds a user by the provided pid
- ///
- /// # Errors
- ///
- /// When could not find user or DB query error
- pub async fn find_by_pid(db: &DatabaseConnection, pid: &str) -> ModelResult {
- let parse_uuid = Uuid::parse_str(pid).map_err(|e| ModelError::Any(e.into()))?;
- let user = users::Entity::find()
- .filter(
- query::condition()
- .eq(users::Column::Pid, parse_uuid)
- .build(),
- )
- .one(db)
- .await?;
- user.ok_or_else(|| ModelError::EntityNotFound)
- }
-
- /// finds a user by the provided api key
- ///
- /// # Errors
- ///
- /// When could not find user by the given token or DB query error
- pub async fn find_by_api_key(db: &DatabaseConnection, api_key: &str) -> ModelResult {
- let user = users::Entity::find()
- .filter(
- query::condition()
- .eq(users::Column::ApiKey, api_key)
- .build(),
- )
- .one(db)
- .await?;
- user.ok_or_else(|| ModelError::EntityNotFound)
- }
-
- /// Verifies whether the provided plain password matches the hashed password
- #[must_use]
- pub fn verify_password(&self, password: &str) -> bool {
- hash::verify_password(password, &self.password)
- }
-
- /// Asynchronously creates a user with a password and saves it to the
- /// database.
- ///
- /// # Errors
- ///
- /// When could not save the user into the DB
- pub async fn create_with_password(
- db: &DatabaseConnection,
- params: &RegisterParams,
- ) -> ModelResult {
- let txn = db.begin().await?;
-
- if users::Entity::find()
- .filter(
- query::condition()
- .eq(users::Column::Email, ¶ms.email)
- .build(),
- )
- .one(&txn)
- .await?
- .is_some()
- {
- return Err(ModelError::EntityAlreadyExists {});
- }
-
- let password_hash =
- hash::hash_password(¶ms.password).map_err(|e| ModelError::Any(e.into()))?;
- let user = users::ActiveModel {
- email: ActiveValue::set(params.email.to_string()),
- password: ActiveValue::set(password_hash),
- name: ActiveValue::set(params.name.to_string()),
- ..Default::default()
- }
- .insert(&txn)
- .await?;
-
- txn.commit().await?;
-
- Ok(user)
- }
-
- /// Creates a JWT
- ///
- /// # Errors
- ///
- /// when could not convert user claims to jwt token
- pub fn generate_jwt(&self, secret: &str, expiration: u64) -> ModelResult {
- let mut claims = Map::new();
- claims.insert("Role".to_string(), "Administrator".into());
- Ok(jwt::JWT::new(secret).generate_token(expiration, self.pid.to_string(), claims)?)
- }
-}
-
-impl super::_entities::users::ActiveModel {
- /// Validate user schema
- ///
- /// # Errors
- ///
- /// when the active model is not valid
-
- /// Sets the email verification information for the user and
- /// updates it in the database.
- ///
- /// This method is used to record the timestamp when the email verification
- /// was sent and generate a unique verification token for the user.
- ///
- /// # Errors
- ///
- /// when has DB query error
- pub async fn set_email_verification_sent(
- mut self,
- db: &DatabaseConnection,
- ) -> ModelResult {
- self.email_verification_sent_at = ActiveValue::set(Some(Local::now().naive_local()));
- self.email_verification_token = ActiveValue::Set(Some(Uuid::new_v4().to_string()));
- Ok(self.update(db).await?)
- }
-
- /// Sets the information for a reset password request,
- /// generates a unique reset password token, and updates it in the
- /// database.
- ///
- /// This method records the timestamp when the reset password token is sent
- /// and generates a unique token for the user.
- ///
- /// # Arguments
- ///
- /// # Errors
- ///
- /// when has DB query error
- pub async fn set_forgot_password_sent(mut self, db: &DatabaseConnection) -> ModelResult {
- self.reset_sent_at = ActiveValue::set(Some(Local::now().naive_local()));
- self.reset_token = ActiveValue::Set(Some(Uuid::new_v4().to_string()));
- Ok(self.update(db).await?)
- }
-
- /// Records the verification time when a user verifies their
- /// email and updates it in the database.
- ///
- /// This method sets the timestamp when the user successfully verifies their
- /// email.
- ///
- /// # Errors
- ///
- /// when has DB query error
- pub async fn verified(mut self, db: &DatabaseConnection) -> ModelResult {
- self.email_verified_at = ActiveValue::set(Some(Local::now().naive_local()));
- Ok(self.update(db).await?)
- }
-
- /// Resets the current user password with a new password and
- /// updates it in the database.
- ///
- /// This method hashes the provided password and sets it as the new password
- /// for the user.
- ///
- /// # Errors
- ///
- /// when has DB query error or could not hashed the given password
- pub async fn reset_password(
- mut self,
- db: &DatabaseConnection,
- password: &str,
- ) -> ModelResult {
- self.password =
- ActiveValue::set(hash::hash_password(password).map_err(|e| ModelError::Any(e.into()))?);
- self.reset_token = ActiveValue::Set(None);
- self.reset_sent_at = ActiveValue::Set(None);
- Ok(self.update(db).await?)
- }
-}
diff --git a/examples/demo/src/models/users_roles.rs b/examples/demo/src/models/users_roles.rs
deleted file mode 100644
index 0a4b38ca9..000000000
--- a/examples/demo/src/models/users_roles.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-use loco_rs::prelude::*;
-use sea_orm::ActiveValue;
-
-pub use super::_entities::users_roles::{self, ActiveModel, Column, Entity, Model};
-
-impl ActiveModelBehavior for ActiveModel {
- // extend activemodel below (keep comment for generators)
-}
-
-impl super::_entities::users_roles::Model {
- pub async fn connect_user_to_role(
- db: &DatabaseConnection,
- user: &super::users::Model,
- role: &super::roles::Model,
- ) -> ModelResult {
- // Find the user role if it exists
- let user_role = users_roles::Entity::find()
- .filter(Column::UsersId.eq(user.id))
- .one(db)
- .await?;
- // Update the user role if it exists, otherwise create it
- if let Some(user_role) = user_role {
- // Delete the user role if the role is different
- if user_role.roles_id == role.id {
- return Ok(user_role);
- }
- // Delete the user role, cannot update since it is a composite key
- user_role.delete(db).await?;
- }
- // Create the user role
- let user_role = users_roles::ActiveModel {
- users_id: ActiveValue::set(user.id),
- roles_id: ActiveValue::set(role.id),
- ..Default::default()
- }
- .insert(db)
- .await?;
- Ok(user_role)
- }
-}
diff --git a/examples/demo/src/tasks/foo.rs b/examples/demo/src/tasks/foo.rs
deleted file mode 100644
index 43193b49d..000000000
--- a/examples/demo/src/tasks/foo.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-//
-use loco_rs::prelude::*;
-
-pub struct Foo;
-#[async_trait]
-impl Task for Foo {
- fn task(&self) -> TaskInfo {
- TaskInfo {
- name: "foo".to_string(),
- detail: "run foo task".to_string(),
- }
- }
- async fn run(&self, _app_context: &AppContext, _vars: &task::Vars) -> Result<()> {
- Ok(())
- }
-}
-//
diff --git a/examples/demo/src/tasks/mod.rs b/examples/demo/src/tasks/mod.rs
deleted file mode 100644
index 6428f4119..000000000
--- a/examples/demo/src/tasks/mod.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-pub mod foo;
-pub mod seed;
-pub mod user_report;
diff --git a/examples/demo/src/tasks/seed.rs b/examples/demo/src/tasks/seed.rs
deleted file mode 100644
index ec782ef7a..000000000
--- a/examples/demo/src/tasks/seed.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-//! This task implements data seeding functionality for initializing new
-//! development/demo environments.
-//!
-//! # Example
-//!
-//! Run the task with the following command:
-//! ```sh
-//! cargo run task
-//! ```
-//!
-//! To override existing data and reset the data structure, use the following
-//! command with the `refresh:true` argument: ```sh
-//! cargo run task seed_data refresh:true
-//! ```
-use loco_rs::{db, prelude::*};
-use migration::Migrator;
-
-use crate::app::App;
-
-#[allow(clippy::module_name_repetitions)]
-pub struct SeedData;
-#[async_trait]
-impl Task for SeedData {
- fn task(&self) -> TaskInfo {
- TaskInfo {
- name: "seed_data".to_string(),
- detail: "Task for seeding data".to_string(),
- }
- }
- async fn run(&self, app_context: &AppContext, vars: &task::Vars) -> Result<()> {
- let refresh = vars
- .cli_arg("refresh")
- .is_ok_and(|refresh| refresh == "true");
-
- if refresh {
- db::reset::(&app_context.db).await?;
- }
- let path = std::path::Path::new("src/fixtures");
- db::run_app_seed::(&app_context, path).await?;
- Ok(())
- }
-}
diff --git a/examples/demo/src/tasks/user_report.rs b/examples/demo/src/tasks/user_report.rs
deleted file mode 100644
index 4440fe404..000000000
--- a/examples/demo/src/tasks/user_report.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-use loco_rs::prelude::*;
-
-use crate::models::_entities::users;
-
-pub struct UserReport;
-#[async_trait]
-impl Task for UserReport {
- fn task(&self) -> TaskInfo {
- TaskInfo {
- name: "user_report".to_string(),
- detail: "output a user report".to_string(),
- }
- }
- async fn run(&self, app_context: &AppContext, vars: &task::Vars) -> Result<()> {
- let users = users::Entity::find().all(&app_context.db).await?;
- println!("args: {vars:?}");
- println!("!!! user_report: listing users !!!");
- println!("------------------------");
- for user in &users {
- println!("user: {}", user.email);
- }
- println!("done: {} users", users.len());
- Ok(())
- }
-}
diff --git a/examples/demo/src/views/auth.rs b/examples/demo/src/views/auth.rs
deleted file mode 100644
index efbf956fe..000000000
--- a/examples/demo/src/views/auth.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-use serde::{Deserialize, Serialize};
-
-use crate::models::_entities::users;
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct UserSession {
- pub token: String,
- pub user: UserDetail,
-}
-#[derive(Debug, Deserialize, Serialize)]
-pub struct UserDetail {
- pub pid: String,
- pub email: String,
- pub name: String,
- pub last_login: String,
-}
-
-impl UserSession {
- #[must_use]
- pub fn new(user: &users::Model, token: &String) -> Self {
- Self {
- token: token.to_string(),
- user: UserDetail {
- pid: user.pid.to_string(),
- email: user.email.to_string(),
- name: user.name.to_string(),
- last_login: "n/a".to_string(),
- },
- }
- }
-}
diff --git a/examples/demo/src/views/engine.rs b/examples/demo/src/views/engine.rs
deleted file mode 100644
index b043e7f39..000000000
--- a/examples/demo/src/views/engine.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-use loco_rs::prelude::*;
-use serde_json::json;
-
-/// Home view
-///
-/// # Errors
-///
-/// This function will return an error if render fails
-pub fn home(v: &impl ViewRenderer) -> Result {
- format::render().view(v, "home/hello.html", json!({}))
-}
diff --git a/examples/demo/src/views/mod.rs b/examples/demo/src/views/mod.rs
deleted file mode 100644
index ef05a6aa2..000000000
--- a/examples/demo/src/views/mod.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-pub mod auth;
-pub mod engine;
-pub mod notes;
-pub mod upload;
-pub mod user;
diff --git a/examples/demo/src/views/notes.rs b/examples/demo/src/views/notes.rs
deleted file mode 100644
index a10ec320e..000000000
--- a/examples/demo/src/views/notes.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-use loco_rs::{
- controller::views::pagination::{Pager, PagerMeta},
- prelude::model::query::{PageResponse, PaginationQuery},
-};
-use serde::{Deserialize, Serialize};
-
-use crate::models::_entities::notes;
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct ListResponse {
- id: i32,
- title: Option,
- content: Option,
-}
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct PaginationResponse {}
-
-impl From for ListResponse {
- fn from(note: notes::Model) -> Self {
- Self {
- id: note.id,
- title: note.title.clone(),
- content: note.content,
- }
- }
-}
-
-impl PaginationResponse {
- #[must_use]
- pub fn response(
- data: PageResponse,
- pagination_query: &PaginationQuery,
- ) -> Pager> {
- Pager {
- results: data
- .page
- .into_iter()
- .map(ListResponse::from)
- .collect::>(),
- info: PagerMeta {
- page: pagination_query.page,
- page_size: pagination_query.page_size,
- total_pages: data.total_pages,
- total_items: data.total_items,
- },
- }
- }
-}
diff --git a/examples/demo/src/views/upload.rs b/examples/demo/src/views/upload.rs
deleted file mode 100644
index e6b7c0026..000000000
--- a/examples/demo/src/views/upload.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-use std::path::{Path, PathBuf};
-
-use serde::{Deserialize, Serialize};
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct Response {
- pub path: PathBuf,
-}
-
-impl Response {
- #[must_use]
- pub fn new(path: &Path) -> Self {
- Self {
- path: path.to_path_buf(),
- }
- }
-}
diff --git a/examples/demo/src/views/user.rs b/examples/demo/src/views/user.rs
deleted file mode 100644
index 086daa13f..000000000
--- a/examples/demo/src/views/user.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-use serde::{Deserialize, Serialize};
-
-use crate::models::{_entities::users, roles};
-
-#[derive(Debug, Deserialize, Serialize)]
-pub struct CurrentResponse {
- pub pid: String,
- pub name: String,
- pub email: String,
-}
-
-impl CurrentResponse {
- #[must_use]
- pub fn new(user: &users::Model) -> Self {
- Self {
- pid: user.pid.to_string(),
- name: user.name.clone(),
- email: user.email.clone(),
- }
- }
-}
-#[derive(Debug, Deserialize, Serialize)]
-pub struct UserResponse {
- pub pid: String,
- pub name: String,
- pub email: String,
- pub role: String,
-}
-
-impl UserResponse {
- #[must_use]
- pub fn new(user: &users::Model, role: &roles::Model) -> Self {
- Self {
- pid: user.pid.to_string(),
- name: user.name.clone(),
- email: user.email.clone(),
- role: role.name.clone().to_string(),
- }
- }
-}
diff --git a/examples/demo/src/workers/downloader.rs b/examples/demo/src/workers/downloader.rs
deleted file mode 100644
index fa094d5b4..000000000
--- a/examples/demo/src/workers/downloader.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-use std::time::Duration;
-
-use loco_rs::prelude::*;
-use serde::{Deserialize, Serialize};
-use tokio::time::sleep;
-
-use crate::models::users;
-
-pub struct DownloadWorker {
- pub ctx: AppContext,
-}
-
-#[derive(Deserialize, Debug, Serialize)]
-pub struct DownloadWorkerArgs {
- pub user_guid: String,
-}
-
-#[async_trait]
-impl BackgroundWorker for DownloadWorker {
- fn build(ctx: &AppContext) -> Self {
- Self { ctx: ctx.clone() }
- }
- async fn perform(&self, args: DownloadWorkerArgs) -> Result<()> {
- // TODO: Some actual work goes here...
- println!("================================================");
- println!("Sending payment report to user {}", args.user_guid);
-
- sleep(Duration::from_millis(2000)).await;
-
- let all = users::Entity::find()
- .all(&self.ctx.db)
- .await
- .map_err(Box::from)?;
- for user in &all {
- println!("user: {}", user.id);
- }
- println!("================================================");
- Ok(())
- }
-}
diff --git a/examples/demo/src/workers/mod.rs b/examples/demo/src/workers/mod.rs
deleted file mode 100644
index acb5733da..000000000
--- a/examples/demo/src/workers/mod.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod downloader;
diff --git a/examples/demo/storage-uploads/.gitignore b/examples/demo/storage-uploads/.gitignore
deleted file mode 100644
index c96a04f00..000000000
--- a/examples/demo/storage-uploads/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*
-!.gitignore
\ No newline at end of file
diff --git a/examples/demo/tests/cli_tests.rs b/examples/demo/tests/cli_tests.rs
deleted file mode 100644
index b5269d0ed..000000000
--- a/examples/demo/tests/cli_tests.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-#[test]
-fn cli_tests() {
- let t = trycmd::TestCases::new();
- t.case("tests/cmd/*.trycmd");
-}
diff --git a/examples/demo/tests/cmd/cli.trycmd b/examples/demo/tests/cmd/cli.trycmd
deleted file mode 100644
index 8c283a481..000000000
--- a/examples/demo/tests/cmd/cli.trycmd
+++ /dev/null
@@ -1,158 +0,0 @@
-```console
-$ demo_app-cli
-? 2
-The one-person framework for Rust
-
-Usage: demo_app-cli [OPTIONS]
-
-Commands:
- start Start an app
- db Perform DB operations
- routes Describe all application endpoints
- middleware Describe all application middlewares
- task Run a custom task
- jobs Managing jobs queue
- scheduler Run the scheduler
- generate code generation creates a set of files and code templates based on a predefined set of rules
- doctor Validate and diagnose configurations
- version Display the app version
- watch Watch and restart the app
- help Print this message or the help of the given subcommand(s)
-
-Options:
- -e, --environment Specify the environment [default: development]
- -h, --help Print help
- -V, --version Print version
-
-```
-
-```console
-$ demo_app-cli db
-? 2
-Perform DB operations
-
-Usage: demo_app-cli db [OPTIONS]
-
-Commands:
- create Create schema
- migrate Migrate schema (up)
- down Run one down migration, or add a number to run multiple down migrations (i.e. `down 2`)
- reset Drop all tables, then reapply all migrations
- status Migration status
- entities Generate entity .rs files from database schema
- truncate Truncate data in tables (without dropping)
- seed Seed your database with initial data or dump tables to files
- schema Dump database schema
- help Print this message or the help of the given subcommand(s)
-
-Options:
- -e, --environment Specify the environment [default: development]
- -h, --help Print help
- -V, --version Print version
-
-```
-
-```console
-$ demo_app-cli generate
-? 2
-code generation creates a set of files and code templates based on a predefined set of rules
-
-Usage: demo_app-cli generate [OPTIONS]
-
-Commands:
- model Generates a new model file for defining the data structure of your application, and test file logic
- migration Generates a new migration file
- scaffold Generates a CRUD scaffold, model and controller
- controller Generate a new controller with the given controller name, and test file
- task Generate a Task based on the given name
- scheduler Generate a scheduler jobs configuration template
- worker Generate worker
- mailer Generate mailer
- deployment Generate a deployment infrastructure
- override Override templates and allows you to take control of them. You can always go back when deleting the local template
- help Print this message or the help of the given subcommand(s)
-
-Options:
- -e, --environment Specify the environment [default: development]
- -h, --help Print help
- -V, --version Print version
-
-```
-
-```console
-$ LOCO_ENV=test demo_app-cli task
-foo [run foo task]
-seed_data [Task for seeding data]
-user_report [output a user report]
-
-```
-
-```console
-$ demo_app-cli routes --environment test
-/_health
- └─ GET /_health
-/_ping
- └─ GET /_ping
-/auth
- ├─ POST /auth/forgot
- ├─ POST /auth/login
- ├─ POST /auth/register
- ├─ POST /auth/reset
- └─ POST /auth/verify
-/cache
- ├─ GET /cache
- ├─ GET /cache/get_or_insert
- └─ POST /cache/insert
-/mylayer
- ├─ GET /mylayer/admin
- ├─ GET /mylayer/echo
- └─ GET /mylayer/user
-/mysession
- └─ GET /mysession
-/notes
- ├─ GET /notes
- │ POST /notes
- │
- ├─ GET /notes/{id}
- │ POST /notes/{id}
- └─ DELETE /notes/{id}
-/response
- ├─ GET /response/album
- ├─ GET /response/empty
- ├─ GET /response/empty_json
- ├─ GET /response/etag
- ├─ GET /response/html
- ├─ GET /response/json
- ├─ GET /response/redirect
- ├─ GET /response/render_with_status_code
- ├─ GET /response/set_cookie
- └─ GET /response/text
-/upload
- └─ POST /upload/file
-/user
- ├─ POST /user/convert/admin
- ├─ POST /user/convert/user
- ├─ GET /user/current
- └─ GET /user/current_api_key
-/view-engine
- ├─ GET /view-engine/hello
- ├─ GET /view-engine/home
- └─ GET /view-engine/simple
-
-```
-
-```console
-$ demo_app-cli doctor
-[..][0m [34mDEBUG[0m [1mapp[0m: [2mloco_rs::bgworker[0m[2m:[0m job queue ping requested [2m[3menvironment[0m[2m=[0mdevelopment[0m
-✅ SeaORM CLI is installed
-✅ DB connection: success
-✅ redis queue: queue connection: success
-✅ Dependencies
-✅ Loco version: latest
-
-```
-
-```console
-$ LOCO_ENV=test demo_app-cli db reset
-
-```
diff --git a/examples/demo/tests/cmd/scheduler.trycmd b/examples/demo/tests/cmd/scheduler.trycmd
deleted file mode 100644
index 35911ad47..000000000
--- a/examples/demo/tests/cmd/scheduler.trycmd
+++ /dev/null
@@ -1,19 +0,0 @@
-```console
-$ LOCO_ENV=test blo-cli scheduler --list
-# job_name schedule tags run
-1 list_if_users * 2 * * * * base, users "user_report"
-2 run_task at 10:00 am - "foo"
-3 write_content run every 1 second base, infra "echo loco >> ./scheduler.txt"
-
-
-```
-
-```console
-$ LOCO_ENV=test blo-cli scheduler --config ./config/scheduler.yaml --list
-# job_name schedule tags run
-1 list_if_users * 2 * * * * base, users "user_report"
-2 run_task at 10:00 am - "foo"
-3 write_content run every 1 second base, infra "echo loco >> ./scheduler.txt"
-
-
-```
diff --git a/examples/demo/tests/mod.rs b/examples/demo/tests/mod.rs
deleted file mode 100644
index b42f2343c..000000000
--- a/examples/demo/tests/mod.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-mod models;
-mod requests;
-mod tasks;
-mod workers;
diff --git a/examples/demo/tests/models/mod.rs b/examples/demo/tests/models/mod.rs
deleted file mode 100644
index 160ce6008..000000000
--- a/examples/demo/tests/models/mod.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-mod roles;
-mod users;
-mod users_roles;
diff --git a/examples/demo/tests/models/roles.rs b/examples/demo/tests/models/roles.rs
deleted file mode 100644
index 05b033100..000000000
--- a/examples/demo/tests/models/roles.rs
+++ /dev/null
@@ -1,146 +0,0 @@
-use demo_app::{
- app::App,
- models::{roles, sea_orm_active_enums, users, users::RegisterParams},
-};
-use loco_rs::testing::prelude::*;
-use serial_test::serial;
-
-macro_rules! configure_insta {
- ($($expr:expr),*) => {
- let mut settings = insta::Settings::clone_current();
- settings.set_prepend_module_to_snapshot(false);
- settings.set_snapshot_suffix("roles");
- let _guard = settings.bind_to_scope();
- };
-}
-
-#[tokio::test]
-#[serial]
-async fn can_add_user_to_admin() {
- configure_insta!();
-
- let boot = boot_test::().await.unwrap();
- let new_user = users::Model::create_with_password(
- &boot.app_context.db,
- &RegisterParams {
- email: "user1@example.com".to_string(),
- password: "1234".to_string(),
- name: "framework".to_string(),
- },
- )
- .await
- .unwrap();
- let role = roles::Model::add_user_to_admin_role(&boot.app_context.db, &new_user)
- .await
- .unwrap();
- assert_eq!(role.name, sea_orm_active_enums::RolesName::Admin);
-}
-
-#[tokio::test]
-#[serial]
-async fn can_add_user_to_user() {
- configure_insta!();
-
- let boot = boot_test::().await.unwrap();
- let new_user = users::Model::create_with_password(
- &boot.app_context.db,
- &RegisterParams {
- email: "user1@example.com".to_string(),
- password: "1234".to_string(),
- name: "framework".to_string(),
- },
- )
- .await
- .unwrap();
- let role = roles::Model::add_user_to_user_role(&boot.app_context.db, &new_user)
- .await
- .unwrap();
- assert_eq!(role.name, sea_orm_active_enums::RolesName::User);
-}
-
-#[tokio::test]
-#[serial]
-async fn can_convert_between_user_and_admin() {
- configure_insta!();
-
- let boot = boot_test::().await.unwrap();
- let new_user = users::Model::create_with_password(
- &boot.app_context.db,
- &RegisterParams {
- email: "user1@example.com".to_string(),
- password: "1234".to_string(),
- name: "framework".to_string(),
- },
- )
- .await
- .unwrap();
- let role = roles::Model::add_user_to_user_role(&boot.app_context.db, &new_user)
- .await
- .unwrap();
- assert_eq!(role.name, sea_orm_active_enums::RolesName::User);
- let role = roles::Model::add_user_to_admin_role(&boot.app_context.db, &new_user)
- .await
- .unwrap();
- assert_eq!(role.name, sea_orm_active_enums::RolesName::Admin);
- let role = roles::Model::add_user_to_user_role(&boot.app_context.db, &new_user)
- .await
- .unwrap();
- assert_eq!(role.name, sea_orm_active_enums::RolesName::User);
-}
-
-#[tokio::test]
-#[serial]
-async fn can_find_user_roles() {
- configure_insta!();
-
- let boot = boot_test::().await.unwrap();
- let new_user = users::Model::create_with_password(
- &boot.app_context.db,
- &RegisterParams {
- email: "user1@example.com".to_string(),
- password: "1234".to_string(),
- name: "framework".to_string(),
- },
- )
- .await
- .unwrap();
- let role = roles::Model::add_user_to_user_role(&boot.app_context.db, &new_user)
- .await
- .unwrap();
- assert_eq!(role.name, sea_orm_active_enums::RolesName::User);
-
- let role = roles::Model::find_by_user(&boot.app_context.db, &new_user)
- .await
- .unwrap();
- assert_eq!(role.name, sea_orm_active_enums::RolesName::User);
-
- let role = roles::Model::add_user_to_admin_role(&boot.app_context.db, &new_user)
- .await
- .unwrap();
- assert_eq!(role.name, sea_orm_active_enums::RolesName::Admin);
-
- let role = roles::Model::find_by_user(&boot.app_context.db, &new_user)
- .await
- .unwrap();
- assert_eq!(role.name, sea_orm_active_enums::RolesName::Admin);
-}
-
-#[tokio::test]
-#[serial]
-async fn cannot_find_user_before_conversation() {
- configure_insta!();
-
- let boot = boot_test::().await.unwrap();
- let new_user = users::Model::create_with_password(
- &boot.app_context.db,
- &RegisterParams {
- email: "user1@example.com".to_string(),
- password: "1234".to_string(),
- name: "framework".to_string(),
- },
- )
- .await
- .unwrap();
- let role = roles::Model::find_by_user(&boot.app_context.db, &new_user).await;
- assert!(role.is_err());
-}
diff --git a/examples/demo/tests/models/snapshots/can_create_with_password@users.snap b/examples/demo/tests/models/snapshots/can_create_with_password@users.snap
deleted file mode 100644
index 6e66fd35a..000000000
--- a/examples/demo/tests/models/snapshots/can_create_with_password@users.snap
+++ /dev/null
@@ -1,21 +0,0 @@
----
-source: tests/models/users.rs
-expression: res
----
-Ok(
- Model {
- created_at: DATE,
- updated_at: DATE,
- id: ID
- pid: PID,
- email: "test@framework.com",
- password: "PASSWORD",
- api_key: "lo-PID",
- name: "framework",
- reset_token: None,
- reset_sent_at: None,
- email_verification_token: None,
- email_verification_sent_at: None,
- email_verified_at: None,
- },
-)
diff --git a/examples/demo/tests/models/snapshots/can_find_by_email-2@users.snap b/examples/demo/tests/models/snapshots/can_find_by_email-2@users.snap
deleted file mode 100644
index 83dc06e1d..000000000
--- a/examples/demo/tests/models/snapshots/can_find_by_email-2@users.snap
+++ /dev/null
@@ -1,7 +0,0 @@
----
-source: tests/models/main.rs
-expression: non_existing_user_results
----
-Err(
- EntityNotFound,
-)
diff --git a/examples/demo/tests/models/snapshots/can_find_by_email@users-2.snap b/examples/demo/tests/models/snapshots/can_find_by_email@users-2.snap
deleted file mode 100644
index 25c700a5a..000000000
--- a/examples/demo/tests/models/snapshots/can_find_by_email@users-2.snap
+++ /dev/null
@@ -1,7 +0,0 @@
----
-source: tests/models/users.rs
-expression: non_existing_user_results
----
-Err(
- EntityNotFound,
-)
diff --git a/examples/demo/tests/models/snapshots/can_find_by_email@users.snap b/examples/demo/tests/models/snapshots/can_find_by_email@users.snap
deleted file mode 100644
index 1c6295990..000000000
--- a/examples/demo/tests/models/snapshots/can_find_by_email@users.snap
+++ /dev/null
@@ -1,21 +0,0 @@
----
-source: tests/models/users.rs
-expression: existing_user
----
-Ok(
- Model {
- created_at: 2023-11-12T12:34:56.789,
- updated_at: 2023-11-12T12:34:56.789,
- id: 1,
- pid: 11111111-1111-1111-1111-111111111111,
- email: "user1@example.com",
- password: "$argon2id$v=19$m=19456,t=2,p=1$JkBeJfBWoNlj4D684c568g$bKRQ+Ud8qwYGIaAu+x+4flGHPS4WJh3ylUUV4sEtDBY",
- api_key: "lo-95ec80d7-cb60-4b70-9b4b-9ef74cb88758",
- name: "user1",
- reset_token: None,
- reset_sent_at: None,
- email_verification_token: None,
- email_verification_sent_at: None,
- email_verified_at: None,
- },
-)
diff --git a/examples/demo/tests/models/snapshots/can_find_by_pid-2@users.snap b/examples/demo/tests/models/snapshots/can_find_by_pid-2@users.snap
deleted file mode 100644
index 83dc06e1d..000000000
--- a/examples/demo/tests/models/snapshots/can_find_by_pid-2@users.snap
+++ /dev/null
@@ -1,7 +0,0 @@
----
-source: tests/models/main.rs
-expression: non_existing_user_results
----
-Err(
- EntityNotFound,
-)
diff --git a/examples/demo/tests/models/snapshots/can_find_by_pid@users-2.snap b/examples/demo/tests/models/snapshots/can_find_by_pid@users-2.snap
deleted file mode 100644
index 25c700a5a..000000000
--- a/examples/demo/tests/models/snapshots/can_find_by_pid@users-2.snap
+++ /dev/null
@@ -1,7 +0,0 @@
----
-source: tests/models/users.rs
-expression: non_existing_user_results
----
-Err(
- EntityNotFound,
-)
diff --git a/examples/demo/tests/models/snapshots/can_find_by_pid@users.snap b/examples/demo/tests/models/snapshots/can_find_by_pid@users.snap
deleted file mode 100644
index 1c6295990..000000000
--- a/examples/demo/tests/models/snapshots/can_find_by_pid@users.snap
+++ /dev/null
@@ -1,21 +0,0 @@
----
-source: tests/models/users.rs
-expression: existing_user
----
-Ok(
- Model {
- created_at: 2023-11-12T12:34:56.789,
- updated_at: 2023-11-12T12:34:56.789,
- id: 1,
- pid: 11111111-1111-1111-1111-111111111111,
- email: "user1@example.com",
- password: "$argon2id$v=19$m=19456,t=2,p=1$JkBeJfBWoNlj4D684c568g$bKRQ+Ud8qwYGIaAu+x+4flGHPS4WJh3ylUUV4sEtDBY",
- api_key: "lo-95ec80d7-cb60-4b70-9b4b-9ef74cb88758",
- name: "user1",
- reset_token: None,
- reset_sent_at: None,
- email_verification_token: None,
- email_verification_sent_at: None,
- email_verified_at: None,
- },
-)
diff --git a/examples/demo/tests/models/snapshots/can_validate_model@users.snap b/examples/demo/tests/models/snapshots/can_validate_model@users.snap
deleted file mode 100644
index 708479af8..000000000
--- a/examples/demo/tests/models/snapshots/can_validate_model@users.snap
+++ /dev/null
@@ -1,9 +0,0 @@
----
-source: tests/models/users.rs
-expression: res
----
-Err(
- Custom(
- "{\"email\":[{\"code\":\"invalid email\",\"message\":null}],\"name\":[{\"code\":\"length\",\"message\":\"Name must be at least 2 characters long.\"}]}",
- ),
-)
diff --git a/examples/demo/tests/models/snapshots/handle_create_with_password_with_duplicate@users.snap b/examples/demo/tests/models/snapshots/handle_create_with_password_with_duplicate@users.snap
deleted file mode 100644
index ff28ea196..000000000
--- a/examples/demo/tests/models/snapshots/handle_create_with_password_with_duplicate@users.snap
+++ /dev/null
@@ -1,7 +0,0 @@
----
-source: tests/models/users.rs
-expression: new_user
----
-Err(
- EntityAlreadyExists,
-)
diff --git a/examples/demo/tests/models/users.rs b/examples/demo/tests/models/users.rs
deleted file mode 100644
index 81d5fef71..000000000
--- a/examples/demo/tests/models/users.rs
+++ /dev/null
@@ -1,223 +0,0 @@
-use demo_app::{
- app::App,
- models::users::{self, Model, RegisterParams},
-};
-use insta::assert_debug_snapshot;
-use loco_rs::{model::ModelError, prelude::*};
-use sea_orm::{ActiveModelTrait, ActiveValue, IntoActiveModel};
-use serial_test::serial;
-
-macro_rules! configure_insta {
- ($($expr:expr),*) => {
- let mut settings = insta::Settings::clone_current();
- settings.set_prepend_module_to_snapshot(false);
- settings.set_snapshot_suffix("users");
- let _guard = settings.bind_to_scope();
- };
-}
-
-#[tokio::test]
-#[serial]
-async fn test_can_validate_model() {
- configure_insta!();
-
- let boot = boot_test::().await.unwrap();
-
- let res = users::ActiveModel {
- name: ActiveValue::set("1".to_string()),
- email: ActiveValue::set("invalid-email".to_string()),
- ..Default::default()
- }
- .insert(&boot.app_context.db)
- .await;
-
- assert_debug_snapshot!(res);
-}
-
-#[tokio::test]
-#[serial]
-async fn can_create_with_password() {
- configure_insta!();
-
- let boot = boot_test::().await.unwrap();
-
- let params = RegisterParams {
- email: "test@framework.com".to_string(),
- password: "1234".to_string(),
- name: "framework".to_string(),
- };
- let res = Model::create_with_password(&boot.app_context.db, ¶ms).await;
-
- insta::with_settings!({
- filters => cleanup_user_model()
- }, {
- assert_debug_snapshot!(res);
- });
-}
-
-#[tokio::test]
-#[serial]
-async fn handle_create_with_password_with_duplicate() {
- configure_insta!();
-
- let boot = boot_test::().await.unwrap();
- seed::(&boot.app_context).await.unwrap();
-
- let new_user: Result = Model::create_with_password(
- &boot.app_context.db,
- &RegisterParams {
- email: "user1@example.com".to_string(),
- password: "1234".to_string(),
- name: "framework".to_string(),
- },
- )
- .await;
- assert_debug_snapshot!(new_user);
-}
-
-#[tokio::test]
-#[serial]
-async fn can_find_by_email() {
- configure_insta!();
-
- let boot = boot_test::().await.unwrap();
- seed::(&boot.app_context).await.unwrap();
-
- let existing_user = Model::find_by_email(&boot.app_context.db, "user1@example.com").await;
- let non_existing_user_results =
- Model::find_by_email(&boot.app_context.db, "un@existing-email.com").await;
-
- assert_debug_snapshot!(existing_user);
- assert_debug_snapshot!(non_existing_user_results);
-}
-
-#[tokio::test]
-#[serial]
-async fn can_find_by_pid() {
- configure_insta!();
-
- let boot = boot_test::().await.unwrap();
- seed::(&boot.app_context).await.unwrap();
-
- let existing_user =
- Model::find_by_pid(&boot.app_context.db, "11111111-1111-1111-1111-111111111111").await;
- let non_existing_user_results =
- Model::find_by_email(&boot.app_context.db, "23232323-2323-2323-2323-232323232323").await;
-
- assert_debug_snapshot!(existing_user);
- assert_debug_snapshot!(non_existing_user_results);
-}
-
-#[tokio::test]
-#[serial]
-async fn can_verification_token() {
- configure_insta!();
-
- let boot = boot_test::().await.unwrap();
- seed::(&boot.app_context).await.unwrap();
-
- let user = Model::find_by_pid(&boot.app_context.db, "11111111-1111-1111-1111-111111111111")
- .await
- .unwrap();
-
- assert!(user.email_verification_sent_at.is_none());
- assert!(user.email_verification_token.is_none());
-
- assert!(user
- .into_active_model()
- .set_email_verification_sent(&boot.app_context.db)
- .await
- .is_ok());
-
- let user = Model::find_by_pid(&boot.app_context.db, "11111111-1111-1111-1111-111111111111")
- .await
- .unwrap();
-
- assert!(user.email_verification_sent_at.is_some());
- assert!(user.email_verification_token.is_some());
-}
-
-#[tokio::test]
-#[serial]
-async fn can_set_forgot_password_sent() {
- configure_insta!();
-
- let boot = boot_test::().await.unwrap();
- seed::(&boot.app_context).await.unwrap();
-
- let user = Model::find_by_pid(&boot.app_context.db, "11111111-1111-1111-1111-111111111111")
- .await
- .unwrap();
-
- assert!(user.reset_sent_at.is_none());
- assert!(user.reset_token.is_none());
-
- assert!(user
- .into_active_model()
- .set_forgot_password_sent(&boot.app_context.db)
- .await
- .is_ok());
-
- let user = Model::find_by_pid(&boot.app_context.db, "11111111-1111-1111-1111-111111111111")
- .await
- .unwrap();
-
- assert!(user.reset_sent_at.is_some());
- assert!(user.reset_token.is_some());
-}
-
-#[tokio::test]
-#[serial]
-async fn can_verified() {
- configure_insta!();
-
- let boot = boot_test::().await.unwrap();
- seed::(&boot.app_context).await.unwrap();
-
- let user = Model::find_by_pid(&boot.app_context.db, "11111111-1111-1111-1111-111111111111")
- .await
- .unwrap();
-
- assert!(user.email_verified_at.is_none());
-
- assert!(user
- .into_active_model()
- .verified(&boot.app_context.db)
- .await
- .is_ok());
-
- let user = Model::find_by_pid(&boot.app_context.db, "11111111-1111-1111-1111-111111111111")
- .await
- .unwrap();
-
- assert!(user.email_verified_at.is_some());
-}
-
-#[tokio::test]
-#[serial]
-async fn can_reset_password() {
- configure_insta!();
-
- let boot = boot_test::().await.unwrap();
- seed::(&boot.app_context).await.unwrap();
-
- let user = Model::find_by_pid(&boot.app_context.db, "11111111-1111-1111-1111-111111111111")
- .await
- .unwrap();
-
- assert!(user.verify_password("12341234"));
-
- assert!(user
- .clone()
- .into_active_model()
- .reset_password(&boot.app_context.db, "new-password")
- .await
- .is_ok());
-
- assert!(
- Model::find_by_pid(&boot.app_context.db, "11111111-1111-1111-1111-111111111111")
- .await
- .unwrap()
- .verify_password("new-password")
- );
-}
diff --git a/examples/demo/tests/models/users_roles.rs b/examples/demo/tests/models/users_roles.rs
deleted file mode 100644
index 619464fdb..000000000
--- a/examples/demo/tests/models/users_roles.rs
+++ /dev/null
@@ -1,96 +0,0 @@
-use demo_app::{
- app::App,
- models::{roles, sea_orm_active_enums, users, users::RegisterParams, users_roles},
-};
-use loco_rs::prelude::*;
-use sea_orm::ColumnTrait;
-use serial_test::serial;
-macro_rules! configure_insta {
- ($($expr:expr),*) => {
- let mut settings = insta::Settings::clone_current();
- settings.set_prepend_module_to_snapshot(false);
- settings.set_snapshot_suffix("users_roles");
- let _guard = settings.bind_to_scope();
- };
-}
-
-#[tokio::test]
-#[serial]
-async fn can_connect_user_to_user_role() {
- configure_insta!();
-
- let boot = boot_test::().await.unwrap();
- let new_user: Result = users::Model::create_with_password(
- &boot.app_context.db,
- &RegisterParams {
- email: "user1@example.com".to_string(),
- password: "1234".to_string(),
- name: "framework".to_string(),
- },
- )
- .await;
- let new_user = new_user.unwrap();
- // create role
- let role =
- roles::Model::upsert_by_name(&boot.app_context.db, sea_orm_active_enums::RolesName::User)
- .await
- .unwrap();
- // connect user to role
- let user_role =
- users_roles::Model::connect_user_to_role(&boot.app_context.db, &new_user, &role)
- .await
- .unwrap();
- assert_eq!(user_role.users_id, new_user.id);
- assert_eq!(user_role.roles_id, role.id);
-
- // Find the user role if it exists by user id
- let user_role = users_roles::Entity::find()
- .filter(users_roles::Column::UsersId.eq(new_user.id.clone()))
- .filter(users_roles::Column::RolesId.eq(role.id.clone()))
- .one(&boot.app_context.db)
- .await
- .unwrap();
- assert!(user_role.is_some());
- let user_role = user_role.unwrap();
- assert_eq!(user_role.users_id, new_user.id);
- assert_eq!(user_role.roles_id, role.id);
-}
-
-#[tokio::test]
-#[serial]
-async fn can_connect_user_to_admin_role() {
- configure_insta!();
-
- let boot = boot_test::().await.unwrap();
- let new_user: Result = users::Model::create_with_password(
- &boot.app_context.db,
- &RegisterParams {
- email: "user1@example.com".to_string(),
- password: "1234".to_string(),
- name: "framework".to_string(),
- },
- )
- .await;
- let new_user = new_user.unwrap();
- // create role
- let role =
- roles::Model::upsert_by_name(&boot.app_context.db, sea_orm_active_enums::RolesName::Admin)
- .await
- .unwrap();
- // connect user to role
- let user_role =
- users_roles::Model::connect_user_to_role(&boot.app_context.db, &new_user, &role)
- .await
- .unwrap();
- assert_eq!(user_role.users_id, new_user.id);
- assert_eq!(user_role.roles_id, role.id);
-
- // Find the user role if it exists by user id
- let user_role = users_roles::Entity::find()
- .filter(users_roles::Column::UsersId.eq(new_user.id.clone()))
- .filter(users_roles::Column::RolesId.eq(role.id.clone()))
- .one(&boot.app_context.db)
- .await
- .unwrap();
- assert!(user_role.is_some());
-}
diff --git a/examples/demo/tests/requests/auth.rs b/examples/demo/tests/requests/auth.rs
deleted file mode 100644
index 93bb6bde4..000000000
--- a/examples/demo/tests/requests/auth.rs
+++ /dev/null
@@ -1,189 +0,0 @@
-use demo_app::{app::App, models::users};
-use insta::{assert_debug_snapshot, with_settings};
-use loco_rs::prelude::*;
-use rstest::rstest;
-use serial_test::serial;
-
-use super::prepare_data;
-
-// TODO: see how to dedup / extract this to app-local test utils
-// not to framework, because that would require a runtime dep on insta
-macro_rules! configure_insta {
- ($($expr:expr),*) => {
- let mut settings = insta::Settings::clone_current();
- settings.set_prepend_module_to_snapshot(false);
- settings.set_snapshot_suffix("auth_request");
- let _guard = settings.bind_to_scope();
- };
-}
-
-#[tokio::test]
-#[serial]
-async fn can_register() {
- configure_insta!();
-
- request::(|request, ctx| async move {
- let email = "test@loco.com";
- let payload = serde_json::json!({
- "name": "loco",
- "email": email,
- "password": "12341234"
- });
-
- let _response = request.post("/auth/register").json(&payload).await;
- let saved_user = users::Model::find_by_email(&ctx.db, email).await;
-
- with_settings!({
- filters => cleanup_user_model()
- }, {
- assert_debug_snapshot!(saved_user);
- });
-
- with_settings!({
- filters => cleanup_email()
- }, {
- assert_debug_snapshot!(ctx.mailer.unwrap().deliveries());
- });
- })
- .await;
-}
-
-#[rstest]
-#[case("login_with_valid_password", "12341234")]
-#[case("login_with_invalid_password", "invalid-password")]
-#[tokio::test]
-#[serial]
-async fn can_login_with_verify(#[case] test_name: &str, #[case] password: &str) {
- configure_insta!();
-
- request::(|request, ctx| async move {
- let email = "test@loco.com";
- let register_payload = serde_json::json!({
- "name": "loco",
- "email": email,
- "password": "12341234"
- });
-
- //Creating a new user
- _ = request.post("/auth/register").json(®ister_payload).await;
-
- let user = users::Model::find_by_email(&ctx.db, email).await.unwrap();
- let verify_payload = serde_json::json!({
- "token": user.email_verification_token,
- });
- request.post("/auth/verify").json(&verify_payload).await;
-
- //verify user request
- let response = request
- .post("/auth/login")
- .json(&serde_json::json!({
- "email": email,
- "password": password
- }))
- .await;
-
- // Make sure email_verified_at is set
- assert!(users::Model::find_by_email(&ctx.db, email)
- .await
- .unwrap()
- .email_verified_at
- .is_some());
-
- with_settings!({
- filters => cleanup_user_model()
- }, {
- assert_debug_snapshot!(test_name, (response.status_code(), response.text()));
- });
- })
- .await;
-}
-
-#[tokio::test]
-#[serial]
-async fn can_login_without_verify() {
- configure_insta!();
-
- request::(|request, _ctx| async move {
- let email = "test@loco.com";
- let password = "12341234";
- let register_payload = serde_json::json!({
- "name": "loco",
- "email": email,
- "password": password
- });
-
- //Creating a new user
- _ = request.post("/auth/register").json(®ister_payload).await;
-
- //verify user request
- let response = request
- .post("/auth/login")
- .json(&serde_json::json!({
- "email": email,
- "password": password
- }))
- .await;
-
- with_settings!({
- filters => cleanup_user_model()
- }, {
- assert_debug_snapshot!((response.status_code(), response.text()));
- });
- })
- .await;
-}
-
-#[tokio::test]
-#[serial]
-async fn can_reset_password() {
- configure_insta!();
-
- request::(|request, ctx| async move {
- let login_data = prepare_data::init_user_login(&request, &ctx).await;
-
- let forgot_payload = serde_json::json!({
- "email": login_data.user.email,
- });
- _ = request.post("/auth/forgot").json(&forgot_payload).await;
-
- let user = users::Model::find_by_email(&ctx.db, &login_data.user.email)
- .await
- .unwrap();
- assert!(user.reset_token.is_some());
- assert!(user.reset_sent_at.is_some());
-
- let new_password = "new-password";
- let reset_payload = serde_json::json!({
- "token": user.reset_token,
- "password": new_password,
- });
-
- let reset_response = request.post("/auth/reset").json(&reset_payload).await;
-
- let user = users::Model::find_by_email(&ctx.db, &user.email)
- .await
- .unwrap();
-
- assert!(user.reset_token.is_none());
- assert!(user.reset_sent_at.is_none());
-
- assert_debug_snapshot!((reset_response.status_code(), reset_response.text()));
-
- let response = request
- .post("/auth/login")
- .json(&serde_json::json!({
- "email": user.email,
- "password": new_password
- }))
- .await;
-
- assert_eq!(response.status_code(), 200);
-
- with_settings!({
- filters => cleanup_email()
- }, {
- assert_debug_snapshot!(ctx.mailer.unwrap().deliveries());
- });
- })
- .await;
-}
diff --git a/examples/demo/tests/requests/cache.rs b/examples/demo/tests/requests/cache.rs
deleted file mode 100644
index 5b69f0fd2..000000000
--- a/examples/demo/tests/requests/cache.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-use demo_app::{app::App, models::users};
-use insta::assert_debug_snapshot;
-use loco_rs::testing::prelude::*;
-use sea_orm::ModelTrait;
-use serial_test::serial;
-
-// TODO: see how to dedup / extract this to app-local test utils
-// not to framework, because that would require a runtime dep on insta
-macro_rules! configure_insta {
- ($($expr:expr),*) => {
- let mut settings = insta::Settings::clone_current();
- settings.set_prepend_module_to_snapshot(false);
- settings.set_snapshot_suffix("cache");
- let _guard = settings.bind_to_scope();
- };
-}
-
-#[tokio::test]
-#[serial]
-async fn ping() {
- configure_insta!();
-
- request::(|request, _ctx| async move {
- let response = request.get("cache").await;
- assert_debug_snapshot!("key_not_exists", (response.text(), response.status_code()));
- let response = request.post("/cache/insert").await;
- assert_debug_snapshot!("insert", (response.text(), response.status_code()));
- let response = request.get("cache").await;
- assert_debug_snapshot!("read_cache_key", (response.text(), response.status_code()));
- })
- .await;
-}
-
-#[tokio::test]
-#[serial]
-async fn can_get_or_insert() {
- configure_insta!();
-
- request::(|request, ctx| async move {
- seed::(&ctx).await.unwrap();
- let response = request.get("/cache/get_or_insert").await;
- assert_eq!(response.text(), "user1");
-
- let user = users::Model::find_by_email(&ctx.db, "user1@example.com")
- .await
- .unwrap();
- user.delete(&ctx.db).await.unwrap();
- let response = request.get("/cache/get_or_insert").await;
- assert_eq!(response.text(), "user1");
- })
- .await;
-}
diff --git a/examples/demo/tests/requests/mod.rs b/examples/demo/tests/requests/mod.rs
deleted file mode 100644
index fbd7fb8b4..000000000
--- a/examples/demo/tests/requests/mod.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-mod auth;
-mod cache;
-mod mylayer;
-mod notes;
-mod ping;
-mod prepare_data;
-mod responses;
-mod upload;
-mod user;
-mod view_engine;
diff --git a/examples/demo/tests/requests/mylayer.rs b/examples/demo/tests/requests/mylayer.rs
deleted file mode 100644
index 27b8a5e72..000000000
--- a/examples/demo/tests/requests/mylayer.rs
+++ /dev/null
@@ -1,179 +0,0 @@
-use demo_app::{app::App, views::user::UserResponse};
-use loco_rs::testing::prelude::*;
-use serial_test::serial;
-
-use crate::requests::prepare_data;
-macro_rules! configure_insta {
- ($($expr:expr),*) => {
- let mut settings = insta::Settings::clone_current();
- settings.set_prepend_module_to_snapshot(false);
- settings.set_snapshot_suffix("mylayer_request");
- let _guard = settings.bind_to_scope();
- };
-}
-#[tokio::test]
-#[serial]
-async fn cannot_get_echo_when_no_role_assigned() {
- configure_insta!();
- request::(|request, ctx| async move {
- let user = prepare_data::init_user_login(&request, &ctx).await;
- let (auth_key, auth_value) = prepare_data::auth_header(&user.token);
- let response = request
- .get("/mylayer/echo")
- .add_header(auth_key, auth_value)
- .await;
- assert_eq!(response.status_code(), 401);
- })
- .await;
-}
-
-#[tokio::test]
-#[serial]
-async fn can_get_echo_when_admin_role_assigned() {
- configure_insta!();
- request::(|request, ctx| async move {
- let user = prepare_data::init_user_login(&request, &ctx).await;
- let (auth_key, auth_value) = prepare_data::auth_header(&user.token);
- let response = request
- .post("/user/convert/admin")
- .add_header(auth_key.clone(), auth_value.clone())
- .await;
- assert_eq!(response.status_code(), 200);
- let body = response.json::();
- assert_eq!(body.role, "Admin");
-
- let response = request
- .get("/mylayer/echo")
- .add_header(auth_key.clone(), auth_value.clone())
- .await;
- assert_eq!(response.status_code(), 200);
- })
- .await;
-}
-#[tokio::test]
-#[serial]
-async fn can_get_echo_when_user_role_assigned() {
- configure_insta!();
- request::(|request, ctx| async move {
- let user = prepare_data::init_user_login(&request, &ctx).await;
- let (auth_key, auth_value) = prepare_data::auth_header(&user.token);
- let response = request
- .post("/user/convert/user")
- .add_header(auth_key.clone(), auth_value.clone())
- .await;
- assert_eq!(response.status_code(), 200);
- let body = response.json::();
- assert_eq!(body.role, "User");
-
- let response = request
- .get("/mylayer/echo")
- .add_header(auth_key.clone(), auth_value.clone())
- .await;
- assert_eq!(response.status_code(), 200);
- })
- .await;
-}
-
-#[tokio::test]
-#[serial]
-async fn cannot_get_admin_when_no_role() {
- configure_insta!();
- request::(|request, ctx| async move {
- let user = prepare_data::init_user_login(&request, &ctx).await;
- let (auth_key, auth_value) = prepare_data::auth_header(&user.token);
- let response = request
- .get("/mylayer/admin")
- .add_header(auth_key, auth_value)
- .await;
- assert_eq!(response.status_code(), 401);
- })
- .await;
-}
-
-#[tokio::test]
-#[serial]
-async fn cannot_get_admin_when_user_role_assigned() {
- configure_insta!();
- request::(|request, ctx| async move {
- let user = prepare_data::init_user_login(&request, &ctx).await;
- let (auth_key, auth_value) = prepare_data::auth_header(&user.token);
- let response = request
- .post("/user/convert/user")
- .add_header(auth_key.clone(), auth_value.clone())
- .await;
- assert_eq!(response.status_code(), 200);
- let body = response.json::();
- assert_eq!(body.role, "User");
-
- let response = request
- .get("/mylayer/admin")
- .add_header(auth_key.clone(), auth_value.clone())
- .await;
- assert_eq!(response.status_code(), 401);
- })
- .await;
-}
-
-#[tokio::test]
-#[serial]
-async fn can_get_admin_when_admin_role_assigned() {
- configure_insta!();
- request::(|request, ctx| async move {
- let user = prepare_data::init_user_login(&request, &ctx).await;
- let (auth_key, auth_value) = prepare_data::auth_header(&user.token);
- let response = request
- .post("/user/convert/admin")
- .add_header(auth_key.clone(), auth_value.clone())
- .await;
- assert_eq!(response.status_code(), 200);
- let body = response.json::();
- assert_eq!(body.role, "Admin");
-
- let response = request
- .get("/mylayer/admin")
- .add_header(auth_key.clone(), auth_value.clone())
- .await;
- assert_eq!(response.status_code(), 200);
- })
- .await;
-}
-
-#[tokio::test]
-#[serial]
-async fn cannot_get_user_when_no_role() {
- configure_insta!();
- request::(|request, ctx| async move {
- let user = prepare_data::init_user_login(&request, &ctx).await;
- let (auth_key, auth_value) = prepare_data::auth_header(&user.token);
- let response = request
- .get("/mylayer/user")
- .add_header(auth_key, auth_value)
- .await;
- assert_eq!(response.status_code(), 401);
- })
- .await;
-}
-
-#[tokio::test]
-#[serial]
-async fn can_get_user_when_user_role_assigned() {
- configure_insta!();
- request::(|request, ctx| async move {
- let user = prepare_data::init_user_login(&request, &ctx).await;
- let (auth_key, auth_value) = prepare_data::auth_header(&user.token);
- let response = request
- .post("/user/convert/user")
- .add_header(auth_key.clone(), auth_value.clone())
- .await;
- assert_eq!(response.status_code(), 200);
- let body = response.json::();
- assert_eq!(body.role, "User");
-
- let response = request
- .get("/mylayer/user")
- .add_header(auth_key.clone(), auth_value.clone())
- .await;
- assert_eq!(response.status_code(), 200);
- })
- .await;
-}
diff --git a/examples/demo/tests/requests/notes.rs b/examples/demo/tests/requests/notes.rs
deleted file mode 100644
index 99d762674..000000000
--- a/examples/demo/tests/requests/notes.rs
+++ /dev/null
@@ -1,129 +0,0 @@
-use demo_app::{app::App, models::_entities::notes::Entity};
-use insta::{assert_debug_snapshot, with_settings};
-use loco_rs::testing::prelude::*;
-use rstest::rstest;
-use sea_orm::entity::prelude::*;
-use serial_test::serial;
-
-// TODO: see how to dedup / extract this to app-local test utils
-// not to framework, because that would require a runtime dep on insta
-macro_rules! configure_insta {
- ($($expr:expr),*) => {
- let mut settings = insta::Settings::clone_current();
- settings.set_prepend_module_to_snapshot(false);
- settings.set_snapshot_suffix("notes_request");
- let _guard = settings.bind_to_scope();
- };
-}
-
-#[rstest]
-#[case("get_notes", serde_json::json!({}))]
-#[case("get_notes_with_page_size", serde_json::json!({"page_size":"1"}))]
-#[case("get_notes_with_size_and_page", serde_json::json!({"page":"2", "page_size": "5"}))]
-#[case("get_notes_with_filters", serde_json::json!({"page":"1", "page_size": "2", "title": "%note%"}))]
-#[tokio::test]
-#[serial]
-async fn can_get_notes(#[case] test_name: &str, #[case] params: serde_json::Value) {
- configure_insta!();
-
- request::(|request, ctx| async move {
- seed::(&ctx).await.unwrap();
-
- let notes = request.get("notes").add_query_params(params).await;
-
- with_settings!({
- filters => {
- let mut combined_filters = get_cleanup_date().clone();
- combined_filters.extend(vec![(r#"\"id\\":\d+"#, r#""id\":ID"#)]);
- combined_filters
- }
- }, {
- assert_debug_snapshot!(
- test_name, (notes.status_code(), notes.text())
- );
- });
- })
- .await;
-}
-
-#[tokio::test]
-#[serial]
-async fn can_add_note() {
- configure_insta!();
-
- request::(|request, _ctx| async move {
- let payload = serde_json::json!({
- "title": "loco",
- "content": "loco note test",
- });
-
- let add_note_request = request.post("notes").json(&payload).await;
-
- with_settings!({
- filters => {
- let mut combined_filters = get_cleanup_date().clone();
- combined_filters.extend(vec![(r#"\"id\\":\d+"#, r#""id\":ID"#)]);
- combined_filters
- }
- }, {
- assert_debug_snapshot!(
- (add_note_request.status_code(), add_note_request.text())
- );
- });
- })
- .await;
-}
-
-#[tokio::test]
-#[serial]
-async fn can_get_note() {
- configure_insta!();
-
- request::(|request, ctx| async move {
- seed::(&ctx).await.unwrap();
-
- let add_note_request = request.get("/notes/1").await;
-
- with_settings!({
- filters => {
- let mut combined_filters = get_cleanup_date().clone();
- combined_filters.extend(vec![(r#"\"id\\":\d+"#, r#""id\":ID"#)]);
- combined_filters
- }
- }, {
- assert_debug_snapshot!(
- (add_note_request.status_code(), add_note_request.text())
- );
- });
- })
- .await;
-}
-
-#[tokio::test]
-#[serial]
-async fn can_delete_note() {
- configure_insta!();
-
- request::(|request, ctx| async move {
- seed::(&ctx).await.unwrap();
-
- let count_before_delete = Entity::find().all(&ctx.db).await.unwrap().len();
- let delete_note_request = request.delete("/notes/1").await;
-
- with_settings!({
- filters => {
- let mut combined_filters = get_cleanup_date().clone();
- combined_filters.extend(vec![(r#"\"id\\":\d+"#, r#""id\":ID"#)]);
- combined_filters
- }
- }, {
- assert_debug_snapshot!(
- (delete_note_request.status_code(), delete_note_request.text())
- );
- });
-
- let count_after_delete = Entity::find().all(&ctx.db).await.unwrap().len();
- assert_eq!(count_after_delete, count_before_delete - 1);
- })
- .await;
-}
diff --git a/examples/demo/tests/requests/ping.rs b/examples/demo/tests/requests/ping.rs
deleted file mode 100644
index 84bfae8f6..000000000
--- a/examples/demo/tests/requests/ping.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-use demo_app::app::App;
-use insta::assert_debug_snapshot;
-use loco_rs::testing::prelude::*;
-use rstest::rstest;
-
-// TODO: see how to dedup / extract this to app-local test utils
-// not to framework, because that would require a runtime dep on insta
-macro_rules! configure_insta {
- ($($expr:expr),*) => {
- let mut settings = insta::Settings::clone_current();
- settings.set_prepend_module_to_snapshot(false);
- settings.set_snapshot_suffix("ping_request");
- let _guard = settings.bind_to_scope();
- };
-}
-
-// This tests the `_ping` endpoint, as well as the `NormalizePathLayer` that
-// removes trailing slashes from the request path.
-#[rstest]
-#[case("ping", "/_ping")]
-#[tokio::test]
-async fn ping(#[case] test_name: &str, #[case] path: &str) {
- configure_insta!();
-
- request::(|request, _ctx| async move {
- let response = request.get(path).await;
-
- assert_debug_snapshot!(test_name, (response.text(), response.status_code()));
- })
- .await;
-}
diff --git a/examples/demo/tests/requests/prepare_data.rs b/examples/demo/tests/requests/prepare_data.rs
deleted file mode 100644
index 8732efe6e..000000000
--- a/examples/demo/tests/requests/prepare_data.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-use axum::http::{HeaderName, HeaderValue};
-use demo_app::{models::users, views::auth::UserSession};
-use loco_rs::{app::AppContext, TestServer};
-
-const USER_EMAIL: &str = "test@loco.com";
-const USER_PASSWORD: &str = "1234";
-
-pub struct LoggedInUser {
- pub user: users::Model,
- pub token: String,
-}
-
-pub async fn init_user_login(request: &TestServer, ctx: &AppContext) -> LoggedInUser {
- let register_payload = serde_json::json!({
- "name": "loco",
- "email": USER_EMAIL,
- "password": USER_PASSWORD
- });
-
- //Creating a new user
- let _res = request.post("/auth/register").json(®ister_payload).await;
- let user = users::Model::find_by_email(&ctx.db, USER_EMAIL)
- .await
- .unwrap();
-
- let verify_payload = serde_json::json!({
- "token": user.email_verification_token,
- });
-
- request.post("/auth/verify").json(&verify_payload).await;
-
- let response = request
- .post("/auth/login")
- .json(&serde_json::json!({
- "email": USER_EMAIL,
- "password": USER_PASSWORD
- }))
- .await;
-
- let session: UserSession = serde_json::from_str(&response.text()).unwrap();
-
- LoggedInUser {
- user: users::Model::find_by_email(&ctx.db, USER_EMAIL)
- .await
- .unwrap(),
- token: session.token,
- }
-}
-
-pub fn auth_header(token: &str) -> (HeaderName, HeaderValue) {
- let auth_header_value = HeaderValue::from_str(&format!("Bearer {}", &token)).unwrap();
-
- (HeaderName::from_static("authorization"), auth_header_value)
-}
diff --git a/examples/demo/tests/requests/responses.rs b/examples/demo/tests/requests/responses.rs
deleted file mode 100644
index 05ae12f61..000000000
--- a/examples/demo/tests/requests/responses.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-use axum::http::HeaderMap;
-use demo_app::app::App;
-use insta::assert_debug_snapshot;
-use loco_rs::testing::prelude::*;
-use rstest::rstest;
-use serial_test::serial;
-// TODO: see how to dedup / extract this to app-local test utils
-// not to framework, because that would require a runtime dep on insta
-macro_rules! configure_insta {
- ($($expr:expr),*) => {
- let mut settings = insta::Settings::clone_current();
- settings.set_prepend_module_to_snapshot(false);
- settings.set_snapshot_suffix("auth_request");
- let _guard = settings.bind_to_scope();
- };
-}
-
-#[rstest]
-#[case("/response/empty")]
-#[case("/response/text")]
-#[case("/response/json")]
-#[case("/response/empty_json")]
-#[case("/response/html")]
-#[case("/response/redirect")]
-#[case("/response/render_with_status_code")]
-#[case("/response/etag")]
-#[case("/response/set_cookie")]
-#[tokio::test]
-#[serial]
-async fn can_return_different_responses(#[case] uri: &str) {
- configure_insta!();
- request::(|request, _ctx| async move {
- let response = request.get(uri).await;
-
- let mut headers = HeaderMap::new();
- for (key, value) in response.headers() {
- if ["content-type", "x-powered-by", "location", "etag"].contains(&key.as_str())
- || value.to_str().unwrap().contains("loco-cookie-name")
- {
- headers.insert(key, value.clone());
- }
- }
-
- assert_debug_snapshot!(
- uri.replace('/', "_"),
- (response.status_code(), response.text(), headers,)
- );
- })
- .await;
-}
diff --git a/examples/demo/tests/requests/snapshots/_response_empty@auth_request.snap b/examples/demo/tests/requests/snapshots/_response_empty@auth_request.snap
deleted file mode 100644
index 2b04ccfd9..000000000
--- a/examples/demo/tests/requests/snapshots/_response_empty@auth_request.snap
+++ /dev/null
@@ -1,11 +0,0 @@
----
-source: tests/requests/responses.rs
-expression: "(response.status_code(), response.text(), headers)"
----
-(
- 200,
- "",
- {
- "x-powered-by": "loco.rs",
- },
-)
diff --git a/examples/demo/tests/requests/snapshots/_response_empty_json@auth_request.snap b/examples/demo/tests/requests/snapshots/_response_empty_json@auth_request.snap
deleted file mode 100644
index 6876b7467..000000000
--- a/examples/demo/tests/requests/snapshots/_response_empty_json@auth_request.snap
+++ /dev/null
@@ -1,12 +0,0 @@
----
-source: tests/requests/responses.rs
-expression: "(response.status_code(), response.text(), headers)"
----
-(
- 200,
- "{}",
- {
- "content-type": "application/json",
- "x-powered-by": "loco.rs",
- },
-)
diff --git a/examples/demo/tests/requests/snapshots/_response_etag@auth_request.snap b/examples/demo/tests/requests/snapshots/_response_etag@auth_request.snap
deleted file mode 100644
index fa17279a4..000000000
--- a/examples/demo/tests/requests/snapshots/_response_etag@auth_request.snap
+++ /dev/null
@@ -1,12 +0,0 @@
----
-source: tests/requests/responses.rs
-expression: "(response.status_code(), response.text(), headers)"
----
-(
- 200,
- "",
- {
- "etag": "loco-etag",
- "x-powered-by": "loco.rs",
- },
-)
diff --git a/examples/demo/tests/requests/snapshots/_response_html@auth_request.snap b/examples/demo/tests/requests/snapshots/_response_html@auth_request.snap
deleted file mode 100644
index 8a6c0d7b5..000000000
--- a/examples/demo/tests/requests/snapshots/_response_html@auth_request.snap
+++ /dev/null
@@ -1,12 +0,0 @@
----
-source: tests/requests/responses.rs
-expression: "(response.status_code(), response.text(), headers)"
----
-(
- 200,
- "hello, world",
- {
- "content-type": "text/html; charset=utf-8",
- "x-powered-by": "loco.rs",
- },
-)
diff --git a/examples/demo/tests/requests/snapshots/_response_json@auth_request.snap b/examples/demo/tests/requests/snapshots/_response_json@auth_request.snap
deleted file mode 100644
index 7d47f003e..000000000
--- a/examples/demo/tests/requests/snapshots/_response_json@auth_request.snap
+++ /dev/null
@@ -1,12 +0,0 @@
----
-source: tests/requests/responses.rs
-expression: "(response.status_code(), response.text(), headers)"
----
-(
- 200,
- "{\"ok\":true}",
- {
- "content-type": "application/json",
- "x-powered-by": "loco.rs",
- },
-)
diff --git a/examples/demo/tests/requests/snapshots/_response_redirect@auth_request.snap b/examples/demo/tests/requests/snapshots/_response_redirect@auth_request.snap
deleted file mode 100644
index ae782b82d..000000000
--- a/examples/demo/tests/requests/snapshots/_response_redirect@auth_request.snap
+++ /dev/null
@@ -1,12 +0,0 @@
----
-source: tests/requests/responses.rs
-expression: "(response.status_code(), response.text(), headers)"
----
-(
- 303,
- "",
- {
- "location": "/dashboard",
- "x-powered-by": "loco.rs",
- },
-)
diff --git a/examples/demo/tests/requests/snapshots/_response_render_with_status_code@auth_request.snap b/examples/demo/tests/requests/snapshots/_response_render_with_status_code@auth_request.snap
deleted file mode 100644
index ac8d04a0f..000000000
--- a/examples/demo/tests/requests/snapshots/_response_render_with_status_code@auth_request.snap
+++ /dev/null
@@ -1,11 +0,0 @@
----
-source: tests/requests/responses.rs
-expression: "(response.status_code(), response.text(), headers)"
----
-(
- 201,
- "",
- {
- "x-powered-by": "loco.rs",
- },
-)
diff --git a/examples/demo/tests/requests/snapshots/_response_set_cookie@auth_request.snap b/examples/demo/tests/requests/snapshots/_response_set_cookie@auth_request.snap
deleted file mode 100644
index a04dd78c3..000000000
--- a/examples/demo/tests/requests/snapshots/_response_set_cookie@auth_request.snap
+++ /dev/null
@@ -1,13 +0,0 @@
----
-source: tests/requests/responses.rs
-expression: "(response.status_code(), response.text(), headers)"
----
-(
- 200,
- "null",
- {
- "set-cookie": "loco-cookie-name=loco-cookie-value; HttpOnly; SameSite=Strict; Secure; Path=/",
- "content-type": "application/json",
- "x-powered-by": "loco.rs",
- },
-)
diff --git a/examples/demo/tests/requests/snapshots/_response_text@auth_request.snap b/examples/demo/tests/requests/snapshots/_response_text@auth_request.snap
deleted file mode 100644
index d0935ab86..000000000
--- a/examples/demo/tests/requests/snapshots/_response_text@auth_request.snap
+++ /dev/null
@@ -1,12 +0,0 @@
----
-source: tests/requests/responses.rs
-expression: "(response.status_code(), response.text(), headers)"
----
-(
- 200,
- "Loco",
- {
- "content-type": "text/plain; charset=utf-8",
- "x-powered-by": "loco.rs",
- },
-)
diff --git a/examples/demo/tests/requests/snapshots/can_add_note@notes_request.snap b/examples/demo/tests/requests/snapshots/can_add_note@notes_request.snap
deleted file mode 100644
index 03fef48b7..000000000
--- a/examples/demo/tests/requests/snapshots/can_add_note@notes_request.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/notes.rs
-expression: "(add_note_request.status_code(), add_note_request.text())"
----
-(
- 200,
- "{\"created_at\":\"DATE\",\"updated_at\":\"DATE\",\"id\":ID,\"title\":\"loco\",\"content\":\"loco note test\"}",
-)
diff --git a/examples/demo/tests/requests/snapshots/can_convert_user_to_admin_role@auth_request.snap b/examples/demo/tests/requests/snapshots/can_convert_user_to_admin_role@auth_request.snap
deleted file mode 100644
index 01cf04062..000000000
--- a/examples/demo/tests/requests/snapshots/can_convert_user_to_admin_role@auth_request.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/user.rs
-expression: "(response.status_code(), response.text())"
----
-(
- 200,
- "{\"pid\":\"PID\",\"name\":\"loco\",\"email\":\"test@loco.com\",\"role\":\"Admin\"}",
-)
diff --git a/examples/demo/tests/requests/snapshots/can_convert_user_to_user_role@auth_request.snap b/examples/demo/tests/requests/snapshots/can_convert_user_to_user_role@auth_request.snap
deleted file mode 100644
index 19ec77244..000000000
--- a/examples/demo/tests/requests/snapshots/can_convert_user_to_user_role@auth_request.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/user.rs
-expression: "(response.status_code(), response.text())"
----
-(
- 200,
- "{\"pid\":\"PID\",\"name\":\"loco\",\"email\":\"test@loco.com\",\"role\":\"User\"}",
-)
diff --git a/examples/demo/tests/requests/snapshots/can_delete_note@notes_request.snap b/examples/demo/tests/requests/snapshots/can_delete_note@notes_request.snap
deleted file mode 100644
index 3481fc36d..000000000
--- a/examples/demo/tests/requests/snapshots/can_delete_note@notes_request.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/notes.rs
-expression: "(delete_note_request.status_code(), delete_note_request.text())"
----
-(
- 200,
- "",
-)
diff --git a/examples/demo/tests/requests/snapshots/can_get_current_user@auth_request.snap b/examples/demo/tests/requests/snapshots/can_get_current_user@auth_request.snap
deleted file mode 100644
index 85d53db17..000000000
--- a/examples/demo/tests/requests/snapshots/can_get_current_user@auth_request.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/user.rs
-expression: "(response.status_code(), response.text())"
----
-(
- 200,
- "{\"pid\":\"PID\",\"name\":\"loco\",\"email\":\"test@loco.com\"}",
-)
diff --git a/examples/demo/tests/requests/snapshots/can_get_current_user_from_cookie_auth@auth_request.snap b/examples/demo/tests/requests/snapshots/can_get_current_user_from_cookie_auth@auth_request.snap
deleted file mode 100644
index 85d53db17..000000000
--- a/examples/demo/tests/requests/snapshots/can_get_current_user_from_cookie_auth@auth_request.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/user.rs
-expression: "(response.status_code(), response.text())"
----
-(
- 200,
- "{\"pid\":\"PID\",\"name\":\"loco\",\"email\":\"test@loco.com\"}",
-)
diff --git a/examples/demo/tests/requests/snapshots/can_get_current_user_with_api_key@auth_request.snap b/examples/demo/tests/requests/snapshots/can_get_current_user_with_api_key@auth_request.snap
deleted file mode 100644
index 85d53db17..000000000
--- a/examples/demo/tests/requests/snapshots/can_get_current_user_with_api_key@auth_request.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/user.rs
-expression: "(response.status_code(), response.text())"
----
-(
- 200,
- "{\"pid\":\"PID\",\"name\":\"loco\",\"email\":\"test@loco.com\"}",
-)
diff --git a/examples/demo/tests/requests/snapshots/can_get_note@notes_request.snap b/examples/demo/tests/requests/snapshots/can_get_note@notes_request.snap
deleted file mode 100644
index a651c579a..000000000
--- a/examples/demo/tests/requests/snapshots/can_get_note@notes_request.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/notes.rs
-expression: "(add_note_request.status_code(), add_note_request.text())"
----
-(
- 200,
- "{\"created_at\":\"DATE\",\"updated_at\":\"DATE\",\"id\":ID,\"title\":null,\"content\":\"content 1\"}",
-)
diff --git a/examples/demo/tests/requests/snapshots/can_get_note_gzip@notes_request.snap b/examples/demo/tests/requests/snapshots/can_get_note_gzip@notes_request.snap
deleted file mode 100644
index 2ea4832dd..000000000
--- a/examples/demo/tests/requests/snapshots/can_get_note_gzip@notes_request.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/notes.rs
-expression: "(add_note_request.status_code(), add_note_request.text())"
----
-(
- 200,
- "{\"created_at\":\"DATE\",\"updated_at\":\"DATE\",\"id\":ID,\"title\":\"Loco note 1\",\"content\":\"content 1\"}",
-)
diff --git a/examples/demo/tests/requests/snapshots/can_get_notes@notes_request.snap b/examples/demo/tests/requests/snapshots/can_get_notes@notes_request.snap
deleted file mode 100644
index 6b25cc056..000000000
--- a/examples/demo/tests/requests/snapshots/can_get_notes@notes_request.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/notes.rs
-expression: "(notes.status_code(), notes.text())"
----
-(
- 200,
- "[{\"created_at\":\"DATE\",\"updated_at\":\"DATE\",\"id\":ID,\"title\":\"Loco note 1\",\"content\":\"Loco note 1 content\"},{\"created_at\":\"DATE\",\"updated_at\":\"DATE\",\"id\":ID,\"title\":\"Loco note 2\",\"content\":\"Loco note 2 content\"}]",
-)
diff --git a/examples/demo/tests/requests/snapshots/can_login_without_verify@auth_request.snap b/examples/demo/tests/requests/snapshots/can_login_without_verify@auth_request.snap
deleted file mode 100644
index 10a558676..000000000
--- a/examples/demo/tests/requests/snapshots/can_login_without_verify@auth_request.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/auth.rs
-expression: "(response.status_code(), response.text())"
----
-(
- 200,
- "{\"token\":\"TOKEN\",\"user\":{\"pid\":\"PID\",\"email\":\"test@loco.com\",\"name\":\"loco\",\"last_login\":\"n/a\"}}",
-)
diff --git a/examples/demo/tests/requests/snapshots/can_register@auth_request-2.snap b/examples/demo/tests/requests/snapshots/can_register@auth_request-2.snap
deleted file mode 100644
index 429e365cd..000000000
--- a/examples/demo/tests/requests/snapshots/can_register@auth_request-2.snap
+++ /dev/null
@@ -1,10 +0,0 @@
----
-source: tests/requests/auth.rs
-expression: ctx.mailer.unwrap().deliveries()
----
-Deliveries {
- count: 1,
- messages: [
- "From: System \r\nTo: test@loco.com\r\nSubject: Welcome =?utf-8?b?bG9jbwo=?=\r\nMIME-Version: 1.0\r\nDate: DATE\r\nContent-Type: multipart/alternative;\r\n boundary=\"IDENTIFIER\"\r\n\r\n--IDENTIFIER\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Transfer-Encoding: 7bit\r\n\r\nWelcome loco, you can now log in.\r\n Verify your account with the link below:\r\n\r\nhttp://localhost/verify/RANDOM_ID\r\n\r\n--IDENTIFIER\r\nContent-Type: text/html; charset=utf-8\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\n;\r\n\r\n\r\n Dear loco,\r\n Welcome to Loco! You can now log in to your account.\r\n Before you get started, please verify your account by clicking the link b=\r\nelow:\r\n \r\nTo: test@loco.com\r\nSubject: Welcome =?utf-8?b?bG9jbwo=?=\r\nMIME-Version: 1.0\r\nDate: DATE\r\nContent-Type: multipart/alternative;\r\n boundary=\"IDENTIFIER\"\r\n\r\n--IDENTIFIER\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Transfer-Encoding: 7bit\r\n\r\nWelcome loco, you can now log in.\r\n Verify your account with the link below:\r\n\r\nhttp://localhost/verify/RANDOM_ID\r\n\r\n--IDENTIFIER\r\nContent-Type: text/html; charset=utf-8\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\n;\r\n\r\n\r\n Dear loco,\r\n Welcome to Loco! You can now log in to your account.\r\n Before you get started, please verify your account by clicking the link b=\r\nelow:\r\n \r\nTo: test@loco.com\r\nSubject: Your reset password =?utf-8?b?bGluawo=?=\r\nMIME-Version: 1.0\r\nDate: DATE\r\nContent-Type: multipart/alternative;\r\n boundary=\"IDENTIFIER\"\r\n\r\n--IDENTIFIER\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Transfer-Encoding: 7bit\r\n\r\nReset your password with this link:\r\n\r\nhttp://localhost/reset/RANDOM_ID\r\n\r\n--IDENTIFIER\r\nContent-Type: text/html; charset=utf-8\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\n\r\n\r\n\r\n Hey loco,\r\n Forgot your password? No worries! You can reset it by clicking the link b=\r\nelow:\r\n R=\r\neset Your Password\r\n If you didn't request a password reset, please ignore this email.\r\n Best regards,
The Loco Team\r\n\r\n\r\n\r\n\r\n--IDENTIFIER--\r\n",
- ],
-}
diff --git a/examples/demo/tests/requests/snapshots/can_reset_password@auth_request.snap b/examples/demo/tests/requests/snapshots/can_reset_password@auth_request.snap
deleted file mode 100644
index 83304ccfe..000000000
--- a/examples/demo/tests/requests/snapshots/can_reset_password@auth_request.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/auth.rs
-expression: "(reset_response.status_code(), reset_response.text())"
----
-(
- 200,
- "{}",
-)
diff --git a/examples/demo/tests/requests/snapshots/get_notes@notes_request.snap b/examples/demo/tests/requests/snapshots/get_notes@notes_request.snap
deleted file mode 100644
index 4a103a03e..000000000
--- a/examples/demo/tests/requests/snapshots/get_notes@notes_request.snap
+++ /dev/null
@@ -1,9 +0,0 @@
----
-source: tests/requests/notes.rs
-assertion_line: 41
-expression: "(notes.status_code(), notes.text())"
----
-(
- 200,
- "{\"results\":[{\"id\":ID,\"title\":null,\"content\":\"content 1\"},{\"id\":ID,\"title\":\"Loco note 2\",\"content\":null},{\"id\":ID,\"title\":\"Loco note 3\",\"content\":\"content 3\"},{\"id\":ID,\"title\":\"Loco note 4\",\"content\":\"content 4\"},{\"id\":ID,\"title\":\"Loco note 5\",\"content\":\"content 5\"},{\"id\":ID,\"title\":\"Loco 6\",\"content\":\"content 6\"},{\"id\":ID,\"title\":\"Loco 7\",\"content\":\"content 7\"},{\"id\":ID,\"title\":\"Loco 8\",\"content\":\"content 8\"},{\"id\":ID,\"title\":\"Loco 9\",\"content\":\"content 9\"},{\"id\":ID,\"title\":\"Loco 10\",\"content\":\"content 10\"},{\"id\":ID,\"title\":\"Loco 11\",\"content\":\"content 11\"}],\"pagination\":{\"page\":1,\"page_size\":25,\"total_pages\":1,\"total_items\":11}}",
-)
diff --git a/examples/demo/tests/requests/snapshots/get_notes_with_filters@notes_request.snap b/examples/demo/tests/requests/snapshots/get_notes_with_filters@notes_request.snap
deleted file mode 100644
index f9cb22e48..000000000
--- a/examples/demo/tests/requests/snapshots/get_notes_with_filters@notes_request.snap
+++ /dev/null
@@ -1,9 +0,0 @@
----
-source: tests/requests/notes.rs
-assertion_line: 41
-expression: "(notes.status_code(), notes.text())"
----
-(
- 200,
- "{\"results\":[{\"id\":ID,\"title\":\"Loco note 2\",\"content\":null},{\"id\":ID,\"title\":\"Loco note 3\",\"content\":\"content 3\"}],\"pagination\":{\"page\":1,\"page_size\":2,\"total_pages\":2,\"total_items\":4}}",
-)
diff --git a/examples/demo/tests/requests/snapshots/get_notes_with_page_size@notes_request.snap b/examples/demo/tests/requests/snapshots/get_notes_with_page_size@notes_request.snap
deleted file mode 100644
index 42447a072..000000000
--- a/examples/demo/tests/requests/snapshots/get_notes_with_page_size@notes_request.snap
+++ /dev/null
@@ -1,9 +0,0 @@
----
-source: tests/requests/notes.rs
-assertion_line: 41
-expression: "(notes.status_code(), notes.text())"
----
-(
- 200,
- "{\"results\":[{\"id\":ID,\"title\":null,\"content\":\"content 1\"}],\"pagination\":{\"page\":1,\"page_size\":1,\"total_pages\":11,\"total_items\":11}}",
-)
diff --git a/examples/demo/tests/requests/snapshots/get_notes_with_size_and_page@notes_request.snap b/examples/demo/tests/requests/snapshots/get_notes_with_size_and_page@notes_request.snap
deleted file mode 100644
index 94b3c8993..000000000
--- a/examples/demo/tests/requests/snapshots/get_notes_with_size_and_page@notes_request.snap
+++ /dev/null
@@ -1,9 +0,0 @@
----
-source: tests/requests/notes.rs
-assertion_line: 41
-expression: "(notes.status_code(), notes.text())"
----
-(
- 200,
- "{\"results\":[{\"id\":ID,\"title\":\"Loco 6\",\"content\":\"content 6\"},{\"id\":ID,\"title\":\"Loco 7\",\"content\":\"content 7\"},{\"id\":ID,\"title\":\"Loco 8\",\"content\":\"content 8\"},{\"id\":ID,\"title\":\"Loco 9\",\"content\":\"content 9\"},{\"id\":ID,\"title\":\"Loco 10\",\"content\":\"content 10\"}],\"pagination\":{\"page\":2,\"page_size\":5,\"total_pages\":3,\"total_items\":11}}",
-)
diff --git a/examples/demo/tests/requests/snapshots/hello@view_engine.snap b/examples/demo/tests/requests/snapshots/hello@view_engine.snap
deleted file mode 100644
index 5375baad7..000000000
--- a/examples/demo/tests/requests/snapshots/hello@view_engine.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/view_engine.rs
-expression: "(response.status_code(), response.text())"
----
-(
- 200,
- "hello",
-)
diff --git a/examples/demo/tests/requests/snapshots/home@view_engine.snap b/examples/demo/tests/requests/snapshots/home@view_engine.snap
deleted file mode 100644
index 144d89820..000000000
--- a/examples/demo/tests/requests/snapshots/home@view_engine.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/view_engine.rs
-expression: "(response.status_code(), response.text())"
----
-(
- 200,
- "\n \n
\n find this tera template at assets/views/home/hello.html
: \n
\n
\n Hello World!, \n
\n Hallo Welt!\n \n\n ",
-)
diff --git a/examples/demo/tests/requests/snapshots/insert@cache.snap b/examples/demo/tests/requests/snapshots/insert@cache.snap
deleted file mode 100644
index e4108c4da..000000000
--- a/examples/demo/tests/requests/snapshots/insert@cache.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/cache.rs
-expression: "(response.text(), response.status_code())"
----
-(
- "",
- 200,
-)
diff --git a/examples/demo/tests/requests/snapshots/key_not_exists@cache.snap b/examples/demo/tests/requests/snapshots/key_not_exists@cache.snap
deleted file mode 100644
index 8fd34bc61..000000000
--- a/examples/demo/tests/requests/snapshots/key_not_exists@cache.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/cache.rs
-expression: "(response.text(), response.status_code())"
----
-(
- "{\"value\":null}",
- 200,
-)
diff --git a/examples/demo/tests/requests/snapshots/login_with_invalid_password@auth_request.snap b/examples/demo/tests/requests/snapshots/login_with_invalid_password@auth_request.snap
deleted file mode 100644
index eb6e89f44..000000000
--- a/examples/demo/tests/requests/snapshots/login_with_invalid_password@auth_request.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/auth.rs
-expression: "(response.status_code(), response.text())"
----
-(
- 401,
- "{\"error\":\"unauthorized\",\"description\":\"You do not have permission to access this resource\"}",
-)
diff --git a/examples/demo/tests/requests/snapshots/login_with_valid_password@auth_request.snap b/examples/demo/tests/requests/snapshots/login_with_valid_password@auth_request.snap
deleted file mode 100644
index 10a558676..000000000
--- a/examples/demo/tests/requests/snapshots/login_with_valid_password@auth_request.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/auth.rs
-expression: "(response.status_code(), response.text())"
----
-(
- 200,
- "{\"token\":\"TOKEN\",\"user\":{\"pid\":\"PID\",\"email\":\"test@loco.com\",\"name\":\"loco\",\"last_login\":\"n/a\"}}",
-)
diff --git a/examples/demo/tests/requests/snapshots/ping@ping_request.snap b/examples/demo/tests/requests/snapshots/ping@ping_request.snap
deleted file mode 100644
index d47e899fe..000000000
--- a/examples/demo/tests/requests/snapshots/ping@ping_request.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/ping.rs
-expression: "(response.text(), response.status_code())"
----
-(
- "{\"ok\":true}",
- 200,
-)
diff --git a/examples/demo/tests/requests/snapshots/ping_with_multiple_trailing_slashes@ping_request.snap b/examples/demo/tests/requests/snapshots/ping_with_multiple_trailing_slashes@ping_request.snap
deleted file mode 100644
index d47e899fe..000000000
--- a/examples/demo/tests/requests/snapshots/ping_with_multiple_trailing_slashes@ping_request.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/ping.rs
-expression: "(response.text(), response.status_code())"
----
-(
- "{\"ok\":true}",
- 200,
-)
diff --git a/examples/demo/tests/requests/snapshots/ping_with_trailing_slash@ping_request.snap b/examples/demo/tests/requests/snapshots/ping_with_trailing_slash@ping_request.snap
deleted file mode 100644
index d47e899fe..000000000
--- a/examples/demo/tests/requests/snapshots/ping_with_trailing_slash@ping_request.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/ping.rs
-expression: "(response.text(), response.status_code())"
----
-(
- "{\"ok\":true}",
- 200,
-)
diff --git a/examples/demo/tests/requests/snapshots/read_cache_key@cache.snap b/examples/demo/tests/requests/snapshots/read_cache_key@cache.snap
deleted file mode 100644
index a06ae0dc3..000000000
--- a/examples/demo/tests/requests/snapshots/read_cache_key@cache.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/cache.rs
-expression: "(response.text(), response.status_code())"
----
-(
- "{\"value\":\"loco cache value\"}",
- 200,
-)
diff --git a/examples/demo/tests/requests/snapshots/simple@view_engine.snap b/examples/demo/tests/requests/snapshots/simple@view_engine.snap
deleted file mode 100644
index 953f50e8e..000000000
--- a/examples/demo/tests/requests/snapshots/simple@view_engine.snap
+++ /dev/null
@@ -1,8 +0,0 @@
----
-source: tests/requests/view_engine.rs
-expression: "(response.status_code(), response.text())"
----
-(
- 200,
- "Loco website",
-)
diff --git a/examples/demo/tests/requests/upload.rs b/examples/demo/tests/requests/upload.rs
deleted file mode 100644
index a5c1fa2e8..000000000
--- a/examples/demo/tests/requests/upload.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use axum_test::multipart::{MultipartForm, Part};
-use demo_app::{app::App, views};
-use loco_rs::testing::prelude::*;
-use serial_test::serial;
-
-#[tokio::test]
-#[serial]
-async fn can_upload_file() {
- request::(|request, ctx| async move {
- let file_content = "loco file upload";
- let file_part = Part::bytes(file_content.as_bytes()).file_name("loco.txt");
-
- let multipart_form = MultipartForm::new().add_part("file", file_part);
-
- let response = request.post("/upload/file").multipart(multipart_form).await;
-
- response.assert_status_ok();
-
- let res: views::upload::Response = serde_json::from_str(&response.text()).unwrap();
-
- let stored_file: String = ctx.storage.download(&res.path).await.unwrap();
-
- assert_eq!(stored_file, file_content);
- })
- .await;
-}
diff --git a/examples/demo/tests/requests/user.rs b/examples/demo/tests/requests/user.rs
deleted file mode 100644
index 4ca15477c..000000000
--- a/examples/demo/tests/requests/user.rs
+++ /dev/null
@@ -1,109 +0,0 @@
-use demo_app::app::App;
-use insta::{assert_debug_snapshot, with_settings};
-use loco_rs::testing::prelude::*;
-use serial_test::serial;
-
-use super::prepare_data;
-
-// TODO: see how to dedup / extract this to app-local test utils
-// not to framework, because that would require a runtime dep on insta
-macro_rules! configure_insta {
- ($($expr:expr),*) => {
- let mut settings = insta::Settings::clone_current();
- settings.set_prepend_module_to_snapshot(false);
- settings.set_snapshot_suffix("auth_request");
- let _guard = settings.bind_to_scope();
- };
-}
-
-#[tokio::test]
-#[serial]
-async fn can_get_current_user() {
- configure_insta!();
-
- request::(|request, ctx| async move {
- let user = prepare_data::init_user_login(&request, &ctx).await;
-
- let (auth_key, auth_value) = prepare_data::auth_header(&user.token);
- let response = request
- .get("/user/current")
- .add_header(auth_key, auth_value)
- .await;
-
- with_settings!({
- filters => cleanup_user_model()
- }, {
- assert_debug_snapshot!((response.status_code(), response.text()));
- });
- })
- .await;
-}
-
-#[tokio::test]
-#[serial]
-async fn can_get_current_user_with_api_key() {
- configure_insta!();
-
- request::(|request, ctx| async move {
- let user_data = prepare_data::init_user_login(&request, &ctx).await;
-
- let (auth_key, auth_value) = prepare_data::auth_header(&user_data.user.api_key);
- let response = request
- .get("/user/current_api_key")
- .add_header(auth_key, auth_value)
- .await;
-
- with_settings!({
- filters => cleanup_user_model()
- }, {
- assert_debug_snapshot!((response.status_code(), response.text()));
- });
- })
- .await;
-}
-
-#[tokio::test]
-#[serial]
-async fn can_convert_user_to_user_role() {
- configure_insta!();
-
- request::(|request, ctx| async move {
- let user = prepare_data::init_user_login(&request, &ctx).await;
-
- let (auth_key, auth_value) = prepare_data::auth_header(&user.token);
- let response = request
- .post("/user/convert/user")
- .add_header(auth_key, auth_value)
- .await;
-
- with_settings!({
- filters => cleanup_user_model()
- }, {
- assert_debug_snapshot!((response.status_code(), response.text()));
- });
- })
- .await;
-}
-
-#[tokio::test]
-#[serial]
-async fn can_convert_user_to_admin_role() {
- configure_insta!();
-
- request::(|request, ctx| async move {
- let user = prepare_data::init_user_login(&request, &ctx).await;
-
- let (auth_key, auth_value) = prepare_data::auth_header(&user.token);
- let response = request
- .post("/user/convert/admin")
- .add_header(auth_key, auth_value)
- .await;
-
- with_settings!({
- filters => cleanup_user_model()
- }, {
- assert_debug_snapshot!((response.status_code(), response.text()));
- });
- })
- .await;
-}
diff --git a/examples/demo/tests/requests/view_engine.rs b/examples/demo/tests/requests/view_engine.rs
deleted file mode 100644
index b8980e0e0..000000000
--- a/examples/demo/tests/requests/view_engine.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use demo_app::app::App;
-use insta::assert_debug_snapshot;
-use loco_rs::testing::prelude::*;
-use rstest::rstest;
-use serial_test::serial;
-// TODO: see how to dedup / extract this to app-local test utils
-// not to framework, because that would require a runtime dep on insta
-macro_rules! configure_insta {
- ($($expr:expr),*) => {
- let mut settings = insta::Settings::clone_current();
- settings.set_prepend_module_to_snapshot(false);
- settings.set_snapshot_suffix("view_engine");
- let _guard = settings.bind_to_scope();
- };
-}
-
-#[rstest]
-#[case("home")]
-#[case("hello")]
-#[case("simple")]
-#[tokio::test]
-#[serial]
-async fn can_get_view_engine(#[case] uri: &str) {
- configure_insta!();
- request::(|request, _ctx| async move {
- let response = request.get(&format!("/view-engine/{uri}")).await;
-
- assert_debug_snapshot!(
- uri.replace('/', "_"),
- (response.status_code(), response.text())
- );
- })
- .await;
-}
diff --git a/examples/demo/tests/tasks/foo.rs b/examples/demo/tests/tasks/foo.rs
deleted file mode 100644
index 4d19e4397..000000000
--- a/examples/demo/tests/tasks/foo.rs
+++ /dev/null
@@ -1,17 +0,0 @@
-use demo_app::app::App;
-use loco_rs::{boot::run_task, task, testing::prelude::*};
-use serial_test::serial;
-
-#[tokio::test]
-#[serial]
-async fn test_can_run_foo_task() {
- let boot = boot_test::