Init
This commit is contained in:
Generated
+329
-43
@@ -128,6 +128,18 @@ version = "1.0.102"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||
|
||||
[[package]]
|
||||
name = "argon2"
|
||||
version = "0.6.0-rc.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7af50940b73bf4e16c15c448a2b121c63f2d68e3e54b6a8731673cb4aa0cdff5"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"blake2",
|
||||
"cpufeatures 0.3.0",
|
||||
"password-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arraydeque"
|
||||
version = "0.5.1"
|
||||
@@ -478,6 +490,58 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90"
|
||||
dependencies = [
|
||||
"axum-core",
|
||||
"bytes",
|
||||
"form_urlencoded",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"itoa",
|
||||
"matchit",
|
||||
"memchr",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"serde_core",
|
||||
"serde_json",
|
||||
"serde_path_to_error",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"sync_wrapper",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
@@ -525,6 +589,15 @@ dependencies = [
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
version = "0.11.0-rc.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "061f1a09225e328e1ffbb378d2d49923c0ca5fee19fb5ac1cc9c1e9d52b93690"
|
||||
dependencies = [
|
||||
"digest 0.11.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
@@ -534,6 +607,15 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blocking"
|
||||
version = "1.6.2"
|
||||
@@ -619,9 +701,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.60"
|
||||
version = "1.2.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20"
|
||||
checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
@@ -729,6 +811,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
||||
|
||||
[[package]]
|
||||
name = "cmov"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.5"
|
||||
@@ -834,9 +922,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crc-catalog"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||
checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853"
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
@@ -924,6 +1012,24 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctutils"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
|
||||
dependencies = [
|
||||
"cmov",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.11"
|
||||
@@ -1007,12 +1113,23 @@ version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"block-buffer 0.10.4",
|
||||
"const-oid",
|
||||
"crypto-common",
|
||||
"crypto-common 0.1.7",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.11.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
|
||||
dependencies = [
|
||||
"block-buffer 0.12.0",
|
||||
"crypto-common 0.2.1",
|
||||
"ctutils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
@@ -1430,7 +1547,7 @@ version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1442,6 +1559,95 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body-util"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
|
||||
|
||||
[[package]]
|
||||
name = "httpdate"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "hybrid-array"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d46837a0ed51fe95bd3b05de33cd64a1ee88fc797477ca48446872504507c5"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.65"
|
||||
@@ -1573,9 +1779,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "idna_adapter"
|
||||
version = "1.2.1"
|
||||
version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
|
||||
checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714"
|
||||
dependencies = [
|
||||
"icu_normalizer",
|
||||
"icu_properties",
|
||||
@@ -1645,9 +1851,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.95"
|
||||
version = "0.3.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca"
|
||||
checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"futures-util",
|
||||
@@ -1749,9 +1955,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.185"
|
||||
version = "0.2.186"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
|
||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@@ -1768,7 +1974,7 @@ dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"plain",
|
||||
"redox_syscall 0.7.4",
|
||||
"redox_syscall 0.7.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1832,6 +2038,12 @@ dependencies = [
|
||||
"regex-automata",
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -1839,7 +2051,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1865,6 +2077,12 @@ dependencies = [
|
||||
"sea-orm-migration",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "1.2.0"
|
||||
@@ -2034,9 +2252,11 @@ dependencies = [
|
||||
name = "oxspeak_server"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"axum",
|
||||
"bitflags",
|
||||
"config",
|
||||
"event_bus",
|
||||
"glob",
|
||||
"log",
|
||||
"migration",
|
||||
"parking_lot",
|
||||
@@ -2091,6 +2311,16 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aab41826031698d6ffcd9cff78ef56ef998e39dc7e5067cdfebe373842d4723b"
|
||||
dependencies = [
|
||||
"getrandom 0.4.2",
|
||||
"phc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathdiff"
|
||||
version = "0.2.3"
|
||||
@@ -2164,6 +2394,17 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phc"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44dc769b75f93afdddd8c7fa12d685292ddeff1e66f7f0f3a234cf1818afe892"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"ctutils",
|
||||
"getrandom 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.17"
|
||||
@@ -2484,9 +2725,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.7.4"
|
||||
version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a"
|
||||
checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@@ -2579,7 +2820,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"num-bigint-dig",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
@@ -2604,9 +2845,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust_decimal"
|
||||
version = "1.41.0"
|
||||
version = "1.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ce901f9a19d251159075a4c37af514c3b8ef99c22e02dd8c19161cf397ee94a"
|
||||
checksum = "0c5108e3d4d903e21aac27f12ba5377b6b34f9f44b325e4894c7924169d06995"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"borsh",
|
||||
@@ -2907,6 +3148,17 @@ dependencies = [
|
||||
"zmij",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_path_to_error"
|
||||
version = "0.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "1.1.1"
|
||||
@@ -2936,7 +3188,7 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2947,7 +3199,7 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2981,7 +3233,7 @@ version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
@@ -3137,7 +3389,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crc",
|
||||
"digest",
|
||||
"digest 0.10.7",
|
||||
"dotenvy",
|
||||
"either",
|
||||
"futures-channel",
|
||||
@@ -3301,6 +3553,12 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||
|
||||
[[package]]
|
||||
name = "synstructure"
|
||||
version = "0.13.2"
|
||||
@@ -3424,9 +3682,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.52.2"
|
||||
version = "1.52.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386"
|
||||
checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"libc",
|
||||
@@ -3512,6 +3770,34 @@ version = "1.1.1+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"pin-project-lite",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"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.44"
|
||||
@@ -3658,9 +3944,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "utoipa"
|
||||
version = "5.4.0"
|
||||
version = "5.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2fcc29c80c21c31608227e0912b2d7fddba57ad76b606890627ba8ee7964e993"
|
||||
checksum = "8bde15df68e80b16c7d16b9616e80770ad158988daa56a27dccd1e55558b0160"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
@@ -3670,9 +3956,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "utoipa-gen"
|
||||
version = "5.4.0"
|
||||
version = "5.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d79d08d92ab8af4c5e8a6da20c47ae3f61a0f1dabc1997cdf2d082b757ca08b"
|
||||
checksum = "6ba0b99ee52df3028635d93840c797102da61f8a7bb3cf751032455895b52ef8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -3759,9 +4045,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.118"
|
||||
version = "0.2.121"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89"
|
||||
checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
@@ -3773,9 +4059,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.68"
|
||||
version = "0.4.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8"
|
||||
checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -3783,9 +4069,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.118"
|
||||
version = "0.2.121"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed"
|
||||
checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -3793,9 +4079,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.118"
|
||||
version = "0.2.121"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904"
|
||||
checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
@@ -3806,9 +4092,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.118"
|
||||
version = "0.2.121"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129"
|
||||
checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -3849,9 +4135,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.95"
|
||||
version = "0.3.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d"
|
||||
checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
||||
+4
-2
@@ -11,7 +11,8 @@ crate-type = ["rlib"]
|
||||
members = [".", "migration", "event_bus"]
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.52.2", features = ["full"] }
|
||||
tokio = { version = "1.52.3", features = ["full"] }
|
||||
axum = "0.8"
|
||||
config = "0.15.22"
|
||||
sea-orm = { version = "2.0.0-rc.38", features = ["sqlx-sqlite", "sqlx-postgres", "sqlx-mysql", "runtime-tokio", "with-chrono", "with-uuid", "with-json", "schema-sync"] }
|
||||
migration = { path = "migration" }
|
||||
@@ -26,4 +27,5 @@ tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "time"]
|
||||
thiserror = "2"
|
||||
utoipa = { version = "5", features = ["uuid"] }
|
||||
log = "0.4"
|
||||
glob = "0.3"
|
||||
bitflags = "2.11.1"
|
||||
argon2 = { version = "0.6.0-rc.8", features = ["password-hash"] }
|
||||
@@ -28,7 +28,19 @@ impl MigrationTrait for Migration {
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(Alias::new("default_permissions"))
|
||||
ColumnDef::new(Alias::new("default_server_permissions"))
|
||||
.big_integer()
|
||||
.not_null()
|
||||
.default(0),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(Alias::new("default_channel_permissions"))
|
||||
.big_integer()
|
||||
.not_null()
|
||||
.default(0),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(Alias::new("default_voice_permissions"))
|
||||
.big_integer()
|
||||
.not_null()
|
||||
.default(0),
|
||||
@@ -120,11 +132,6 @@ impl MigrationTrait for Migration {
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(Alias::new("default_permissions"))
|
||||
.big_integer()
|
||||
.null(),
|
||||
)
|
||||
// Indexes créés après
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
@@ -317,7 +324,19 @@ impl MigrationTrait for Migration {
|
||||
.default(false),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(Alias::new("permissions"))
|
||||
ColumnDef::new(Alias::new("server_permissions"))
|
||||
.big_integer()
|
||||
.not_null()
|
||||
.default(0),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(Alias::new("channel_permissions"))
|
||||
.big_integer()
|
||||
.not_null()
|
||||
.default(0),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(Alias::new("voice_permissions"))
|
||||
.big_integer()
|
||||
.not_null()
|
||||
.default(0),
|
||||
@@ -361,12 +380,6 @@ impl MigrationTrait for Migration {
|
||||
.not_null()
|
||||
.default("member".to_owned()),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(Alias::new("permissions"))
|
||||
.big_integer()
|
||||
.not_null()
|
||||
.default(0),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(Alias::new("joined_at"))
|
||||
.timestamp_with_time_zone()
|
||||
@@ -553,7 +566,19 @@ impl MigrationTrait for Migration {
|
||||
.col(ColumnDef::new("server_id").uuid().not_null())
|
||||
.col(ColumnDef::new("name").string().not_null())
|
||||
.col(
|
||||
ColumnDef::new("permissions")
|
||||
ColumnDef::new(Alias::new("server_permissions"))
|
||||
.big_integer()
|
||||
.not_null()
|
||||
.default(0),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(Alias::new("channel_permissions"))
|
||||
.big_integer()
|
||||
.not_null()
|
||||
.default(0),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(Alias::new("voice_permissions"))
|
||||
.big_integer()
|
||||
.not_null()
|
||||
.default(0),
|
||||
@@ -570,6 +595,12 @@ impl MigrationTrait for Migration {
|
||||
.not_null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new("edited_at")
|
||||
.timestamp_with_time_zone()
|
||||
.null()
|
||||
.default(Expr::current_timestamp()),
|
||||
)
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk_group_server")
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
pub mod state;
|
||||
|
||||
use crate::config::AppConfig;
|
||||
use crate::database::Database;
|
||||
use migration::{Migrator, MigratorTrait};
|
||||
pub use state::AppState;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct App {
|
||||
pub state: AppState,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub async fn build(config: AppConfig) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
let db_manager = Database::init(&config.database.url).await?;
|
||||
let db = db_manager.get_connection().clone();
|
||||
|
||||
Migrator::up(&db, None).await?;
|
||||
|
||||
let state = AppState {
|
||||
db,
|
||||
config: Arc::new(config),
|
||||
};
|
||||
|
||||
Ok(Self { state })
|
||||
}
|
||||
|
||||
pub async fn run(self) -> Result<(), Box<dyn std::error::Error>> {
|
||||
tracing::info!("Starting services...");
|
||||
|
||||
tokio::select! {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
use crate::config::AppConfig;
|
||||
use sea_orm::DatabaseConnection;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AppState {
|
||||
pub db: DatabaseConnection,
|
||||
pub config: Arc<AppConfig>,
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::time::Duration;
|
||||
use sea_orm::{ConnectOptions, Database as SeaDatabase, DatabaseConnection, DbErr};
|
||||
use migration::{Migrator, MigratorTrait};
|
||||
use std::time::Duration;
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Database {
|
||||
@@ -19,10 +19,6 @@ impl Database {
|
||||
|
||||
let connection = SeaDatabase::connect(opt).await?;
|
||||
|
||||
// On lance les migrations ici.
|
||||
// Si ça échoue, le programme s'arrête proprement à l'init.
|
||||
Migrator::up(&connection, None).await?;
|
||||
|
||||
Ok(Self { connection })
|
||||
}
|
||||
|
||||
@@ -31,4 +27,4 @@ impl Database {
|
||||
pub fn get_connection(&self) -> &DatabaseConnection {
|
||||
&self.connection
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub mod server;
|
||||
@@ -0,0 +1,23 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use axum::Router;
|
||||
use tokio::net::TcpListener;
|
||||
|
||||
use crate::config::AppConfig;
|
||||
use crate::routes;
|
||||
|
||||
pub async fn start(config: &AppConfig) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let addr = SocketAddr::new(
|
||||
std::net::IpAddr::V4(config.network.host),
|
||||
config.network.tcp_port,
|
||||
);
|
||||
|
||||
let app: Router = routes::router();
|
||||
|
||||
let listener = TcpListener::bind(addr).await?;
|
||||
tracing::info!(%addr, "HTTP server listening");
|
||||
|
||||
axum::serve(listener, app).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,4 +1,11 @@
|
||||
pub mod config;
|
||||
pub mod database;
|
||||
pub mod http;
|
||||
pub mod models;
|
||||
// pub mod permissions_old;
|
||||
pub mod core;
|
||||
pub mod permissions;
|
||||
pub mod repositories;
|
||||
pub mod routes;
|
||||
pub mod udp;
|
||||
pub mod utils;
|
||||
|
||||
+9
-2
@@ -1,4 +1,7 @@
|
||||
use migration::{Migrator, MigratorTrait};
|
||||
use oxspeak_server_lib::config::AppConfig;
|
||||
use oxspeak_server_lib::core::App;
|
||||
use oxspeak_server_lib::database::Database;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@@ -19,10 +22,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
AppConfig::gen_config()?;
|
||||
tracing::info!(config_path = "config.toml", "Generated");
|
||||
tracing::info!("Config generated, please edit config.toml");
|
||||
return Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
let config = AppConfig::load()?;
|
||||
tracing::info!(?config, "Loaded config");
|
||||
|
||||
// 3. Build & Run
|
||||
let app = App::build(config).await?;
|
||||
app.run().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -11,9 +11,13 @@ pub struct Model {
|
||||
pub id: Uuid,
|
||||
pub server_id: Uuid,
|
||||
pub name: String,
|
||||
pub permissions: u64,
|
||||
pub is_default: bool,
|
||||
pub created_at: DateTimeUtc,
|
||||
|
||||
/// Permissions serveur par défaut (stockées en i64, lues en u64)
|
||||
pub server_permissions: i64,
|
||||
pub channel_permissions: i64,
|
||||
pub voice_permissions: i64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
||||
+20
-1
@@ -1,5 +1,6 @@
|
||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.19
|
||||
|
||||
use crate::permissions::{ChannelPermission, PermissionSet, ServerPermission, VoicePermission};
|
||||
use sea_orm::entity::prelude::*;
|
||||
use sea_orm::prelude::async_trait::async_trait;
|
||||
use sea_orm::Set;
|
||||
@@ -13,7 +14,11 @@ pub struct Model {
|
||||
pub password: Option<String>,
|
||||
pub created_at: DateTimeUtc,
|
||||
pub updated_at: DateTimeUtc,
|
||||
pub default_permissions: u64,
|
||||
|
||||
/// Permissions serveur par défaut (stockées en i64, lues en u64)
|
||||
pub default_server_permissions: i64,
|
||||
pub default_channel_permissions: i64,
|
||||
pub default_voice_permissions: i64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
@@ -47,9 +52,23 @@ impl Related<super::server_user::Entity> for Entity {
|
||||
#[async_trait]
|
||||
impl ActiveModelBehavior for ActiveModel {
|
||||
fn new() -> Self {
|
||||
let default_perm = PermissionSet::DEFAULT;
|
||||
Self {
|
||||
id: Set(Uuid::new_v4()),
|
||||
default_server_permissions: Set(default_perm.server.bits() as i64),
|
||||
default_channel_permissions: Set(default_perm.channel.bits() as i64),
|
||||
default_voice_permissions: Set(default_perm.voice.bits() as i64),
|
||||
..ActiveModelTrait::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Model {
|
||||
pub fn default_permissions(&self) -> PermissionSet {
|
||||
PermissionSet {
|
||||
server: ServerPermission::from_bits_truncate(self.default_server_permissions as u64),
|
||||
channel: ChannelPermission::from_bits_truncate(self.default_channel_permissions as u64),
|
||||
voice: VoicePermission::from_bits_truncate(self.default_voice_permissions as u64),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,11 @@ pub struct Model {
|
||||
pub updated_at: DateTimeUtc,
|
||||
pub is_admin: bool,
|
||||
pub is_owner: bool,
|
||||
pub permissions: u64,
|
||||
|
||||
/// Permissions serveur par défaut (stockées en i64, lues en u64)
|
||||
pub server_permissions: i64,
|
||||
pub channel_permissions: i64,
|
||||
pub voice_permissions: i64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
use bitflags::bitflags;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
bitflags! {
|
||||
/// Permissions liées aux serveurs (gestion globale)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct ServerPermission: u64 {
|
||||
const ManageServer = 1 << 0;
|
||||
const ManageRoles = 1 << 1;
|
||||
const KickMember = 1 << 2;
|
||||
const BanMember = 1 << 3;
|
||||
// ... jusqu'à 64 perms serveur
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Permissions liées aux canaux (texte + vocal)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct ChannelPermission: u64 {
|
||||
const ReadChannel = 1 << 0;
|
||||
const SendMessage = 1 << 1;
|
||||
const DeleteMessage = 1 << 2;
|
||||
const DeleteOthersMessage = 1 << 3;
|
||||
const ManageChannel = 1 << 4;
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Permissions liées au vocal
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct VoicePermission: u64 {
|
||||
const JoinChannel = 1 << 0;
|
||||
const VoiceSpeak = 1 << 1;
|
||||
const VoiceMuteOthers = 1 << 2;
|
||||
const VoiceDeafenOthers = 1 << 3;
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
/// Agrégat de permissions traversant tous les domaines.
|
||||
/// Utile pour les API et les contrôles complets.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
|
||||
pub struct PermissionSet {
|
||||
pub server: ServerPermission,
|
||||
pub channel: ChannelPermission,
|
||||
pub voice: VoicePermission,
|
||||
}
|
||||
|
||||
impl PermissionSet {
|
||||
pub const fn new(
|
||||
server: ServerPermission,
|
||||
channel: ChannelPermission,
|
||||
voice: VoicePermission,
|
||||
) -> Self {
|
||||
Self {
|
||||
server,
|
||||
channel,
|
||||
voice,
|
||||
}
|
||||
}
|
||||
|
||||
pub const DEFAULT: Self = Self::new(
|
||||
ServerPermission::empty(),
|
||||
ChannelPermission::from_bits_truncate(
|
||||
ChannelPermission::ReadChannel.bits()
|
||||
| ChannelPermission::SendMessage.bits()
|
||||
| ChannelPermission::DeleteMessage.bits(),
|
||||
),
|
||||
VoicePermission::from_bits_truncate(
|
||||
VoicePermission::JoinChannel.bits() | VoicePermission::VoiceSpeak.bits(),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
use bitflags::bitflags;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
bitflags! {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
|
||||
#[serde(transparent)] // Permet de sérialiser comme un simple entier
|
||||
pub struct Permission: u64 {
|
||||
/// Pouvoir voir le canal dans la liste et lire les messages
|
||||
const ReadChannel = 1 << 0;
|
||||
/// Pouvoir rejoindre le canal vocal
|
||||
const JoinChannel = 1 << 1;
|
||||
/// Pouvoir envoyer des messages
|
||||
const SendMessage = 1 << 2;
|
||||
/// Pouvoir supprimer ses propres messages
|
||||
const DeleteMessage = 1 << 3;
|
||||
/// Pouvoir supprimer les messages des autres (Modérateur)
|
||||
const DeleteOthersMessage = 1 << 4;
|
||||
/// Pouvoir modifier les paramètres du canal
|
||||
const ManageChannel = 1 << 5;
|
||||
/// Pouvoir gérer les groupes/permissions du serveur
|
||||
const ManageRoles = 1 << 6;
|
||||
/// Pouvoir expulser des membres du serveur
|
||||
const KickMember = 1 << 7;
|
||||
/// Pouvoir parler en vocal
|
||||
const VoiceSpeak = 1 << 8;
|
||||
/// Pouvoir rendre muet les autres utilisateurs en vocal
|
||||
const VoiceMuteOthers = 1 << 9;
|
||||
/// Pouvoir modifier les paramètres globaux du serveur
|
||||
const ManageServer = 1 << 10;
|
||||
}
|
||||
}
|
||||
|
||||
impl Permission {
|
||||
// ─── Presets de permissions ────────────────────────────────────────────
|
||||
|
||||
/// Permissions par défaut pour un utilisateur standard
|
||||
pub const DEFAULT: Self = Self::from_bits_truncate(
|
||||
Self::ReadChannel.bits()
|
||||
| Self::JoinChannel.bits()
|
||||
| Self::SendMessage.bits()
|
||||
| Self::DeleteMessage.bits()
|
||||
| Self::VoiceSpeak.bits(),
|
||||
);
|
||||
|
||||
/// Permissions de modération (sans gestion serveur)
|
||||
pub const MODERATOR: Self = Self::from_bits_truncate(
|
||||
Self::DEFAULT.bits()
|
||||
| Self::DeleteOthersMessage.bits()
|
||||
| Self::KickMember.bits()
|
||||
| Self::VoiceMuteOthers.bits(),
|
||||
);
|
||||
|
||||
/// Permissions d'administrateur (accès complet hormis l'ownership)
|
||||
pub const ADMIN: Self = Self::from_bits_truncate(
|
||||
Self::MODERATOR.bits() | Self::ManageChannel.bits() | Self::ManageRoles.bits(),
|
||||
);
|
||||
|
||||
/// Toutes les permissions (équivalent owner)
|
||||
pub const OWNER: Self = Self::all();
|
||||
|
||||
// ─── Groupes logiques ──────────────────────────────────────────────────
|
||||
|
||||
/// Toutes les permissions liées au chat
|
||||
pub const CHAT_PERMISSIONS: Self = Self::from_bits_truncate(
|
||||
Self::ReadChannel.bits()
|
||||
| Self::SendMessage.bits()
|
||||
| Self::DeleteMessage.bits()
|
||||
| Self::DeleteOthersMessage.bits(),
|
||||
);
|
||||
|
||||
/// Toutes les permissions liées au vocal
|
||||
pub const VOICE_PERMISSIONS: Self = Self::from_bits_truncate(
|
||||
Self::JoinChannel.bits() | Self::VoiceSpeak.bits() | Self::VoiceMuteOthers.bits(),
|
||||
);
|
||||
|
||||
/// Toutes les permissions de gestion (Manage*)
|
||||
pub const MANAGEMENT_PERMISSIONS: Self = Self::from_bits_truncate(
|
||||
Self::ManageChannel.bits() | Self::ManageRoles.bits() | Self::ManageServer.bits(),
|
||||
);
|
||||
|
||||
// ─── Helpers de vérification ───────────────────────────────────────────
|
||||
|
||||
/// Vérifie si l'utilisateur peut effectuer des actions de modération
|
||||
#[inline]
|
||||
pub fn is_moderator(&self) -> bool {
|
||||
self.contains(Self::DeleteOthersMessage) || self.contains(Self::KickMember)
|
||||
}
|
||||
|
||||
/// Vérifie si l'utilisateur a au moins une permission de gestion
|
||||
#[inline]
|
||||
pub fn is_admin(&self) -> bool {
|
||||
self.intersects(Self::MANAGEMENT_PERMISSIONS)
|
||||
}
|
||||
|
||||
/// Vérifie si l'utilisateur a tous les droits (owner)
|
||||
#[inline]
|
||||
pub fn is_owner(&self) -> bool {
|
||||
*self == Self::OWNER
|
||||
}
|
||||
|
||||
/// Vérifie si l'utilisateur peut accéder à un canal vocal
|
||||
#[inline]
|
||||
pub fn can_use_voice(&self) -> bool {
|
||||
self.contains(Self::JoinChannel) && self.contains(Self::VoiceSpeak)
|
||||
}
|
||||
|
||||
/// Vérifie si l'utilisateur peut interagir avec le chat (lire ET écrire)
|
||||
#[inline]
|
||||
pub fn can_chat(&self) -> bool {
|
||||
self.contains(Self::ReadChannel | Self::SendMessage)
|
||||
}
|
||||
|
||||
// ─── Conversion / Sérialisation ────────────────────────────────────────
|
||||
|
||||
/// Retourne la liste des noms des permissions actives
|
||||
/// Utile pour les API REST ou les logs
|
||||
pub fn to_names(&self) -> Vec<&'static str> {
|
||||
self.iter_names().map(|(name, _)| name).collect()
|
||||
}
|
||||
|
||||
/// Construit depuis une liste de noms (pour parsing JSON par ex)
|
||||
pub fn from_names(names: &[&str]) -> Self {
|
||||
names
|
||||
.iter()
|
||||
.filter_map(|name| Self::from_name(name))
|
||||
.fold(Self::empty(), |acc, p| acc | p)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for Permission {
|
||||
fn from(bits: u64) -> Self {
|
||||
Self::from_bits_truncate(bits)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Permission> for u64 {
|
||||
fn from(perms: Permission) -> Self {
|
||||
perms.bits()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
Ce module permet de :
|
||||
|
||||
- Gérer/simplifier les interactions avec la base de données
|
||||
- Avoir un système de Signal qui se déclenche lors de modifications
|
||||
dans la base de données (Création, Modification, Suppression)
|
||||
@@ -0,0 +1,46 @@
|
||||
use std::sync::Arc;
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, QueryFilter,
|
||||
};
|
||||
use crate::models::category;
|
||||
use crate::repositories::RepositoryContext;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CategoryRepository {
|
||||
pub context: Arc<RepositoryContext>
|
||||
}
|
||||
|
||||
impl CategoryRepository {
|
||||
pub async fn get_by_id(&self, id: uuid::Uuid) -> Result<Option<category::Model>, DbErr> {
|
||||
category::Entity::find_by_id(id).one(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn get_all(&self) -> Result<Vec<category::Model>, DbErr> {
|
||||
category::Entity::find().all(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn get_by_server_id(&self, server_id: uuid::Uuid) -> Result<Vec<category::Model>, DbErr> {
|
||||
category::Entity::find()
|
||||
.filter(category::Column::ServerId.eq(server_id))
|
||||
.all(&self.context.db)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn update(&self, active: category::ActiveModel) -> Result<category::Model, DbErr> {
|
||||
let category = active.update(&self.context.db).await?;
|
||||
self.context.events.emit("Category_updated", category.clone());
|
||||
Ok(category)
|
||||
}
|
||||
|
||||
pub async fn create(&self, active: category::ActiveModel) -> Result<category::Model, DbErr> {
|
||||
let category = active.insert(&self.context.db).await?;
|
||||
self.context.events.emit("Category_created", category.clone());
|
||||
Ok(category)
|
||||
}
|
||||
|
||||
pub async fn delete(&self, id: uuid::Uuid) -> Result<(), DbErr> {
|
||||
category::Entity::delete_by_id(id).exec(&self.context.db).await?;
|
||||
self.context.events.emit("Category_deleted", id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
use crate::models::channel;
|
||||
use crate::repositories::RepositoryContext;
|
||||
use sea_orm::{ActiveModelTrait, DbErr, EntityTrait};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ChannelRepository {
|
||||
pub context: Arc<RepositoryContext>,
|
||||
}
|
||||
|
||||
impl ChannelRepository {
|
||||
pub async fn get_by_id(&self, id: uuid::Uuid) -> Result<Option<channel::Model>, DbErr> {
|
||||
channel::Entity::find_by_id(id).one(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn update(&self, active: channel::ActiveModel) -> Result<channel::Model, DbErr> {
|
||||
let channel = active.update(&self.context.db).await?;
|
||||
self.context.events.emit("channel_updated", channel.clone());
|
||||
Ok(channel)
|
||||
}
|
||||
|
||||
pub async fn create(&self, active: channel::ActiveModel) -> Result<channel::Model, DbErr> {
|
||||
let channel = active.insert(&self.context.db).await?;
|
||||
self.context.events.emit("channel_created", channel.clone());
|
||||
Ok(channel)
|
||||
}
|
||||
|
||||
pub async fn delete(&self, id: uuid::Uuid) -> Result<(), DbErr> {
|
||||
channel::Entity::delete_by_id(id)
|
||||
.exec(&self.context.db)
|
||||
.await?;
|
||||
self.context.events.emit("channel_deleted", id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
use crate::models::group;
|
||||
use crate::repositories::RepositoryContext;
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, QueryFilter};
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GroupRepository {
|
||||
pub context: Arc<RepositoryContext>,
|
||||
}
|
||||
|
||||
impl GroupRepository {
|
||||
pub async fn get_all_by_server(&self, server_id: Uuid) -> Result<Vec<group::Model>, DbErr> {
|
||||
group::Entity::find()
|
||||
.filter(group::Column::ServerId.eq(server_id))
|
||||
.all(&self.context.db)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_by_id(&self, id: Uuid) -> Result<Option<group::Model>, DbErr> {
|
||||
group::Entity::find_by_id(id).one(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn create(&self, active: group::ActiveModel) -> Result<group::Model, DbErr> {
|
||||
let group = active.insert(&self.context.db).await?;
|
||||
self.context.events.emit("group_created", group.clone());
|
||||
Ok(group)
|
||||
}
|
||||
|
||||
pub async fn update(&self, active: group::ActiveModel) -> Result<group::Model, DbErr> {
|
||||
let group = active.update(&self.context.db).await?;
|
||||
self.context.events.emit("group_updated", group.clone());
|
||||
Ok(group)
|
||||
}
|
||||
|
||||
pub async fn delete(&self, id: Uuid) -> Result<bool, DbErr> {
|
||||
let res = group::Entity::delete_by_id(id)
|
||||
.exec(&self.context.db)
|
||||
.await?;
|
||||
self.context.events.emit("group_deleted", id);
|
||||
Ok(res.rows_affected > 0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
use std::sync::Arc;
|
||||
use sea_orm::{DbErr, EntityTrait, ActiveModelTrait};
|
||||
use crate::models::message;
|
||||
use crate::repositories::RepositoryContext;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MessageRepository {
|
||||
pub context: Arc<RepositoryContext>
|
||||
}
|
||||
|
||||
impl MessageRepository {
|
||||
pub async fn get_by_id(&self, id: uuid::Uuid) -> Result<Option<message::Model>, DbErr> {
|
||||
message::Entity::find_by_id(id).one(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn update(&self, active: message::ActiveModel) -> Result<message::Model, DbErr> {
|
||||
let message = active.update(&self.context.db).await?;
|
||||
self.context.events.emit("message_updated", message.clone());
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
pub async fn create(&self, active: message::ActiveModel) -> Result<message::Model, DbErr> {
|
||||
let message = active.insert(&self.context.db).await?;
|
||||
self.context.events.emit("message_created", message.clone());
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
pub async fn delete(&self, id: uuid::Uuid) -> Result<(), DbErr> {
|
||||
message::Entity::delete_by_id(id).exec(&self.context.db).await?;
|
||||
self.context.events.emit("message_deleted", id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
use crate::repositories::category::CategoryRepository;
|
||||
use crate::repositories::channel::ChannelRepository;
|
||||
use crate::repositories::group::GroupRepository;
|
||||
use crate::repositories::message::MessageRepository;
|
||||
use crate::repositories::server::ServerRepository;
|
||||
use crate::repositories::user::UserRepository;
|
||||
use event_bus::EventBus;
|
||||
use sea_orm::DatabaseConnection;
|
||||
use std::sync::Arc;
|
||||
|
||||
mod category;
|
||||
mod channel;
|
||||
mod group;
|
||||
mod message;
|
||||
mod server;
|
||||
pub mod types;
|
||||
mod user;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RepositoryContext {
|
||||
db: DatabaseConnection,
|
||||
events: Arc<EventBus>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Repositories {
|
||||
pub server: ServerRepository,
|
||||
pub category: CategoryRepository,
|
||||
pub channel: ChannelRepository,
|
||||
pub group: GroupRepository,
|
||||
pub message: MessageRepository,
|
||||
pub user: UserRepository,
|
||||
}
|
||||
|
||||
impl Repositories {
|
||||
pub fn new(db: &DatabaseConnection, events: Arc<EventBus>) -> Self {
|
||||
let context = Arc::new(RepositoryContext {
|
||||
db: db.clone(),
|
||||
events,
|
||||
});
|
||||
|
||||
Self {
|
||||
server: ServerRepository {
|
||||
context: context.clone(),
|
||||
},
|
||||
category: CategoryRepository {
|
||||
context: context.clone(),
|
||||
},
|
||||
channel: ChannelRepository {
|
||||
context: context.clone(),
|
||||
},
|
||||
group: GroupRepository {
|
||||
context: context.clone(),
|
||||
},
|
||||
message: MessageRepository {
|
||||
context: context.clone(),
|
||||
},
|
||||
user: UserRepository {
|
||||
context: context.clone(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
use super::types::{ServerExplorerItem, ServerTree};
|
||||
use super::RepositoryContext;
|
||||
use crate::models::{category, channel, group, server};
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, QueryFilter, Set};
|
||||
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ServerRepository {
|
||||
pub context: Arc<RepositoryContext>,
|
||||
}
|
||||
|
||||
impl ServerRepository {
|
||||
pub async fn get_all(&self) -> Result<Vec<server::Model>, DbErr> {
|
||||
server::Entity::find().all(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn get_by_id(&self, id: uuid::Uuid) -> Result<Option<server::Model>, DbErr> {
|
||||
server::Entity::find_by_id(id).one(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn update(&self, active: server::ActiveModel) -> Result<server::Model, DbErr> {
|
||||
let server = active.update(&self.context.db).await?;
|
||||
self.context.events.emit("server_updated", server.clone());
|
||||
Ok(server)
|
||||
}
|
||||
|
||||
pub async fn create(&self, active: server::ActiveModel) -> Result<server::Model, DbErr> {
|
||||
let server = active.insert(&self.context.db).await?;
|
||||
|
||||
// Créer le groupe par défaut pour le serveur
|
||||
let default_group = group::ActiveModel {
|
||||
server_id: Set(server.id),
|
||||
name: Set("Membres".to_string()),
|
||||
is_default: Set(true),
|
||||
..Default::default()
|
||||
};
|
||||
default_group.insert(&self.context.db).await?;
|
||||
|
||||
self.context.events.emit("server_created", server.clone());
|
||||
Ok(server)
|
||||
}
|
||||
|
||||
pub async fn delete(&self, id: uuid::Uuid) -> Result<bool, DbErr> {
|
||||
let res = server::Entity::delete_by_id(id)
|
||||
.exec(&self.context.db)
|
||||
.await?;
|
||||
self.context.events.emit("server_deleted", id);
|
||||
Ok(res.rows_affected > 0)
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers
|
||||
impl ServerRepository {
|
||||
pub async fn get_tree(&self, server_id: Uuid) -> Result<ServerTree, DbErr> {
|
||||
// 1. Récupération des catégories avec leurs channels
|
||||
let categories_with_channels = category::Entity::find()
|
||||
.filter(category::Column::ServerId.eq(server_id))
|
||||
.find_with_related(channel::Entity)
|
||||
.all(&self.context.db)
|
||||
.await?;
|
||||
|
||||
// 2. Récupération des channels orphelins (sans catégorie)
|
||||
let orphan_channels = channel::Entity::find()
|
||||
.filter(channel::Column::ServerId.eq(server_id))
|
||||
.filter(channel::Column::CategoryId.is_null())
|
||||
.all(&self.context.db)
|
||||
.await?;
|
||||
|
||||
// 3. Transformation et tri des enfants
|
||||
let mut items: Vec<ServerExplorerItem> = Vec::new();
|
||||
|
||||
for (cat, mut channels) in categories_with_channels {
|
||||
// On trie les channels internes (obligatoire car SQL ne garantit aucun ordre ici)
|
||||
channels.sort_by(|a, b| {
|
||||
a.position
|
||||
.cmp(&b.position)
|
||||
.then(a.created_at.cmp(&b.created_at))
|
||||
});
|
||||
items.push(ServerExplorerItem::Category(cat, channels));
|
||||
}
|
||||
|
||||
for chan in orphan_channels {
|
||||
items.push(ServerExplorerItem::Channel(chan));
|
||||
}
|
||||
|
||||
// 4. Tri final de la liste globale (Mélange catégories et orphelins)
|
||||
items.sort_by(|a, b| {
|
||||
let pos_cmp = a.position().cmp(&b.position());
|
||||
|
||||
if pos_cmp == std::cmp::Ordering::Equal {
|
||||
// Départage par date si position identique
|
||||
let date_a = match a {
|
||||
ServerExplorerItem::Category(c, _) => c.created_at,
|
||||
ServerExplorerItem::Channel(c) => c.created_at,
|
||||
};
|
||||
let date_b = match b {
|
||||
ServerExplorerItem::Category(c, _) => c.created_at,
|
||||
ServerExplorerItem::Channel(c) => c.created_at,
|
||||
};
|
||||
date_a.cmp(&date_b)
|
||||
} else {
|
||||
pos_cmp
|
||||
}
|
||||
});
|
||||
|
||||
Ok(ServerTree { items })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
use crate::models::{category, channel};
|
||||
|
||||
pub enum ServerExplorerItem {
|
||||
Category(category::Model, Vec<channel::Model>),
|
||||
Channel(channel::Model),
|
||||
}
|
||||
|
||||
// Pour pouvoir trier facilement
|
||||
impl ServerExplorerItem {
|
||||
pub fn position(&self) -> i32 {
|
||||
match self {
|
||||
ServerExplorerItem::Category(cat, _) => cat.position,
|
||||
ServerExplorerItem::Channel(chan) => chan.position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ServerTree {
|
||||
pub items: Vec<ServerExplorerItem>,
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
use crate::models::user;
|
||||
use crate::repositories::RepositoryContext;
|
||||
use crate::utils::password;
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ColumnTrait, DbErr, EntityTrait, IntoActiveModel, PaginatorTrait,
|
||||
QueryFilter, Set,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UserRepository {
|
||||
pub context: Arc<RepositoryContext>,
|
||||
}
|
||||
|
||||
impl UserRepository {
|
||||
pub async fn get_all(&self) -> Result<Vec<user::Model>, DbErr> {
|
||||
user::Entity::find().all(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn count(&self) -> Result<u64, DbErr> {
|
||||
user::Entity::find().count(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn get_by_id(&self, id: uuid::Uuid) -> Result<Option<user::Model>, DbErr> {
|
||||
user::Entity::find_by_id(id).one(&self.context.db).await
|
||||
}
|
||||
|
||||
pub async fn get_by_username(&self, username: String) -> Result<Option<user::Model>, DbErr> {
|
||||
user::Entity::find()
|
||||
.filter(user::Column::Username.eq(username))
|
||||
.one(&self.context.db)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn check_password(
|
||||
&self,
|
||||
username: String,
|
||||
password: String,
|
||||
) -> Result<user::Model, DbErr> {
|
||||
let user = self.get_by_username(username).await?;
|
||||
if let Some(user) = user {
|
||||
let password_ok = password::verify_password(password.as_str(), user.password.as_str())
|
||||
.map_err(|_| DbErr::Custom("Password hashing failed".to_string()))?;
|
||||
|
||||
if password_ok {
|
||||
return Ok(user);
|
||||
}
|
||||
}
|
||||
Err(DbErr::Custom("Invalid username or password".to_string()))
|
||||
}
|
||||
|
||||
pub async fn update(&self, active: user::ActiveModel) -> Result<user::Model, DbErr> {
|
||||
let user = active.update(&self.context.db).await?;
|
||||
self.context.events.emit("user_updated", user.clone());
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn create(&self, active: user::ActiveModel) -> Result<user::Model, DbErr> {
|
||||
let user = active.insert(&self.context.db).await?;
|
||||
self.context.events.emit("user_created", user.clone());
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn set_password(&self, id: uuid::Uuid, password: String) -> Result<(), DbErr> {
|
||||
let user = self
|
||||
.get_by_id(id)
|
||||
.await?
|
||||
.ok_or_else(|| DbErr::Custom("User not found".to_string()))?;
|
||||
|
||||
let mut active = user.into_active_model();
|
||||
let password = password::hash_password(&password)
|
||||
.map_err(|_| DbErr::Custom("Password hashing failed".to_string()))?;
|
||||
active.password = Set(password);
|
||||
|
||||
let user = self.update(active).await?;
|
||||
|
||||
self.context.events.emit("user_changed", user);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete(&self, id: uuid::Uuid) -> Result<(), DbErr> {
|
||||
user::Entity::delete_by_id(id)
|
||||
.exec(&self.context.db)
|
||||
.await?;
|
||||
self.context.events.emit("user_deleted", id);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub struct Attachment {}
|
||||
@@ -0,0 +1,10 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateAttachmentRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UpdateAttachmentRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct AttachmentResponse {}
|
||||
@@ -0,0 +1,22 @@
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
pub async fn get_all() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn get_by_id() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn create() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn update() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn delete() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
use super::{domain::Attachment, dto::AttachmentResponse};
|
||||
|
||||
pub fn to_response(_item: Attachment) -> AttachmentResponse {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod domain;
|
||||
pub mod dto;
|
||||
pub mod handlers;
|
||||
pub mod mapper;
|
||||
pub mod routes;
|
||||
pub mod service;
|
||||
@@ -0,0 +1,14 @@
|
||||
use axum::{Router, routing::get};
|
||||
|
||||
use super::handlers;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/attachments", get(handlers::get_all).post(handlers::create))
|
||||
.route(
|
||||
"/attachments/:id",
|
||||
get(handlers::get_by_id)
|
||||
.put(handlers::update)
|
||||
.delete(handlers::delete),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use super::domain::Attachment;
|
||||
|
||||
pub async fn find_all() -> Vec<Attachment> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn find_by_id(_id: u64) -> Option<Attachment> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn create(_item: Attachment) -> Attachment {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update(_id: u64, _item: Attachment) -> Option<Attachment> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete(_id: u64) -> bool {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub struct Category {}
|
||||
@@ -0,0 +1,10 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateCategoryRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UpdateCategoryRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CategoryResponse {}
|
||||
@@ -0,0 +1,22 @@
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
pub async fn get_all() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn get_by_id() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn create() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn update() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn delete() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
use super::{domain::Category, dto::CategoryResponse};
|
||||
|
||||
pub fn to_response(_item: Category) -> CategoryResponse {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod domain;
|
||||
pub mod dto;
|
||||
pub mod handlers;
|
||||
pub mod mapper;
|
||||
pub mod routes;
|
||||
pub mod service;
|
||||
@@ -0,0 +1,14 @@
|
||||
use axum::{Router, routing::get};
|
||||
|
||||
use super::handlers;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/categorys", get(handlers::get_all).post(handlers::create))
|
||||
.route(
|
||||
"/categorys/:id",
|
||||
get(handlers::get_by_id)
|
||||
.put(handlers::update)
|
||||
.delete(handlers::delete),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use super::domain::Category;
|
||||
|
||||
pub async fn find_all() -> Vec<Category> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn find_by_id(_id: u64) -> Option<Category> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn create(_item: Category) -> Category {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update(_id: u64, _item: Category) -> Option<Category> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete(_id: u64) -> bool {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub struct Channel {}
|
||||
@@ -0,0 +1,10 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateChannelRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UpdateChannelRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ChannelResponse {}
|
||||
@@ -0,0 +1,22 @@
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
pub async fn get_all() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn get_by_id() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn create() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn update() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn delete() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
use super::{domain::Channel, dto::ChannelResponse};
|
||||
|
||||
pub fn to_response(_item: Channel) -> ChannelResponse {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod domain;
|
||||
pub mod dto;
|
||||
pub mod handlers;
|
||||
pub mod mapper;
|
||||
pub mod routes;
|
||||
pub mod service;
|
||||
@@ -0,0 +1,14 @@
|
||||
use axum::{Router, routing::get};
|
||||
|
||||
use super::handlers;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/channels", get(handlers::get_all).post(handlers::create))
|
||||
.route(
|
||||
"/channels/:id",
|
||||
get(handlers::get_by_id)
|
||||
.put(handlers::update)
|
||||
.delete(handlers::delete),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use super::domain::Channel;
|
||||
|
||||
pub async fn find_all() -> Vec<Channel> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn find_by_id(_id: u64) -> Option<Channel> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn create(_item: Channel) -> Channel {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update(_id: u64, _item: Channel) -> Option<Channel> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete(_id: u64) -> bool {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub struct Group {}
|
||||
@@ -0,0 +1,10 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateGroupRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UpdateGroupRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct GroupResponse {}
|
||||
@@ -0,0 +1,22 @@
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
pub async fn get_all() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn get_by_id() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn create() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn update() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn delete() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
use super::{domain::Group, dto::GroupResponse};
|
||||
|
||||
pub fn to_response(_item: Group) -> GroupResponse {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod domain;
|
||||
pub mod dto;
|
||||
pub mod handlers;
|
||||
pub mod mapper;
|
||||
pub mod routes;
|
||||
pub mod service;
|
||||
@@ -0,0 +1,14 @@
|
||||
use axum::{Router, routing::get};
|
||||
|
||||
use super::handlers;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/groups", get(handlers::get_all).post(handlers::create))
|
||||
.route(
|
||||
"/groups/:id",
|
||||
get(handlers::get_by_id)
|
||||
.put(handlers::update)
|
||||
.delete(handlers::delete),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use super::domain::Group;
|
||||
|
||||
pub async fn find_all() -> Vec<Group> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn find_by_id(_id: u64) -> Option<Group> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn create(_item: Group) -> Group {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update(_id: u64, _item: Group) -> Option<Group> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete(_id: u64) -> bool {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub struct Message {}
|
||||
@@ -0,0 +1,10 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateMessageRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UpdateMessageRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct MessageResponse {}
|
||||
@@ -0,0 +1,22 @@
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
pub async fn get_all() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn get_by_id() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn create() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn update() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn delete() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
use super::{domain::Message, dto::MessageResponse};
|
||||
|
||||
pub fn to_response(_item: Message) -> MessageResponse {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod domain;
|
||||
pub mod dto;
|
||||
pub mod handlers;
|
||||
pub mod mapper;
|
||||
pub mod routes;
|
||||
pub mod service;
|
||||
@@ -0,0 +1,14 @@
|
||||
use axum::{Router, routing::get};
|
||||
|
||||
use super::handlers;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/messages", get(handlers::get_all).post(handlers::create))
|
||||
.route(
|
||||
"/messages/:id",
|
||||
get(handlers::get_by_id)
|
||||
.put(handlers::update)
|
||||
.delete(handlers::delete),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use super::domain::Message;
|
||||
|
||||
pub async fn find_all() -> Vec<Message> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn find_by_id(_id: u64) -> Option<Message> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn create(_item: Message) -> Message {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update(_id: u64, _item: Message) -> Option<Message> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete(_id: u64) -> bool {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
use axum::Router;
|
||||
|
||||
pub mod attachment;
|
||||
pub mod category;
|
||||
pub mod channel;
|
||||
pub mod group;
|
||||
pub mod message;
|
||||
pub mod server;
|
||||
pub mod user;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.merge(user::routes::router())
|
||||
.merge(server::routes::router())
|
||||
.merge(channel::routes::router())
|
||||
.merge(message::routes::router())
|
||||
.merge(group::routes::router())
|
||||
.merge(category::routes::router())
|
||||
.merge(attachment::routes::router())
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub struct Server {}
|
||||
@@ -0,0 +1,10 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateServerRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UpdateServerRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ServerResponse {}
|
||||
@@ -0,0 +1,22 @@
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
pub async fn get_all() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn get_by_id() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn create() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn update() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn delete() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
use super::{domain::Server, dto::ServerResponse};
|
||||
|
||||
pub fn to_response(_item: Server) -> ServerResponse {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod domain;
|
||||
pub mod dto;
|
||||
pub mod handlers;
|
||||
pub mod mapper;
|
||||
pub mod routes;
|
||||
pub mod service;
|
||||
@@ -0,0 +1,14 @@
|
||||
use axum::{Router, routing::get};
|
||||
|
||||
use super::handlers;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/servers", get(handlers::get_all).post(handlers::create))
|
||||
.route(
|
||||
"/servers/:id",
|
||||
get(handlers::get_by_id)
|
||||
.put(handlers::update)
|
||||
.delete(handlers::delete),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use super::domain::Server;
|
||||
|
||||
pub async fn find_all() -> Vec<Server> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn find_by_id(_id: u64) -> Option<Server> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn create(_item: Server) -> Server {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update(_id: u64, _item: Server) -> Option<Server> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete(_id: u64) -> bool {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub struct User {}
|
||||
@@ -0,0 +1,10 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateUserRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UpdateUserRequest {}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UserResponse {}
|
||||
@@ -0,0 +1,22 @@
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
|
||||
pub async fn get_all() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn get_by_id() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn create() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn update() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
pub async fn delete() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
use super::{domain::User, dto::UserResponse};
|
||||
|
||||
pub fn to_response(_user: User) -> UserResponse {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
pub mod domain;
|
||||
pub mod dto;
|
||||
pub mod handlers;
|
||||
pub mod mapper;
|
||||
pub mod routes;
|
||||
pub mod service;
|
||||
@@ -0,0 +1,14 @@
|
||||
use axum::{routing::get, Router};
|
||||
|
||||
use super::handlers;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/users", get(handlers::get_all).post(handlers::create))
|
||||
.route(
|
||||
"/users/:id",
|
||||
get(handlers::get_by_id)
|
||||
.put(handlers::update)
|
||||
.delete(handlers::delete),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use super::domain::User;
|
||||
|
||||
pub async fn find_all() -> Vec<User> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn find_by_id(_id: u64) -> Option<User> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn create(_user: User) -> User {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update(_id: u64, _user: User) -> Option<User> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete(_id: u64) -> bool {
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod password;
|
||||
@@ -0,0 +1,25 @@
|
||||
use argon2::{
|
||||
password_hash::{phc::PasswordHash, PasswordHasher, PasswordVerifier}, Algorithm, Argon2, Params,
|
||||
Version,
|
||||
};
|
||||
|
||||
/// Hache un password avec Argon2id
|
||||
/// Génère automatiquement un salt cryptographiquement sûr
|
||||
pub fn hash_password(password: &str) -> Result<String, argon2::password_hash::Error> {
|
||||
let params = Params::new(65540, 18, 1, None)?;
|
||||
let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, params);
|
||||
|
||||
argon2
|
||||
.hash_password(password.as_bytes())
|
||||
.map(|hash| hash.to_string())
|
||||
}
|
||||
|
||||
/// Vérifie un password contre son hash
|
||||
pub fn verify_password(password: &str, hash: &str) -> Result<bool, argon2::password_hash::Error> {
|
||||
let parsed_hash = PasswordHash::new(hash)?;
|
||||
let argon2 = Argon2::default();
|
||||
|
||||
Ok(argon2
|
||||
.verify_password(password.as_bytes(), &parsed_hash)
|
||||
.is_ok())
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
# Projet : Runtime de Signaux/Slots Haute Performance en Rust
|
||||
|
||||
## Objectif
|
||||
|
||||
Créer un runtime événementiel inspiré de Qt Signals/Slots mais pensé pour :
|
||||
|
||||
- haute fréquence,
|
||||
- multi-thread,
|
||||
- audio temps réel,
|
||||
- architecture modulaire,
|
||||
- runtime distribué par workers,
|
||||
- typage fort compile-time,
|
||||
- signaux async/sync,
|
||||
- performance extrême.
|
||||
|
||||
Le runtime doit pouvoir servir de base à :
|
||||
- moteur événementiel,
|
||||
- runtime applicatif,
|
||||
- scheduler coopératif,
|
||||
- actor system,
|
||||
- infrastructure temps réel.
|
||||
|
||||
IMPORTANT :
|
||||
Le runtime doit rester générique et indépendant du réseau.
|
||||
Aucune logique UDP/TCP/socket ne doit être implémentée automatiquement.
|
||||
L’intégration réseau sera réalisée manuellement plus tard par le développeur.
|
||||
|
||||
---
|
||||
|
||||
# Vision Architecture
|
||||
|
||||
```text
|
||||
Application
|
||||
↓
|
||||
Signal Runtime
|
||||
├── Worker 0
|
||||
│ ├── Event Loop
|
||||
│ ├── Audio Systems
|
||||
│ ├── Metrics
|
||||
│ └── Channels
|
||||
│
|
||||
├── Worker 1
|
||||
│ ├── Auth
|
||||
│ ├── Sessions
|
||||
│ └── Runtime Tasks
|
||||
│
|
||||
└── Worker N
|
||||
PHASE 1 — Runtime Minimal
|
||||
Objectif
|
||||
|
||||
Créer :
|
||||
|
||||
runtime global,
|
||||
workers,
|
||||
queues,
|
||||
event loop,
|
||||
dispatch d’événements.
|
||||
A implémenter
|
||||
Runtime
|
||||
struct Runtime {
|
||||
workers: Vec<Worker>,
|
||||
}
|
||||
Worker
|
||||
struct Worker {
|
||||
id: usize,
|
||||
sender: Sender<Event>,
|
||||
}
|
||||
Event Loop
|
||||
|
||||
Boucle infinie :
|
||||
|
||||
loop {
|
||||
let event = rx.recv().unwrap();
|
||||
event.execute();
|
||||
}
|
||||
Event Trait
|
||||
trait ExecutableEvent {
|
||||
fn execute(self: Box<Self>);
|
||||
}
|
||||
PHASE 2 — Système de Signaux
|
||||
Objectif
|
||||
|
||||
Créer :
|
||||
|
||||
Signal<T>,
|
||||
connect(),
|
||||
emit(),
|
||||
subscribers,
|
||||
callbacks dynamiques.
|
||||
A implémenter
|
||||
Signal générique
|
||||
struct Signal<T> {
|
||||
subscribers: Vec<Subscriber<T>>,
|
||||
}
|
||||
Subscriber
|
||||
|
||||
Contient :
|
||||
|
||||
callback,
|
||||
target_worker,
|
||||
connection_type.
|
||||
Connection Types
|
||||
enum ConnectionType {
|
||||
Direct,
|
||||
Queued,
|
||||
}
|
||||
Emit
|
||||
|
||||
Le signal :
|
||||
|
||||
ne doit jamais exécuter directement un callback queued,
|
||||
doit créer un Event,
|
||||
envoyer vers le worker cible.
|
||||
PHASE 3 — Runtime Global
|
||||
Objectif
|
||||
|
||||
Créer :
|
||||
|
||||
runtime singleton,
|
||||
accès global,
|
||||
routing automatique.
|
||||
A implémenter
|
||||
OnceLock
|
||||
static RUNTIME: OnceLock<Runtime>;
|
||||
Dispatch global
|
||||
runtime.dispatch(worker_id, event);
|
||||
Routing intelligent
|
||||
|
||||
Le runtime doit :
|
||||
|
||||
choisir le bon worker,
|
||||
envoyer dans la bonne queue,
|
||||
éviter les locks globaux.
|
||||
PHASE 4 — Proc Macros
|
||||
Objectif
|
||||
|
||||
Créer :
|
||||
|
||||
#[derive(Signals)],
|
||||
auto-enregistrement,
|
||||
génération automatique de metadata.
|
||||
Crate séparée
|
||||
signals_derive/
|
||||
Technologies
|
||||
syn
|
||||
quote
|
||||
proc_macro2
|
||||
Features
|
||||
Détection automatique
|
||||
|
||||
Détecter :
|
||||
|
||||
Signal<T>
|
||||
|
||||
dans une struct.
|
||||
|
||||
Génération
|
||||
|
||||
Générer :
|
||||
|
||||
impl AudioEngine {
|
||||
pub fn register_signals(...)
|
||||
}
|
||||
Support futur
|
||||
|
||||
Prévoir :
|
||||
|
||||
#[signal]
|
||||
metadata runtime,
|
||||
introspection légère.
|
||||
PHASE 5 — Thread Affinity
|
||||
Objectif
|
||||
|
||||
Chaque objet appartient à un worker.
|
||||
|
||||
A implémenter
|
||||
RuntimeObject
|
||||
struct RuntimeObject {
|
||||
worker_id: usize,
|
||||
}
|
||||
Règles
|
||||
les signaux queued doivent s’exécuter sur le worker cible,
|
||||
éviter les accès cross-thread directs.
|
||||
PHASE 6 — Weak References
|
||||
Objectif
|
||||
|
||||
Reproduire l’auto-cleanup de Qt.
|
||||
|
||||
A implémenter
|
||||
|
||||
Utiliser :
|
||||
|
||||
Arc<T>
|
||||
Weak<T>
|
||||
Comportement
|
||||
|
||||
Si le receiver est détruit :
|
||||
|
||||
la connexion devient invalide,
|
||||
le runtime l’ignore automatiquement.
|
||||
PHASE 7 — Event Bus Typé
|
||||
Objectif
|
||||
|
||||
Permettre :
|
||||
|
||||
runtime.emit(EventType)
|
||||
runtime.subscribe::<EventType>()
|
||||
Contraintes
|
||||
typage compile-time,
|
||||
pas de string routing obligatoire,
|
||||
support Any/TypeId.
|
||||
PHASE 8 — Optimisation Performance
|
||||
Objectif
|
||||
|
||||
Préparer haute fréquence.
|
||||
|
||||
Optimisations attendues
|
||||
Eviter
|
||||
Mutex globaux,
|
||||
allocations fréquentes,
|
||||
Box inutiles,
|
||||
contention CPU.
|
||||
Ajouter
|
||||
queues lock-free,
|
||||
batching,
|
||||
event pooling,
|
||||
cache locality,
|
||||
sharding runtime.
|
||||
PHASE 9 — Runtime Sharding
|
||||
Objectif
|
||||
|
||||
Eviter le bottleneck central.
|
||||
|
||||
Architecture
|
||||
Shard 0
|
||||
Shard 1
|
||||
Shard 2
|
||||
|
||||
Chaque shard :
|
||||
|
||||
possède ses subscribers,
|
||||
possède ses queues,
|
||||
possède ses workers.
|
||||
PHASE 10 — Intégration Applicative Générique
|
||||
Objectif
|
||||
|
||||
Permettre à des systèmes externes de pousser des événements dans le runtime.
|
||||
|
||||
IMPORTANT :
|
||||
Le runtime ne doit contenir aucune logique réseau native.
|
||||
Il doit uniquement exposer des APIs génériques permettant :
|
||||
|
||||
d’émettre des événements,
|
||||
de s’abonner à des événements,
|
||||
de router les tâches.
|
||||
|
||||
Les couches réseau, audio, fichiers, protocoles ou IO seront branchées manuellement par le développeur.
|
||||
|
||||
Exemple attendu
|
||||
runtime.emit(MyCustomEvent {
|
||||
...
|
||||
});
|
||||
PHASE 11 — Scheduler Coopératif
|
||||
Objectif
|
||||
|
||||
Fusionner :
|
||||
|
||||
signaux,
|
||||
scheduler,
|
||||
tâches coopératives.
|
||||
Inspiré de
|
||||
Go runtime,
|
||||
Tokio,
|
||||
Erlang.
|
||||
Features
|
||||
spawn()
|
||||
spawn(async move {})
|
||||
sleep()
|
||||
wakeup()
|
||||
channels()
|
||||
PHASE 12 — Async Runtime Integration
|
||||
Objectif
|
||||
|
||||
Compatibilité :
|
||||
|
||||
Tokio,
|
||||
async/await,
|
||||
futures.
|
||||
Support
|
||||
Async subscribers
|
||||
signal.connect(async move |event| {
|
||||
...
|
||||
});
|
||||
PHASE 13 — Monitoring & Introspection
|
||||
Objectif
|
||||
|
||||
Observer :
|
||||
|
||||
workers,
|
||||
queues,
|
||||
charge CPU,
|
||||
nombre d’événements,
|
||||
latence.
|
||||
Metrics
|
||||
events/sec,
|
||||
queue depth,
|
||||
dropped events,
|
||||
worker usage.
|
||||
PHASE 14 — API Finale
|
||||
Objectif
|
||||
|
||||
API élégante type Qt.
|
||||
|
||||
Exemple cible
|
||||
#[derive(Signals)]
|
||||
struct AudioEngine {
|
||||
packet_received: Signal<AudioPacket>,
|
||||
}
|
||||
|
||||
audio.packet_received.connect(&decoder, |d, p| {
|
||||
d.decode(p);
|
||||
});
|
||||
|
||||
audio.packet_received.emit(packet);
|
||||
Contraintes Techniques
|
||||
Langage
|
||||
|
||||
Rust stable uniquement au début.
|
||||
|
||||
Dépendances possibles
|
||||
crossbeam
|
||||
flume
|
||||
parking_lot
|
||||
dashmap (optionnel)
|
||||
tokio (plus tard)
|
||||
Eviter au début
|
||||
async partout,
|
||||
macros trop complexes,
|
||||
ECS complet,
|
||||
reflection lourde.
|
||||
Objectif Long Terme
|
||||
|
||||
Créer un runtime capable de :
|
||||
|
||||
gérer des centaines de milliers/millions d’événements,
|
||||
servir de cœur à des applications temps réel,
|
||||
fournir un scheduler coopératif,
|
||||
remplacer une architecture classique callbacks/mutex.
|
||||
Reference in New Issue
Block a user