- fix: issue where login failed to return the user if their role was null
- add: register route for api-service - update: spawn a thread for the rest api in the api service
This commit is contained in:
@@ -8,7 +8,7 @@ use tonic::transport::Channel;
|
||||
use tower_http::cors::{Any, CorsLayer};
|
||||
|
||||
use auth::auth_service_client::AuthServiceClient;
|
||||
use auth::LoginRequest;
|
||||
use auth::{LoginRequest, RegisterRequest};
|
||||
use log::error;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
@@ -27,6 +27,19 @@ struct RestLoginResponse {
|
||||
token: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct RestRegisterRequest {
|
||||
username: String,
|
||||
email: String,
|
||||
password: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct RestRegisterResponse {
|
||||
success: bool,
|
||||
message: String,
|
||||
}
|
||||
|
||||
async fn login_handler(
|
||||
State(grpc_client): State<Arc<Mutex<AuthServiceClient<Channel>>>>,
|
||||
Json(payload): Json<RestLoginRequest>,
|
||||
@@ -49,9 +62,34 @@ async fn login_handler(
|
||||
}
|
||||
}
|
||||
|
||||
async fn register_handler(
|
||||
State(grpc_client): State<Arc<Mutex<AuthServiceClient<Channel>>>>,
|
||||
Json(payload): Json<RestRegisterRequest>,
|
||||
) -> Result<Json<RestRegisterResponse>, axum::http::StatusCode> {
|
||||
let mut client = grpc_client.lock().await;
|
||||
|
||||
let request = tonic::Request::new(RegisterRequest {
|
||||
username: payload.username,
|
||||
email: payload.email,
|
||||
password: payload.password,
|
||||
});
|
||||
|
||||
match client.register(request).await {
|
||||
Ok(response) => Ok(Json(RestRegisterResponse {
|
||||
success: response.into_inner().user_id != 0,
|
||||
message: "Registration successful".to_string(),
|
||||
})),
|
||||
Err(e) => {
|
||||
error!("gRPC Login call failed: {}", e);
|
||||
Err(axum::http::StatusCode::INTERNAL_SERVER_ERROR)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub async fn serve_rest_api(
|
||||
grpc_client: Arc<Mutex<AuthServiceClient<Channel>>>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
) -> Result<(), Box<dyn std::error::Error + Send>> {
|
||||
let cors = CorsLayer::new()
|
||||
.allow_origin(Any) // Allow requests from any origin
|
||||
.allow_methods([Method::GET, Method::POST]) // Allow specific methods
|
||||
@@ -59,6 +97,7 @@ pub async fn serve_rest_api(
|
||||
|
||||
let app = Router::new()
|
||||
.route("/api/login", post(login_handler))
|
||||
.route("/api/register", post(register_handler))
|
||||
.with_state(grpc_client)
|
||||
.layer(cors);
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
// Start the Axum REST API
|
||||
info!("Starting REST API on {}:{}", addr, port);
|
||||
axum_gateway::serve_rest_api(grpc_client).await?;
|
||||
tokio::spawn(axum_gateway::serve_rest_api(grpc_client));
|
||||
|
||||
select! {
|
||||
_ = signal::ctrl_c() => {},
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::users::{hash_password, verify_user};
|
||||
use chrono::{Duration, Utc};
|
||||
use rand::Rng;
|
||||
use tonic::{Request, Response, Status};
|
||||
use tracing::{info, warn};
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
pub struct MyAuthService<T: DatabaseClientTrait + Clone> {
|
||||
pub db_client: T,
|
||||
@@ -62,11 +62,19 @@ impl<T: DatabaseClientTrait + Send + Sync + Clone + 'static> AuthService for MyA
|
||||
let hashed_password = hash_password(&req.password);
|
||||
|
||||
// Create user in the database
|
||||
let user = self.db_client.clone().create_user(&req.username, &req.email, &hashed_password)
|
||||
.await
|
||||
.map_err(|e| Status::internal(format!("Database error: {}", e)))?;
|
||||
let result = self.db_client.clone().create_user(&req.username, &req.email, &hashed_password)
|
||||
.await;
|
||||
|
||||
Ok(Response::new(RegisterResponse { user_id: user.user_id }))
|
||||
match result {
|
||||
Ok(user) => Ok(Response::new(RegisterResponse {
|
||||
user_id: user.user_id,
|
||||
message: "User registered successfully".into(),
|
||||
})),
|
||||
Err(e) => {
|
||||
error!("Failed to register user: {:?}", e);
|
||||
Err(Status::internal("Failed to register user"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn request_password_reset(
|
||||
|
||||
@@ -21,7 +21,6 @@ pub fn verify_password(password: &str, hash: &str) -> bool {
|
||||
|
||||
pub async fn verify_user<T: DatabaseClientTrait>(mut db_client: T,
|
||||
username: &str, password: &str) -> Option<String> {
|
||||
// Placeholder: Replace with a gRPC call to the Database Service
|
||||
let user = db_client.get_user_by_username(username).await.ok()?;
|
||||
|
||||
if verify_password(password, &user.hashed_password) {
|
||||
|
||||
@@ -20,7 +20,7 @@ impl UserService for MyDatabaseService {
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
hashed_password: user.hashed_password,
|
||||
roles: user.roles,
|
||||
roles: user.roles.unwrap_or_else(Vec::new),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ impl UserService for MyDatabaseService {
|
||||
) -> Result<Response<CreateUserResponse>, Status> {
|
||||
let req = request.into_inner();
|
||||
|
||||
let user_id = self.db.user_repo.create_user(&req.username, &req.email, &req.hashed_password, &[])
|
||||
let user_id = self.db.user_repo.create_user(&req.username, &req.email, &req.hashed_password)
|
||||
.await
|
||||
.map_err(|_| Status::internal("Failed to create user"))?;
|
||||
|
||||
@@ -53,7 +53,7 @@ impl UserService for MyDatabaseService {
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
hashed_password: user.hashed_password,
|
||||
roles: user.roles,
|
||||
roles: user.roles.unwrap_or_else(Vec::new),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ impl UserService for MyDatabaseService {
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
hashed_password: user.hashed_password,
|
||||
roles: user.roles,
|
||||
roles: user.roles.unwrap_or_else(Vec::new),
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ use crate::redis_cache::{RedisCache, Cache}; // Import RedisCache and Cache Trai
|
||||
use serde::{Serialize, Deserialize};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::{debug};
|
||||
|
||||
#[derive(Debug, FromRow, Serialize, Deserialize)]
|
||||
pub struct User {
|
||||
@@ -10,7 +11,7 @@ pub struct User {
|
||||
pub username: String,
|
||||
pub email: String,
|
||||
pub hashed_password: String,
|
||||
pub roles: Vec<String>,
|
||||
pub roles: Option<Vec<String>>,
|
||||
pub created_at: chrono::NaiveDateTime,
|
||||
pub updated_at: chrono::NaiveDateTime,
|
||||
}
|
||||
@@ -79,19 +80,21 @@ impl UserRepository {
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn create_user(&self, username: &str, email: &str, hashed_password: &str, roles: &[String]) -> Result<i32, sqlx::Error> {
|
||||
let row = sqlx::query(
|
||||
"INSERT INTO users (username, email, hashed_password, roles, created_at, updated_at) \
|
||||
VALUES ($1, $2, $3, $4, NOW(), NOW()) RETURNING id",
|
||||
pub async fn create_user(&self, username: &str, email: &str, hashed_password: &str) -> Result<i32, sqlx::Error> {
|
||||
let result = sqlx::query!(
|
||||
r#"
|
||||
INSERT INTO users (username, email, hashed_password)
|
||||
VALUES ($1, $2, $3)
|
||||
RETURNING id
|
||||
"#,
|
||||
username,
|
||||
email,
|
||||
hashed_password
|
||||
)
|
||||
.bind(username)
|
||||
.bind(email)
|
||||
.bind(hashed_password)
|
||||
.bind(roles)
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
|
||||
Ok(row.get("id"))
|
||||
Ok(result.id)
|
||||
}
|
||||
|
||||
pub async fn update_user_email(&self, user_id: i32, new_email: &str) -> Result<(), sqlx::Error> {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
dockerfile: ./api-service/Dockerfile
|
||||
ports:
|
||||
- "8080:8080"
|
||||
- "8081:8081"
|
||||
env_file:
|
||||
- ./api-service/.env
|
||||
- .env
|
||||
|
||||
@@ -37,6 +37,7 @@ message RegisterRequest {
|
||||
|
||||
message RegisterResponse {
|
||||
int32 user_id = 1;
|
||||
string message = 2;
|
||||
}
|
||||
|
||||
message PasswordResetRequest {
|
||||
|
||||
Reference in New Issue
Block a user