feat(session): integrate actix-web framework with enhanced session management

- Added actix-web and actix-multipart dependencies to Cargo.toml
- Integrated actix-web ResponseError trait for AppError handling
- Migrated session module to use actix-web request lifecycle management
- Enhanced Session struct with request-local state handling capabilities
- Implemented proper HTTP status code mapping for various error types
- Added comprehensive session middleware integration points
- Updated session state persistence and modification tracking logic
- Integrated proper JSON response formatting for error messages
- Added support for session renewal, purge, and unchanged state management
This commit is contained in:
zhenyi
2026-06-07 17:41:57 +08:00
parent 6a8e978073
commit 4e2c1c932a
11 changed files with 793 additions and 77 deletions
Generated
+606 -38
View File
@@ -18,6 +18,227 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
[[package]]
name = "actix-codec"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a"
dependencies = [
"bitflags",
"bytes",
"futures-core",
"futures-sink",
"memchr",
"pin-project-lite",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "actix-http"
version = "3.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93acb4a42f64936f9b8cae4a433b237599dd6eb6ed06124eb67132ef8cc90662"
dependencies = [
"actix-codec",
"actix-rt",
"actix-service",
"actix-utils",
"base64 0.22.1",
"bitflags",
"brotli",
"bytes",
"bytestring",
"derive_more 2.1.1",
"encoding_rs",
"flate2",
"foldhash 0.1.5",
"futures-core",
"h2 0.3.27",
"http 0.2.12",
"httparse",
"httpdate",
"itoa",
"language-tags",
"local-channel",
"mime",
"percent-encoding",
"pin-project-lite",
"rand 0.10.1",
"sha1 0.11.0",
"smallvec",
"tokio",
"tokio-util",
"tracing",
"zstd",
]
[[package]]
name = "actix-macros"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "actix-multipart"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5118a26dee7e34e894f7e85aa0ee5080ae4c18bf03c0e30d49a80e418f00a53"
dependencies = [
"actix-multipart-derive",
"actix-utils",
"actix-web",
"derive_more 0.99.20",
"futures-core",
"futures-util",
"httparse",
"local-waker",
"log",
"memchr",
"mime",
"rand 0.8.6",
"serde",
"serde_json",
"serde_plain",
"tempfile",
"tokio",
]
[[package]]
name = "actix-multipart-derive"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e11eb847f49a700678ea2fa73daeb3208061afa2b9d1a8527c03390f4c4a1c6b"
dependencies = [
"darling",
"parse-size",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "actix-router"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14f8c75c51892f18d9c46150c5ac7beb81c95f78c8b83a634d49f4ca32551fe7"
dependencies = [
"bytestring",
"cfg-if",
"http 0.2.12",
"regex",
"regex-lite",
"serde",
"tracing",
]
[[package]]
name = "actix-rt"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92589714878ca59a7626ea19734f0e07a6a875197eec751bb5d3f99e64998c63"
dependencies = [
"futures-core",
"tokio",
]
[[package]]
name = "actix-server"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502"
dependencies = [
"actix-rt",
"actix-service",
"actix-utils",
"futures-core",
"futures-util",
"mio",
"socket2 0.5.10",
"tokio",
"tracing",
]
[[package]]
name = "actix-service"
version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e46f36bf0e5af44bdc4bdb36fbbd421aa98c79a9bce724e1edeb3894e10dc7f"
dependencies = [
"futures-core",
"pin-project-lite",
]
[[package]]
name = "actix-utils"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8"
dependencies = [
"local-waker",
"pin-project-lite",
]
[[package]]
name = "actix-web"
version = "4.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff87453bc3b56e9b2b23c1cc0b1be8797184accf51d2abe0f8a33ec275d316bf"
dependencies = [
"actix-codec",
"actix-http",
"actix-macros",
"actix-router",
"actix-rt",
"actix-server",
"actix-service",
"actix-utils",
"actix-web-codegen",
"bytes",
"bytestring",
"cfg-if",
"cookie",
"derive_more 2.1.1",
"encoding_rs",
"foldhash 0.1.5",
"futures-core",
"futures-util",
"impl-more",
"itoa",
"language-tags",
"log",
"mime",
"once_cell",
"pin-project-lite",
"regex",
"regex-lite",
"serde",
"serde_json",
"serde_urlencoded",
"smallvec",
"socket2 0.6.4",
"time",
"tracing",
"url",
]
[[package]]
name = "actix-web-codegen"
version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8"
dependencies = [
"actix-router",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "adler2"
version = "2.0.1"
@@ -34,6 +255,31 @@ dependencies = [
"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 0.2.17",
]
[[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 = "aho-corasick"
version = "1.1.4"
@@ -61,6 +307,21 @@ dependencies = [
"equator",
]
[[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"
@@ -86,9 +347,11 @@ checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
name = "appks"
version = "0.1.0"
dependencies = [
"actix-multipart",
"actix-web",
"argon2",
"async-nats",
"base64",
"base64 0.22.1",
"captcha-rs",
"chacha20poly1305",
"chrono",
@@ -200,7 +463,7 @@ version = "0.49.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad3cd6df81292728e2a8cb1f1dcb4d7e7a1ab59b80c14fbbcba2baf9d5cf86a"
dependencies = [
"base64",
"base64 0.22.1",
"bytes",
"futures-util",
"memchr",
@@ -359,7 +622,7 @@ dependencies = [
"axum-core 0.4.5",
"bytes",
"futures-util",
"http",
"http 1.4.1",
"http-body",
"http-body-util",
"itoa",
@@ -385,7 +648,7 @@ dependencies = [
"axum-core 0.5.6",
"bytes",
"futures-util",
"http",
"http 1.4.1",
"http-body",
"http-body-util",
"itoa",
@@ -410,7 +673,7 @@ dependencies = [
"async-trait",
"bytes",
"futures-util",
"http",
"http 1.4.1",
"http-body",
"http-body-util",
"mime",
@@ -429,7 +692,7 @@ checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1"
dependencies = [
"bytes",
"futures-core",
"http",
"http 1.4.1",
"http-body",
"http-body-util",
"mime",
@@ -439,6 +702,12 @@ dependencies = [
"tower-service",
]
[[package]]
name = "base64"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
[[package]]
name = "base64"
version = "0.22.1"
@@ -502,6 +771,27 @@ dependencies = [
"hybrid-array",
]
[[package]]
name = "brotli"
version = "8.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8119e4516436f5708bbc474a9d395bf12f1b5395e93a92a56e647ac3388c8610"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
"brotli-decompressor",
]
[[package]]
name = "brotli-decompressor"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5962523e1b92ce1b5e793d9169b9943eece10d39f62550bc04bb605d75b94924"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
]
[[package]]
name = "built"
version = "0.8.1"
@@ -541,6 +831,15 @@ dependencies = [
"serde",
]
[[package]]
name = "bytestring"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86566c496f2f47d9b8147a4c8b02ffdb69c919fe0c2b2e7195d22cbba0e635c9"
dependencies = [
"bytes",
]
[[package]]
name = "captcha-rs"
version = "0.5.0"
@@ -548,7 +847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea23e9ba29e482e553d48391849195b95f055ffb059f785a28c9c5a046844223"
dependencies = [
"ab_glyph",
"base64",
"base64 0.22.1",
"image",
"imageproc",
"rand 0.9.4",
@@ -688,6 +987,45 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "const-oid"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
[[package]]
name = "convert_case"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "convert_case"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "cookie"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
dependencies = [
"aes-gcm",
"base64 0.20.0",
"hkdf 0.12.4",
"hmac 0.12.1",
"percent-encoding",
"rand 0.8.6",
"sha2 0.10.9",
"subtle",
"time",
"version_check",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
@@ -822,6 +1160,15 @@ dependencies = [
"hybrid-array",
]
[[package]]
name = "ctr"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
dependencies = [
"cipher",
]
[[package]]
name = "ctutils"
version = "0.4.2"
@@ -857,6 +1204,41 @@ dependencies = [
"syn",
]
[[package]]
name = "darling"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.20.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "dashmap"
version = "6.2.1"
@@ -883,7 +1265,7 @@ version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
dependencies = [
"const-oid",
"const-oid 0.9.6",
"pem-rfc7468",
"zeroize",
]
@@ -898,6 +1280,42 @@ dependencies = [
"serde_core",
]
[[package]]
name = "derive_more"
version = "0.99.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f"
dependencies = [
"convert_case 0.4.0",
"proc-macro2",
"quote",
"rustc_version",
"syn",
]
[[package]]
name = "derive_more"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
dependencies = [
"convert_case 0.10.0",
"proc-macro2",
"quote",
"rustc_version",
"syn",
"unicode-xid",
]
[[package]]
name = "digest"
version = "0.10.7"
@@ -905,7 +1323,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer 0.10.4",
"const-oid",
"const-oid 0.9.6",
"crypto-common 0.1.6",
"subtle",
]
@@ -917,6 +1335,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
"block-buffer 0.12.0",
"const-oid 0.10.2",
"crypto-common 0.2.2",
"ctutils",
]
@@ -1026,7 +1445,7 @@ version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0452bcc559431b16f472b7ab86e2f9ccd5f3c2da3795afbd6b773665e047fe"
dependencies = [
"http",
"http 1.4.1",
"prost 0.13.5",
"tokio",
"tokio-stream",
@@ -1309,6 +1728,16 @@ dependencies = [
"wasip3",
]
[[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 = "gif"
version = "0.14.2"
@@ -1427,6 +1856,25 @@ version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f70749695b063ecbf6b62949ccccde2e733ec3ecbbd71d467dca4e5c6c97cca0"
[[package]]
name = "h2"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d"
dependencies = [
"bytes",
"fnv",
"futures-core",
"futures-sink",
"futures-util",
"http 0.2.12",
"indexmap 2.14.0",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "h2"
version = "0.4.14"
@@ -1438,7 +1886,7 @@ dependencies = [
"fnv",
"futures-core",
"futures-sink",
"http",
"http 1.4.1",
"indexmap 2.14.0",
"slab",
"tokio",
@@ -1552,6 +2000,17 @@ dependencies = [
"digest 0.11.3",
]
[[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.4.1"
@@ -1569,7 +2028,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
"bytes",
"http",
"http 1.4.1",
]
[[package]]
@@ -1580,7 +2039,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
dependencies = [
"bytes",
"futures-core",
"http",
"http 1.4.1",
"http-body",
"pin-project-lite",
]
@@ -1622,8 +2081,8 @@ dependencies = [
"bytes",
"futures-channel",
"futures-core",
"h2",
"http",
"h2 0.4.14",
"http 1.4.1",
"http-body",
"httparse",
"httpdate",
@@ -1640,7 +2099,7 @@ version = "0.27.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
dependencies = [
"http",
"http 1.4.1",
"hyper",
"hyper-util",
"rustls",
@@ -1669,11 +2128,11 @@ version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
dependencies = [
"base64",
"base64 0.22.1",
"bytes",
"futures-channel",
"futures-util",
"http",
"http 1.4.1",
"http-body",
"hyper",
"ipnet",
@@ -1800,6 +2259,12 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[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.1.0"
@@ -1880,6 +2345,12 @@ version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40fac9d56ed6437b198fddba683305e8e2d651aa42647f00f5ae542e7f5c94a2"
[[package]]
name = "impl-more"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2"
[[package]]
name = "indexmap"
version = "1.9.3"
@@ -2014,6 +2485,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "language-tags"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -2079,6 +2556,23 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
[[package]]
name = "local-channel"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8"
dependencies = [
"futures-core",
"futures-sink",
"local-waker",
]
[[package]]
name = "local-waker"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487"
[[package]]
name = "lock_api"
version = "0.4.14"
@@ -2190,6 +2684,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys 0.61.2",
]
@@ -2419,14 +2914,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622acbc9100d3c10e2ee15804b0caa40e55c933d5aa53814cd520805b7958a49"
dependencies = [
"async-trait",
"base64",
"base64 0.22.1",
"bytes",
"chrono",
"form_urlencoded",
"futures-channel",
"futures-core",
"futures-util",
"http",
"http 1.4.1",
"http-body-util",
"humantime",
"hyper",
@@ -2506,6 +3001,12 @@ dependencies = [
"windows-link",
]
[[package]]
name = "parse-size"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "487f2ccd1e17ce8c1bfab3a65c89525af41cfad4c8659021a1e9a2aacd73b89b"
[[package]]
name = "password-hash"
version = "0.5.0"
@@ -2642,6 +3143,18 @@ dependencies = [
"universal-hash",
]
[[package]]
name = "polyval"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"opaque-debug",
"universal-hash",
]
[[package]]
name = "portable-atomic"
version = "1.13.1"
@@ -3186,6 +3699,12 @@ dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-lite"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973"
[[package]]
name = "regex-syntax"
version = "0.8.10"
@@ -3198,12 +3717,12 @@ version = "0.12.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
dependencies = [
"base64",
"base64 0.22.1",
"bytes",
"futures-core",
"futures-util",
"h2",
"http",
"h2 0.4.14",
"http 1.4.1",
"http-body",
"http-body-util",
"hyper",
@@ -3240,12 +3759,12 @@ version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3"
dependencies = [
"base64",
"base64 0.22.1",
"bytes",
"encoding_rs",
"futures-core",
"h2",
"http",
"h2 0.4.14",
"http 1.4.1",
"http-body",
"http-body-util",
"hyper",
@@ -3300,7 +3819,7 @@ version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
dependencies = [
"const-oid",
"const-oid 0.9.6",
"digest 0.10.7",
"num-bigint-dig",
"num-integer",
@@ -3576,6 +4095,15 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_plain"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50"
dependencies = [
"serde",
]
[[package]]
name = "serde_repr"
version = "0.1.20"
@@ -3813,7 +4341,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05b44e85bf579a8eeb4ceaa77a3a523baf2bf0e9bac7e40f405d537b5d2d5ccb"
dependencies = [
"base64",
"base64 0.22.1",
"bytes",
"cfg-if",
"chrono",
@@ -3917,7 +4445,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87a2bdd6e83f6b3ea525ca9fee568030508b58355a43d0b2c1674d5f79dcd65e"
dependencies = [
"atoi",
"base64",
"base64 0.22.1",
"bitflags",
"byteorder",
"chrono",
@@ -3996,6 +4524,12 @@ dependencies = [
"unicode-properties",
]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"
@@ -4234,11 +4768,11 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f591660438b3038dd04d16c938271c79e7e06260ad2ea2885a4861bfb238605d"
dependencies = [
"base64",
"base64 0.22.1",
"bytes",
"futures-core",
"futures-sink",
"http",
"http 1.4.1",
"httparse",
"rand 0.8.6",
"ring",
@@ -4258,10 +4792,10 @@ dependencies = [
"async-stream",
"async-trait",
"axum 0.7.9",
"base64",
"base64 0.22.1",
"bytes",
"h2",
"http",
"h2 0.4.14",
"http 1.4.1",
"http-body",
"http-body-util",
"hyper",
@@ -4287,10 +4821,10 @@ checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef"
dependencies = [
"async-trait",
"axum 0.8.9",
"base64",
"base64 0.22.1",
"bytes",
"h2",
"http",
"h2 0.4.14",
"http 1.4.1",
"http-body",
"http-body-util",
"hyper",
@@ -4409,7 +4943,7 @@ dependencies = [
"bitflags",
"bytes",
"futures-util",
"http",
"http 1.4.1",
"http-body",
"pin-project-lite",
"tower 0.5.3",
@@ -4559,6 +5093,12 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
[[package]]
name = "unicode-segmentation"
version = "1.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
[[package]]
name = "unicode-xid"
version = "0.2.6"
@@ -5335,6 +5875,34 @@ version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
[[package]]
name = "zstd"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "7.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
dependencies = [
"zstd-sys",
]
[[package]]
name = "zstd-sys"
version = "2.0.16+zstd.1.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
dependencies = [
"cc",
"pkg-config",
]
[[package]]
name = "zune-core"
version = "0.5.1"
+2
View File
@@ -47,6 +47,8 @@ tokio-stream = "0.1"
async-nats = "0.49"
futures-util = "0.3"
utoipa = { version = "5.5.0", features = ["uuid","chrono","auto_into_responses","actix_extras","decimal","macros"]}
actix-web = { version = "4", features = ["secure-cookies"] }
actix-multipart = "0.7"
[build-dependencies]
tonic-prost-build = "0.14.6"
+3
View File
@@ -0,0 +1,3 @@
pub mod openapi;
pub mod response;
pub mod routes;
+4
View File
@@ -0,0 +1,4 @@
use utoipa::OpenApi;
#[derive(OpenApi)]
pub struct OpenApiDoc;
+19
View File
@@ -0,0 +1,19 @@
use serde::Serialize;
#[derive(Debug, Serialize, utoipa::ToSchema)]
pub struct ApiResponse<T: Serialize> {
pub data: T,
}
#[derive(Debug, Serialize, utoipa::ToSchema)]
pub struct ApiEmptyResponse {
pub message: String,
}
#[derive(Debug, Serialize, utoipa::ToSchema)]
pub struct ApiListResponse<T: Serialize> {
pub data: Vec<T>,
pub total: i64,
pub page: i64,
pub per_page: i64,
}
+6
View File
@@ -0,0 +1,6 @@
use actix_web::web;
use actix_web::web::scope;
pub fn init_routes(cfg: &mut web::ServiceConfig) {
cfg.service(scope("/api/v1"));
}
+43
View File
@@ -1,3 +1,4 @@
use actix_web::HttpResponse;
use thiserror::Error;
pub type AppResult<T> = Result<T, AppError>;
@@ -103,3 +104,45 @@ pub enum AppError {
#[error("transaction error")]
TxnError,
}
impl actix_web::ResponseError for AppError {
fn status_code(&self) -> actix_web::http::StatusCode {
use actix_web::http::StatusCode;
match self {
AppError::Unauthorized => StatusCode::UNAUTHORIZED,
AppError::Forbidden(_) => StatusCode::FORBIDDEN,
AppError::NotFound(_) | AppError::UserNotFound => StatusCode::NOT_FOUND,
AppError::BadRequest(_) | AppError::Parse(_) | AppError::CaptchaError => {
StatusCode::BAD_REQUEST
}
AppError::Conflict(_) | AppError::AccountAlreadyExists | AppError::EmailExists => {
StatusCode::CONFLICT
}
AppError::QuotaExceeded(_) => StatusCode::TOO_MANY_REQUESTS,
AppError::InvalidPassword
| AppError::PasswordTooWeak
| AppError::InvalidTwoFactorCode
| AppError::TwoFactorRequired
| AppError::TwoFactorAlreadyEnabled
| AppError::TwoFactorNotSetup
| AppError::TwoFactorNotEnabled
| AppError::InvalidResetToken
| AppError::ResetTokenExpired
| AppError::InvalidEmailCode
| AppError::RsaDecodeError
| AppError::RsaGenerationError => StatusCode::BAD_REQUEST,
AppError::PasswordHashError(_) => StatusCode::INTERNAL_SERVER_ERROR,
_ => StatusCode::INTERNAL_SERVER_ERROR,
}
}
fn error_response(&self) -> HttpResponse {
let status = self.status_code();
let message = if status == actix_web::http::StatusCode::INTERNAL_SERVER_ERROR {
"internal server error".to_string()
} else {
self.to_string()
};
HttpResponse::build(status).json(serde_json::json!({ "error": message }))
}
}
+1 -1
View File
@@ -1,3 +1,4 @@
pub mod api;
pub mod cache;
pub mod config;
pub mod error;
@@ -9,4 +10,3 @@ pub mod queue;
pub mod service;
pub mod session;
pub mod storage;
pub mod api;
+13
View File
@@ -541,6 +541,19 @@ impl WorkspaceService {
.ok_or(AppError::NotFound("workspace not found".into()))
}
pub(crate) async fn find_workspace_by_name(&self, name: &str) -> Result<Workspace, AppError> {
sqlx::query_as::<_, Workspace>(
"SELECT id, owner_id, name, description, avatar_url, visibility, plan, status, \
default_role, is_personal, archived_at, created_at, updated_at, deleted_at \
FROM workspace WHERE lower(name) = lower($1) AND deleted_at IS NULL",
)
.bind(name)
.fetch_optional(self.ctx.db.reader())
.await
.map_err(AppError::Database)?
.ok_or(AppError::NotFound("workspace not found".into()))
}
pub async fn workspace_user_role(
&self,
user_uid: Uuid,
+2
View File
@@ -1,9 +1,11 @@
pub mod config;
pub mod middleware;
#[allow(clippy::module_inception)]
pub mod session;
pub mod storage;
pub use self::{
middleware::{CookieContentSecurity, SessionMiddleware, TtlExtensionPolicy},
session::{Session, SessionState, SessionStatus, SessionUser},
storage::{RedisSessionStore, SessionKey, SessionStore, generate_session_key},
};
+94 -38
View File
@@ -1,6 +1,10 @@
use std::cell::{Ref, RefCell};
use std::future::{Ready, ready};
use std::mem;
use std::rc::Rc;
use actix_web::dev::{Extensions, Payload, ServiceRequest, ServiceResponse};
use actix_web::{FromRequest, HttpMessage, HttpRequest};
use serde::Serialize;
use serde::de::DeserializeOwned;
use serde_json::{Map, Value};
@@ -10,21 +14,30 @@ use crate::error::AppError;
const SESSION_USER_KEY: &str = "session:user_uid";
/// Request-local session handle.
///
/// Clones share the same interior state through request extensions, so changes
/// made by extractors/handlers can be collected by `SessionMiddleware` at the
/// end of the request lifecycle.
#[derive(Clone)]
pub struct Session(Rc<RefCell<SessionInner>>);
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub enum SessionStatus {
/// Session state has changed and must be persisted.
Changed,
/// Session and cookie must be deleted. Further mutations are ignored.
Purged,
/// Session key must be regenerated while preserving current state.
Renewed,
/// Session has not been modified during this request.
#[default]
Unchanged,
}
#[derive(Default)]
struct SessionInner {
state: Map<String, Value>,
state: SessionState,
status: SessionStatus,
}
@@ -33,7 +46,7 @@ impl Session {
Self::default()
}
pub fn from_state(state: Map<String, Value>) -> Self {
pub fn from_state(state: SessionState) -> Self {
Self(Rc::new(RefCell::new(SessionInner {
state,
status: SessionStatus::Unchanged,
@@ -41,34 +54,36 @@ impl Session {
}
pub fn get<T: DeserializeOwned>(&self, key: &str) -> Result<Option<T>, AppError> {
if let Some(value) = self.0.borrow().state.get(key) {
Ok(Some(serde_json::from_value(value.clone())?))
} else {
Ok(None)
}
let value = self.0.borrow().state.get(key).cloned();
value
.map(serde_json::from_value)
.transpose()
.map_err(AppError::Json)
}
pub fn contains_key(&self, key: &str) -> bool {
self.0.borrow().state.contains_key(key)
}
pub fn entries(&self) -> Ref<'_, Map<String, Value>> {
pub fn entries(&self) -> Ref<'_, SessionState> {
Ref::map(self.0.borrow(), |inner| &inner.state)
}
pub fn status(&self) -> SessionStatus {
Ref::map(self.0.borrow(), |inner| &inner.status).clone()
self.0.borrow().status.clone()
}
pub fn is_empty(&self) -> bool {
self.0.borrow().state.is_empty()
}
pub fn insert<T: Serialize>(&self, key: impl Into<String>, value: T) -> Result<(), AppError> {
let value = serde_json::to_value(value)?;
let mut inner = self.0.borrow_mut();
if inner.status != SessionStatus::Purged {
if inner.status != SessionStatus::Renewed {
inner.status = SessionStatus::Changed;
}
let val = serde_json::to_value(&value)?;
inner.state.insert(key.into(), val);
mark_changed(&mut inner.status);
inner.state.insert(key.into(), value);
}
Ok(())
@@ -83,22 +98,20 @@ impl Session {
F: FnOnce(T) -> T,
{
let mut inner = self.0.borrow_mut();
let key_str = key.into();
let key = key.into();
if let Some(val) = inner.state.get(&key_str) {
if inner.status == SessionStatus::Purged {
return Ok(());
}
let value: T = serde_json::from_value(val.clone())?;
let updated = serde_json::to_value(updater(value))?;
if inner.status != SessionStatus::Renewed {
inner.status = SessionStatus::Changed;
}
inner.state.insert(key_str, updated);
if inner.status == SessionStatus::Purged {
return Ok(());
}
let Some(value) = inner.state.get(&key).cloned() else {
return Ok(());
};
let value = serde_json::from_value(value)?;
let updated = serde_json::to_value(updater(value))?;
mark_changed(&mut inner.status);
inner.state.insert(key, updated);
Ok(())
}
@@ -121,14 +134,12 @@ impl Session {
pub fn remove(&self, key: &str) -> Option<Value> {
let mut inner = self.0.borrow_mut();
if inner.status != SessionStatus::Purged {
if inner.status != SessionStatus::Renewed {
inner.status = SessionStatus::Changed;
}
return inner.state.remove(key);
if inner.status == SessionStatus::Purged {
return None;
}
None
mark_changed(&mut inner.status);
inner.state.remove(key)
}
pub fn remove_as<T: DeserializeOwned>(&self, key: &str) -> Option<Result<T, AppError>> {
@@ -140,9 +151,7 @@ impl Session {
let mut inner = self.0.borrow_mut();
if inner.status != SessionStatus::Purged {
if inner.status != SessionStatus::Renewed {
inner.status = SessionStatus::Changed;
}
mark_changed(&mut inner.status);
inner.state.clear();
}
}
@@ -174,13 +183,45 @@ impl Session {
}
pub fn take_state(&self) -> SessionState {
let mut inner = self.0.borrow_mut();
std::mem::take(&mut inner.state)
mem::take(&mut self.0.borrow_mut().state)
}
pub fn mark_unchanged(&self) {
self.0.borrow_mut().status = SessionStatus::Unchanged;
}
pub(crate) fn set_session(req: &mut ServiceRequest, state: SessionState) {
let session = Self::get_session(&mut req.extensions_mut());
let mut inner = session.0.borrow_mut();
inner.state = state;
inner.status = SessionStatus::Unchanged;
}
pub(crate) fn get_changes<B>(res: &mut ServiceResponse<B>) -> (SessionStatus, SessionState) {
let Some(inner) = res
.request()
.extensions()
.get::<Rc<RefCell<SessionInner>>>()
.cloned()
else {
return (SessionStatus::Unchanged, SessionState::new());
};
let mut inner = inner.borrow_mut();
let status = inner.status.clone();
let state = mem::take(&mut inner.state);
(status, state)
}
pub(crate) fn get_session(extensions: &mut Extensions) -> Session {
if let Some(inner) = extensions.get::<Rc<RefCell<SessionInner>>>() {
return Session(Rc::clone(inner));
}
let inner = Rc::new(RefCell::new(SessionInner::default()));
extensions.insert(Rc::clone(&inner));
Session(inner)
}
}
impl Default for Session {
@@ -193,3 +234,18 @@ pub type SessionState = Map<String, Value>;
#[derive(Debug, Clone, Copy)]
pub struct SessionUser(pub Uuid);
impl FromRequest for Session {
type Error = AppError;
type Future = Ready<Result<Self, Self::Error>>;
fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
ready(Ok(Self::get_session(&mut req.extensions_mut())))
}
}
fn mark_changed(status: &mut SessionStatus) {
if *status != SessionStatus::Renewed {
*status = SessionStatus::Changed;
}
}