diff --git a/Cargo.toml b/Cargo.toml index a2417f0..723c71f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,13 @@ [workspace] members = [ + "api-service", "auth-service", "character-service", "database-service", "packet-service", + "session-service", "world-service", - "utils", "launcher", "api-service", + "utils", + "launcher" ] resolver = "2" diff --git a/api-service/Cargo.toml b/api-service/Cargo.toml index 22d26f7..598504f 100644 --- a/api-service/Cargo.toml +++ b/api-service/Cargo.toml @@ -17,7 +17,7 @@ tracing = "0.1.41" tracing-subscriber = "0.3.19" utils = { path = "../utils" } dotenv = "0.15" -tower-http = { version = "0.6.2", features = ["cors"] } +tower-http = { version = "0.6.2", features = ["cors", "trace"] } [build-dependencies] tonic-build = "0.12.3" diff --git a/api-service/src/axum_gateway.rs b/api-service/src/axum_gateway.rs index a8099a5..60fcf14 100644 --- a/api-service/src/axum_gateway.rs +++ b/api-service/src/axum_gateway.rs @@ -1,15 +1,19 @@ -use axum::extract::State; +use axum::extract::{ConnectInfo, State}; use axum::{routing::post, Json, Router}; use serde::{Deserialize, Serialize}; use std::env; +use std::net::SocketAddr; use std::sync::Arc; use axum::http::Method; use tonic::transport::Channel; use tower_http::cors::{Any, CorsLayer}; +use tower_http::trace::TraceLayer; +use tower::ServiceBuilder; + use auth::auth_service_client::AuthServiceClient; use auth::{LoginRequest, RegisterRequest}; -use log::error; +use log::{error, info}; use tokio::sync::Mutex; pub mod auth { @@ -25,6 +29,7 @@ struct RestLoginRequest { #[derive(Serialize, Deserialize)] struct RestLoginResponse { token: String, + session_id: String, } #[derive(Serialize, Deserialize)] @@ -41,19 +46,27 @@ struct RestRegisterResponse { } async fn login_handler( + ConnectInfo(addr): ConnectInfo, State(grpc_client): State>>>, Json(payload): Json, ) -> Result, axum::http::StatusCode> { + let ip_address = addr.ip().to_string(); + + info!("Client IP Address: {}", ip_address); + let request = tonic::Request::new(LoginRequest { username: payload.username.clone(), password: payload.password.clone(), + ip_address }); let mut client = grpc_client.lock().await; // Lock the mutex to get mutable access match client.login(request).await { Ok(response) => { - let token = response.into_inner().token; - Ok(Json(RestLoginResponse { token })) + let resp = response.into_inner(); + let token = resp.token; + let session_id = resp.session_id; + Ok(Json(RestLoginResponse { token, session_id })) } Err(e) => { error!("gRPC Login call failed: {}", e); @@ -63,6 +76,7 @@ async fn login_handler( } async fn register_handler( + ConnectInfo(addr): ConnectInfo, State(grpc_client): State>>>, Json(payload): Json, ) -> Result, axum::http::StatusCode> { @@ -106,7 +120,7 @@ pub async fn serve_rest_api( let listener = tokio::net::TcpListener::bind(format!("{}:{}", addr, port)) .await .unwrap(); - axum::serve(listener, app.into_make_service()) + axum::serve(listener, app.into_make_service_with_connect_info::()) .await .unwrap(); diff --git a/auth-service/Cargo.toml b/auth-service/Cargo.toml index 89cafbc..a562078 100644 --- a/auth-service/Cargo.toml +++ b/auth-service/Cargo.toml @@ -25,6 +25,7 @@ rand = "0.8.5" warp = "0.3.7" reqwest = { version = "0.12.9", features = ["json"] } utils = { path = "../utils" } +uuid = "1.11.0" [build-dependencies] tonic-build = "0.12.3" diff --git a/auth-service/build.rs b/auth-service/build.rs index 40b5a0b..e000692 100644 --- a/auth-service/build.rs +++ b/auth-service/build.rs @@ -11,6 +11,6 @@ fn main() { tonic_build::configure() .build_server(false) // Generate gRPC client code .compile_well_known_types(true) - .compile_protos(&["../proto/user_db_api.proto"], &["../proto"]) + .compile_protos(&["../proto/user_db_api.proto", "../proto/session_service_api.proto"], &["../proto"]) .unwrap_or_else(|e| panic!("Failed to compile protos {:?}", e)); } diff --git a/auth-service/src/grpc.rs b/auth-service/src/grpc.rs index 402ac88..f25c986 100644 --- a/auth-service/src/grpc.rs +++ b/auth-service/src/grpc.rs @@ -1,6 +1,9 @@ +use std::sync::Arc; use crate::auth::auth_service_server::AuthService; -use crate::auth::{LoginRequest, LoginResponse, PasswordResetRequest, PasswordResetResponse, RegisterRequest, RegisterResponse, ResetPasswordRequest, ResetPasswordResponse, ValidateTokenRequest, ValidateTokenResponse}; -use crate::database_client::{DatabaseClientTrait}; +use crate::auth::{LoginRequest, LoginResponse, PasswordResetRequest, PasswordResetResponse, RegisterRequest, RegisterResponse, ResetPasswordRequest, ResetPasswordResponse, ValidateTokenRequest, ValidateTokenResponse, ValidateSessionRequest, ValidateSessionResponse, Empty, LogoutRequest}; +use crate::database_client::{DatabaseClient, DatabaseClientTrait}; +use crate::session::session_service_client::SessionServiceClient; +use crate::session::{CreateSessionRequest, GetSessionRequest, DeleteSessionRequest}; use crate::jwt::{generate_token, validate_token}; use crate::users::{hash_password, verify_user}; use chrono::{Duration, Utc}; @@ -8,12 +11,13 @@ use rand::Rng; use tonic::{Request, Response, Status}; use tracing::{error, info, warn}; -pub struct MyAuthService { - pub db_client: T, +pub struct MyAuthService { + pub db_client: Arc, + pub session_client: Arc>, } #[tonic::async_trait] -impl AuthService for MyAuthService { +impl AuthService for MyAuthService { async fn login( &self, request: Request, @@ -22,17 +26,49 @@ impl AuthService for MyA info!("Login attempt for username: {}", req.username); - if let Some(user_id) = verify_user(self.db_client.clone(), &req.username, &req.password).await { + if let Some(user_id) = verify_user(self.db_client.as_ref().clone(), &req.username, &req.password).await { let token = generate_token(&user_id, vec!["user".to_string()]) .map_err(|_| Status::internal("Token generation failed"))?; + let session_id = uuid::Uuid::new_v4().to_string(); + let response = self + .session_client.as_ref().clone() + .create_session(CreateSessionRequest { + session_id: session_id.clone(), + user_id: user_id.parse().unwrap(), + username: req.username.to_string(), + character_id: 0, + ip_address: req.ip_address.to_string(), + }) + .await; + + let session = match response { + Ok(session) => session, + Err(_) => return Err(Status::internal("Session creation failed")), + }; + let session_id = session.into_inner().session_id; + info!("Login successful for username: {}", req.username); - Ok(Response::new(LoginResponse { token, user_id })) + Ok(Response::new(LoginResponse { token, user_id, session_id })) } else { warn!("Invalid login attempt for username: {}", req.username); Err(Status::unauthenticated("Invalid credentials")) } } + + async fn logout( + &self, + request: Request, + ) -> Result, Status> { + let req = request.into_inner(); + self.session_client.as_ref().clone() + .delete_session(DeleteSessionRequest { + session_id: req.session_id.clone(), + }) + .await?; + + Ok(Response::new(Empty {})) + } async fn validate_token( &self, @@ -52,6 +88,30 @@ impl AuthService for MyA } } + async fn validate_session( + &self, + request: Request, + ) -> Result, Status> { + let req = request.into_inner(); + let response = self + .session_client.as_ref().clone() + .get_session(GetSessionRequest { + session_id: req.session_id, + }) + .await; + + match response { + Ok(res) => { + println!("Session valid: {:?}", res.into_inner()); + Ok(Response::new(ValidateSessionResponse { valid: true })) + } + Err(_) => { + println!("Session invalid or not found"); + Ok(Response::new(ValidateSessionResponse { valid: false })) + } + } + } + async fn register( &self, request: Request, @@ -62,7 +122,7 @@ impl AuthService for MyA let hashed_password = hash_password(&req.password); // Create user in the database - let result = self.db_client.clone().create_user(&req.username, &req.email, &hashed_password) + let result = self.db_client.as_ref().clone().create_user(&req.username, &req.email, &hashed_password) .await; match result { @@ -83,7 +143,7 @@ impl AuthService for MyA ) -> Result, Status> { let email = request.into_inner().email; - let user = self.db_client.clone().get_user_by_email(&email).await; + let user = self.db_client.as_ref().clone().get_user_by_email(&email).await; // Check if the email exists if user.ok().is_some() { @@ -98,14 +158,14 @@ impl AuthService for MyA let expires_at = Utc::now() + Duration::hours(1); // Store the reset token in the database - self.db_client.clone() + self.db_client.as_ref().clone() .store_password_reset(&email, &reset_token, expires_at) .await .map_err(|e| Status::internal(format!("Database error: {}", e)))?; // Send the reset email // send_email(&email, "Password Reset Request", &format!( - // "Click the link to reset your password: https://example.com/reset?token={}", + // "Click the link to reset your password: https://azgstudio.com/reset?token={}", // reset_token // )) // .map_err(|e| Status::internal(format!("Email error: {}", e)))?; diff --git a/auth-service/src/lib.rs b/auth-service/src/lib.rs index 8167bba..0bc7ac8 100644 --- a/auth-service/src/lib.rs +++ b/auth-service/src/lib.rs @@ -4,10 +4,14 @@ pub mod database_client; pub mod users; pub mod auth { - tonic::include_proto!("auth"); // Path matches the package name in auth.proto + tonic::include_proto!("auth"); } pub mod database { - tonic::include_proto!("user_db_api"); // Matches package name in user_db_api.proto + tonic::include_proto!("user_db_api"); +} + +pub mod session { + tonic::include_proto!("session_service_api"); } #[cfg(test)] diff --git a/auth-service/src/main.rs b/auth-service/src/main.rs index 7d82e7e..85a3b10 100644 --- a/auth-service/src/main.rs +++ b/auth-service/src/main.rs @@ -1,19 +1,18 @@ use auth_service::auth::auth_service_server::AuthServiceServer; use auth_service::database_client::DatabaseClient; use auth_service::database_client::DatabaseClientTrait; +use auth_service::session::session_service_client::SessionServiceClient; use auth_service::grpc::MyAuthService; use dotenv::dotenv; use std::collections::HashMap; use std::env; -use std::net::ToSocketAddrs; use std::str::FromStr; +use std::sync::Arc; use tokio::{select, signal}; use tonic::transport::Server; -use tracing::log::debug; use tracing::{info, Level}; use utils::consul_registration; use utils::service_discovery::get_service_address; -use warp::Filter; #[tokio::main] async fn main() -> Result<(), Box> { @@ -36,6 +35,7 @@ async fn main() -> Result<(), Box> { let health_check_url = format!("http://{}:{}/health", service_address, health_port); let health_check_endpoint_addr = format!("{}:{}", service_address, health_port); let db_nodes = get_service_address(&consul_url, "database-service").await?; + let session_nodes = get_service_address(&consul_url, "session-service").await?; // Register service with Consul let service_id = consul_registration::get_or_generate_service_id(); @@ -58,12 +58,17 @@ async fn main() -> Result<(), Box> { let db_address = db_nodes.get(0).unwrap(); let db_url = format!("http://{}:{}", db_address.ServiceAddress, db_address.ServicePort); - let database_client = DatabaseClient::connect(&db_url).await?; + let db_client = Arc::new(DatabaseClient::connect(&db_url).await?); + + let session_address = session_nodes.get(0).unwrap(); + let session_address = format!("http://{}:{}", session_address.ServiceAddress, session_address.ServicePort); + let session_client = Arc::new(SessionServiceClient::connect(session_address).await?); let full_addr = format!("{}:{}", &addr, port); let address = full_addr.parse().expect("Invalid address"); let auth_service = MyAuthService { - db_client: database_client, + db_client, + session_client }; println!("Authentication Service running on {}", addr); diff --git a/database-service/src/db.rs b/database-service/src/db.rs index 005a02d..9686201 100644 --- a/database-service/src/db.rs +++ b/database-service/src/db.rs @@ -1,6 +1,6 @@ use crate::users::UserRepository; use crate::characters::CharacterRepository; -use crate::redis_cache::RedisCache; +use utils::redis_cache::RedisCache; use sqlx::PgPool; use std::sync::Arc; use tokio::sync::Mutex; diff --git a/database-service/src/lib.rs b/database-service/src/lib.rs index cabb231..2b328f7 100644 --- a/database-service/src/lib.rs +++ b/database-service/src/lib.rs @@ -1,5 +1,4 @@ pub mod users; pub mod characters; -pub mod redis_cache; pub mod db; pub mod grpc; \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 969a24c..53a197e 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,6 +24,7 @@ environment: - HEALTH_CHECK_PORT=8080 depends_on: + - session-service - database-service - consul @@ -93,6 +94,18 @@ - auth-service - consul + session-service: + build: + context: ./ + dockerfile: ./session-service/Dockerfile + ports: + - "50055:50055" + env_file: + - ./session-service/.env + - .env + depends_on: + - consul + db: image: postgres:17 env_file: diff --git a/frontend/src/components/LoginPage.js b/frontend/src/components/LoginPage.js index ed332fb..8a08f4c 100644 --- a/frontend/src/components/LoginPage.js +++ b/frontend/src/components/LoginPage.js @@ -34,12 +34,12 @@ const LoginPage = () => { const response = await axios.post(apiUrl, { username, password }); // Extract token and server info from response - const { token, serverIp } = response.data; + const { token, session_id } = response.data; setMessage("Login successful! Launching game..."); const { ServiceAddress, ServicePort } = await getServiceAddress("packet-service"); - window.location.href = `osirose-launcher://launch?otp=${encodeURIComponent(token)}&ip=${encodeURIComponent(ServiceAddress)}&port=${encodeURIComponent(ServicePort)}&username=${encodeURIComponent(username)}`; + window.location.href = `osirose-launcher://launch?otp=${encodeURIComponent(token)}&session=${encodeURIComponent(session_id)}&ip=${encodeURIComponent(ServiceAddress)}&port=${encodeURIComponent(ServicePort)}&username=${encodeURIComponent(username)}`; } catch (error) { setMessage("Login failed: " + error.response?.data?.error || error.message); } diff --git a/launcher/src/launcher.rs b/launcher/src/launcher.rs index ee1bbc1..470f7ea 100644 --- a/launcher/src/launcher.rs +++ b/launcher/src/launcher.rs @@ -50,6 +50,9 @@ pub(crate) fn launch_game(url: String) { is_direct = true; command.arg("_direct").arg("_otp").arg(value.to_string()); } + Cow::Borrowed("session") => { + command.arg("_session").arg(value.to_string()); + } Cow::Borrowed("username") => { command.arg("_userid").arg(value.to_string()); } diff --git a/packet-service/Cargo.toml b/packet-service/Cargo.toml index eb58931..c343390 100644 --- a/packet-service/Cargo.toml +++ b/packet-service/Cargo.toml @@ -23,6 +23,8 @@ tonic = "0.12.3" prost = "0.13.4" utils = { path = "../utils" } warp = "0.3.7" +dashmap = "6.1.0" +uuid = { version = "1.11.0", features = ["v4"] } [build-dependencies] tonic-build = "0.12.3" diff --git a/packet-service/src/auth_client.rs b/packet-service/src/auth_client.rs index 1a3b8d5..b1040e2 100644 --- a/packet-service/src/auth_client.rs +++ b/packet-service/src/auth_client.rs @@ -1,5 +1,5 @@ use crate::auth::auth_service_client::AuthServiceClient; -use crate::auth::{LoginRequest, LoginResponse, ValidateTokenRequest, ValidateTokenResponse}; +use crate::auth::{Empty, LoginRequest, LoginResponse, LogoutRequest, ValidateTokenRequest, ValidateTokenResponse}; use tonic::transport::Channel; pub struct AuthClient { @@ -12,10 +12,11 @@ impl AuthClient { Ok(AuthClient { client }) } - pub async fn login(&mut self, username: &str, password: &str) -> Result> { + pub async fn login(&mut self, username: &str, password: &str, ip_address: &str) -> Result> { let request = LoginRequest { username: username.to_string(), password: password.to_string(), + ip_address: ip_address.to_string(), }; let response = self.client.login(request).await?; @@ -24,10 +25,19 @@ impl AuthClient { pub async fn login_token(&mut self, token: &str) -> Result> { let request = ValidateTokenRequest { - token: token.to_string(), + token: token.to_string() }; let response = self.client.validate_token(request).await?; Ok(response.into_inner()) } + + pub async fn logout(&mut self, session_id: &str) -> Result> { + let request = LogoutRequest { + session_id: session_id.to_string(), + }; + + let response = self.client.logout(request).await?; + Ok(response.into_inner()) + } } diff --git a/packet-service/src/connection_service.rs b/packet-service/src/connection_service.rs new file mode 100644 index 0000000..cafa5f9 --- /dev/null +++ b/packet-service/src/connection_service.rs @@ -0,0 +1,30 @@ +use dashmap::DashMap; +use std::sync::Arc; +use uuid::Uuid; +use crate::connection_state::ConnectionState; + +pub struct ConnectionService { + pub connections: Arc>, // Map connection ID to state +} + +impl ConnectionService { + pub fn new() -> Self { + Self { + connections: Arc::new(DashMap::new()), + } + } + + pub fn add_connection(&self) -> String { + let connection_id = Uuid::new_v4().to_string(); + self.connections.insert(connection_id.clone(), ConnectionState::new()); + connection_id + } + + pub fn get_connection(&self, connection_id: &str) -> Option { + self.connections.get(connection_id).map(|entry| entry.clone()) + } + + pub fn remove_connection(&self, connection_id: &str) { + self.connections.remove(connection_id); + } +} diff --git a/packet-service/src/connection_state.rs b/packet-service/src/connection_state.rs new file mode 100644 index 0000000..859b8d1 --- /dev/null +++ b/packet-service/src/connection_state.rs @@ -0,0 +1,20 @@ +use std::collections::HashMap; + +#[derive(Clone, Debug)] +pub struct ConnectionState { + pub user_id: Option, + pub session_id: Option, + pub character_id: Option, + pub additional_data: HashMap, // Flexible data storage +} + +impl ConnectionState { + pub fn new() -> Self { + Self { + user_id: None, + session_id: None, + character_id: None, + additional_data: HashMap::new(), + } + } +} diff --git a/packet-service/src/handlers/auth.rs b/packet-service/src/handlers/auth.rs index 8c00a3e..8c81298 100644 --- a/packet-service/src/handlers/auth.rs +++ b/packet-service/src/handlers/auth.rs @@ -15,17 +15,17 @@ use crate::packets::*; use std::collections::HashMap; use std::env; use std::error::Error; +use std::net::SocketAddr; use std::sync::Arc; use tokio::net::TcpStream; use tokio::sync::Mutex; use tonic::{Code, Status}; use tracing::{debug, error, info, warn}; use utils::service_discovery; +use crate::connection_service::ConnectionService; +use crate::packets::cli_logout_req::CliLogoutReq; pub(crate) async fn handle_accept_req(stream: &mut TcpStream, packet: Packet) -> Result<(), Box> { - // let request = CliAcceptReq::decode(packet.payload.as_slice()); - - // We need to do reply to this packet let data = SrvAcceptReply { result: srv_accept_reply::Result::Accepted, rand_value: 0 }; let response_packet = Packet::new(PacketType::PakssAcceptReply, &data)?; @@ -39,7 +39,19 @@ pub(crate) async fn handle_join_server_req(stream: &mut TcpStream, packet: Packe Ok(()) } -pub(crate) async fn handle_login_req(stream: &mut TcpStream, packet: Packet, auth_client: Arc>) -> Result<(), Box> { +pub(crate) async fn handle_logout_req(stream: &mut TcpStream, packet: Packet, auth_client: Arc>, connection_service: Arc, connection_id: String) -> Result<(), Box> { + let request = CliLogoutReq::decode(packet.payload.as_slice()); + let mut auth_client = auth_client.lock().await; + if let Some(mut state) = connection_service.get_connection(&connection_id) { + let session_id = state.session_id.clone().unwrap(); + auth_client.logout(&session_id).await?; + Ok(()) + } else { + Err("Unable to find connection state".into()) + } +} + +pub(crate) async fn handle_login_req(stream: &mut TcpStream, packet: Packet, auth_client: Arc>, connection_service: Arc, connection_id: String, addr: SocketAddr) -> Result<(), Box> { debug!("decoding packet payload of size {}", packet.payload.as_slice().len()); let data = CliLoginTokenReq::decode(packet.payload.as_slice())?; debug!("{:?}", data); @@ -56,6 +68,11 @@ pub(crate) async fn handle_login_req(stream: &mut TcpStream, packet: Packet, aut } else { debug!("Successfully logged in"); + if let Some(mut state) = connection_service.get_connection(&connection_id) { + state.user_id = Some(response.user_id.parse().unwrap()); + // auth_client.logout(&session_id).await?; + } + let consul_url = env::var("CONSUL_URL").unwrap_or_else(|_| "http://127.0.0.1:8500".to_string()); let servers = service_discovery::get_service_address(&consul_url, "character-service").await.unwrap_or_else(|err| { warn!(err); @@ -125,10 +142,10 @@ pub(crate) async fn handle_server_select_req(stream: &mut TcpStream, packet: Pac let data = SrvSrvSelectReply { result: srv_srv_select_reply::Result::Failed, - session_id: 0, - crypt_val: 0, - ip: NullTerminatedString::new(""), - port: 0, + session_id: 0, // Client should already have this value + crypt_val: 0, // This is only for the old encryption + ip: NullTerminatedString::new(""), // If this is empty, the client should stay connected (requires client change) + port: 0, // See comment about ip above }; let response_packet = Packet::new(PacketType::PaklcSrvSelectReply, &data)?; diff --git a/packet-service/src/main.rs b/packet-service/src/main.rs index da501a1..08a5392 100644 --- a/packet-service/src/main.rs +++ b/packet-service/src/main.rs @@ -18,6 +18,7 @@ use tracing::{debug, error, info, warn}; use utils::consul_registration; use utils::service_discovery::get_service_address; use warp::Filter; +use crate::connection_service::ConnectionService; mod packet_type; mod packet; @@ -30,6 +31,9 @@ mod handlers; mod bufferpool; mod metrics; mod auth_client; +mod connection_state; +mod connection_service; + pub mod auth { tonic::include_proto!("auth"); // Path matches the package name in auth.proto } @@ -38,7 +42,7 @@ const BUFFER_POOL_SIZE: usize = 1000; const MAX_CONCURRENT_CONNECTIONS: usize = 100; -async fn handle_connection(stream: &mut TcpStream, pool: Arc, auth_client: Arc>) -> Result<(), Box> { +async fn handle_connection(stream: &mut TcpStream, pool: Arc, auth_client: Arc>, connection_service: Arc, connection_id: String) -> Result<(), Box> { ACTIVE_CONNECTIONS.inc(); while let Some(mut buffer) = pool.acquire().await { // Read data into the buffer @@ -53,13 +57,19 @@ async fn handle_connection(stream: &mut TcpStream, pool: Arc, auth_c Ok(packet) => { debug!("Parsed Packet: {:?}", packet); // Handle the parsed packet (route it, process it, etc.) - router::route_packet(stream, packet, auth_client.clone()).await?; + router::route_packet(stream, packet, auth_client.clone(), connection_service.clone(), connection_id.clone()).await?; } Err(e) => warn!("Failed to parse packet: {}", e), } pool.release(buffer).await; } + + if let Some(state) = connection_service.get_connection(&connection_id) { + let session_id = state.session_id.unwrap(); + let mut auth_client = auth_client.lock().await; + auth_client.logout(&session_id).await?; + } ACTIVE_CONNECTIONS.dec(); Ok(()) } @@ -113,12 +123,14 @@ async fn main() -> Result<(), Box> { let semaphore = Arc::new(Semaphore::new(MAX_CONCURRENT_CONNECTIONS)); let listener = TcpListener::bind(full_addr.clone()).await.unwrap(); let buffer_pool = BufferPool::new(BUFFER_POOL_SIZE); + let connection_service = Arc::new(ConnectionService::new()); info!("Packet service listening on {}", full_addr); loop { let (mut socket, addr) = listener.accept().await.unwrap(); let auth_client = auth_client.clone(); + let connection_service = connection_service.clone(); info!("New connection from {}", addr); let pool = buffer_pool.clone(); @@ -127,9 +139,11 @@ async fn main() -> Result<(), Box> { // Spawn a new task for each connection tokio::spawn(async move { let _permit = permit; - if let Err(e) = handle_connection(&mut socket, pool, auth_client).await { + let connection_id = connection_service.add_connection(); + if let Err(e) = handle_connection(&mut socket, pool, auth_client, connection_service.clone(), connection_id.clone()).await { error!("Error handling connection: {}", e); } + connection_service.remove_connection(&connection_id); }); } }); diff --git a/packet-service/src/router.rs b/packet-service/src/router.rs index 8534253..7356b8d 100644 --- a/packet-service/src/router.rs +++ b/packet-service/src/router.rs @@ -7,15 +7,17 @@ use std::sync::Arc; use tokio::net::TcpStream; use tokio::sync::Mutex; use tracing::{debug, warn}; +use crate::connection_service::ConnectionService; -pub async fn route_packet(stream: &mut TcpStream, packet: Packet, auth_client: Arc>) -> Result<(), Box> { +pub async fn route_packet(stream: &mut TcpStream, packet: Packet, auth_client: Arc>, connection_service: Arc, connection_id: String) -> Result<(), Box> { debug!("Routing packet: {:?}", packet); match packet.packet_type { PacketType::PakcsAlive => Ok(()), PacketType::PakcsAcceptReq => auth::handle_accept_req(stream, packet).await, PacketType::PakcsJoinServerTokenReq => auth::handle_join_server_req(stream, packet).await, // Login Stuff - PacketType::PakcsLoginTokenReq => auth::handle_login_req(stream, packet, auth_client).await, + PacketType::PakcsLoginTokenReq => auth::handle_login_req(stream, packet, auth_client, connection_service, connection_id, stream.peer_addr()?).await, + PacketType::PakcsLogoutReq => auth::handle_logout_req(stream, packet, auth_client, connection_service, connection_id).await, PacketType::PakcsSrvSelectReq => auth::handle_server_select_req(stream, packet).await, PacketType::PakcsChannelListReq => auth::handle_channel_list_req(stream, packet).await, diff --git a/proto/auth.proto b/proto/auth.proto index de260ec..0b11af7 100644 --- a/proto/auth.proto +++ b/proto/auth.proto @@ -4,7 +4,9 @@ package auth; service AuthService { rpc Login(LoginRequest) returns (LoginResponse); + rpc Logout(LogoutRequest) returns (Empty); rpc ValidateToken(ValidateTokenRequest) returns (ValidateTokenResponse); + rpc ValidateSession(ValidateSessionRequest) returns (ValidateSessionResponse); rpc Register (RegisterRequest) returns (RegisterResponse); rpc RequestPasswordReset (PasswordResetRequest) returns (PasswordResetResponse); rpc ResetPassword (ResetPasswordRequest) returns (ResetPasswordResponse); @@ -13,11 +15,17 @@ service AuthService { message LoginRequest { string username = 1; string password = 2; + string ip_address = 3; } message LoginResponse { string token = 1; string user_id = 2; + string session_id = 3; +} + +message LogoutRequest { + string session_id = 1; } message ValidateTokenRequest { @@ -29,6 +37,14 @@ message ValidateTokenResponse { string user_id = 2; } +message ValidateSessionRequest { + string session_id = 1; +} + +message ValidateSessionResponse { + bool valid = 1; +} + message RegisterRequest { string username = 1; string email = 2; @@ -56,3 +72,5 @@ message ResetPasswordRequest { message ResetPasswordResponse { string message = 1; } + +message Empty {} \ No newline at end of file diff --git a/utils/Cargo.toml b/utils/Cargo.toml index cbd6e32..3f3c884 100644 --- a/utils/Cargo.toml +++ b/utils/Cargo.toml @@ -12,3 +12,7 @@ uuid = { version = "1.11.0", features = ["v4"] } warp = "0.3.7" tokio = "1.41.1" bincode = { version = "2.0.0-rc.3", features = ["derive", "serde"] } +redis = "0.27.5" +deadpool-redis = "0.18.0" +async-trait = "0.1.83" +serde_json = "1.0.133"