Init
This commit is contained in:
212
Cargo.lock
generated
212
Cargo.lock
generated
@@ -375,6 +375,19 @@ version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
|
||||
|
||||
[[package]]
|
||||
name = "bcrypt"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abaf6da45c74385272ddf00e1ac074c7d8a6c1a1dda376902bd6a427522a8b2c"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"blowfish",
|
||||
"getrandom 0.3.4",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bigdecimal"
|
||||
version = "0.4.9"
|
||||
@@ -441,6 +454,16 @@ dependencies = [
|
||||
"piper",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blowfish"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "borsh"
|
||||
version = "1.6.0"
|
||||
@@ -1137,8 +1160,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1575,6 +1600,21 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonwebtoken"
|
||||
version = "9.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"js-sys",
|
||||
"pem",
|
||||
"ring",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"simple_asn1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kv-log-macro"
|
||||
version = "1.0.7"
|
||||
@@ -1713,6 +1753,15 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.6"
|
||||
@@ -1741,9 +1790,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
@@ -1827,9 +1876,11 @@ dependencies = [
|
||||
"argon2",
|
||||
"axum",
|
||||
"base64",
|
||||
"bcrypt",
|
||||
"chrono",
|
||||
"env_logger",
|
||||
"futures-util",
|
||||
"jsonwebtoken",
|
||||
"log",
|
||||
"migration",
|
||||
"parking_lot",
|
||||
@@ -1842,7 +1893,10 @@ dependencies = [
|
||||
"ssh-key",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
"uuid",
|
||||
"validator",
|
||||
]
|
||||
@@ -1925,6 +1979,16 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem"
|
||||
version = "3.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pem-rfc7468"
|
||||
version = "0.7.0"
|
||||
@@ -2281,6 +2345,20 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom 0.2.16",
|
||||
"libc",
|
||||
"untrusted",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rkyv"
|
||||
version = "0.7.45"
|
||||
@@ -2720,6 +2798,18 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
|
||||
|
||||
[[package]]
|
||||
name = "simple_asn1"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"thiserror",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.11"
|
||||
@@ -3127,30 +3217,30 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.44"
|
||||
version = "0.3.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
|
||||
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"serde_core",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.6"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
|
||||
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.24"
|
||||
version = "0.2.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
|
||||
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
@@ -3307,8 +3397,10 @@ checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tower-layer",
|
||||
@@ -3358,6 +3450,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"valuable",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing-log"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
||||
dependencies = [
|
||||
"log",
|
||||
"once_cell",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3367,12 +3471,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
"once_cell",
|
||||
"regex-automata",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3431,6 +3538,12 @@ version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.7"
|
||||
@@ -3504,6 +3617,12 @@ dependencies = [
|
||||
"syn 2.0.110",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
|
||||
|
||||
[[package]]
|
||||
name = "value-bag"
|
||||
version = "1.12.0"
|
||||
@@ -3689,6 +3808,15 @@ dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
@@ -3722,6 +3850,22 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm 0.52.6",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.5"
|
||||
@@ -3732,7 +3876,7 @@ dependencies = [
|
||||
"windows_aarch64_gnullvm 0.53.1",
|
||||
"windows_aarch64_msvc 0.53.1",
|
||||
"windows_i686_gnu 0.53.1",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_gnullvm 0.53.1",
|
||||
"windows_i686_msvc 0.53.1",
|
||||
"windows_x86_64_gnu 0.53.1",
|
||||
"windows_x86_64_gnullvm 0.53.1",
|
||||
@@ -3745,6 +3889,12 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.1"
|
||||
@@ -3757,6 +3907,12 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.1"
|
||||
@@ -3769,12 +3925,24 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.1"
|
||||
@@ -3787,6 +3955,12 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.1"
|
||||
@@ -3799,6 +3973,12 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.1"
|
||||
@@ -3811,6 +3991,12 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.1"
|
||||
@@ -3823,6 +4009,12 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.1"
|
||||
|
||||
@@ -44,8 +44,8 @@ tokio = { version = "1.49", features = ["full"] }
|
||||
axum = { version = "0.8.8", features = ["macros", "ws"] }
|
||||
#utoipa = "5.4"
|
||||
#utoipa-swagger-ui = { version = "9.0", features = ["axum"] }
|
||||
#tower = "0.5"
|
||||
tower-http = { version = "0.6", features = ["trace", "cors", "timeout"] }
|
||||
tower = { version = "0.5", features = ["util"] }
|
||||
tower-http = { version = "0.6", features = ["trace", "cors", "timeout", "catch-panic"] }
|
||||
|
||||
# UDP
|
||||
socket2 = "0.6"
|
||||
@@ -56,6 +56,8 @@ migration = { path = "migration" }
|
||||
|
||||
# logs
|
||||
log = "0.4"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
env_logger = "0.11.8"
|
||||
|
||||
# utils
|
||||
@@ -72,3 +74,5 @@ rand = "0.9"
|
||||
ssh-key = { version = "0.6", features = ["default", "crypto"] }
|
||||
base64 = "0.22"
|
||||
argon2 = "0.5.3"
|
||||
jsonwebtoken = "9.3.1"
|
||||
bcrypt = "0.17.0"
|
||||
@@ -23,14 +23,17 @@ impl App {
|
||||
pub async fn init(config: Config) -> Self {
|
||||
let event_bus = EventBus::new(1024);
|
||||
|
||||
let db = Database::init(&config.database_url()).await.expect("Failed to initialize database");
|
||||
let db = Database::init(&config.database_url())
|
||||
.await
|
||||
.expect("Failed to initialize database");
|
||||
let repositories = Repositories::new(db.get_connection(), event_bus.clone());
|
||||
|
||||
let state = AppState{
|
||||
let state = AppState {
|
||||
db: db.clone(),
|
||||
event_bus: event_bus.clone(),
|
||||
repositories: repositories.clone(),
|
||||
clients: Clients::new()
|
||||
clients: Clients::new(),
|
||||
config: config.clone(),
|
||||
};
|
||||
|
||||
let udp_server = UDPServer::new(config.bind_addr());
|
||||
@@ -86,7 +89,5 @@ impl App {
|
||||
println!("Nettoyage et fermeture de l'application.");
|
||||
}
|
||||
|
||||
async fn shutdown(&self) {
|
||||
|
||||
}
|
||||
async fn shutdown(&self) {}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::config::Config;
|
||||
use crate::database::Database;
|
||||
use crate::event_bus::EventBus;
|
||||
use crate::hub::Clients;
|
||||
@@ -9,6 +10,7 @@ pub struct AppState {
|
||||
pub event_bus: EventBus,
|
||||
pub repositories: Repositories,
|
||||
pub clients: Clients,
|
||||
pub config: Config,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::models::channel;
|
||||
use crate::models::channel::ChannelType;
|
||||
use sea_orm::Set;
|
||||
use serde::Serialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
@@ -27,11 +27,11 @@ impl From<channel::Model> for ChannelResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CreateChannelRequest {
|
||||
pub server_id: Option<Uuid>,
|
||||
pub category_id: Option<Uuid>,
|
||||
pub position: i32,
|
||||
pub position: Option<i32>,
|
||||
pub channel_type: ChannelType,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
@@ -41,10 +41,21 @@ impl From<CreateChannelRequest> for channel::ActiveModel {
|
||||
Self {
|
||||
server_id: Set(request.server_id),
|
||||
category_id: Set(request.category_id),
|
||||
position: Set(request.position),
|
||||
position: Set(request.position.unwrap_or(0)),
|
||||
channel_type: Set(request.channel_type),
|
||||
name: Set(request.name),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CreateChannelRequest {
|
||||
pub fn apply_to(self, mut am: channel::ActiveModel) -> channel::ActiveModel {
|
||||
am.server_id = Set(self.server_id);
|
||||
am.category_id = Set(self.category_id);
|
||||
am.position = Set(self.position.unwrap_or(0));
|
||||
am.channel_type = Set(self.channel_type);
|
||||
am.name = Set(self.name);
|
||||
am
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,13 +28,19 @@ pub struct CreateMessageRequest {
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
impl From<CreateMessageRequest> for message::ActiveModel {
|
||||
fn from(request: CreateMessageRequest) -> Self {
|
||||
Self {
|
||||
channel_id: Set(request.channel_id),
|
||||
user_id: Set(Uuid::new_v4()),
|
||||
content: Set(request.content),
|
||||
impl CreateMessageRequest {
|
||||
pub fn into_active_model(self, author_id: Uuid) -> message::ActiveModel {
|
||||
message::ActiveModel {
|
||||
channel_id: Set(self.channel_id),
|
||||
user_id: Set(author_id),
|
||||
content: Set(self.content),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_to(self, mut am: message::ActiveModel) -> message::ActiveModel {
|
||||
am.channel_id = Set(self.channel_id);
|
||||
am.content = Set(self.content);
|
||||
am
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use uuid::Uuid;
|
||||
pub struct UserResponse {
|
||||
pub id: Uuid,
|
||||
pub username: String,
|
||||
pub pub_key: String,
|
||||
}
|
||||
|
||||
impl From<user::Model> for UserResponse {
|
||||
@@ -14,6 +15,7 @@ impl From<user::Model> for UserResponse {
|
||||
Self {
|
||||
id: model.id,
|
||||
username: model.username,
|
||||
pub_key: model.pub_key,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,3 +33,10 @@ impl From<CreateUserRequest> for user::ActiveModel {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CreateUserRequest {
|
||||
pub fn apply_to(self, mut am: user::ActiveModel) -> user::ActiveModel {
|
||||
am.username = Set(self.username);
|
||||
am
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,5 @@ pub mod event_bus;
|
||||
pub mod hub;
|
||||
pub mod models;
|
||||
pub mod repositories;
|
||||
pub mod serializers;
|
||||
|
||||
pub mod interfaces;
|
||||
|
||||
10
src/main.rs
10
src/main.rs
@@ -1,14 +1,18 @@
|
||||
use ox_speak_server_lib::app::App;
|
||||
use ox_speak_server_lib::config;
|
||||
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
env_logger::init();
|
||||
tracing_subscriber::fmt()
|
||||
.with_env_filter(
|
||||
tracing_subscriber::EnvFilter::try_from_default_env()
|
||||
.unwrap_or_else(|_| "ox_speak_server=info,tower_http=info".into()),
|
||||
)
|
||||
.init();
|
||||
|
||||
let config = match config::Config::from_file("config.toml") {
|
||||
Ok(config) => config,
|
||||
Err(e) => panic!("Error loading configuration: {}", e)
|
||||
Err(e) => panic!("Error loading configuration: {}", e),
|
||||
};
|
||||
println!("Configuration loaded: {:?}", config);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use axum::Json;
|
||||
use serde_json::json;
|
||||
use sea_orm::DbErr;
|
||||
use serde_json::json;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum HTTPError {
|
||||
@@ -10,6 +10,7 @@ pub enum HTTPError {
|
||||
NotFound,
|
||||
BadRequest(String),
|
||||
InternalServerError(String),
|
||||
Unauthorized,
|
||||
}
|
||||
|
||||
// Conversion automatique depuis DbErr (erreurs SeaORM)
|
||||
@@ -35,12 +36,17 @@ impl IntoResponse for HTTPError {
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, "Database error")
|
||||
}
|
||||
HTTPError::NotFound => (StatusCode::NOT_FOUND, "Resource not found"),
|
||||
HTTPError::Unauthorized => (StatusCode::UNAUTHORIZED, "Unauthorized"),
|
||||
HTTPError::BadRequest(msg) => {
|
||||
return (StatusCode::BAD_REQUEST, Json(json!({ "error": msg }))).into_response();
|
||||
}
|
||||
HTTPError::InternalServerError(msg) => {
|
||||
eprintln!("Internal error: {}", msg);
|
||||
return (StatusCode::INTERNAL_SERVER_ERROR, Json(json!({ "error": msg }))).into_response();
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Json(json!({ "error": msg })),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
use axum::{
|
||||
extract::State,
|
||||
http::Request,
|
||||
middleware::Next,
|
||||
response::Response,
|
||||
};
|
||||
use axum::{extract::State, http::Request, middleware::Next, response::Response};
|
||||
use std::time::Instant;
|
||||
use tracing::info;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::app::AppState;
|
||||
use crate::network::http::context::{CurrentUser, RequestContext};
|
||||
use crate::utils::auth::verify_jwt;
|
||||
|
||||
pub async fn context_middleware(
|
||||
State(app_state): State<AppState>,
|
||||
@@ -22,20 +19,27 @@ pub async fn context_middleware(
|
||||
let method = req.method().clone();
|
||||
let uri = req.uri().clone();
|
||||
|
||||
// Exemple: récupérer un user depuis un token (pseudo-code)
|
||||
// Ici je laisse volontairement une logique minimaliste/placeholder.
|
||||
// Le but: montrer où tu branches ta vraie auth.
|
||||
// Authentification par JWT
|
||||
let user: Option<CurrentUser> = {
|
||||
let _maybe_auth = req
|
||||
.headers()
|
||||
req.headers()
|
||||
.get(axum::http::header::AUTHORIZATION)
|
||||
.and_then(|v| v.to_str().ok());
|
||||
|
||||
// TODO: vérifier token -> user_id -> charger en DB avec app_state
|
||||
// Some(CurrentUser { id: ..., username: ... })
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.and_then(|auth_header| {
|
||||
if auth_header.starts_with("Bearer ") {
|
||||
Some(&auth_header[7..])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.and_then(|token| verify_jwt(token, &app_state.config.jwt.secret).ok())
|
||||
.map(|claims| CurrentUser {
|
||||
id: claims.user_id,
|
||||
username: claims.username,
|
||||
})
|
||||
};
|
||||
|
||||
let user_id = user.as_ref().map(|u| u.id);
|
||||
|
||||
// Injecte le contexte dans la requête
|
||||
req.extensions_mut().insert(RequestContext {
|
||||
request_id,
|
||||
@@ -45,12 +49,14 @@ pub async fn context_middleware(
|
||||
user,
|
||||
});
|
||||
|
||||
println!(">>> Incoming [{}] {} {}", request_id, method, uri);
|
||||
info!(
|
||||
request_id = %request_id,
|
||||
user_id = ?user_id,
|
||||
method = %method,
|
||||
uri = %uri,
|
||||
"Incoming request"
|
||||
);
|
||||
|
||||
// Passe la requête au reste de la stack
|
||||
let resp = next.run(req).await;
|
||||
|
||||
println!("<<< Response [{}]: {}", request_id, resp.status());
|
||||
|
||||
resp
|
||||
next.run(req).await
|
||||
}
|
||||
@@ -1,14 +1,25 @@
|
||||
use std::sync::Arc;
|
||||
use axum::{middleware, Router};
|
||||
use tower::ServiceBuilder;
|
||||
use tower_http::catch_panic::CatchPanicLayer;
|
||||
use tower_http::cors::CorsLayer;
|
||||
use tower_http::trace::TraceLayer;
|
||||
|
||||
use crate::app::AppState;
|
||||
use crate::network::http::middleware::context_middleware;
|
||||
use crate::network::http::{web, AppRouter};
|
||||
use crate::network::http::web;
|
||||
|
||||
pub fn setup_route(app_state: AppState) -> Router {
|
||||
let middleware_stack = ServiceBuilder::new()
|
||||
.layer(CatchPanicLayer::new())
|
||||
.layer(TraceLayer::new_for_http())
|
||||
.layer(middleware::from_fn_with_state(
|
||||
app_state.clone(),
|
||||
context_middleware,
|
||||
));
|
||||
|
||||
Router::new()
|
||||
.merge(web::setup_route())
|
||||
.layer(middleware::from_fn_with_state(app_state.clone(), context_middleware))
|
||||
.layer(middleware_stack)
|
||||
.with_state(app_state)
|
||||
.layer(CorsLayer::permissive())
|
||||
}
|
||||
@@ -1,13 +1,53 @@
|
||||
use crate::app::AppState;
|
||||
use crate::network::http::{AppRouter, HTTPError};
|
||||
use crate::utils::auth::create_jwt;
|
||||
use crate::utils::toolbox::ssh_generate_challenge;
|
||||
use axum::extract::State;
|
||||
use axum::routing::post;
|
||||
use axum::Json;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ssh_key::{Algorithm as SshAlgorithm, PublicKey, Signature};
|
||||
|
||||
fn setup_route() -> AppRouter {
|
||||
pub fn setup_route() -> AppRouter {
|
||||
AppRouter::new()
|
||||
.route("/login", post(login))
|
||||
.route("/ssh-challenge", post(ssh_challenge))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LoginRequest {
|
||||
username: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct LoginResponse {
|
||||
token: String,
|
||||
username: String,
|
||||
}
|
||||
|
||||
pub async fn login(
|
||||
State(state): State<AppState>,
|
||||
Json(payload): Json<LoginRequest>,
|
||||
) -> Result<Json<LoginResponse>, HTTPError> {
|
||||
let user = state
|
||||
.repositories
|
||||
.user
|
||||
.check_password(payload.username.clone(), payload.password)
|
||||
.await
|
||||
.map_err(|_| HTTPError::Unauthorized)?;
|
||||
|
||||
let token = create_jwt(
|
||||
user.id,
|
||||
&user.username,
|
||||
&state.config.jwt.secret,
|
||||
state.config.jwt.expiration,
|
||||
)
|
||||
.map_err(|e| HTTPError::InternalServerError("Failed to generate token".to_string()))?;
|
||||
|
||||
Ok(Json(LoginResponse {
|
||||
token,
|
||||
username: user.username,
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -24,82 +64,14 @@ pub async fn ssh_challenge(
|
||||
State(state): State<AppState>,
|
||||
Json(payload): Json<SshChallengeRequest>,
|
||||
) -> Result<Json<SshChallengeResponse>, HTTPError> {
|
||||
log::info!(
|
||||
"POST /auth/ssh-challenge - Challenge request for user: {}",
|
||||
payload.username
|
||||
);
|
||||
|
||||
let user = state
|
||||
let _user = state
|
||||
.repositories
|
||||
.user
|
||||
.get_by_username(payload.username.clone())
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
log::warn!(
|
||||
"POST /auth/ssh-challenge - User not found: {}",
|
||||
payload.username
|
||||
);
|
||||
HTTPError::NotFound
|
||||
})?;
|
||||
.ok_or_else(|| HTTPError::NotFound)?;
|
||||
|
||||
let challenge = ssh_generate_challenge(32);
|
||||
log::info!(
|
||||
"POST /auth/ssh-challenge - Challenge generated for user: {}",
|
||||
payload.username
|
||||
);
|
||||
|
||||
// todo : stocker le challenge dans AppState
|
||||
// bien penser à ajouter un délai d'expiration
|
||||
// songer à mettre une session id ?
|
||||
// Peut être que l'utilisateur se connectera depuis différent device
|
||||
// state.store_challenge(&payload.username, challenge.clone())
|
||||
|
||||
Ok(Json(SshChallengeResponse { challenge }))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
enum SignatureAlgorithm {
|
||||
#[serde(rename = "rsa")]
|
||||
Rsa,
|
||||
#[serde(rename = "ed25519")]
|
||||
Ed25519,
|
||||
#[serde(rename = "ecdsa")]
|
||||
Ecdsa,
|
||||
}
|
||||
|
||||
impl SignatureAlgorithm {
|
||||
fn to_ssh_algorithm(&self) -> SshAlgorithm {
|
||||
match self {
|
||||
SignatureAlgorithm::Rsa => SshAlgorithm::Rsa { hash: None },
|
||||
SignatureAlgorithm::Ed25519 => SshAlgorithm::Ed25519,
|
||||
SignatureAlgorithm::Ecdsa => SshAlgorithm::Ecdsa {
|
||||
curve: ssh_key::EcdsaCurve::NistP256,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SshVerifyRequest {
|
||||
username: String,
|
||||
signature: String,
|
||||
algorithm: SignatureAlgorithm,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct SshVerifyResponse {
|
||||
// todo : remplir avec la réponse jwt - à établir après la construction jwt
|
||||
}
|
||||
|
||||
pub async fn ssh_verify() {}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LoginRequest {
|
||||
username: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct SshChallengeResponse {
|
||||
challenge: String,
|
||||
}
|
||||
|
||||
@@ -3,9 +3,8 @@ use crate::interfaces::http::dto::category::{CategoryResponse, CreateCategoryReq
|
||||
use crate::models::category;
|
||||
use crate::network::http::RequestContext;
|
||||
use crate::network::http::{AppRouter, HTTPError};
|
||||
use crate::serializers::CategorySerializer;
|
||||
use axum::extract::{Path, Query, State};
|
||||
use axum::http::{Extensions, StatusCode};
|
||||
use axum::http::StatusCode;
|
||||
use axum::routing::{delete, get, post, put};
|
||||
use axum::{Extension, Json};
|
||||
use sea_orm::{ActiveModelTrait, EntityTrait, IntoActiveModel};
|
||||
@@ -31,8 +30,6 @@ pub async fn category_list(
|
||||
Extension(_ctx): Extension<RequestContext>,
|
||||
Query(query): Query<CategoryQuery>,
|
||||
) -> Result<Json<Vec<CategoryResponse>>, HTTPError> {
|
||||
log::info!("GET /categories/ - Query: server_id={:?}", query.server_id);
|
||||
|
||||
let categories = if let Some(server_id) = query.server_id {
|
||||
app_state
|
||||
.repositories
|
||||
@@ -43,7 +40,6 @@ pub async fn category_list(
|
||||
app_state.repositories.category.get_all().await?
|
||||
};
|
||||
|
||||
log::info!("GET /categories/ - Found {} categories", categories.len());
|
||||
Ok(Json(
|
||||
categories.into_iter().map(CategoryResponse::from).collect(),
|
||||
))
|
||||
@@ -53,19 +49,13 @@ pub async fn category_detail(
|
||||
State(app_state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<Json<CategoryResponse>, HTTPError> {
|
||||
log::info!("GET /categories/{id}/ - Fetching category: {}", id);
|
||||
|
||||
let category = app_state
|
||||
.repositories
|
||||
.category
|
||||
.get_by_id(id)
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
log::warn!("GET /categories/{id}/ - Category not found: {}", id);
|
||||
HTTPError::NotFound
|
||||
})?;
|
||||
.ok_or_else(|| HTTPError::NotFound)?;
|
||||
|
||||
log::info!("GET /categories/{id}/ - Category found: {}", id);
|
||||
Ok(Json(CategoryResponse::from(category)))
|
||||
}
|
||||
|
||||
@@ -73,18 +63,9 @@ pub async fn category_create(
|
||||
State(app_state): State<AppState>,
|
||||
Json(serializer): Json<CreateCategoryRequest>,
|
||||
) -> Result<Json<CategoryResponse>, HTTPError> {
|
||||
log::info!(
|
||||
"POST /categories/ - Creating category: {:?}",
|
||||
serializer.name
|
||||
);
|
||||
|
||||
let active: category::ActiveModel = serializer.into();
|
||||
let category: category::Model = active.insert(app_state.db.get_connection()).await?;
|
||||
|
||||
log::info!(
|
||||
"POST /categories/ - Category created with id: {}",
|
||||
category.id
|
||||
);
|
||||
Ok(Json(CategoryResponse::from(category)))
|
||||
}
|
||||
|
||||
@@ -93,15 +74,10 @@ pub async fn category_update(
|
||||
Path(id): Path<Uuid>,
|
||||
Json(serializer): Json<CreateCategoryRequest>,
|
||||
) -> Result<Json<CategoryResponse>, HTTPError> {
|
||||
log::info!("PUT /categories/{id}/ - Updating category: {}", id);
|
||||
|
||||
let category = category::Entity::find_by_id(id)
|
||||
.one(app_state.db.get_connection())
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
log::warn!("PUT /categories/{id}/ - Category not found: {}", id);
|
||||
HTTPError::NotFound
|
||||
})?;
|
||||
.ok_or_else(|| HTTPError::NotFound)?;
|
||||
|
||||
let active = category.into_active_model();
|
||||
|
||||
@@ -111,7 +87,6 @@ pub async fn category_update(
|
||||
.update(app_state.db.get_connection())
|
||||
.await?;
|
||||
|
||||
log::info!("PUT /categories/{id}/ - Category updated: {}", id);
|
||||
Ok(Json(CategoryResponse::from(category)))
|
||||
}
|
||||
|
||||
@@ -119,17 +94,13 @@ pub async fn category_delete(
|
||||
State(app_state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<StatusCode, HTTPError> {
|
||||
log::info!("DELETE /categories/{id}/ - Deleting category: {}", id);
|
||||
|
||||
let result = category::Entity::delete_by_id(id)
|
||||
.exec(app_state.db.get_connection())
|
||||
.await?;
|
||||
|
||||
if result.rows_affected == 0 {
|
||||
log::warn!("DELETE /categories/{id}/ - Category not found: {}", id);
|
||||
Err(HTTPError::NotFound)
|
||||
} else {
|
||||
log::info!("DELETE /categories/{id}/ - Category deleted: {}", id);
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use axum::Json;
|
||||
use crate::app::AppState;
|
||||
use crate::interfaces::http::dto::channel::{ChannelResponse, CreateChannelRequest};
|
||||
use crate::models::channel;
|
||||
use crate::network::http::{AppRouter, HTTPError};
|
||||
use axum::extract::{Path, State};
|
||||
use axum::http::StatusCode;
|
||||
use axum::routing::{delete, get, post, put};
|
||||
use axum::Json;
|
||||
use sea_orm::{ActiveModelTrait, EntityTrait, IntoActiveModel};
|
||||
use uuid::Uuid;
|
||||
use crate::app::AppState;
|
||||
use crate::models::channel;
|
||||
use crate::network::http::{AppRouter, HTTPError};
|
||||
use crate::serializers::ChannelSerializer;
|
||||
|
||||
pub fn setup_route() -> AppRouter {
|
||||
AppRouter::new()
|
||||
@@ -19,42 +19,44 @@ pub fn setup_route() -> AppRouter {
|
||||
}
|
||||
|
||||
pub async fn channel_list(
|
||||
State(app_state): State<AppState>
|
||||
) -> Result<Json<Vec<ChannelSerializer>>, HTTPError> {
|
||||
State(app_state): State<AppState>,
|
||||
) -> Result<Json<Vec<ChannelResponse>>, HTTPError> {
|
||||
let channels = channel::Entity::find()
|
||||
.all(app_state.db.get_connection())
|
||||
.await?;
|
||||
|
||||
Ok(Json(channels.into_iter().map(ChannelSerializer::from).collect()))
|
||||
Ok(Json(
|
||||
channels.into_iter().map(ChannelResponse::from).collect(),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn channel_detail(
|
||||
State(app_state): State<AppState>,
|
||||
Path(id): Path<Uuid>
|
||||
) -> Result<Json<ChannelSerializer>, HTTPError> {
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<Json<ChannelResponse>, HTTPError> {
|
||||
let channel = channel::Entity::find_by_id(id)
|
||||
.one(app_state.db.get_connection())
|
||||
.await?
|
||||
.ok_or(HTTPError::NotFound)?;
|
||||
|
||||
Ok(Json(ChannelSerializer::from(channel)))
|
||||
Ok(Json(ChannelResponse::from(channel)))
|
||||
}
|
||||
|
||||
pub async fn channel_create(
|
||||
State(app_state): State<AppState>,
|
||||
Json(serializer): Json<ChannelSerializer>
|
||||
) -> Result<Json<ChannelSerializer>, HTTPError> {
|
||||
let active = serializer.into_active_model();
|
||||
Json(dto): Json<CreateChannelRequest>,
|
||||
) -> Result<Json<ChannelResponse>, HTTPError> {
|
||||
let active: channel::ActiveModel = dto.into();
|
||||
let channel: channel::Model = active.insert(app_state.db.get_connection()).await?;
|
||||
|
||||
Ok(Json(ChannelSerializer::from(channel)))
|
||||
Ok(Json(ChannelResponse::from(channel)))
|
||||
}
|
||||
|
||||
pub async fn channel_update(
|
||||
State(app_state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
Json(serializer): Json<ChannelSerializer>,
|
||||
) -> Result<Json<ChannelSerializer>, HTTPError> {
|
||||
Json(dto): Json<CreateChannelRequest>,
|
||||
) -> Result<Json<ChannelResponse>, HTTPError> {
|
||||
let channel = channel::Entity::find_by_id(id)
|
||||
.one(app_state.db.get_connection())
|
||||
.await?
|
||||
@@ -62,16 +64,17 @@ pub async fn channel_update(
|
||||
|
||||
let active = channel.into_active_model();
|
||||
|
||||
let channel: channel::Model = serializer.apply_to_active_model(active)
|
||||
let channel: channel::Model = dto
|
||||
.apply_to(active)
|
||||
.update(app_state.db.get_connection())
|
||||
.await?;
|
||||
|
||||
Ok(Json(ChannelSerializer::from(channel)))
|
||||
Ok(Json(ChannelResponse::from(channel)))
|
||||
}
|
||||
|
||||
pub async fn channel_delete(
|
||||
State(app_state): State<AppState>,
|
||||
Path(id): Path<Uuid>
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<StatusCode, HTTPError> {
|
||||
let result = channel::Entity::delete_by_id(id)
|
||||
.exec(app_state.db.get_connection())
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use axum::Json;
|
||||
use crate::app::AppState;
|
||||
use crate::interfaces::http::dto::message::{CreateMessageRequest, MessageResponse};
|
||||
use crate::models::message;
|
||||
use crate::network::http::{AppRouter, HTTPError, RequestContext};
|
||||
use axum::extract::{Path, State};
|
||||
use axum::http::StatusCode;
|
||||
use axum::routing::{delete, get, post, put};
|
||||
use axum::{Extension, Json};
|
||||
use sea_orm::{ActiveModelTrait, EntityTrait, IntoActiveModel};
|
||||
use uuid::Uuid;
|
||||
use crate::app::AppState;
|
||||
use crate::models::message;
|
||||
use crate::network::http::{AppRouter, HTTPError};
|
||||
use crate::serializers::MessageSerializer;
|
||||
|
||||
pub fn setup_route() -> AppRouter {
|
||||
AppRouter::new()
|
||||
@@ -19,58 +19,63 @@ pub fn setup_route() -> AppRouter {
|
||||
}
|
||||
|
||||
pub async fn message_list(
|
||||
State(app_state): State<AppState>
|
||||
) -> Result<Json<Vec<MessageSerializer>>, HTTPError> {
|
||||
State(app_state): State<AppState>,
|
||||
) -> Result<Json<Vec<MessageResponse>>, HTTPError> {
|
||||
let messages = message::Entity::find()
|
||||
.all(app_state.db.get_connection())
|
||||
.await?;
|
||||
|
||||
Ok(Json(messages.into_iter().map(MessageSerializer::from).collect()))
|
||||
Ok(Json(
|
||||
messages.into_iter().map(MessageResponse::from).collect(),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn message_detail(
|
||||
State(app_state): State<AppState>,
|
||||
Path(id): Path<Uuid>
|
||||
) -> Result<Json<MessageSerializer>, HTTPError> {
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<Json<MessageResponse>, HTTPError> {
|
||||
let message = message::Entity::find_by_id(id)
|
||||
.one(app_state.db.get_connection())
|
||||
.await?
|
||||
.ok_or(HTTPError::NotFound)?;
|
||||
|
||||
Ok(Json(MessageSerializer::from(message)))
|
||||
Ok(Json(MessageResponse::from(message)))
|
||||
}
|
||||
|
||||
pub async fn message_create(
|
||||
State(app_state): State<AppState>,
|
||||
Json(serializer): Json<MessageSerializer>
|
||||
) -> Result<Json<MessageSerializer>, HTTPError> {
|
||||
let active = serializer.into_active_model();
|
||||
Extension(ctx): Extension<RequestContext>,
|
||||
Json(dto): Json<CreateMessageRequest>,
|
||||
) -> Result<Json<MessageResponse>, HTTPError> {
|
||||
let author_id = ctx.user.map(|u| u.id).unwrap_or_else(Uuid::new_v4);
|
||||
let active = dto.into_active_model(author_id);
|
||||
let message: message::Model = active.insert(app_state.db.get_connection()).await?;
|
||||
|
||||
Ok(Json(MessageSerializer::from(message)))
|
||||
Ok(Json(MessageResponse::from(message)))
|
||||
}
|
||||
|
||||
pub async fn message_update(
|
||||
State(app_state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
Json(serializer): Json<MessageSerializer>,
|
||||
) -> Result<Json<MessageSerializer>, HTTPError> {
|
||||
Json(dto): Json<CreateMessageRequest>,
|
||||
) -> Result<Json<MessageResponse>, HTTPError> {
|
||||
let message = message::Entity::find_by_id(id)
|
||||
.one(app_state.db.get_connection())
|
||||
.await?
|
||||
.ok_or(HTTPError::NotFound)?;
|
||||
|
||||
let active = message.into_active_model();
|
||||
let message: message::Model = serializer.apply_to_active_model(active)
|
||||
let message: message::Model = dto
|
||||
.apply_to(active)
|
||||
.update(app_state.db.get_connection())
|
||||
.await?;
|
||||
|
||||
Ok(Json(MessageSerializer::from(message)))
|
||||
Ok(Json(MessageResponse::from(message)))
|
||||
}
|
||||
|
||||
pub async fn message_delete(
|
||||
State(app_state): State<AppState>,
|
||||
Path(id): Path<Uuid>
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<StatusCode, HTTPError> {
|
||||
let result = message::Entity::delete_by_id(id)
|
||||
.exec(app_state.db.get_connection())
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
use crate::network::http::AppRouter;
|
||||
|
||||
mod auth;
|
||||
mod category;
|
||||
mod channel;
|
||||
mod message;
|
||||
mod server;
|
||||
mod user;
|
||||
mod auth;
|
||||
|
||||
pub fn setup_route() -> AppRouter {
|
||||
|
||||
AppRouter::new()
|
||||
.nest("/category", category::setup_route())
|
||||
.nest("/channel", channel::setup_route())
|
||||
.nest("/message", message::setup_route())
|
||||
.nest("/server", server::setup_route())
|
||||
.nest("/user", user::setup_route())
|
||||
.nest("/auth", auth::setup_route())
|
||||
}
|
||||
@@ -1,42 +1,48 @@
|
||||
use axum::{Extension, Json};
|
||||
use crate::app::AppState;
|
||||
use crate::interfaces::http::dto::user::UserResponse;
|
||||
use crate::network::http::{AppRouter, HTTPError};
|
||||
use axum::extract::{Path, State};
|
||||
use axum::routing::get;
|
||||
use axum::{Extension, Json};
|
||||
use uuid::Uuid;
|
||||
use crate::app::AppState;
|
||||
use crate::network::http::{AppRouter, HTTPError};
|
||||
use crate::serializers::UserSerializer;
|
||||
|
||||
pub fn setup_route() -> AppRouter {
|
||||
AppRouter::new()
|
||||
.route("/users/", get(user_list))
|
||||
.route("/users/{id}/", get(user_detail))
|
||||
.route("/me", get(get_me))
|
||||
.route("/", get(user_list))
|
||||
.route("/{id}", get(user_detail))
|
||||
}
|
||||
|
||||
pub async fn get_me(
|
||||
Extension(ctx): Extension<crate::network::http::RequestContext>,
|
||||
) -> Result<Json<UserResponse>, HTTPError> {
|
||||
let user = ctx.user.ok_or(HTTPError::Unauthorized)?;
|
||||
Ok(Json(UserResponse {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
pub_key: "".to_string(), // On peut laisser vide ou charger en DB si besoin
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn user_list(
|
||||
State(app_state): State<AppState>,
|
||||
Extension(_ctx): Extension<crate::network::http::RequestContext>
|
||||
) -> Result<Json<Vec<UserSerializer>>, HTTPError> {
|
||||
log::info!("GET /users/ - Fetching user list");
|
||||
|
||||
Extension(_ctx): Extension<crate::network::http::RequestContext>,
|
||||
) -> Result<Json<Vec<UserResponse>>, HTTPError> {
|
||||
let users = app_state.repositories.user.get_all().await?;
|
||||
log::info!("GET /users/ - Found {} users", users.len());
|
||||
|
||||
Ok(Json(users.into_iter().map(UserSerializer::from).collect()))
|
||||
Ok(Json(users.into_iter().map(UserResponse::from).collect()))
|
||||
}
|
||||
|
||||
pub async fn user_detail(
|
||||
State(app_state): State<AppState>,
|
||||
Path(id): Path<Uuid>
|
||||
) -> Result<Json<UserSerializer>, HTTPError> {
|
||||
log::info!("GET /users/{id}/ - Fetching user with id: {}", id);
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<Json<UserResponse>, HTTPError> {
|
||||
let user = app_state
|
||||
.repositories
|
||||
.user
|
||||
.get_by_id(id)
|
||||
.await?
|
||||
.ok_or_else(|| HTTPError::NotFound)?;
|
||||
|
||||
let user = app_state.repositories.user
|
||||
.get_by_id(id).await?
|
||||
.ok_or_else(|| {
|
||||
log::warn!("GET /users/{id}/ - User not found: {}", id);
|
||||
HTTPError::NotFound
|
||||
})?;
|
||||
|
||||
log::info!("GET /users/{id}/ - User found: {}", id);
|
||||
Ok(Json(UserSerializer::from(user)))
|
||||
Ok(Json(UserResponse::from(user)))
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use uuid::Uuid;
|
||||
use crate::models::category;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct CategorySerializer {
|
||||
#[serde(skip_deserializing)]
|
||||
pub id: Option<Uuid>,
|
||||
|
||||
pub server_id: Uuid,
|
||||
|
||||
pub name: String,
|
||||
|
||||
#[serde(skip_deserializing)]
|
||||
pub created_at: Option<String>,
|
||||
|
||||
#[serde(skip_deserializing)]
|
||||
pub updated_at: Option<String>,
|
||||
}
|
||||
|
||||
impl From<category::Model> for CategorySerializer {
|
||||
fn from(model: category::Model) -> Self {
|
||||
Self {
|
||||
id: Some(model.id),
|
||||
server_id: model.server_id,
|
||||
name: model.name,
|
||||
created_at: Some(model.created_at.to_string()),
|
||||
updated_at: Some(model.updated_at.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CategorySerializer {
|
||||
pub fn into_active_model(self) -> category::ActiveModel {
|
||||
category::ActiveModel {
|
||||
server_id: Set(self.server_id),
|
||||
name: Set(self.name),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_to_active_model(self, mut active_model: category::ActiveModel) -> category::ActiveModel {
|
||||
active_model.server_id = Set(self.server_id);
|
||||
active_model.name = Set(self.name);
|
||||
active_model
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use uuid::Uuid;
|
||||
use crate::models::channel;
|
||||
use crate::models::channel::ChannelType;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ChannelSerializer {
|
||||
#[serde(skip_deserializing)]
|
||||
pub id: Option<Uuid>,
|
||||
pub server_id: Option<Uuid>,
|
||||
pub category_id: Option<Uuid>,
|
||||
pub name: Option<String>,
|
||||
pub position: Option<i32>,
|
||||
pub channel_type: ChannelType,
|
||||
|
||||
#[serde(skip_deserializing)]
|
||||
pub created_at: Option<String>,
|
||||
#[serde(skip_deserializing)]
|
||||
pub updated_at: Option<String>,
|
||||
}
|
||||
|
||||
impl From<channel::Model> for ChannelSerializer {
|
||||
fn from(model: channel::Model) -> Self {
|
||||
Self {
|
||||
id: Some(model.id),
|
||||
server_id: model.server_id,
|
||||
category_id: model.category_id,
|
||||
name: model.name,
|
||||
position: Some(model.position),
|
||||
channel_type: model.channel_type,
|
||||
created_at: Some(model.created_at.to_string()),
|
||||
updated_at: Some(model.updated_at.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChannelSerializer {
|
||||
pub fn into_active_model(self) -> channel::ActiveModel {
|
||||
channel::ActiveModel {
|
||||
server_id: Set(self.server_id),
|
||||
category_id: Set(self.category_id),
|
||||
name: Set(self.name),
|
||||
position: Set(self.position.unwrap_or(0)),
|
||||
channel_type: Set(self.channel_type),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_to_active_model(self, mut active_model: channel::ActiveModel) -> channel::ActiveModel {
|
||||
active_model.server_id = Set(self.server_id);
|
||||
active_model.category_id = Set(self.category_id);
|
||||
active_model.name = Set(self.name);
|
||||
active_model.position = Set(self.position.unwrap_or(0));
|
||||
active_model.channel_type = Set(self.channel_type);
|
||||
active_model
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use uuid::Uuid;
|
||||
use crate::models::message;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct MessageSerializer {
|
||||
#[serde(skip_deserializing)]
|
||||
pub id: Option<Uuid>,
|
||||
pub channel_id: Option<Uuid>,
|
||||
pub author_id: Option<Uuid>,
|
||||
pub content: String,
|
||||
|
||||
#[serde(skip_deserializing)]
|
||||
pub created_at: Option<String>,
|
||||
#[serde(skip_deserializing)]
|
||||
pub updated_at: Option<String>,
|
||||
}
|
||||
|
||||
impl From<message::Model> for MessageSerializer {
|
||||
fn from(model: message::Model) -> Self {
|
||||
Self {
|
||||
id: Some(model.id),
|
||||
channel_id: Some(model.channel_id),
|
||||
author_id: Some(model.user_id),
|
||||
content: model.content,
|
||||
created_at: Some(model.created_at.to_string()),
|
||||
updated_at: Some(model.edited_at.unwrap_or(model.created_at).to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MessageSerializer {
|
||||
pub fn into_active_model(self) -> message::ActiveModel {
|
||||
message::ActiveModel {
|
||||
channel_id: Set(self.channel_id.unwrap()),
|
||||
user_id: Set(self.author_id.unwrap()),
|
||||
content: Set(self.content),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_to_active_model(self, mut active_model: message::ActiveModel) -> message::ActiveModel {
|
||||
active_model.channel_id = Set(self.channel_id.unwrap());
|
||||
active_model.user_id = Set(self.author_id.unwrap());
|
||||
active_model.content = Set(self.content);
|
||||
active_model
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
mod server;
|
||||
mod category;
|
||||
mod channel;
|
||||
mod message;
|
||||
mod user;
|
||||
|
||||
trait BaseSerializer<M>
|
||||
where
|
||||
M: ActiveModelTrait,
|
||||
{
|
||||
fn into_active_model(self) -> M;
|
||||
|
||||
fn apply_to_active_model(self, active_model: M) -> M;
|
||||
}
|
||||
|
||||
use sea_orm::ActiveModelTrait;
|
||||
pub use server::*;
|
||||
pub use category::*;
|
||||
pub use channel::*;
|
||||
pub use message::*;
|
||||
pub use user::*;
|
||||
@@ -1,91 +0,0 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use uuid::Uuid;
|
||||
use crate::models::server;
|
||||
use crate::repositories::types::{ServerExplorerItem, ServerTree};
|
||||
use super::{CategorySerializer, ChannelSerializer};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ServerSerializer {
|
||||
#[serde(skip_deserializing)]
|
||||
pub id: Option<Uuid>,
|
||||
|
||||
pub name: String,
|
||||
|
||||
#[serde(skip_serializing)]
|
||||
pub password: Option<String>,
|
||||
|
||||
#[serde(skip_deserializing)]
|
||||
pub created_at: Option<String>,
|
||||
|
||||
#[serde(skip_deserializing)]
|
||||
pub updated_at: Option<String>,
|
||||
}
|
||||
|
||||
// On part du Model (données « propres » venant de la BDD),
|
||||
// pas de l'ActiveModel (qui contient des ActiveValue<T>).
|
||||
impl From<server::Model> for ServerSerializer {
|
||||
fn from(model: server::Model) -> Self {
|
||||
Self {
|
||||
id: Some(model.id),
|
||||
name: model.name,
|
||||
password: model.password,
|
||||
created_at: Some(model.created_at.to_string()),
|
||||
updated_at: Some(model.updated_at.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ServerSerializer {
|
||||
/// équivalent de `create()` d’un serializer DRF
|
||||
pub fn into_active_model(self) -> server::ActiveModel {
|
||||
server::ActiveModel {
|
||||
name: Set(self.name),
|
||||
// champ Option<String> -> tu peux le passer directement à Set(...)
|
||||
password: Set(self.password),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// équivalent de `update(instance, validated_data)` en DRF
|
||||
pub fn apply_to_active_model(self, mut active_model: server::ActiveModel) -> server::ActiveModel {
|
||||
active_model.name = Set(self.name);
|
||||
active_model.password = Set(self.password);
|
||||
active_model
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
pub enum TreeItemSerializer {
|
||||
Category {
|
||||
#[serde(flatten)]
|
||||
category: CategorySerializer,
|
||||
channels: Vec<ChannelSerializer>
|
||||
},
|
||||
Channel(ChannelSerializer)
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct ServerTreeSerializer {
|
||||
pub items: Vec<TreeItemSerializer>,
|
||||
}
|
||||
|
||||
impl From<ServerTree> for ServerTreeSerializer {
|
||||
fn from(layout: ServerTree) -> Self {
|
||||
Self {
|
||||
items: layout.items.into_iter().map(|item| match item {
|
||||
ServerExplorerItem::Category(cat, chans) => {
|
||||
TreeItemSerializer::Category {
|
||||
category: CategorySerializer::from(cat),
|
||||
channels: chans.into_iter().map(ChannelSerializer::from).collect(),
|
||||
}
|
||||
},
|
||||
ServerExplorerItem::Channel(chan) => {
|
||||
TreeItemSerializer::Channel(ChannelSerializer::from(chan))
|
||||
}
|
||||
}).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
use crate::models::{category, user};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct UserSerializer {
|
||||
#[serde(skip_deserializing)]
|
||||
pub id: Option<Uuid>,
|
||||
pub username: String,
|
||||
pub pub_key: String,
|
||||
|
||||
#[serde(skip_deserializing)]
|
||||
pub created_at: String,
|
||||
#[serde(skip_deserializing)]
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
impl From<user::Model> for UserSerializer {
|
||||
fn from(model: user::Model) -> Self {
|
||||
Self {
|
||||
id: Some(model.id),
|
||||
username: model.username,
|
||||
pub_key: model.pub_key,
|
||||
created_at: model.created_at.to_string(),
|
||||
updated_at: model.updated_at.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UserSerializer {
|
||||
pub fn into_active_model(self) -> user::ActiveModel {
|
||||
user::ActiveModel {
|
||||
username: Set(self.username),
|
||||
pub_key: Set(self.pub_key),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_to_active_model(self, mut active_model: user::ActiveModel) -> user::ActiveModel { active_model.username = Set(self.username);
|
||||
active_model.pub_key = Set(self.pub_key);
|
||||
active_model
|
||||
}
|
||||
}
|
||||
48
src/utils/auth.rs
Normal file
48
src/utils/auth.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Claims {
|
||||
pub user_id: Uuid, // User ID
|
||||
pub expire_at: usize, // Expiration time
|
||||
pub created_at: usize, // Issued at
|
||||
pub username: String,
|
||||
}
|
||||
|
||||
pub fn create_jwt(
|
||||
user_id: Uuid,
|
||||
username: &str,
|
||||
secret: &str,
|
||||
expiration_seconds: u64,
|
||||
) -> Result<String, jsonwebtoken::errors::Error> {
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("Time went backwards")
|
||||
.as_secs();
|
||||
|
||||
let claims = Claims {
|
||||
user_id: user_id,
|
||||
expire_at: (now + expiration_seconds) as usize,
|
||||
created_at: now as usize,
|
||||
username: username.to_string(),
|
||||
};
|
||||
|
||||
encode(
|
||||
&Header::default(),
|
||||
&claims,
|
||||
&EncodingKey::from_secret(secret.as_ref()),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn verify_jwt(token: &str, secret: &str) -> Result<Claims, jsonwebtoken::errors::Error> {
|
||||
let validation = Validation::default();
|
||||
let token_data = decode::<Claims>(
|
||||
token,
|
||||
&DecodingKey::from_secret(secret.as_ref()),
|
||||
&validation,
|
||||
)?;
|
||||
|
||||
Ok(token_data.claims)
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod auth;
|
||||
pub mod password;
|
||||
pub mod ssh_auth;
|
||||
pub mod toolbox;
|
||||
|
||||
Reference in New Issue
Block a user