add user conv impl

This commit is contained in:
2025-12-29 17:23:05 +01:00
parent 5a8d1e447b
commit 8671b03856
15 changed files with 316 additions and 382 deletions

View File

@@ -1,16 +1,17 @@
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use axum::{
Json, extract::{
FromRequest,FromRequestParts, State,
FromRequest,FromRequestParts, State, Path,
}, http::StatusCode, response::{IntoResponse, sse::KeepAlive}
};
//use axum::extract::ws::Message;
use chrono::NaiveDateTime;
use rusqlite::{Name, params};
use rusqlite::{Name, Statement, params};
use rusqlite::OptionalExtension;
use serde::Serialize;
use serde_json::{json, to_value};
@@ -56,58 +57,189 @@ pub async fn add_user_to_conv(
let pool = state.hotel_pools.get_pool(hotel_id);
let conn = match pool.get(){
let mut conn = match pool.get(){
Ok(conn) => conn,
Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error"))
};
let mut statement = match conn.prepare(
"SELECT 1 FROM conversation WHERE creator_id = ?1 AND id = ?2" ,
){
Ok(statement) => statement,
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "prepare failed".to_string())
let is_creator = match conn
.query_row(
"SELECT 1 FROM conversation WHERE creator_id = ?1 AND id = ?2",
params![user_id, payload.conv_id],
|_| Ok(()),
)
.optional()
{
Ok(Some(_)) => true,
Ok(None) => false,
Err(_) => {
return (
StatusCode::INTERNAL_SERVER_ERROR,
"Creator check failed".to_string(),
)
}
};
match statement.exists(params![user_id, payload.conv_id]) {
Ok(true) => {
//user is creator
}
Ok(false) => {
//user is not the creator
return (StatusCode::FORBIDDEN, "Not the creato of the conversation".to_string())
}
Err(_) => {
return(StatusCode::INTERNAL_SERVER_ERROR, "Query failed".to_string())
}
if !is_creator {
return (
StatusCode::FORBIDDEN,
"Not the creator of the conversation".to_string(),
);
}
for target_id in &payload.users {
let rows_inserted = match conn.execute(
"INSERT INTO conversation_participants (conversation_id, user_id) VALUES (?1, ?2)",
params![payload.conv_id, target_id],
) {
Ok(n) => n,
Err(err) => {
return (StatusCode::INTERNAL_SERVER_ERROR, format!("Err adding user {}: {}", target_id, err));
}
};
if rows_inserted == 0 {
return (StatusCode::NOT_FOUND, format!("Could not add user {}", target_id));
//fix this
let existing: HashSet<u32> = {
let mut stmt = match conn.prepare(
"SELECT user_id FROM conversation_participants WHERE conversation_id = ?1",
) {
Ok(s) => s,
Err(_) => {
return (
StatusCode::INTERNAL_SERVER_ERROR,
"Prepare participants stmt failed".to_string(),
)
}
};
match stmt.query_map(params![payload.conv_id], |row| row.get(0)) {
Ok(rows) => rows.filter_map(Result::ok).collect(),
Err(_) => {
return (
StatusCode::INTERNAL_SERVER_ERROR,
"Query participants failed".to_string(),
)
}
}
}; // ← stmt dropped HERE
let payload_users: HashSet<u32> = payload.users.into_iter().collect();
let to_add: Vec<u32> = payload_users
.difference(&existing)
.copied()
.collect();
let to_remove : Vec<u32> = existing
.difference(&payload_users)
.copied()
.collect();
let tx = match conn.transaction() {
Ok(t) => t,
Err(_) => {
return (
StatusCode::INTERNAL_SERVER_ERROR,
"Transaction start failed for update conv participants".to_string(),
)
}
};
for user_id in to_add {
if let Err(err) = tx.execute(
"INSERT INTO conversation_participants (conversation_id, user_id)
VALUES (?1, ?2)",
params![payload.conv_id, user_id],
) {
return (
StatusCode::INTERNAL_SERVER_ERROR,
format!("Insert failed for {}: {}", user_id, err),
);
}
}
for user_id in to_remove {
if let Err(err) = tx.execute(
"DELETE FROM conversation_participants
WHERE conversation_id = ?1 AND user_id = ?2",
params![payload.conv_id, user_id],
) {
return (
StatusCode::INTERNAL_SERVER_ERROR,
format!("Delete failed for {}: {}", user_id, err),
);
}
}
if let Err(_) = tx.commit() {
return (
StatusCode::INTERNAL_SERVER_ERROR,
"Transaction commit failed".to_string(),
);
}
return (StatusCode::OK, "ok".to_string());
}
/*
#[derive(Deserialize, Serialize, Debug)]
pub struct Get
*/
pub async fn get_conv_users(
State(state): State<AppState>,
AuthClaims{user_id, hotel_id}: AuthClaims,
Path(conv_id): Path<(i32)>,
) -> impl IntoResponse {
let pool = state.hotel_pools.get_pool(hotel_id);
let conn = match pool.get (){
Ok(c)=> c,
Err(err)=> return (StatusCode::INTERNAL_SERVER_ERROR, format!("Error opening pol connection : {err}") )
};
let mut stmt = match conn.prepare(
"SELECT conversation_id, name FROM conversation_participants WHERE user_id = ?1",
) {
Ok(s) => s,
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Prepare failed: {}", e)),
};
//fix this
let existing: HashSet<u32> = {
let mut stmt = match conn.prepare(
"SELECT user_id FROM conversation_participants WHERE conversation_id = ?1",
) {
Ok(s) => s,
Err(_) => {
return (
StatusCode::INTERNAL_SERVER_ERROR,
"Prepare participants stmt failed".to_string(),
)
}
};
match stmt.query_map(params![conv_id], |row| row.get(0)) {
Ok(rows) => rows.filter_map(Result::ok).collect(),
Err(_) => {
return (
StatusCode::INTERNAL_SERVER_ERROR,
"Query participants failed".to_string(),
)
}
}
}; // ← stmt dropped HERE
let present: Vec<u32> = existing
.into_iter()
.collect();
match serde_json::to_string(&present) {
Ok(json) => (StatusCode::OK, json),
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("Serialization failed: {}", e)),
}
}
pub async fn send_message(
State(state): State<AppState>,
AuthClaims {user_id, hotel_id}: AuthClaims,
SendMessagePayload(payload):SendMessagePayload
) -> impl IntoResponse {
//TODO: make sur the convid is valid
let pool = state.hotel_pools.get_pool(hotel_id);
let conn = match pool.get(){

View File

@@ -19,7 +19,7 @@ pub fn chat_routes() -> Router<AppState> {
.route("/add_users_conv", put(add_user_to_conv))
.route("/send_message", post(send_message))
.route("/get_conv", post(get_convs))
.route("/get_message", get(get_message))
.route("/get_message", post(get_message))
.route("/hotel_users", post(get_hotel_users))
.route("/get_conv_users/{conv_id}", post(get_conv_users))
}

View File

@@ -1,6 +1,7 @@
use axum::serve;
use axum::Extension;
use axum::extract::{ws::{Message, WebSocket, WebSocketUpgrade}, State};
use jsonwebtoken::{DecodingKey, EncodingKey};
use reqwest::header::AUTHORIZATION;
use reqwest::header::CONTENT_TYPE;
@@ -25,8 +26,8 @@ use crate::utils::auth::JwtKeys;
use std::env;
use dotenvy::dotenv;
use tower_http::cors::{CorsLayer, Any};
//use tower_http::cors::Origin;
use tower_http::cors::{CorsLayer, Any,};
use axum::http::{Method, HeaderValue};
pub async fn notify_discord(msg: &str) -> Result<(), reqwest::Error> {
@@ -91,13 +92,15 @@ std::panic::set_hook(Box::new(|info| {
decoding: DecodingKey::from_secret(jwt_secret.as_ref()),
};
let allowed_origins = vec![
"http://82.66.253.209",
"http://localhost:5173",
];
let cors = CorsLayer::new()
.allow_origin("http://localhost:5173".parse::<HeaderValue>().unwrap())
.allow_credentials(true)
.allow_methods([Method::GET, Method::POST, Method::PUT , Method::OPTIONS])
.allow_headers([CONTENT_TYPE, AUTHORIZATION]);
let cors = CorsLayer::very_permissive()
.allow_credentials(true)
.allow_methods([Method::GET, Method::POST, Method::PUT, Method::OPTIONS])
.allow_headers([CONTENT_TYPE, AUTHORIZATION]);
let app = create_router(state)
.layer(Extension(jwt_keys))
.layer(cors);

View File

@@ -332,6 +332,7 @@ struct LoginResponse {
#[derive(Serialize)]
struct MultiLoginResponse {
user_id: i32,
tokens: Vec<String>,
}
@@ -674,7 +675,7 @@ pub async fn login_refresh_token (
}
//Json(tokens).into_response()
Json(MultiLoginResponse { tokens }).into_response()
Json(MultiLoginResponse { user_id, tokens }).into_response()
}