added refresh token generator
This commit is contained in:
51
src/note_endpoint_json.txt
Normal file
51
src/note_endpoint_json.txt
Normal file
@@ -0,0 +1,51 @@
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct GetMessagesValues {
|
||||
pub conv_id: u32,
|
||||
pub timestamp: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
pub async fn get_message(
|
||||
State(state): State<AppState>,
|
||||
AuthClaims { user_id, hotel_id, username }: AuthClaims,
|
||||
Json(payload): Json<GetMessagesValues>, // ✅ no custom payload wrapper
|
||||
) -> Result<impl IntoResponse, (StatusCode, String)> {
|
||||
|
||||
let pool = state.hotel_pools.get_pool(hotel_id);
|
||||
|
||||
let conn = pool.get()
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Pool error".to_string()))?;
|
||||
|
||||
let from_time = match payload.timestamp.as_deref() {
|
||||
Some("0") | None => "1970-01-01 00:00:00",
|
||||
Some(ts) => ts,
|
||||
};
|
||||
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT id, sender_id, content, sent_at
|
||||
FROM message
|
||||
WHERE conversation_id = ?1
|
||||
AND sent_at > ?2
|
||||
ORDER BY sent_at DESC
|
||||
LIMIT 50"
|
||||
).map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Prepare failed".to_string()))?;
|
||||
|
||||
let messages = stmt.query_map(
|
||||
params![payload.conv_id, from_time],
|
||||
|row| {
|
||||
Ok(Message {
|
||||
id: row.get(0)?,
|
||||
sender_id: row.get(1)?,
|
||||
content: row.get(2)?,
|
||||
sent_at: row.get(3)?,
|
||||
})
|
||||
}
|
||||
)
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Query failed".to_string()))?
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Collect failed".to_string()))?;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
use std::time::Duration;
|
||||
use axum::{
|
||||
body::{to_bytes, Body},
|
||||
http::{Request as HttpRequest, StatusCode, },
|
||||
http::{Request as HttpRequest, StatusCode, header::{SET_COOKIE, HeaderValue} },
|
||||
http::request::Parts,
|
||||
middleware::Next,
|
||||
response::{Response, IntoResponse},
|
||||
Json,
|
||||
extract::{Path, State, FromRequest, FromRequestParts, Extension}
|
||||
};
|
||||
|
||||
use axum_extra::extract::TypedHeader;
|
||||
//use axum_extra::TypedHeader;
|
||||
|
||||
use headers::UserAgent;
|
||||
|
||||
use axum::extract::FromRef;
|
||||
use axum::extract::Request as ExtractRequest;
|
||||
use jsonwebtoken::{decode, DecodingKey, Validation, encode, EncodingKey, Header, Algorithm};
|
||||
@@ -16,14 +22,19 @@ use serde_json::Value;
|
||||
use chrono::{Utc};
|
||||
use rusqlite::{params, Connection, OptionalExtension};
|
||||
|
||||
use rand_core::OsRng;
|
||||
use rand_core::{RngCore, OsRng};
|
||||
use argon2::{
|
||||
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
|
||||
Argon2};
|
||||
|
||||
//use crate::utils::db_pool::;
|
||||
use uuid::Uuid;
|
||||
|
||||
//use crate::utils::db_pool::;
|
||||
use crate::utils::db_pool::{HotelPool,AppState};
|
||||
|
||||
use base64::{engine::general_purpose, Engine as _};
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct JwtKeys {
|
||||
pub encoding: EncodingKey,
|
||||
@@ -386,9 +397,77 @@ pub async fn clean_auth_loging(
|
||||
Json(LoginResponse { token }).into_response()
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct CreateRefreshTokenValue {
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
pub device_id: Uuid,
|
||||
pub timestamp: Option<String>,
|
||||
|
||||
}
|
||||
|
||||
#[axum::debug_handler]
|
||||
pub async fn create_refresh_token(
|
||||
State(state): State<AppState>,
|
||||
user_agent: Option<TypedHeader<UserAgent>>,
|
||||
Json(payload): Json<CreateRefreshTokenValue>
|
||||
) -> Result<impl IntoResponse, (StatusCode, String)> { // ← Add Result here
|
||||
|
||||
let user_agent_str = user_agent
|
||||
.map(|ua| ua.to_string())
|
||||
.unwrap_or_else(|| "Unknown".to_string());
|
||||
|
||||
let device_id_str = payload.device_id.to_string();
|
||||
|
||||
let argon2 = Argon2::default();
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
let mut bytes = [0u8; 64];
|
||||
OsRng.fill_bytes(&mut bytes);
|
||||
|
||||
let hashed_token = argon2
|
||||
.hash_password(&bytes, &salt)
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
|
||||
.to_string();
|
||||
|
||||
let raw_token = general_purpose::STANDARD.encode(&bytes);
|
||||
|
||||
let conn = state.logs_pool.get()
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB connection error".to_string()))?;
|
||||
|
||||
let user_row = conn.query_row(
|
||||
"SELECT id, password FROM users WHERE username = ?1",
|
||||
params![&payload.username],
|
||||
|row| {
|
||||
let user_id: i32 = row.get(0)?;
|
||||
let password: String = row.get(1)?;
|
||||
Ok((user_id, password))
|
||||
},
|
||||
).optional()
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB get user id error".to_string()))?;
|
||||
|
||||
let (user_id, stored_hash) = user_row
|
||||
.ok_or((StatusCode::NOT_FOUND, "User not found".to_string()))?;
|
||||
|
||||
if !verify_password(&payload.password, &stored_hash) {
|
||||
return Err((StatusCode::UNAUTHORIZED, "Invalid credentials".to_string()));
|
||||
}
|
||||
|
||||
conn.execute(
|
||||
"INSERT INTO refresh_token (user_id, token_hash, device_id, user_agent) VALUES (?1, ?2, ?3, ?4)",
|
||||
params![user_id, hashed_token, device_id_str, user_agent_str],
|
||||
)
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "DB insert error".to_string()))?;
|
||||
|
||||
let cookie_value = format!("refresh_token={}; HttpOnly; Secure; Path=/", raw_token);
|
||||
|
||||
let mut response = (StatusCode::CREATED, "Refresh token created successfully").into_response();
|
||||
response.headers_mut().insert(
|
||||
SET_COOKIE,
|
||||
HeaderValue::from_str(&cookie_value).unwrap(),
|
||||
);
|
||||
|
||||
Ok(response) // ← Wrap in Ok()
|
||||
}
|
||||
|
||||
|
||||
fn internal_error<E: std::fmt::Display>(err: E) -> (StatusCode, String) {
|
||||
|
||||
@@ -6,6 +6,9 @@ use axum::{
|
||||
use crate::utils::auth::*;
|
||||
use crate::utils::db_pool::{HotelPool, AppState, };
|
||||
use crate::utils::websocket::ws_handler;
|
||||
use headers::UserAgent;
|
||||
use axum_extra::TypedHeader;
|
||||
|
||||
|
||||
// ROOTS
|
||||
pub fn utils_routes() -> Router<AppState> {
|
||||
@@ -17,6 +20,8 @@ pub fn utils_routes() -> Router<AppState> {
|
||||
.route("/tokentest", put(token_tester))
|
||||
.route("/force_update_password", put(ForceUpdatePassword))
|
||||
.route("/update_password", put(UpdatePassword))
|
||||
|
||||
.route("/create_refresh", post(create_refresh_token))
|
||||
|
||||
//.with_state(state)
|
||||
}
|
||||
Reference in New Issue
Block a user