diff --git a/atoma-auth/src/auth.rs b/atoma-auth/src/auth.rs index fbaa6f7..70e942a 100644 --- a/atoma-auth/src/auth.rs +++ b/atoma-auth/src/auth.rs @@ -7,7 +7,7 @@ use crate::google::{self, fetch_google_public_keys}; use crate::{AtomaAuthConfig, Sui}; use anyhow::anyhow; use atoma_state::{ - types::{AtomaAtomaStateManagerEvent, TokenResponse}, + types::{AtomaAtomaStateManagerEvent, TokenResponse, UserProfile}, AtomaStateManagerError, }; use atoma_utils::hashing::blake2b_hash; @@ -317,16 +317,20 @@ impl Auth { hex::encode(hash_result) } - /// Register user with username/password. - /// This method will register a new user with a username and password + /// Register user with email/password. + /// This method will register a new user with a email and password /// The password is hashed and stored in the DB /// The method will generate a new refresh and access token #[instrument(level = "info", skip(self, password))] - pub async fn register(&self, username: &str, password: &str) -> Result<(String, String)> { + pub async fn register( + &self, + user_profile: &UserProfile, + password: &str, + ) -> Result<(String, String)> { let (result_sender, result_receiver) = oneshot::channel(); self.state_manager_sender .send(AtomaAtomaStateManagerEvent::RegisterUserWithPassword { - username: username.to_string(), + user_profile: user_profile.clone(), password: self.hash_string(password), result_sender, })?; @@ -346,17 +350,16 @@ impl Auth { #[instrument(level = "info", skip(self, password))] pub async fn check_user_password( &self, - username: &str, + email: &str, password: &str, ) -> Result<(String, String)> { let (result_sender, result_receiver) = oneshot::channel(); - self.state_manager_sender.send( - AtomaAtomaStateManagerEvent::GetUserIdByUsernamePassword { - username: username.to_string(), + self.state_manager_sender + .send(AtomaAtomaStateManagerEvent::GetUserIdByEmailPassword { + email: email.to_string(), password: self.hash_string(password), result_sender, - }, - )?; + })?; let user_id = result_receiver .await?? .map(|user_id| user_id as u64) @@ -388,7 +391,7 @@ impl Auth { }; self.state_manager_sender .send(AtomaAtomaStateManagerEvent::OAuth { - username: email, + email, result_sender, })?; let user_id = result_receiver.await??; @@ -1059,7 +1062,7 @@ active_address: "0x939cfcc7fcbc71ce983203bcb36fa498901932ab9293dfa2b271203e71603 #[tokio::test] async fn test_token_flow() { let user_id = 123; - let username = "user"; + let email = "email"; let password = "top_secret"; let (auth, receiver) = setup_test().await; let hash_password = auth.hash_string(password); @@ -1067,12 +1070,12 @@ active_address: "0x939cfcc7fcbc71ce983203bcb36fa498901932ab9293dfa2b271203e71603 // First event is for the user to log in to get the tokens let event = receiver.recv_async().await.unwrap(); match event { - AtomaAtomaStateManagerEvent::GetUserIdByUsernamePassword { - username: event_username, + AtomaAtomaStateManagerEvent::GetUserIdByEmailPassword { + email: event_email, password: event_password, result_sender, } => { - assert_eq!(username, event_username); + assert_eq!(email, event_email); assert_eq!(hash_password, event_password); result_sender.send(Ok(Some(user_id))).unwrap(); } @@ -1115,7 +1118,7 @@ active_address: "0x939cfcc7fcbc71ce983203bcb36fa498901932ab9293dfa2b271203e71603 } }); let (refresh_token, access_token) = - auth.check_user_password(username, password).await.unwrap(); + auth.check_user_password(email, password).await.unwrap(); // Refresh token should not have refresh token hash let claims = auth.validate_token(&refresh_token, true).unwrap(); assert_eq!(claims.user_id, user_id); @@ -1149,10 +1152,10 @@ active_address: "0x939cfcc7fcbc71ce983203bcb36fa498901932ab9293dfa2b271203e71603 let event = receiver.recv_async().await.unwrap(); match event { AtomaAtomaStateManagerEvent::OAuth { - username: event_username, + email: event_email, result_sender, } => { - assert_eq!(event_username, "email"); + assert_eq!(event_email, "email"); result_sender.send(Ok(1)).unwrap(); } _ => panic!("Unexpected event"), diff --git a/atoma-proxy-service/docs/openapi.yml b/atoma-proxy-service/docs/openapi.yml index 44dc13e..df28238 100644 --- a/atoma-proxy-service/docs/openapi.yml +++ b/atoma-proxy-service/docs/openapi.yml @@ -64,7 +64,7 @@ paths: * `proxy_service_state` - The shared state containing the state manager * `headers` - The headers of the request - * `body` - The request body containing the API token to revoke + * `body` - The request body containing the API token id to revoke # Returns @@ -98,7 +98,7 @@ paths: # Arguments * `proxy_service_state` - The shared state containing the state manager - * `body` - The request body containing the username and password of the new user + * `body` - The request body containing the email and password of the new user # Returns @@ -108,7 +108,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/AuthRequest' + $ref: '#/components/schemas/RegisterAuthRequest' required: true responses: '200': @@ -116,7 +116,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/AuthRequest' + $ref: '#/components/schemas/RegisterAuthRequest' '500': description: Failed to register user /login: @@ -128,7 +128,7 @@ paths: # Arguments * `proxy_service_state` - The shared state containing the state manager - * `body` - The request body containing the username and password of the user + * `body` - The request body containing the email and password of the user # Returns @@ -138,7 +138,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/AuthRequest' + $ref: '#/components/schemas/LoginAuthRequest' required: true responses: '200': @@ -146,7 +146,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/AuthRequest' + $ref: '#/components/schemas/RegisterAuthRequest' '500': description: Failed to login user /api_tokens: @@ -355,6 +355,41 @@ paths: description: Failed to get user profile security: - bearerAuth: [] + /set_user_profile: + get: + tags: + - Auth + summary: Retrieves the user profile for the user. + description: |- + # Arguments + + * `proxy_service_state` - The shared state containing the state manager + * `headers` - The headers of the request + + # Returns + + * `Result>` - A JSON response containing the user profile + + # Errors + + * If the user ID cannot be retrieved from the token, returns a 401 Unauthorized error + * If the user profile cannot be retrieved, returns a 500 Internal Server Error + operationId: set_user_profile + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UserProfile' + required: true + responses: + '200': + description: Sets the user profile for the user + '401': + description: Unauthorized request + '500': + description: Failed to set user profile + security: + - bearerAuth: [] /salt: get: tags: @@ -641,19 +676,6 @@ paths: description: Failed to get graph data components: schemas: - AuthRequest: - type: object - description: Request payload for user authentication - required: - - username - - password - properties: - password: - type: string - description: The user's password - username: - type: string - description: The user's unique identifier CreateTokenRequest: type: object description: |- @@ -665,6 +687,20 @@ components: properties: name: type: string + description: The name of the token + LoginAuthRequest: + type: object + description: Request payload for user authentication + required: + - email + - password + properties: + email: + type: string + description: The user's unique identifier + password: + type: string + description: The user's password ProofRequest: type: object description: |- @@ -677,6 +713,19 @@ components: signature: type: string description: The signature of the user to prove ownership of the sui address + RegisterAuthRequest: + type: object + description: Request payload for user authentication + required: + - user_profile + - password + properties: + password: + type: string + description: The user's password + user_profile: + $ref: '#/components/schemas/UserProfile' + description: The user's unique identifier RevokeApiTokenRequest: type: object description: Request payload for revoking an API token @@ -686,7 +735,7 @@ components: api_token_id: type: integer format: int64 - description: The API token to be revoked + description: The API token id to be revoked UsdcPaymentRequest: type: object description: |- @@ -704,6 +753,21 @@ components: transaction_digest: type: string description: The transaction digest of the payment + UserProfile: + type: object + description: |- + Represents a user profile + This struct is used to represent the response for the get_user_profile endpoint. + required: + - email + - name + properties: + email: + type: string + description: The user's email + name: + type: string + description: The user's name securitySchemes: bearerAuth: type: http diff --git a/atoma-proxy-service/src/components/openapi.rs b/atoma-proxy-service/src/components/openapi.rs index dc71d75..eeeced1 100644 --- a/atoma-proxy-service/src/components/openapi.rs +++ b/atoma-proxy-service/src/components/openapi.rs @@ -11,10 +11,11 @@ use crate::{ handlers::{ auth::{ GenerateApiTokenOpenApi, GetAllApiTokensOpenApi, GetBalance, GetSalt, GetSuiAddress, - GetUserProfile, LoginOpenApi, RegisterOpenApi, RevokeApiTokenOpenApi, UpdateSuiAddress, - UsdcPayment, GENERATE_API_TOKEN_PATH, GET_ALL_API_TOKENS_PATH, GET_BALANCE_PATH, - GET_SALT_PATH, GET_SUI_ADDRESS_PATH, GET_USER_PROFILE_PATH, LOGIN_PATH, REGISTER_PATH, - REVOKE_API_TOKEN_PATH, UPDATE_SUI_ADDRESS_PATH, USDC_PAYMENT_PATH, + GetUserProfile, LoginOpenApi, RegisterOpenApi, RevokeApiTokenOpenApi, SetUserProfile, + UpdateSuiAddress, UsdcPayment, GENERATE_API_TOKEN_PATH, GET_ALL_API_TOKENS_PATH, + GET_BALANCE_PATH, GET_SALT_PATH, GET_SUI_ADDRESS_PATH, GET_USER_PROFILE_PATH, + LOGIN_PATH, REGISTER_PATH, REVOKE_API_TOKEN_PATH, SET_USER_PROFILE_PATH, + UPDATE_SUI_ADDRESS_PATH, USDC_PAYMENT_PATH, }, stacks::{ GetCurrentStacksOpenApi, GetStacksByUserId, GET_ALL_STACKS_FOR_USER_PATH, @@ -49,6 +50,7 @@ pub fn openapi_router() -> Router { (path = GET_ALL_STACKS_FOR_USER_PATH, api = GetStacksByUserId, tags = ["Stacks"]), (path = GET_BALANCE_PATH, api = GetBalance, tags = ["Auth"]), (path = GET_USER_PROFILE_PATH, api = GetUserProfile, tags = ["Auth"]), + (path = SET_USER_PROFILE_PATH, api = SetUserProfile, tags = ["Auth"]), (path = GET_SALT_PATH, api = GetSalt, tags = ["Auth"]), (path = TASKS_PATH, api = GetAllTasksOpenApi, tags = ["Tasks"]), (path = COMPUTE_UNITS_PROCESSED_PATH, api = GetComputeUnitsProcessed, tags = ["Stats"]), diff --git a/atoma-proxy-service/src/handlers/auth.rs b/atoma-proxy-service/src/handlers/auth.rs index c770782..485a687 100644 --- a/atoma-proxy-service/src/handlers/auth.rs +++ b/atoma-proxy-service/src/handlers/auth.rs @@ -1,7 +1,7 @@ use atoma_auth::AuthError; use atoma_state::types::{ - AuthRequest, AuthResponse, CreateTokenRequest, ProofRequest, RevokeApiTokenRequest, - TokenResponse, UsdcPaymentRequest, UserProfile, + AuthResponse, CreateTokenRequest, LoginAuthRequest, ProofRequest, RegisterAuthRequest, + RevokeApiTokenRequest, TokenResponse, UsdcPaymentRequest, UserProfile, }; use axum::{ extract::State, @@ -47,6 +47,9 @@ pub const GET_BALANCE_PATH: &str = "/balance"; /// Get user profile endpoint pub const GET_USER_PROFILE_PATH: &str = "/user_profile"; +/// Set user profile endpoint +pub const SET_USER_PROFILE_PATH: &str = "/set_user_profile"; + /// Set user's salt endpoint. pub const GET_SALT_PATH: &str = "/salt"; @@ -81,6 +84,7 @@ pub fn auth_router() -> Router { .route(GET_SUI_ADDRESS_PATH, get(get_sui_address)) .route(GET_BALANCE_PATH, get(get_balance)) .route(GET_USER_PROFILE_PATH, get(get_user_profile)) + .route(SET_USER_PROFILE_PATH, post(set_user_profile)) .route(GET_SALT_PATH, get(get_salt)); #[cfg(feature = "google-oauth")] let router = router.route(GOOGLE_OAUTH_PATH, post(google_oauth)); @@ -252,7 +256,7 @@ pub struct RegisterOpenApi; /// # Arguments /// /// * `proxy_service_state` - The shared state containing the state manager -/// * `body` - The request body containing the username and password of the new user +/// * `body` - The request body containing the email and password of the new user /// /// # Returns /// @@ -261,18 +265,18 @@ pub struct RegisterOpenApi; post, path = "", responses( - (status = OK, description = "Registers a new user", body = AuthRequest), + (status = OK, description = "Registers a new user", body = RegisterAuthRequest), (status = INTERNAL_SERVER_ERROR, description = "Failed to register user") ) )] #[instrument(level = "trace", skip_all)] pub async fn register( State(proxy_service_state): State, - body: Json, + body: Json, ) -> Result> { let (refresh_token, access_token) = proxy_service_state .auth - .register(&body.username, &body.password) + .register(&body.user_profile, &body.password) .await .map_err(|e| { error!("Failed to register user: {:?}", e); @@ -301,7 +305,7 @@ pub struct LoginOpenApi; /// # Arguments /// /// * `proxy_service_state` - The shared state containing the state manager -/// * `body` - The request body containing the username and password of the user +/// * `body` - The request body containing the email and password of the user /// /// # Returns /// @@ -310,18 +314,18 @@ pub struct LoginOpenApi; post, path = "", responses( - (status = OK, description = "Logs in a user", body = AuthRequest), + (status = OK, description = "Logs in a user", body = RegisterAuthRequest), (status = INTERNAL_SERVER_ERROR, description = "Failed to login user") ) )] #[instrument(level = "trace", skip_all)] pub async fn login( State(proxy_service_state): State, - body: Json, + body: Json, ) -> Result> { let (refresh_token, access_token) = proxy_service_state .auth - .check_user_password(&body.username, &body.password) + .check_user_password(&body.email, &body.password) .await .map_err(|e| { error!("Failed to login user: {:?}", e); @@ -659,6 +663,70 @@ pub async fn get_user_profile( )) } +/// OpenAPI documentation for the set_user_profile endpoint. +/// +/// This struct is used to generate OpenAPI documentation for the set_user_profile +/// endpoint. It uses the `utoipa` crate's derive macro to automatically generate +/// the OpenAPI specification from the code. +#[derive(OpenApi)] +#[openapi(paths(set_user_profile))] +pub struct SetUserProfile; + +/// Retrieves the user profile for the user. +/// +/// # Arguments +/// +/// * `proxy_service_state` - The shared state containing the state manager +/// * `headers` - The headers of the request +/// +/// # Returns +/// +/// * `Result>` - A JSON response containing the user profile +/// +/// # Errors +/// +/// * If the user ID cannot be retrieved from the token, returns a 401 Unauthorized error +/// * If the user profile cannot be retrieved, returns a 500 Internal Server Error +#[utoipa::path( + get, + path = "", + security( + ("bearerAuth" = []) + ), + responses( + (status = OK, description = "Sets the user profile for the user"), + (status = UNAUTHORIZED, description = "Unauthorized request"), + (status = INTERNAL_SERVER_ERROR, description = "Failed to set user profile") + ) +)] +#[instrument(level = "info", skip_all)] +pub async fn set_user_profile( + State(proxy_service_state): State, + headers: HeaderMap, + Json(user_profile): Json, +) -> Result> { + let jwt = get_jwt_from_headers(&headers)?; + + let user_id = proxy_service_state + .auth + .get_user_id_from_token(jwt) + .await + .map_err(|e| { + error!("Failed to get user ID from token: {:?}", e); + StatusCode::UNAUTHORIZED + })?; + + proxy_service_state + .atoma_state + .set_user_profile(user_id, user_profile) + .await + .map_err(|e| { + error!("Failed to get user profile: {:?}", e); + StatusCode::INTERNAL_SERVER_ERROR + })?; + Ok(Json(())) +} + /// OpenAPI documentation for the get_salt endpoint. /// /// This struct is used to generate OpenAPI documentation for the get_salt diff --git a/atoma-state/src/handlers.rs b/atoma-state/src/handlers.rs index 1942898..dbec345 100644 --- a/atoma-state/src/handlers.rs +++ b/atoma-state/src/handlers.rs @@ -1232,34 +1232,34 @@ pub async fn handle_state_manager_event( .send(public_key) .map_err(|_| AtomaStateManagerError::ChannelSendError)?; } - AtomaAtomaStateManagerEvent::GetUserIdByUsernamePassword { - username, + AtomaAtomaStateManagerEvent::GetUserIdByEmailPassword { + email, password, result_sender, } => { let user_id = state_manager .state - .get_user_id_by_username_password(&username, &password) + .get_user_id_by_email_password(&email, &password) .await; result_sender .send(user_id) .map_err(|_| AtomaStateManagerError::ChannelSendError)?; } AtomaAtomaStateManagerEvent::OAuth { - username, + email, result_sender, } => { - let user_id = state_manager.state.oauth(&username).await; + let user_id = state_manager.state.oauth(&email).await; result_sender .send(user_id) .map_err(|_| AtomaStateManagerError::ChannelSendError)?; } AtomaAtomaStateManagerEvent::RegisterUserWithPassword { - username, + user_profile, password, result_sender, } => { - let user_id = state_manager.state.register(&username, &password).await; + let user_id = state_manager.state.register(user_profile, &password).await; result_sender .send(user_id) .map_err(|_| AtomaStateManagerError::ChannelSendError)?; diff --git a/atoma-state/src/migrations/20250305142648_username-name.sql b/atoma-state/src/migrations/20250305142648_username-name.sql new file mode 100644 index 0000000..9bfb54b --- /dev/null +++ b/atoma-state/src/migrations/20250305142648_username-name.sql @@ -0,0 +1,5 @@ +ALTER TABLE users RENAME COLUMN username TO email; + +ALTER TABLE users ADD COLUMN name TEXT; + +UPDATE users SET name = email; diff --git a/atoma-state/src/state_manager.rs b/atoma-state/src/state_manager.rs index 87a8300..22eb1f1 100644 --- a/atoma-state/src/state_manager.rs +++ b/atoma-state/src/state_manager.rs @@ -1064,7 +1064,7 @@ impl AtomaState { /// /// # Arguments /// - /// * `username` - The username of the user. + /// * `email` - The email of the user. /// * `password_hash` - The password hash of the user. /// /// # Returns @@ -1083,14 +1083,19 @@ impl AtomaState { /// ```rust,ignore /// use atoma_node::atoma_state::AtomaStateManager; /// - /// async fn register_user(state_manager: &AtomaStateManager, username: &str, password_hash: &str) -> Result, AtomaStateManagerError> { - /// state_manager.register(username, password_hash).await + /// async fn register_user(state_manager: &AtomaStateManager, email: &str, password_hash: &str) -> Result, AtomaStateManagerError> { + /// state_manager.register(email, password_hash).await /// } /// ``` #[instrument(level = "trace", skip(self))] - pub async fn register(&self, username: &str, password_hash: &str) -> Result> { - let result = sqlx::query("INSERT INTO users (username, password_hash) VALUES ($1, $2) ON CONFLICT (username) DO NOTHING RETURNING id") - .bind(username) + pub async fn register( + &self, + user_profile: UserProfile, + password_hash: &str, + ) -> Result> { + let result = sqlx::query("INSERT INTO users (email, name, password_hash) VALUES ($1, $2, $3) ON CONFLICT (email) DO NOTHING RETURNING id") + .bind(user_profile.email) + .bind(user_profile.name) .bind(password_hash) .fetch_optional(&self.db) .await?; @@ -3539,13 +3544,13 @@ impl AtomaState { Ok(()) } - /// Get the user_id by username and password. + /// Get the user_id by email and password. /// - /// This method queries the `users` table to get the user_id by username and password. + /// This method queries the `users` table to get the user_id by email and password. /// /// # Arguments /// - /// * `username` - The username of the user. + /// * `email` - The email of the user. /// * `hashed_password` - The hashed password of the user. /// /// # Returns @@ -3565,18 +3570,18 @@ impl AtomaState { /// ```rust,ignore /// use atoma_node::atoma_state::AtomaStateManager; /// - /// async fn get_user_id(state_manager: &AtomaStateManager, username: &str, hashed_password: &str) -> Result, AtomaStateManagerError> { - /// state_manager.get_user_id_by_username_password(username, hashed_password).await + /// async fn get_user_id(state_manager: &AtomaStateManager, email: &str, hashed_password: &str) -> Result, AtomaStateManagerError> { + /// state_manager.get_user_id_by_email_password(email, hashed_password).await /// } /// ``` #[instrument(level = "trace", skip(self))] - pub async fn get_user_id_by_username_password( + pub async fn get_user_id_by_email_password( &self, - username: &str, + email: &str, hashed_password: &str, ) -> Result> { - let user = sqlx::query("SELECT id FROM users WHERE username = $1 AND password_hash = $2") - .bind(username) + let user = sqlx::query("SELECT id FROM users WHERE email = $1 AND password_hash = $2") + .bind(email) .bind(hashed_password) .fetch_optional(&self.db) .await?; @@ -3584,13 +3589,13 @@ impl AtomaState { Ok(user.map(|user| user.get("id"))) } - /// Get the id of the user by username (register if not in the table yet). + /// Get the id of the user by email (register if not in the table yet). /// - /// This method queries the `users` table to get the user_id by username. If the user is not found, it will insert the user into the table. + /// This method queries the `users` table to get the user_id by email. If the user is not found, it will insert the user into the table. /// /// # Arguments /// - /// * `username` - The username of the user. + /// * `email` - The email of the user. /// /// # Returns /// @@ -3608,13 +3613,13 @@ impl AtomaState { /// ```rust,ignore /// use atoma_node::atoma_state::AtomaStateManager; /// - /// async fn oauth(state_manager: &AtomaStateManager, username: &str) -> Result { - /// state_manager.oauth(username).await + /// async fn oauth(state_manager: &AtomaStateManager, email: &str) -> Result { + /// state_manager.oauth(email).await /// } /// ``` - pub async fn oauth(&self, username: &str) -> Result { - let user = sqlx::query("INSERT INTO users (username) VALUES ($1) ON CONFLICT (username) DO UPDATE SET username = EXCLUDED.username RETURNING id") - .bind(username) + pub async fn oauth(&self, email: &str) -> Result { + let user = sqlx::query("INSERT INTO users (email) VALUES ($1) ON CONFLICT (email) DO UPDATE SET email = EXCLUDED.email RETURNING id") + .bind(email) .fetch_one(&self.db).await?; Ok(user.get("id")) @@ -4013,7 +4018,7 @@ impl AtomaState { /// ``` #[instrument(level = "trace", skip_all)] pub async fn get_user_profile(&self, user_id: i64) -> Result { - let user = sqlx::query("SELECT username FROM users WHERE id = $1") + let user = sqlx::query("SELECT email, name FROM users WHERE id = $1") .bind(user_id) .fetch_one(&self.db) .await?; @@ -4021,6 +4026,47 @@ impl AtomaState { UserProfile::from_row(&user).map_err(AtomaStateManagerError::from) } + /// Set the user profile by user_id. + /// + /// This method sets the user profile by user_id from the `users` table. + /// + /// # Arguments + /// + /// * `user_id` - The unique identifier of the user. + /// * `user_profile` - The profile of the user. + /// + /// + /// # Returns + /// + /// - `Result<()>`: A result containing either: + /// - `Ok(())`: The user profile for the user_id was set + /// - `Err(AtomaStateManagerError)`: An error if the database query fails. + /// + /// # Errors + /// + /// This function will return an error if: + /// - The database query fails to execute. + /// + /// # Example + /// + /// ```rust,ignore + /// use atoma_node::atoma_state::AtomaStateManager; + /// + /// async fn get_profile(state_manager: &AtomaStateManager, user_id: i64, user_profile:UserProfile) -> Result<(), AtomaStateManagerError> { + /// state_manager.set_user_profile(user_id, user_profile).await + /// } + /// ``` + #[instrument(level = "trace", skip_all)] + pub async fn set_user_profile(&self, user_id: i64, user_profile: UserProfile) -> Result<()> { + sqlx::query("UPDATE users SET email = $2, name = $3 WHERE id = $1") + .bind(user_id) + .bind(user_profile.email) + .bind(user_profile.name) + .execute(&self.db) + .await?; + Ok(()) + } + /// Get latency performance for the last `last_hours` hours. /// /// This method fetches the latency performance for the last `last_hours` hours from the `stats_latency` table. diff --git a/atoma-state/src/tests.rs b/atoma-state/src/tests.rs index 0050e1a..66b30c1 100644 --- a/atoma-state/src/tests.rs +++ b/atoma-state/src/tests.rs @@ -297,9 +297,10 @@ async fn create_test_stack( num_compute_units: i64, user_id: i64, ) -> sqlx::Result<()> { - sqlx::query("INSERT INTO users (id, username, password_hash) VALUES ($1, $2, $3)") + sqlx::query("INSERT INTO users (id, name, email, password_hash) VALUES ($1, $2, $3, $4)") .bind(user_id) - .bind(format!("test_user_{user_id}")) // Create unique username + .bind(format!("user_{user_id}")) // Create unique email + .bind(format!("test_user_{user_id}")) // Create unique email .bind("test_password_hash") // Default password hash .execute(pool) .await?; diff --git a/atoma-state/src/types.rs b/atoma-state/src/types.rs index bc07556..54bb1f3 100644 --- a/atoma-state/src/types.rs +++ b/atoma-state/src/types.rs @@ -30,9 +30,18 @@ pub struct RevokeApiTokenRequest { /// Request payload for user authentication #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromRow, ToSchema)] -pub struct AuthRequest { +pub struct RegisterAuthRequest { /// The user's unique identifier - pub username: String, + pub user_profile: UserProfile, + /// The user's password + pub password: String, +} + +/// Request payload for user authentication +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromRow, ToSchema)] +pub struct LoginAuthRequest { + /// The user's unique identifier + pub email: String, /// The user's password pub password: String, } @@ -132,10 +141,12 @@ pub struct ComputedUnitsProcessedResponse { /// Represents a user profile /// This struct is used to represent the response for the get_user_profile endpoint. -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromRow)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromRow, ToSchema)] pub struct UserProfile { - /// Username of the user - pub username: String, + /// The user's email + pub email: String, + /// The user's name + pub name: String, } /// Represents a latency response. @@ -760,28 +771,28 @@ pub enum AtomaAtomaStateManagerEvent { }, /// Registers a new user with a password RegisterUserWithPassword { - /// The username of the user - username: String, + /// The email of the user + user_profile: UserProfile, /// The password of the user password: String, /// Channel to send back the user ID /// Returns Ok(Option) with the user ID or an error if the query fails result_sender: oneshot::Sender>>, }, - /// Retrieves the user ID by username and password - GetUserIdByUsernamePassword { - /// The username of the user - username: String, + /// Retrieves the user ID by email and password + GetUserIdByEmailPassword { + /// The email of the user + email: String, /// The password of the user password: String, /// Channel to send back the user ID /// Returns Ok(Option) with the user ID or an error if the query fails result_sender: oneshot::Sender>>, }, - /// Retrieves the user ID by oauth username + /// Retrieves the user ID by oauth email OAuth { - /// The username of the user - username: String, + /// The email of the user + email: String, /// The result sender to send back the user ID result_sender: oneshot::Sender>, },