Init
This commit is contained in:
Generated
+1
@@ -2350,6 +2350,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"config",
|
||||
"event_bus",
|
||||
"form_urlencoded",
|
||||
"futures-util",
|
||||
"jsonwebtoken",
|
||||
"log",
|
||||
|
||||
@@ -38,3 +38,4 @@ validator = { version = "0.20.0", features = ["derive"] }
|
||||
async-trait = "0.1.89"
|
||||
anyhow = "1.0.102"
|
||||
futures-util = "0.3"
|
||||
form_urlencoded = "1.2.2"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {defineStore} from 'pinia'
|
||||
import {useApi} from "@/composables/useApi";
|
||||
import {useGatewayStore} from "@/stores/gateway.ts";
|
||||
|
||||
export interface User {
|
||||
id: string
|
||||
@@ -36,6 +37,10 @@ export const useAuthStore = defineStore('auth', {
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
this.user = data.user
|
||||
|
||||
// Initialisation du gateway
|
||||
const gatewayStore = useGatewayStore()
|
||||
await gatewayStore.connect()
|
||||
} else {
|
||||
this.logout()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import {defineStore} from 'pinia';
|
||||
import {useAppStore} from "@/stores/app.ts";
|
||||
import {useAuthStore} from "@/stores/auth.ts";
|
||||
|
||||
type GatewayStatus = 'disconnected' | 'connecting' | 'connected' | 'error'
|
||||
|
||||
@@ -15,9 +17,21 @@ export const useGatewayStore = defineStore('gateway', {
|
||||
return
|
||||
}
|
||||
|
||||
this.status = 'connecting'
|
||||
const appStore = useAppStore()
|
||||
const authStore = useAuthStore()
|
||||
|
||||
const wsUrl = `ws://localhost:3000/ws`
|
||||
|
||||
this.status = 'connecting'
|
||||
const token = authStore.token
|
||||
if (!token) {
|
||||
this.status = 'error'
|
||||
return
|
||||
}
|
||||
|
||||
const apiUri = appStore.baseurl ? new URL(appStore.baseurl) : new URL(window.location.href)
|
||||
const wsProtocol = apiUri.protocol === 'https:' ? 'wss:' : 'ws:'
|
||||
|
||||
const wsUrl = `${wsProtocol}//${apiUri.host}/ws/gateway?token=${encodeURIComponent(token)}`
|
||||
const socket = new WebSocket(wsUrl)
|
||||
|
||||
socket.onopen = () => {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import {defineStore} from "pinia";
|
||||
|
||||
export const useServerStore = defineStore("server", {
|
||||
state: () => ({}),
|
||||
state: () => ({
|
||||
servers: []
|
||||
}),
|
||||
actions: {}
|
||||
});
|
||||
+23
-15
@@ -91,30 +91,38 @@ pub async fn auth_middleware(
|
||||
mut req: Request<Body>,
|
||||
next: Next,
|
||||
) -> Response {
|
||||
// Extraction du JWT depuis le header Authorization
|
||||
let user: Option<CurrentUser> = match req
|
||||
// Extraction du JWT : d'abord via le header Authorization, sinon via la query string "token"
|
||||
let token = req
|
||||
.headers()
|
||||
.get(header::AUTHORIZATION)
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.and_then(|auth_header| {
|
||||
if auth_header.starts_with("Bearer ") {
|
||||
Some(&auth_header[7..])
|
||||
Some(auth_header[7..].to_string())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.and_then(|token| verify_jwt(token, &app_state.config.jwt.secret).ok())
|
||||
{
|
||||
Some(claims) => app_state
|
||||
.repositories
|
||||
.user
|
||||
.get_by_id(claims.user_id)
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(CurrentUser),
|
||||
None => None,
|
||||
};
|
||||
.or_else(|| {
|
||||
req.uri().query().and_then(|q| {
|
||||
form_urlencoded::parse(q.as_bytes())
|
||||
.find(|(key, _)| key == "token")
|
||||
.map(|(_, value)| value.into_owned())
|
||||
})
|
||||
});
|
||||
|
||||
let user: Option<CurrentUser> =
|
||||
match token.and_then(|t| verify_jwt(&t, &app_state.config.jwt.secret).ok()) {
|
||||
Some(claims) => app_state
|
||||
.repositories
|
||||
.user
|
||||
.get_by_id(claims.user_id)
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(CurrentUser),
|
||||
None => None,
|
||||
};
|
||||
|
||||
// Mise à jour du RequestContext existant
|
||||
if let Some(user) = &user {
|
||||
|
||||
+15
-1
@@ -4,7 +4,7 @@ use sea_orm::entity::prelude::*;
|
||||
use sea_orm::prelude::async_trait::async_trait;
|
||||
use sea_orm::Set;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[derive(Clone, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "user")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
@@ -18,6 +18,20 @@ pub struct Model {
|
||||
pub is_superuser: bool,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Model {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("User")
|
||||
.field("id", &self.id)
|
||||
.field("username", &self.username)
|
||||
.field("password", &"<redacted>")
|
||||
.field("pub_key", &self.pub_key)
|
||||
.field("created_at", &self.created_at)
|
||||
.field("updated_at", &self.updated_at)
|
||||
.field("is_superuser", &self.is_superuser)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(has_many = "super::channel_user::Entity")]
|
||||
|
||||
@@ -34,8 +34,15 @@ impl UserRepository {
|
||||
pub async fn check_password(&self, username: &str, password: &str) -> AnyResult<user::Model> {
|
||||
let user = self.get_by_username(username.to_string()).await?;
|
||||
if let Some(user) = user {
|
||||
let password_ok = password::verify_password(password, user.password.as_str())
|
||||
.map_err(|e| anyhow::anyhow!("Password hashing failed: {}", e))?;
|
||||
let password_to_verify = password.to_string();
|
||||
let hash_to_verify = user.password.clone();
|
||||
|
||||
let password_ok = tokio::task::spawn_blocking(move || {
|
||||
password::verify_password(&password_to_verify, &hash_to_verify)
|
||||
})
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("Join error: {}", e))?
|
||||
.map_err(|e| anyhow::anyhow!("Password hashing failed: {}", e))?;
|
||||
|
||||
if password_ok {
|
||||
return Ok(user);
|
||||
@@ -63,8 +70,14 @@ impl UserRepository {
|
||||
.ok_or_else(|| anyhow::anyhow!("User not found"))?;
|
||||
|
||||
let mut active = user.into_active_model();
|
||||
let password = password::hash_password(&password)
|
||||
.map_err(|e| anyhow::anyhow!("Password hashing failed: {}", e))?;
|
||||
let password_to_hash = password.clone();
|
||||
|
||||
let password =
|
||||
tokio::task::spawn_blocking(move || password::hash_password(&password_to_hash))
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!("Join error: {}", e))?
|
||||
.map_err(|e| anyhow::anyhow!("Password hashing failed: {}", e))?;
|
||||
|
||||
active.password = Set(password);
|
||||
|
||||
let user = self.update(active).await?;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use crate::auth::token::verify_jwt;
|
||||
use crate::core::AppState;
|
||||
use crate::http::context::CurrentUser;
|
||||
use crate::models::user::Model as User;
|
||||
@@ -13,7 +12,6 @@ use axum::{
|
||||
use futures_util::{sink::SinkExt, stream::StreamExt};
|
||||
use serde::Deserialize;
|
||||
use tokio::sync::mpsc;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct WsQuery {
|
||||
@@ -49,9 +47,6 @@ async fn handle_socket(socket: WebSocket, state: AppState, user: User) {
|
||||
client.on_connect().await;
|
||||
state.gateway.add_client(client.clone());
|
||||
|
||||
// // Enregistrement du client (Connect)
|
||||
// on_connect(user_id, tx, &state).await;
|
||||
//
|
||||
// Task pour envoyer les messages du canal mpsc vers le WebSocket
|
||||
let mut send_task = tokio::spawn(async move {
|
||||
while let Some(message) = rx.recv().await {
|
||||
@@ -60,7 +55,7 @@ async fn handle_socket(socket: WebSocket, state: AppState, user: User) {
|
||||
}
|
||||
}
|
||||
});
|
||||
//
|
||||
|
||||
// Task pour recevoir les messages du WebSocket
|
||||
let client_clone = client.clone();
|
||||
let state_clone = state.clone();
|
||||
@@ -69,54 +64,14 @@ async fn handle_socket(socket: WebSocket, state: AppState, user: User) {
|
||||
client_clone.on_message(message).await;
|
||||
}
|
||||
});
|
||||
//
|
||||
|
||||
// Attente de la fin d'une des tâches (déconnexion)
|
||||
tokio::select! {
|
||||
_ = (&mut send_task) => recv_task.abort(),
|
||||
_ = (&mut recv_task) => send_task.abort(),
|
||||
};
|
||||
//
|
||||
|
||||
state.gateway.remove_client(client.clone());
|
||||
// // Déconnexion (Disconnect)
|
||||
// on_disconnect(user_id, &state).await;
|
||||
}
|
||||
|
||||
pub async fn on_connect(user_id: Uuid, tx: mpsc::UnboundedSender<Message>, state: &AppState) {
|
||||
tracing::info!("Client connected: {}", user_id);
|
||||
let mut clients = state
|
||||
.gateway
|
||||
.clients
|
||||
.write()
|
||||
.expect("Failed to lock clients for writing");
|
||||
clients.insert(user_id, GatewayClient { user_id, tx });
|
||||
}
|
||||
|
||||
pub async fn on_disconnect(user_id: Uuid, state: &AppState) {
|
||||
tracing::info!("Client disconnected: {}", user_id);
|
||||
let mut clients = state
|
||||
.gateway
|
||||
.clients
|
||||
.write()
|
||||
.expect("Failed to lock clients for writing");
|
||||
clients.remove(&user_id);
|
||||
}
|
||||
|
||||
pub async fn on_message(user_id: Uuid, message: Message, _state: &AppState) {
|
||||
tracing::debug!("Message received from {}: {:?}", user_id, message);
|
||||
|
||||
// Exemple d'utilisation de l'état/repositories
|
||||
// let user_opt = state.repositories.user.get_by_id(user_id).await.ok().flatten();
|
||||
|
||||
match message {
|
||||
Message::Text(text) => {
|
||||
tracing::info!("Received text from {}: {}", user_id, text);
|
||||
// Logique de dispatch ou de traitement ici
|
||||
}
|
||||
Message::Binary(_) => {
|
||||
tracing::info!("Received binary from {}", user_id);
|
||||
}
|
||||
Message::Close(_) => {
|
||||
tracing::info!("Received close from {}", user_id);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
client.on_disconnect().await;
|
||||
}
|
||||
|
||||
@@ -49,9 +49,13 @@ impl GatewayClient {
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_connect(&self) {}
|
||||
async fn on_connect(&self) {
|
||||
tracing::info!("Client connected: {:?}", self.user);
|
||||
}
|
||||
|
||||
async fn on_disconnect(&self) {}
|
||||
async fn on_disconnect(&self) {
|
||||
tracing::info!("Client disconnected: {:?}", self.user);
|
||||
}
|
||||
|
||||
async fn on_message(&self, message: Message) {
|
||||
match message {
|
||||
|
||||
+4
-2
@@ -31,10 +31,12 @@ pub fn router() -> OxRouter {
|
||||
let api_routes = Router::new()
|
||||
.merge(secure_routes)
|
||||
.merge(auth::routes::router())
|
||||
.merge(core::routes::router())
|
||||
.merge(gateway::routes::router());
|
||||
.merge(core::routes::router());
|
||||
|
||||
let ws_routes = Router::new().merge(gateway::routes::router());
|
||||
|
||||
Router::new()
|
||||
.nest("/api", api_routes)
|
||||
.nest("/ws", ws_routes)
|
||||
.merge(SwaggerUi::new("/swagger").url("/api-docs/openapi.json", openapi::ApiDoc::openapi()))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user