simple Log In endpoint without encryption
This commit is contained in:
23
src/main.rs
23
src/main.rs
@@ -4,8 +4,9 @@ use tokio::net::TcpListener;
|
||||
mod utils;
|
||||
mod routes;
|
||||
mod rooms;
|
||||
|
||||
use crate::utils::db_pool::HotelPools;
|
||||
use r2d2::{Pool};
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use crate::utils::db_pool::{HotelPool,AppState};
|
||||
use routes::create_router;
|
||||
|
||||
|
||||
@@ -13,8 +14,22 @@ use routes::create_router;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
let hotel_pools = HotelPools::new();
|
||||
let app = create_router(hotel_pools);
|
||||
|
||||
let hotel_pools = HotelPool::new();
|
||||
let logs_manager = SqliteConnectionManager::file("db/auth.sqlite");
|
||||
let logs_pool = Pool::builder()
|
||||
.max_size(5)
|
||||
.build(logs_manager)
|
||||
.expect("Failed to build logs pool");
|
||||
|
||||
let state = AppState {
|
||||
hotel_pools,
|
||||
logs_pool,
|
||||
jwt_secret: "your_jwt_secret_key s".to_string(), // better: load from env var
|
||||
};
|
||||
|
||||
|
||||
let app = create_router(state);
|
||||
let listener = TcpListener::bind("0.0.0.0:3000").await?;
|
||||
serve(listener, app).into_future().await?;
|
||||
Ok(())
|
||||
|
||||
@@ -3,13 +3,15 @@ use axum::response::IntoResponse;
|
||||
use axum::http::StatusCode;
|
||||
|
||||
use crate::rooms::extractor::UpdateRoomPayload;
|
||||
use crate::utils::db_pool::*;
|
||||
|
||||
|
||||
use std::sync::Arc;
|
||||
use r2d2::{Pool};
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use dashmap::DashMap;
|
||||
use rusqlite::params;
|
||||
use crate::utils::db_pool::*;
|
||||
|
||||
|
||||
|
||||
pub async fn hello_rooms() -> String {
|
||||
@@ -29,14 +31,12 @@ pub async fn fake_room_update(
|
||||
}
|
||||
|
||||
pub async fn fake_db_update(
|
||||
|
||||
State(hotel_pools): State<HotelPools>,
|
||||
State(state): State<AppState>,
|
||||
Path(room_id): Path<i32>,
|
||||
UpdateRoomPayload(payload): UpdateRoomPayload,
|
||||
|
||||
) -> impl IntoResponse {
|
||||
|
||||
let pool = hotel_pools.get_pool(payload.hotel_id);
|
||||
let pool = state.hotel_pools.get_pool(payload.hotel_id);
|
||||
let conn = match pool.get() {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error: {err}")),
|
||||
@@ -57,23 +57,9 @@ pub async fn fake_db_update(
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
//fake db handler
|
||||
pub async fn update_room(UpdateRoomPayload(payload): UpdateRoom) -> impl IntoResponse {
|
||||
match fake_db_update(&payload.token, payload.room_id, &payload.status).await {
|
||||
Ok(msg) => msg,
|
||||
Err(err) => format!("Error: {}", err),
|
||||
}
|
||||
struct RoomRequest {
|
||||
item_id: i32,
|
||||
item_amount: i32,
|
||||
token: String,
|
||||
}
|
||||
|
||||
async fn fake_db_update(token: &str, room_id: i32, status: &str) -> Result<String, String> {
|
||||
// Pretend we check the token in the DB
|
||||
if token != "valid_token" {
|
||||
return Err("Invalid token".into());
|
||||
}
|
||||
|
||||
// Pretend we update the room status in the DB
|
||||
Ok(format!("Room {} updated to '{}'", room_id, status))
|
||||
}
|
||||
*/
|
||||
@@ -4,14 +4,18 @@ use axum::{
|
||||
};
|
||||
|
||||
use crate::rooms::handler::*;
|
||||
use crate::utils::db_pool::HotelPools;
|
||||
use crate::utils::db_pool::{
|
||||
HotelPool,
|
||||
AppState,
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ROOTS
|
||||
pub fn rooms_routes() -> Router<HotelPools> {
|
||||
pub fn rooms_routes() -> Router {
|
||||
|
||||
Router::new()
|
||||
.route("/", get(hello_rooms) )
|
||||
.route("/fakeUpdate/{room_id}", put(fake_room_update))
|
||||
.route("/fake_db_update/{room_id}", put(fake_db_update))
|
||||
|
||||
}
|
||||
@@ -1,24 +1,26 @@
|
||||
use axum::{
|
||||
Router,
|
||||
};
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use r2d2::{Pool};
|
||||
|
||||
use crate::rooms::routes::rooms_routes;
|
||||
use crate::utils::routes::utils_routes;
|
||||
|
||||
pub mod inventory;
|
||||
use crate::utils::db_pool::HotelPools;
|
||||
use crate::utils::db_pool::{AppState};
|
||||
|
||||
//TODO: add secret fomr dotenv here
|
||||
|
||||
|
||||
|
||||
pub fn create_router(state: AppState) -> Router {
|
||||
|
||||
pub fn create_router(hotel_pools: HotelPools) -> Router {
|
||||
Router::new()
|
||||
.nest("/rooms", rooms_routes())
|
||||
.with_state(hotel_pools) // 👈 hotel_db is passed in as argument
|
||||
|
||||
.nest("/auth", utils_routes())
|
||||
.with_state(state) // 👈 hotel_db is passed in as argument
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
pub fn create_router() -> Router {
|
||||
Router::new()
|
||||
.nest("/rooms", rooms_routes())
|
||||
//.nest("/inventory", inventory::inventory_routes)
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
0
src/services/mod.rs
Normal file
0
src/services/mod.rs
Normal file
227
src/utils/auth.rs
Normal file
227
src/utils/auth.rs
Normal file
@@ -0,0 +1,227 @@
|
||||
use std::time::Duration;
|
||||
use axum::{
|
||||
body::{to_bytes, Body},
|
||||
http::{Request as HttpRequest, StatusCode},
|
||||
middleware::Next,
|
||||
response::{Response, IntoResponse},
|
||||
Json,
|
||||
extract::{Path, State, FromRequest}
|
||||
};
|
||||
use axum::extract::Request as ExtractRequest;
|
||||
use jsonwebtoken::{decode, DecodingKey, Validation, encode, EncodingKey, Header};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use chrono::{Utc};
|
||||
use rusqlite::{params, Connection, OptionalExtension};
|
||||
|
||||
//use crate::utils::db_pool::;
|
||||
use crate::utils::db_pool::{
|
||||
HotelPool,
|
||||
AppState,
|
||||
};
|
||||
|
||||
pub async fn auth_middleware(
|
||||
mut req: HttpRequest<Body>,
|
||||
next: Next,
|
||||
) -> Result<Response, StatusCode> {
|
||||
// buffer body
|
||||
let body = std::mem::take(req.body_mut());
|
||||
|
||||
// Buffer into bytes
|
||||
let bytes = to_bytes(body, 1024 * 1024)
|
||||
.await
|
||||
.map_err(|_| StatusCode::BAD_REQUEST)?;
|
||||
|
||||
|
||||
// parse JSON as generic `Value`
|
||||
let value: Value = serde_json::from_slice(&bytes)
|
||||
.map_err(|_| StatusCode::BAD_REQUEST)?;
|
||||
|
||||
// pull out token
|
||||
let token = value
|
||||
.get("token")
|
||||
.and_then(|t| t.as_str())
|
||||
.ok_or(StatusCode::UNAUTHORIZED)?;
|
||||
|
||||
// verify token
|
||||
let key = DecodingKey::from_secret("your_jwt_secret_key".as_ref());
|
||||
let validation = Validation::default();
|
||||
let claims = decode::<Claims>(token, &key, &validation)
|
||||
.map_err(|_| StatusCode::UNAUTHORIZED)?
|
||||
.claims;
|
||||
|
||||
// inject claims for downstream handlers
|
||||
req.extensions_mut().insert(claims);
|
||||
|
||||
// restore the original body so the handler can parse it into *its own* struct
|
||||
req = req.map(|_| Body::from(bytes));
|
||||
|
||||
|
||||
Ok(next.run(req).await)
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct LoginValues {
|
||||
username : String,
|
||||
password : String,
|
||||
hotel_id: i32,
|
||||
}
|
||||
|
||||
pub struct LoginPayload(pub LoginValues);
|
||||
|
||||
impl<S> FromRequest<S> for LoginPayload
|
||||
where S: Send + Sync,
|
||||
{
|
||||
type Rejection = (StatusCode, String);
|
||||
|
||||
async fn from_request(req: ExtractRequest, state: &S) -> Result<Self, Self::Rejection> {
|
||||
let Json(payload) = Json::<LoginValues>::from_request(req, state)
|
||||
.await
|
||||
.map_err(|err| (StatusCode::BAD_REQUEST, format!("Invalid body: {}", err)))?;
|
||||
|
||||
Ok(LoginPayload(payload))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize,Debug, Serialize, Clone)]
|
||||
struct Claims{
|
||||
id: i32,
|
||||
hotel_id: i32,
|
||||
//display_name
|
||||
username: String,
|
||||
exp: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct LoginResponse {
|
||||
token: String,
|
||||
}
|
||||
|
||||
//pub async fn auth_register();
|
||||
/*
|
||||
pub async fn auth_loggin(
|
||||
// State(hotel_pools): State<HotelPools>,
|
||||
LoginPayload(payload): LoginPayload,
|
||||
) -> impl IntoResponse {
|
||||
|
||||
let pool = .get_pool(payload.hotel_id);
|
||||
let conn = pool.get().unwrap();
|
||||
/*
|
||||
let conn = match pool.get() {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error: {err}")),
|
||||
};
|
||||
*/
|
||||
let mut statement = conn
|
||||
.prepare("SELECT id, displayname, hotel_id FROM users WHERE username = ? 1 AND password = ?2 ")
|
||||
.unwrap();
|
||||
|
||||
let user_row = statement
|
||||
.query_row(params![&payload.username, &payload.password], |row| {
|
||||
let id: i32 = row.get(0)?;
|
||||
let displayname: String = row.get(1)?;
|
||||
let hotel_id: i32 = row.get(2)?;
|
||||
Ok((id, displayname, hotel_id))
|
||||
})
|
||||
.optional()
|
||||
.unwrap(); // returns Ok(Some(...)) or Ok(None)
|
||||
|
||||
if let Some((id, displayname, hotel_id)) = user_row {
|
||||
let expiration = chrono::Utc::now()
|
||||
.checked_add_signed(chrono::Duration::minutes(15))
|
||||
.unwrap()
|
||||
.timestamp() as usize;
|
||||
|
||||
let claims = Claims {
|
||||
id,
|
||||
hotel_id,
|
||||
username: displayname, // or payload.username if you prefer
|
||||
exp: expiration,
|
||||
};
|
||||
|
||||
let token = encode(
|
||||
&Header::default(),
|
||||
&claims,
|
||||
&EncodingKey::from_secret("TEST".as_bytes()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
return (StatusCode::OK, Json(LoginResponse { token }));
|
||||
}
|
||||
// Fallback if login failed — wrap String in Json<LoginResponse> or use axum::Json
|
||||
return (StatusCode::UNAUTHORIZED, Json(LoginResponse { token: "".to_string() }));
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
pub async fn clean_auth_loging(
|
||||
State(state): State<AppState>,
|
||||
LoginPayload(payload): LoginPayload
|
||||
) -> impl IntoResponse {
|
||||
// 1️⃣ Get a connection from logs pool
|
||||
let conn = match state.logs_pool.get() {
|
||||
Ok(c) => c,
|
||||
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB connection error").into_response(),
|
||||
};
|
||||
|
||||
let user_row = match conn.query_row(
|
||||
"SELECT id, password, hotel_id, displayname FROM users WHERE username = ?1",
|
||||
params![&payload.username],
|
||||
|row| {
|
||||
let user_id: i32 = row.get(0)?;
|
||||
let password: String = row.get(1)?;
|
||||
let hotel_id: i32 = row.get(2)?;
|
||||
let displayname: String = row.get(3)?;
|
||||
Ok((user_id, password, hotel_id, displayname))
|
||||
},
|
||||
).optional() {
|
||||
Ok(opt) => opt,
|
||||
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB query error").into_response(),
|
||||
};
|
||||
|
||||
let (user_id, stored_password, hotel_id, displayname) = match user_row {
|
||||
Some(u) => u,
|
||||
None => return (StatusCode::UNAUTHORIZED, "Invalid credentials").into_response(),
|
||||
};
|
||||
|
||||
if payload.password != stored_password {
|
||||
return (StatusCode::UNAUTHORIZED, "Invalid credentials").into_response();
|
||||
}
|
||||
|
||||
let expiration = chrono::Utc::now()
|
||||
.checked_add_signed(chrono::Duration::hours(15))
|
||||
.unwrap()
|
||||
.timestamp() as usize;
|
||||
|
||||
|
||||
let claims = serde_json::json!({
|
||||
"id": user_id,
|
||||
"hotel_id": payload.hotel_id,
|
||||
"username": payload.username,
|
||||
"exp": expiration
|
||||
});
|
||||
|
||||
let token = match encode(
|
||||
&Header::default(),
|
||||
&claims,
|
||||
&EncodingKey::from_secret(state.jwt_secret.as_ref()),
|
||||
) {
|
||||
Ok(t) => t,
|
||||
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "JWT creation failed").into_response(),
|
||||
};
|
||||
Json(LoginResponse { token }).into_response()
|
||||
}
|
||||
|
||||
fn internal_error<E: std::fmt::Display>(err: E) -> (StatusCode, String) {
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, format!("Internal error: {}", err))
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
match result {
|
||||
Ok(rows) if rows > 0 => (StatusCode::OK, format!("Logged")),
|
||||
Ok(_) => (StatusCode::NOT_FOUND, "No used".to_string()),
|
||||
Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {err}")),
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -6,30 +6,38 @@ use r2d2_sqlite::SqliteConnectionManager;
|
||||
type HotelId = i32; // or i32 if you want numeric ids
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HotelPools {
|
||||
pools: Arc<DashMap<HotelId, Pool<SqliteConnectionManager>>>,
|
||||
pub struct AppState {
|
||||
pub hotel_pools: HotelPool,
|
||||
pub logs_pool: Pool<SqliteConnectionManager>,
|
||||
pub jwt_secret: String
|
||||
}
|
||||
|
||||
impl HotelPools {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HotelPool {
|
||||
hotel_pools: Arc<DashMap<HotelId, Pool<SqliteConnectionManager>>>,
|
||||
}
|
||||
|
||||
impl HotelPool {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
pools: Arc::new(DashMap::new()),
|
||||
hotel_pools: Arc::new(DashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_pool(&self, hotel_id: i32) -> Pool<SqliteConnectionManager> {
|
||||
if let Some(pool) = self.pools.get(&hotel_id) {
|
||||
if let Some(pool) = self.hotel_pools.get(&hotel_id) {
|
||||
return pool.clone();
|
||||
}
|
||||
|
||||
let db_path = format!("db/{}.sqlite", hotel_id);
|
||||
let manager = SqliteConnectionManager::file(db_path);
|
||||
let pool = Pool::builder()
|
||||
let hotels_manager = SqliteConnectionManager::file(db_path);
|
||||
let db_pool = Pool::builder()
|
||||
.max_size(5) // adjust based on load
|
||||
.build(manager)
|
||||
.build(hotels_manager)
|
||||
.expect("Failed to build pool");
|
||||
|
||||
self.pools.insert(hotel_id, pool.clone());
|
||||
pool
|
||||
self.hotel_pools.insert(hotel_id, db_pool.clone());
|
||||
db_pool
|
||||
}
|
||||
}
|
||||
@@ -1 +1,3 @@
|
||||
pub mod db_pool;
|
||||
pub mod db_pool;
|
||||
pub mod auth;
|
||||
pub mod routes;
|
||||
16
src/utils/routes.rs
Normal file
16
src/utils/routes.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use axum::{
|
||||
routing::{get, put, post},
|
||||
Router,
|
||||
};
|
||||
|
||||
use crate::utils::auth::*;
|
||||
use crate::utils::db_pool::{HotelPool, AppState};
|
||||
|
||||
|
||||
// ROOTS
|
||||
pub fn utils_routes() -> Router<AppState> {
|
||||
|
||||
Router::new()
|
||||
.route("/login", put(clean_auth_loging))
|
||||
//.with_state(state)
|
||||
}
|
||||
Reference in New Issue
Block a user