增加jwt、validator、bcrypto功能,增加用户接口、增加提取器以及中间件
This commit is contained in:
parent
3f1941667c
commit
43f3867630
|
@ -240,15 +240,42 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-extra"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "895ff42f72016617773af68fb90da2a9677d89c62338ec09162d4909d86fdd8f"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"axum-core",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"headers",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-with-seaorm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"askama",
|
||||
"async-trait",
|
||||
"axum",
|
||||
"axum-extra",
|
||||
"bcrypt",
|
||||
"chrono",
|
||||
"config",
|
||||
"dotenvy",
|
||||
"jsonwebtoken",
|
||||
"lazy_static",
|
||||
"sea-orm",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -301,6 +328,19 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bcrypt"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d1c9c15093eb224f0baa400f38fcd713fc1391a6f1c389d886beef146d60a3"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"blowfish",
|
||||
"getrandom",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bigdecimal"
|
||||
version = "0.3.1"
|
||||
|
@ -348,6 +388,16 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blowfish"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh"
|
||||
version = "1.3.1"
|
||||
|
@ -380,9 +430,9 @@ checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
|||
|
||||
[[package]]
|
||||
name = "bytecheck"
|
||||
version = "0.6.11"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627"
|
||||
checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2"
|
||||
dependencies = [
|
||||
"bytecheck_derive",
|
||||
"ptr_meta",
|
||||
|
@ -391,9 +441,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bytecheck_derive"
|
||||
version = "0.6.11"
|
||||
version = "0.6.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61"
|
||||
checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -448,6 +498,16 @@ dependencies = [
|
|||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[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 = "config"
|
||||
version = "0.13.4"
|
||||
|
@ -787,8 +847,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -844,6 +906,30 @@ dependencies = [
|
|||
"hashbrown 0.14.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "headers"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"bytes",
|
||||
"headers-core",
|
||||
"http",
|
||||
"httpdate",
|
||||
"mime",
|
||||
"sha1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "headers-core"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4"
|
||||
dependencies = [
|
||||
"http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
|
@ -855,9 +941,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.4"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f"
|
||||
checksum = "d0c62115964e08cb8039170eb33c1d0e2388a256930279edca206fff675f82c3"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
|
@ -1025,6 +1111,12 @@ dependencies = [
|
|||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "if_chain"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.2"
|
||||
|
@ -1046,6 +1138,15 @@ dependencies = [
|
|||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
|
@ -1063,9 +1164,9 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
|||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.67"
|
||||
version = "0.3.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1"
|
||||
checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
@ -1081,6 +1182,21 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonwebtoken"
|
||||
version = "9.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7ea04a7c5c055c175f189b6dc6ba036fd62306b58c66c9f6389036c503a3f4"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"js-sys",
|
||||
"pem",
|
||||
"ring",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"simple_asn1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
|
@ -1443,6 +1559,16 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
|
||||
|
||||
[[package]]
|
||||
name = "pem"
|
||||
version = "3.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b8fcc794035347fb64beda2d3b462595dd2753e3f268d89c5aae77e8cf2c310"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem-rfc7468"
|
||||
version = "0.7.0"
|
||||
|
@ -1460,9 +1586,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
|||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.7.6"
|
||||
version = "2.7.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f200d8d83c44a45b21764d1916299752ca035d15ecd46faca3e9a2a2bf6ad06"
|
||||
checksum = "219c0dcc30b6a27553f9cc242972b67f75b60eb0db71f0b5462f38b058c41546"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror",
|
||||
|
@ -1471,9 +1597,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.7.6"
|
||||
version = "2.7.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcd6ab1236bbdb3a49027e920e693192ebfe8913f6d60e294de57463a493cfde"
|
||||
checksum = "22e1288dbd7786462961e69bfd4df7848c1e37e8b74303dbdab82c3a9cdd2809"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
|
@ -1481,9 +1607,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.7.6"
|
||||
version = "2.7.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a31940305ffc96863a735bef7c7994a00b325a7138fdbc5bda0f1a0476d3275"
|
||||
checksum = "1381c29a877c6d34b8c176e734f35d7f7f5b3adaefe940cb4d1bb7af94678e2e"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
|
@ -1494,9 +1620,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.7.6"
|
||||
version = "2.7.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7ff62f5259e53b78d1af898941cdcdccfae7385cf7d793a6e55de5d05bb4b7d"
|
||||
checksum = "d0934d6907f148c22a3acbda520c7eed243ad7487a30f51f6ce52b58b7077a8a"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"pest",
|
||||
|
@ -1736,9 +1862,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
|||
|
||||
[[package]]
|
||||
name = "rend"
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2571463863a6bd50c32f94402933f03457a3fbaf697a707c5be741e459f08fd"
|
||||
checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c"
|
||||
dependencies = [
|
||||
"bytecheck",
|
||||
]
|
||||
|
@ -1759,9 +1885,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rkyv"
|
||||
version = "0.7.43"
|
||||
version = "0.7.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "527a97cdfef66f65998b5f3b637c26f5a5ec09cc52a3f9932313ac645f4190f5"
|
||||
checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"bytecheck",
|
||||
|
@ -1777,9 +1903,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rkyv_derive"
|
||||
version = "0.7.43"
|
||||
version = "0.7.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5c462a1328c8e67e4d6dbad1eb0355dd43e8ab432c6e227a43657f16ade5033"
|
||||
checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1941,9 +2067,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sea-orm"
|
||||
version = "0.12.12"
|
||||
version = "0.12.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0cbf88748872fa54192476d6d49d0775e208566a72656e267e45f6980b926c8d"
|
||||
checksum = "6632f499b80cc6aaa781b302e4c9fae663e0e3dcf2640e9d80034d5b10731efe"
|
||||
dependencies = [
|
||||
"async-stream",
|
||||
"async-trait",
|
||||
|
@ -1969,9 +2095,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sea-orm-macros"
|
||||
version = "0.12.12"
|
||||
version = "0.12.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0dbc880d47aa53c6a572e39c99402c7fad59b50766e51e0b0fc1306510b0555"
|
||||
checksum = "ec13bfb4c4aef208f68dbea970dd40d13830c868aa8dcb4e106b956e6bb4f2fa"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
|
@ -2129,6 +2255,18 @@ version = "0.1.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
|
||||
|
||||
[[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",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
|
@ -2495,13 +2633,12 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
|||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.9.0"
|
||||
version = "3.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
|
||||
checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall",
|
||||
"rustix",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
@ -2800,9 +2937,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.10.1"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||
|
||||
[[package]]
|
||||
name = "unicode_categories"
|
||||
|
@ -2855,6 +2992,33 @@ dependencies = [
|
|||
"serde_derive",
|
||||
"serde_json",
|
||||
"url",
|
||||
"validator_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator_derive"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc44ca3088bb3ba384d9aecf40c6a23a676ce23e09bdaca2073d99c207f864af"
|
||||
dependencies = [
|
||||
"if_chain",
|
||||
"lazy_static",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"syn 1.0.109",
|
||||
"validator_types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator_types"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "111abfe30072511849c5910134e8baf8dc05de4c0e5903d681cbd5c9c4d611e3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2883,9 +3047,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.90"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406"
|
||||
checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
|
@ -2893,9 +3057,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.90"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd"
|
||||
checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
|
@ -2908,9 +3072,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.90"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999"
|
||||
checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
@ -2918,9 +3082,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.90"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7"
|
||||
checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2931,9 +3095,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.90"
|
||||
version = "0.2.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b"
|
||||
checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838"
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
|
@ -3121,9 +3285,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
|||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.37"
|
||||
version = "0.5.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7cad8365489051ae9f054164e459304af2e7e9bb407c958076c8bf4aef52da5"
|
||||
checksum = "5389a154b01683d28c77f8f68f49dea75f0a4da32557a58f68ee51ebba472d29"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
|
|
@ -18,4 +18,9 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter", "time", "lo
|
|||
serde_json = "1.0.113"
|
||||
chrono = "0.4.33"
|
||||
state = "0.6.0"
|
||||
validator = "0.16.1"
|
||||
lazy_static = "1.4.0"
|
||||
validator = { version = "0.16.1", features = ["derive"] }
|
||||
jsonwebtoken = "9.2.0"
|
||||
async-trait = "0.1.77"
|
||||
bcrypt = "0.15.0"
|
||||
axum-extra = { version = "0.9.2", features = ["typed-header"] }
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
jwt_secret = "123456"
|
||||
|
||||
[server]
|
||||
port = 9527
|
||||
host = "0.0.0.0"
|
||||
|
@ -5,3 +7,4 @@ host = "0.0.0.0"
|
|||
[mysql]
|
||||
max_cons = 5
|
||||
dsn = "mysql://root:mysql123!%40%23@127.0.0.1:3306/study"
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
[web]
|
||||
addr = "0.0.0.0:9527"
|
||||
|
||||
[mysql]
|
||||
max_cons = 5
|
||||
dns = "mysql://root:mysql123!%40%23@127.0.0.1:3306/study"
|
|
@ -1,9 +1,15 @@
|
|||
use crate::common::response::AppResponse;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AppErrorType {
|
||||
System,
|
||||
Config,
|
||||
Database,
|
||||
Notfound,
|
||||
Validate,
|
||||
Template,
|
||||
Authorization,
|
||||
Forbidden,
|
||||
}
|
||||
|
||||
type Cause = Box<dyn std::error::Error>;
|
||||
|
@ -38,7 +44,11 @@ impl AppError {
|
|||
|
||||
impl std::fmt::Display for AppError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
let msg = match &self.error {
|
||||
AppErrorItem::Cause(err) => err.to_string(),
|
||||
AppErrorItem::Message(msg) => msg.to_string(),
|
||||
};
|
||||
write!(f, "{}", msg)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,13 +72,22 @@ impl From<sea_orm::DbErr> for AppError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<jsonwebtoken::errors::Error> for AppError {
|
||||
fn from(err: jsonwebtoken::errors::Error) -> Self {
|
||||
Self::from_err(Box::new(err), AppErrorType::Authorization)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<validator::ValidationErrors> for AppError {
|
||||
fn from(err: validator::ValidationErrors) -> Self {
|
||||
Self::from_err(Box::new(err), AppErrorType::Validate)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl axum::response::IntoResponse for AppError {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
let msg = match self.error {
|
||||
AppErrorItem::Cause(err) => err.to_string(),
|
||||
AppErrorItem::Message(msg) => msg.to_string(),
|
||||
};
|
||||
msg.into_response()
|
||||
AppResponse::<()>::from_error(self).into_response()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
use async_trait::async_trait;
|
||||
use axum::extract::FromRequestParts;
|
||||
use axum::http::request::Parts;
|
||||
use axum::http::StatusCode;
|
||||
use axum::RequestPartsExt;
|
||||
use axum_extra::headers::authorization::{Authorization as ExtraAuthorization, Bearer};
|
||||
use axum_extra::TypedHeader;
|
||||
use tracing::info;
|
||||
|
||||
use crate::common::err::{AppError, AppErrorType};
|
||||
use crate::common::jwt::JwtClaims;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Authorization(pub JwtClaims);
|
||||
|
||||
|
||||
#[async_trait]
|
||||
impl<S> FromRequestParts<S> for Authorization {
|
||||
type Rejection = (StatusCode, AppError);
|
||||
|
||||
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
||||
let TypedHeader(ExtraAuthorization(bearer)) = parts
|
||||
.extract::<TypedHeader<ExtraAuthorization<Bearer>>>()
|
||||
.await
|
||||
.map_err(|error| {
|
||||
(StatusCode::UNAUTHORIZED, AppError::from_err(Box::new(error), AppErrorType::Authorization))
|
||||
})?;
|
||||
|
||||
info!("Authorization from_request_parts");
|
||||
|
||||
let claims = JwtClaims::verify(bearer.token())
|
||||
.map_err(|error| (StatusCode::UNAUTHORIZED, error))?;
|
||||
|
||||
Ok(Self(claims))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
pub mod authorization;
|
|
@ -0,0 +1,65 @@
|
|||
use chrono::{Duration, Utc};
|
||||
use jsonwebtoken::{DecodingKey, EncodingKey, Header, Validation};
|
||||
use jsonwebtoken::errors::ErrorKind;
|
||||
use lazy_static::lazy_static;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::APPLICATION_CONTEXT;
|
||||
use crate::common::err::{AppError, AppErrorType};
|
||||
use crate::common::Result;
|
||||
use crate::config::app::AppConfig;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref JWT_SECRET: String = {
|
||||
let app_cfg = APPLICATION_CONTEXT.get::<AppConfig>();
|
||||
app_cfg.jwt_secret.clone()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct JwtClaims {
|
||||
// 账号id
|
||||
pub id: u32,
|
||||
// 账号
|
||||
pub email: String,
|
||||
// 过期时间
|
||||
pub exp: i64,
|
||||
// 签名时间
|
||||
pub iat: i64,
|
||||
}
|
||||
|
||||
impl JwtClaims {
|
||||
pub fn new(id: u32, email: String) -> Self {
|
||||
let iat = Utc::now();
|
||||
let exp = iat + Duration::hours(24);
|
||||
|
||||
Self {
|
||||
id,
|
||||
email,
|
||||
exp: exp.timestamp(),
|
||||
iat: iat.timestamp(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sign(&self) -> Result<String> {
|
||||
let encoding_key = EncodingKey::from_secret(JWT_SECRET.as_bytes());
|
||||
jsonwebtoken::encode(&Header::default(), self, &encoding_key)
|
||||
.map_err(AppError::from)
|
||||
}
|
||||
|
||||
pub fn verify(token: &str) -> Result<JwtClaims> {
|
||||
let decode_key = DecodingKey::from_secret(JWT_SECRET.as_bytes());
|
||||
|
||||
let validation = Validation::default();
|
||||
|
||||
match jsonwebtoken::decode::<JwtClaims>(token, &decode_key, &validation) {
|
||||
Ok(c) => Ok(c.claims),
|
||||
Err(e) => match e.kind() {
|
||||
ErrorKind::InvalidToken => Err(AppError::from_msg("Token失效", AppErrorType::Authorization)),
|
||||
ErrorKind::InvalidIssuer => Err(AppError::from_msg("InvalidIssuer", AppErrorType::Authorization)),
|
||||
_ => Err(AppError::from_msg("InvalidToken other errors", AppErrorType::Authorization))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
pub mod template;
|
||||
pub mod err;
|
||||
pub mod response;
|
||||
pub mod jwt;
|
||||
pub mod extract;
|
||||
mod pagination;
|
||||
pub mod util;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, err::AppError>;
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct Paginate<T: Serialize> {
|
||||
pub total: u32,
|
||||
pub page: u32,
|
||||
pub page_size: u32,
|
||||
pub data: T,
|
||||
}
|
||||
|
||||
impl<T: Serialize> Paginate<T> {
|
||||
pub fn new(total: u32, page: u32, page_size: u32, data: T) -> Self {
|
||||
Self { total, page, page_size, data }
|
||||
}
|
||||
}
|
|
@ -1,7 +1,14 @@
|
|||
use axum::http::{header, HeaderMap, StatusCode};
|
||||
use axum::response::Html;
|
||||
use axum::response::{Html, IntoResponse, Json, Response};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::common::err::AppError;
|
||||
|
||||
use super::Result;
|
||||
|
||||
pub const CODE_SUCCESS: i8 = 0;
|
||||
pub const CODE_FAIL: i8 = -1;
|
||||
|
||||
pub type HtmlResponse = Html<String>;
|
||||
pub type RedirectResponse = (StatusCode, HeaderMap, ());
|
||||
|
||||
|
@ -11,5 +18,45 @@ pub fn redirect(url: &str) -> Result<RedirectResponse> {
|
|||
Ok((StatusCode::FOUND, header, ()))
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Clone)]
|
||||
pub struct AppResponse<T: Serialize> {
|
||||
pub code: Option<i8>,
|
||||
pub msg: Option<String>,
|
||||
pub data: Option<T>,
|
||||
}
|
||||
|
||||
impl<T: Serialize> IntoResponse for AppResponse<T> {
|
||||
fn into_response(self) -> Response {
|
||||
Json(self).into_response()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Serialize> AppResponse<T> {
|
||||
pub fn from_data(data: T) -> Self {
|
||||
Self {
|
||||
code: Some(CODE_SUCCESS),
|
||||
msg: Some("success".to_string()),
|
||||
data: Some(data),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_error(err: AppError) -> Self {
|
||||
Self {
|
||||
code: Some(CODE_FAIL),
|
||||
msg: Some(err.to_string()),
|
||||
data: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_result(result: Result<T>) -> Self {
|
||||
match result {
|
||||
Ok(data) => Self::from_data(data),
|
||||
Err(err) => Self::from_error(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
pub mod default_datetime_format {
|
||||
use chrono::{DateTime, Local, NaiveDateTime};
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
const FORMAT: &'static str = "%Y-%m-%d %H:%M:%S";
|
||||
|
||||
pub fn serialize<S>(
|
||||
date: &DateTime<Local>,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let s = format!("{}", date.format(FORMAT));
|
||||
serializer.serialize_str(&s)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(
|
||||
deserializer: D,
|
||||
) -> Result<DateTime<Local>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let datetime_str = String::deserialize(deserializer)?;
|
||||
let dt = NaiveDateTime::parse_from_str(&datetime_str, FORMAT).map_err(serde::de::Error::custom)?;
|
||||
Ok(dt.and_local_timezone(Local).unwrap())
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
pub mod datetime_format;
|
|
@ -7,4 +7,5 @@ use crate::config::server::ServerConfig;
|
|||
pub struct AppConfig {
|
||||
pub mysql: MysqlConfig,
|
||||
pub server: ServerConfig,
|
||||
pub jwt_secret: String,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
pub mod category;
|
||||
pub mod article;
|
||||
pub mod article_tag;
|
||||
pub mod tag;
|
||||
pub mod tag;
|
||||
pub mod users;
|
|
@ -0,0 +1,22 @@
|
|||
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.12
|
||||
|
||||
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 {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: u32,
|
||||
pub email: String,
|
||||
#[serde(skip_serializing)]
|
||||
pub password_hash: String,
|
||||
pub create_time: DateTimeLocal,
|
||||
#[serde(skip_serializing)]
|
||||
pub is_del: bool,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
11
src/form.rs
11
src/form.rs
|
@ -1,12 +1,23 @@
|
|||
use serde::Deserialize;
|
||||
use validator::{Validate};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CategoryForm {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ArticleForm {
|
||||
pub title: String,
|
||||
pub category_id: u32,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Validate, Deserialize)]
|
||||
pub struct UserSignForm {
|
||||
#[validate(email(message = "请输入正确格式的邮箱"))]
|
||||
pub email: String,
|
||||
#[validate(length(min = 6, message = "密码长度至少为 6"))]
|
||||
pub password: String,
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
use axum::{Form, Router};
|
||||
use axum::extract::Query;
|
||||
use axum::routing::get;
|
||||
use axum::Form;
|
||||
|
||||
use crate::common::{response::HtmlResponse, Result};
|
||||
use crate::common::response::{redirect, RedirectResponse};
|
||||
|
@ -9,13 +8,6 @@ use crate::form::ArticleForm;
|
|||
use crate::param::ArticleParams;
|
||||
use crate::service::article::ArticleService;
|
||||
|
||||
pub fn init_router() -> Router {
|
||||
Router::new()
|
||||
.route("/", get(index))
|
||||
.route("/add", get(add_ui).post(add))
|
||||
.route("/tags", get(list_with_tags))
|
||||
}
|
||||
|
||||
pub async fn index(Query(params): Query<ArticleParams>) -> Result<HtmlResponse> {
|
||||
let handler_name = "article/index";
|
||||
let tpl = ArticleService::index(handler_name, params).await?;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use axum::{Form, Router};
|
||||
use axum::extract::{Path, Query};
|
||||
use axum::routing::get;
|
||||
use axum::Form;
|
||||
|
||||
use crate::{form, view};
|
||||
use crate::common::{response::HtmlResponse, Result};
|
||||
|
@ -9,22 +8,6 @@ use crate::common::template::render;
|
|||
use crate::param::{CategoryParams, DelParams};
|
||||
use crate::service::category::CategoryService;
|
||||
|
||||
pub fn init_router() -> Router {
|
||||
Router::new()
|
||||
.route("/", get(index))
|
||||
.route(
|
||||
"/add",
|
||||
get(add_ui).post(add),
|
||||
)
|
||||
.route(
|
||||
"/edit/:id",
|
||||
get(edit_ui).post(edit),
|
||||
)
|
||||
.route("/del/:id", get(del))
|
||||
.route("/del/:id/:real", get(del))
|
||||
.route("/articles/:id", get(articles))
|
||||
}
|
||||
|
||||
pub async fn index(Query(params): Query<CategoryParams>) -> Result<HtmlResponse> {
|
||||
let handler_name = "category/index";
|
||||
let tpl = CategoryService::index(handler_name, params).await?;
|
||||
|
|
|
@ -4,4 +4,5 @@ pub mod category;
|
|||
|
||||
mod index;
|
||||
pub mod article;
|
||||
pub mod user;
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
use axum::Json;
|
||||
|
||||
use crate::common::extract::authorization::Authorization;
|
||||
use crate::common::response::AppResponse;
|
||||
use crate::common::Result;
|
||||
use crate::entity::users;
|
||||
use crate::form::UserSignForm;
|
||||
use crate::service::user::UserService;
|
||||
|
||||
pub async fn userinfo(Authorization(jwt_claims): Authorization) -> Result<AppResponse<users::Model>> {
|
||||
let user = UserService::userinfo(jwt_claims.id).await?;
|
||||
Ok(AppResponse::from_data(user))
|
||||
}
|
||||
|
||||
pub async fn sign_in(Json(jsn): Json<UserSignForm>) -> Result<String> {
|
||||
UserService::sign_in(jsn).await
|
||||
}
|
||||
|
||||
pub async fn list() -> Result<AppResponse<Vec<users::Model>>> {
|
||||
let user = UserService::list().await?;
|
||||
Ok(AppResponse::from_data(user))
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
use ::state::type_map::TypeMapSendSync;
|
||||
use tracing::info;
|
||||
|
||||
pub mod state;
|
||||
pub mod entity;
|
||||
// pub mod state;
|
||||
pub mod handler;
|
||||
|
@ -13,6 +12,7 @@ pub mod config;
|
|||
pub mod router;
|
||||
pub mod initialize;
|
||||
pub mod common;
|
||||
pub mod middleware;
|
||||
|
||||
|
||||
/// 整个项目上下文 ApplicationContext
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
use axum::Router;
|
||||
use axum::routing::get;
|
||||
|
||||
use crate::handler::article::{add, add_ui, index, list_with_tags};
|
||||
|
||||
pub fn init_router() -> Router {
|
||||
Router::new()
|
||||
.route("/", get(index))
|
||||
.route("/add", get(add_ui).post(add))
|
||||
.route("/tags", get(list_with_tags))
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
use axum::Router;
|
||||
use axum::routing::get;
|
||||
|
||||
use crate::handler::category::{add, add_ui, articles, del, edit, edit_ui, index};
|
||||
|
||||
pub fn init_router() -> Router {
|
||||
Router::new()
|
||||
.route("/", get(index))
|
||||
.route(
|
||||
"/add",
|
||||
get(add_ui).post(add),
|
||||
)
|
||||
.route(
|
||||
"/edit/:id",
|
||||
get(edit_ui).post(edit),
|
||||
)
|
||||
.route("/del/:id", get(del))
|
||||
.route("/del/:id/:real", get(del))
|
||||
.route("/articles/:id", get(articles))
|
||||
}
|
|
@ -1,13 +1,18 @@
|
|||
mod article;
|
||||
mod category;
|
||||
mod user;
|
||||
|
||||
use axum::routing::get;
|
||||
use crate::handler;
|
||||
use crate::handler::{category, article};
|
||||
use crate::handler::{index};
|
||||
|
||||
pub fn init() -> axum::Router {
|
||||
let category_router = category::init_router();
|
||||
let article_router = article::init_router();
|
||||
let user_router = user::init_router();
|
||||
|
||||
axum::Router::new()
|
||||
.route("/", get(handler::index))
|
||||
.route("/", get(index))
|
||||
.nest("/category", category_router)
|
||||
.nest("/article", article_router)
|
||||
.nest("/user", user_router)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
use axum::middleware::from_extractor;
|
||||
use axum::Router;
|
||||
use axum::routing::{get, post};
|
||||
|
||||
use crate::common::extract::authorization::Authorization;
|
||||
use crate::handler::user::{list, sign_in, userinfo};
|
||||
|
||||
pub fn init_router() -> Router {
|
||||
need_auth_routers().merge(not_need_auth_routers())
|
||||
}
|
||||
|
||||
pub fn need_auth_routers() -> Router {
|
||||
Router::new()
|
||||
// 在此之前的利用中间价鉴权
|
||||
.layer(from_extractor::<Authorization>())
|
||||
// 之后的利用提取器鉴权
|
||||
.route("/userinfo", get(userinfo))
|
||||
}
|
||||
|
||||
pub fn not_need_auth_routers() -> Router {
|
||||
Router::new()
|
||||
.route("/sign_in", post(sign_in))
|
||||
.route("/list", get(list))
|
||||
}
|
|
@ -1,2 +1,3 @@
|
|||
pub mod article;
|
||||
pub mod category;
|
||||
pub mod category;
|
||||
pub mod user;
|
|
@ -0,0 +1,61 @@
|
|||
use bcrypt::DEFAULT_COST;
|
||||
use chrono::Local;
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, Condition, DatabaseConnection, EntityTrait, QueryFilter};
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use validator::{Validate};
|
||||
|
||||
use crate::APPLICATION_CONTEXT;
|
||||
use crate::common::{err::AppError, Result};
|
||||
use crate::common::err::AppErrorType;
|
||||
use crate::common::jwt::JwtClaims;
|
||||
use crate::entity::users;
|
||||
use crate::form::UserSignForm;
|
||||
|
||||
pub struct UserService;
|
||||
|
||||
|
||||
impl UserService {
|
||||
pub async fn list() -> Result<Vec<users::Model>> {
|
||||
let conn = APPLICATION_CONTEXT.get::<DatabaseConnection>();
|
||||
users::Entity::find().all(conn).await.map_err(AppError::from)
|
||||
}
|
||||
|
||||
pub async fn userinfo(id: u32) -> Result<users::Model> {
|
||||
let conn = APPLICATION_CONTEXT.get::<DatabaseConnection>();
|
||||
let user = users::Entity::find_by_id(id).one(conn).await.map_err(AppError::from)?;
|
||||
match user {
|
||||
None => Err(AppError::notfound()),
|
||||
Some(user) => Ok(user),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn sign_in(jsn: UserSignForm) -> Result<String> {
|
||||
jsn.validate().map_err(AppError::from)?;
|
||||
|
||||
let conn = APPLICATION_CONTEXT.get::<DatabaseConnection>();
|
||||
|
||||
let condition = Condition::all().add(users::Column::Email.eq(&jsn.email));
|
||||
|
||||
let user = users::Entity::find().filter(condition).one(conn).await?;
|
||||
|
||||
if user.is_some() {
|
||||
return Err(AppError::from_msg("用户已存在", AppErrorType::Database));
|
||||
}
|
||||
|
||||
let hashed_password = bcrypt::hash(&jsn.password, DEFAULT_COST).unwrap();
|
||||
|
||||
let user = users::ActiveModel {
|
||||
email: Set(jsn.email),
|
||||
password_hash: Set(hashed_password),
|
||||
create_time: Set(Local::now()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let user = user.save(conn).await.map_err(AppError::from)?;
|
||||
|
||||
let jwt_token = JwtClaims::new(user.id.unwrap(), user.email.unwrap()).sign();
|
||||
|
||||
jwt_token
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
|
||||
use sea_orm::DatabaseConnection;
|
||||
|
||||
pub struct AppState {
|
||||
pub conn: DatabaseConnection,
|
||||
}
|
Loading…
Reference in New Issue