This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"todo-tree.tree.showBadges": true
|
||||
}
|
||||
14
.vscode/tasks.json
vendored
14
.vscode/tasks.json
vendored
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "cargo",
|
||||
"command": "build",
|
||||
"problemMatcher": [
|
||||
"$rustc"
|
||||
],
|
||||
"group": "build",
|
||||
"label": "rust: cargo build"
|
||||
}
|
||||
]
|
||||
}
|
||||
23
Dockerfile
23
Dockerfile
@@ -1,23 +0,0 @@
|
||||
FROM rust:latest AS builder
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN cargo build --release
|
||||
|
||||
FROM debian:bookworm-slim
|
||||
|
||||
# Create the app user with UID 1001
|
||||
RUN useradd -u 1001 -m appuser
|
||||
|
||||
# Create working directory (only needed if your app expects /app)
|
||||
WORKDIR /app
|
||||
|
||||
# Copy binary from builder
|
||||
COPY --from=builder /app/target/release/hotel-api-rs /usr/local/bin/hotel-api-rs
|
||||
|
||||
# Switch to non-root user
|
||||
USER 1001
|
||||
|
||||
# Expose API port
|
||||
EXPOSE 8080
|
||||
|
||||
CMD ["/usr/local/bin/hotel-api-rs"]
|
||||
37
authbruh.rs
37
authbruh.rs
@@ -1,37 +0,0 @@
|
||||
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 clean_auth_loging(
|
||||
State(state): State<AppState>,
|
||||
// LoginPayload(payload): LoginPayload,
|
||||
) -> impl IntoResponse {
|
||||
// You can access state.pool and state.jwt_secret here
|
||||
format!("Got secret: {}", state.jwt_secret)
|
||||
}
|
||||
|
||||
|
||||
fn internal_error<E: std::fmt::Display>(err: E) -> (StatusCode, String) {
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, format!("Internal error: {}", err))
|
||||
}
|
||||
|
||||
|
||||
BIN
db/1.sqlite
BIN
db/1.sqlite
Binary file not shown.
BIN
db/1.sqlite-shm
BIN
db/1.sqlite-shm
Binary file not shown.
BIN
db/1.sqlite-wal
BIN
db/1.sqlite-wal
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,51 +0,0 @@
|
||||
CREATE TABLE rooms (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
number TEXT NOT NULL UNIQUE,
|
||||
status TEXT NOT NULL DEFAULT 'clean'
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "conversation" (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL,
|
||||
`creator_id` INTEGER,
|
||||
`title` TEXT,
|
||||
`created_at` TEXT DEFAULT (CURRENT_TIMESTAMP)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "message" (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`conversation_id` INTEGER REFERENCES `conversation`(`id`),
|
||||
`sender_id` INTEGER NOT NULL, `content` TEXT NOT NULL,
|
||||
`sent_at` TEXT DEFAULT (CURRENT_TIMESTAMP)
|
||||
);
|
||||
|
||||
CREATE TABLE "conversation_participants" (
|
||||
`conversation_id` INTEGER NOT NULL REFERENCES `conversation`(`id`),
|
||||
`user_id` INTEGER NOT NULL,
|
||||
`joined_at` TEXT DEFAULT (CURRENT_TIMESTAMP)
|
||||
PRIMARY KEY (conversation_id, user_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "inventory" (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`amount` INTEGER,
|
||||
`item_name` TEXT,
|
||||
`user_id` INT,
|
||||
`updated_at` TEXT DEFAULT (CURRENT_TIMESTAMP)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "room_history" (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`room_id` INTEGER NOT NULL REFERENCES `rooms`(`id`),
|
||||
`room_number` TEXT NOT NULL,
|
||||
`status` TEXT,
|
||||
`updated_at` TEXT DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "inventory_history" (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL,
|
||||
`item_id` INTEGER NOT NULL REFERENCES `inventory`(`id`),
|
||||
`amount` INTEGER NOT NULL,
|
||||
`item_name` TEXT,
|
||||
`user_id` INT,
|
||||
`updated_at` TEXT DEFAULT (CURRENT_TIMESTAMP));
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
use axum::{
|
||||
extract::{
|
||||
FromRequest, Request
|
||||
},
|
||||
http::StatusCode,
|
||||
Json,
|
||||
extract::{FromRequest, Request},
|
||||
http::StatusCode,
|
||||
};
|
||||
use chrono::NaiveDateTime;
|
||||
use serde::Deserialize;
|
||||
|
||||
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct CreateConversationValues{
|
||||
pub struct CreateConversationValues {
|
||||
//pub creator_id: i32, // already in token ?
|
||||
pub name: String,
|
||||
}
|
||||
@@ -19,7 +15,8 @@ pub struct CreateConversationValues{
|
||||
pub struct CreateConversationPayload(pub CreateConversationValues);
|
||||
|
||||
impl<S> FromRequest<S> for CreateConversationPayload
|
||||
where S: Send + Sync,
|
||||
where
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = (StatusCode, String);
|
||||
|
||||
@@ -27,23 +24,22 @@ where S: Send + Sync,
|
||||
let Json(payload) = Json::<CreateConversationValues>::from_request(req, state)
|
||||
.await
|
||||
.map_err(|err| (StatusCode::BAD_REQUEST, format!("Invalid body: {}", err)))?;
|
||||
|
||||
|
||||
Ok(CreateConversationPayload(payload))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct AddUserConversationValues{
|
||||
pub struct AddUserConversationValues {
|
||||
pub conv_id: u32,
|
||||
pub users: Vec<u32>
|
||||
pub users: Vec<u32>,
|
||||
}
|
||||
|
||||
pub struct AddUserConversationPayload(pub AddUserConversationValues);
|
||||
|
||||
impl<S> FromRequest<S> for AddUserConversationPayload
|
||||
where S: Send + Sync,
|
||||
where
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = (StatusCode, String);
|
||||
|
||||
@@ -51,14 +47,13 @@ where S: Send + Sync,
|
||||
let Json(payload) = Json::<AddUserConversationValues>::from_request(req, state)
|
||||
.await
|
||||
.map_err(|err| (StatusCode::BAD_REQUEST, format!("Invalid body: {}", err)))?;
|
||||
|
||||
|
||||
Ok(AddUserConversationPayload(payload))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct SendMessageValues{
|
||||
pub struct SendMessageValues {
|
||||
pub conv_id: u32,
|
||||
pub message: String,
|
||||
}
|
||||
@@ -66,7 +61,8 @@ pub struct SendMessageValues{
|
||||
pub struct SendMessagePayload(pub SendMessageValues);
|
||||
|
||||
impl<S> FromRequest<S> for SendMessagePayload
|
||||
where S: Send + Sync,
|
||||
where
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = (StatusCode, String);
|
||||
|
||||
@@ -74,23 +70,22 @@ where S: Send + Sync,
|
||||
let Json(payload) = Json::<SendMessageValues>::from_request(req, state)
|
||||
.await
|
||||
.map_err(|err| (StatusCode::BAD_REQUEST, format!("Invalid body: {}", err)))?;
|
||||
|
||||
|
||||
Ok(SendMessagePayload(payload))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct GetMessagesValues{
|
||||
pub struct GetMessagesValues {
|
||||
pub conv_id: u32,
|
||||
pub timestamp :Option<String>,
|
||||
pub timestamp: Option<String>,
|
||||
}
|
||||
|
||||
pub struct GetMessagesPayload(pub GetMessagesValues);
|
||||
|
||||
impl<S> FromRequest<S> for GetMessagesPayload
|
||||
where S: Send + Sync,
|
||||
where
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = (StatusCode, String);
|
||||
|
||||
@@ -98,9 +93,7 @@ where S: Send + Sync,
|
||||
let Json(payload) = Json::<GetMessagesValues>::from_request(req, state)
|
||||
.await
|
||||
.map_err(|err| (StatusCode::BAD_REQUEST, format!("Invalid body: {}", err)))?;
|
||||
|
||||
|
||||
Ok(GetMessagesPayload(payload))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,40 +1,35 @@
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use axum::{
|
||||
Json, extract::{
|
||||
FromRequest,FromRequestParts, State, Path,
|
||||
|
||||
}, http::StatusCode, response::{IntoResponse, sse::KeepAlive}
|
||||
|
||||
Json,
|
||||
extract::{FromRequest, FromRequestParts, Path, State},
|
||||
http::StatusCode,
|
||||
response::{IntoResponse, sse::KeepAlive},
|
||||
};
|
||||
//use axum::extract::ws::Message;
|
||||
use chrono::NaiveDateTime;
|
||||
use rusqlite::{Name, Statement, params};
|
||||
use rusqlite::OptionalExtension;
|
||||
use rusqlite::{Name, Statement, params};
|
||||
use serde::Serialize;
|
||||
use serde_json::{json, to_value};
|
||||
|
||||
use crate::chat::extractor::{
|
||||
AddUserConversationPayload, CreateConversationPayload, GetMessagesPayload, SendMessagePayload
|
||||
AddUserConversationPayload, CreateConversationPayload, GetMessagesPayload, SendMessagePayload,
|
||||
};
|
||||
use crate::utils::db_pool::{AppState};
|
||||
use crate::utils::auth::AuthClaims;
|
||||
|
||||
use crate::utils::db_pool::AppState;
|
||||
|
||||
//TODO: update conversation title
|
||||
//FIXME: make a default title if empty
|
||||
//FIXME: make a default title if empty
|
||||
pub async fn create_conversation(
|
||||
State(state): State<AppState>,
|
||||
AuthClaims {user_id, hotel_id}: AuthClaims,
|
||||
CreateConversationPayload(payload): CreateConversationPayload
|
||||
AuthClaims { user_id, hotel_id }: AuthClaims,
|
||||
CreateConversationPayload(payload): CreateConversationPayload,
|
||||
) -> impl IntoResponse {
|
||||
|
||||
let pool = state.hotel_pools.get_pool(hotel_id);
|
||||
|
||||
let conn = match pool.get(){
|
||||
let conn = match pool.get() {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error"))
|
||||
Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error")),
|
||||
};
|
||||
|
||||
let result = conn.execute(
|
||||
@@ -47,7 +42,7 @@ pub async fn create_conversation(
|
||||
Err(err) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Error when creating the conversation: {err}")
|
||||
format!("Error when creating the conversation: {err}"),
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -55,62 +50,67 @@ pub async fn create_conversation(
|
||||
if rows == 0 {
|
||||
return (
|
||||
StatusCode::NOT_FOUND,
|
||||
"not able to create the conversation".to_string()
|
||||
"not able to create the conversation".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
let conv_id = conn.last_insert_rowid();
|
||||
|
||||
let user_conv_rel = conn.execute(
|
||||
"INSERT INTO conversation_participants (conversation_id, user_id, name)
|
||||
VALUES (?1, ?2, ?3)",
|
||||
params![conv_id, user_id, payload.name]
|
||||
params![conv_id, user_id, payload.name],
|
||||
);
|
||||
|
||||
match user_conv_rel {
|
||||
Ok(r) => return (StatusCode::OK, format!("Created conversation {}", payload.name)),
|
||||
Err(err) => return (
|
||||
Ok(r) => {
|
||||
return (
|
||||
StatusCode::OK,
|
||||
format!("Created conversation {}", payload.name),
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Error when creating the conversation: {err}")
|
||||
)
|
||||
format!("Error when creating the conversation: {err}"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// extra logic here (more queries, validations, etc.)
|
||||
|
||||
//(StatusCode::OK, format!("Created conversation {}", payload.name))
|
||||
|
||||
}
|
||||
|
||||
//FIXME: add title to conv
|
||||
pub async fn add_user_to_conv(
|
||||
State(state): State<AppState>,
|
||||
AuthClaims {user_id, hotel_id}: AuthClaims,
|
||||
AddUserConversationPayload(payload):AddUserConversationPayload
|
||||
AuthClaims { user_id, hotel_id }: AuthClaims,
|
||||
AddUserConversationPayload(payload): AddUserConversationPayload,
|
||||
) -> impl IntoResponse {
|
||||
|
||||
let pool = state.hotel_pools.get_pool(hotel_id);
|
||||
|
||||
let mut conn = match pool.get(){
|
||||
let mut conn = match pool.get() {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error"))
|
||||
Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error")),
|
||||
};
|
||||
|
||||
let creator_name: Option<String> = match conn
|
||||
.query_row(
|
||||
"SELECT name FROM conversation WHERE creator_id = ?1 AND id = ?2",
|
||||
params![user_id, payload.conv_id],
|
||||
|row| row.get(0),
|
||||
)
|
||||
.optional() {
|
||||
Ok(name) => name,
|
||||
//Ok(None) => false,
|
||||
Err(_) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"Creator check failed".to_string(),
|
||||
.query_row(
|
||||
"SELECT name FROM conversation WHERE creator_id = ?1 AND id = ?2",
|
||||
params![user_id, payload.conv_id],
|
||||
|row| row.get(0),
|
||||
)
|
||||
}
|
||||
.optional()
|
||||
{
|
||||
Ok(name) => name,
|
||||
//Ok(None) => false,
|
||||
Err(_) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"Creator check failed".to_string(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let is_creator = creator_name.is_some();
|
||||
@@ -122,42 +122,36 @@ pub async fn add_user_to_conv(
|
||||
);
|
||||
}
|
||||
|
||||
//fix this
|
||||
//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(),
|
||||
)
|
||||
}
|
||||
};
|
||||
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(),
|
||||
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_add: Vec<u32> = payload_users.difference(&existing).copied().collect();
|
||||
|
||||
let to_remove : Vec<u32> = existing
|
||||
.difference(&payload_users)
|
||||
.copied()
|
||||
.collect();
|
||||
let to_remove: Vec<u32> = existing.difference(&payload_users).copied().collect();
|
||||
|
||||
let tx = match conn.transaction() {
|
||||
Ok(t) => t,
|
||||
@@ -165,11 +159,11 @@ pub async fn add_user_to_conv(
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"Transaction start failed for update conv participants".to_string(),
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
for user_id in to_add {
|
||||
for user_id in to_add {
|
||||
if let Err(err) = tx.execute(
|
||||
"INSERT INTO conversation_participants (conversation_id, user_id, name)
|
||||
VALUES (?1, ?2, ?3)",
|
||||
@@ -202,8 +196,7 @@ for user_id in to_add {
|
||||
);
|
||||
}
|
||||
|
||||
return (StatusCode::OK, "ok".to_string());
|
||||
|
||||
return (StatusCode::OK, "ok".to_string());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -214,36 +207,44 @@ pub struct Get
|
||||
|
||||
pub async fn get_conv_users(
|
||||
State(state): State<AppState>,
|
||||
AuthClaims{user_id, hotel_id}: AuthClaims,
|
||||
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 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",
|
||||
) {
|
||||
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)),
|
||||
Err(e) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Prepare failed: {}", e),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
//fix this
|
||||
//fix this
|
||||
let existing: HashSet<u32> = {
|
||||
let mut stmt = match conn.prepare(
|
||||
"SELECT user_id FROM conversation_participants WHERE conversation_id = ?1",
|
||||
) {
|
||||
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(),
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -253,113 +254,123 @@ pub async fn get_conv_users(
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"Query participants failed".to_string(),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}; // ← stmt dropped HERE
|
||||
|
||||
let present: Vec<u32> = existing
|
||||
.into_iter()
|
||||
.collect();
|
||||
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)),
|
||||
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
|
||||
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(){
|
||||
let conn = match pool.get() {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error"))
|
||||
Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error")),
|
||||
};
|
||||
|
||||
let mut statement = match conn.prepare(
|
||||
"SELECT 1 FROM conversation_participants WHERE user_id = ?1 AND conversation_id = ?2" ,
|
||||
){
|
||||
"SELECT 1 FROM conversation_participants WHERE user_id = ?1 AND conversation_id = ?2",
|
||||
) {
|
||||
Ok(statement) => statement,
|
||||
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "prepare failed".to_string())
|
||||
Err(_) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"prepare failed".to_string(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
match statement.exists(params![user_id, payload.conv_id]) {
|
||||
Ok(true) => {
|
||||
// user is part of the conversation — continue
|
||||
}
|
||||
Ok(false) => {
|
||||
// early exit: not part of the conversation
|
||||
return (StatusCode::FORBIDDEN, "Not part of the conversation".to_string());
|
||||
}
|
||||
Err(_) => {
|
||||
// query failed
|
||||
return (StatusCode::INTERNAL_SERVER_ERROR, "Query failed".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let (message_id, sent_at): (i64, String) = match conn.query_row(
|
||||
"INSERT INTO message (sender_id, content, conversation_id)
|
||||
VALUES (?1, ?2, ?3)
|
||||
RETURNING id, sent_at",
|
||||
params![user_id, payload.message, payload.conv_id],
|
||||
|row| Ok((
|
||||
row.get::<_, i64>(0)?,
|
||||
row.get::<_, String>(1)?,
|
||||
)),
|
||||
) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("DB insert failed: {err}"),
|
||||
);
|
||||
}
|
||||
};
|
||||
//let message_id = conn.last_insert_rowid();
|
||||
// FIXME: add sent_at and message id in the response.
|
||||
|
||||
|
||||
// --- send to conversation participants ---
|
||||
let mut stmt_participants = conn
|
||||
.prepare("SELECT user_id FROM conversation_participants WHERE conversation_id = ?1")
|
||||
.expect("prepare participants failed");
|
||||
|
||||
let participant_ids: Vec<i32> = stmt_participants
|
||||
.query_map(params![payload.conv_id], |row| row.get(0))
|
||||
.expect("query_map failed")
|
||||
.filter_map(Result::ok)
|
||||
.collect();
|
||||
|
||||
if let Some(hotel_users) = state.ws_map.get(&hotel_id) {
|
||||
let update_msg = serde_json::json!({
|
||||
"event_type": "chat_message",
|
||||
"conv_id": payload.conv_id,
|
||||
"sender_id": user_id,
|
||||
"content": payload.message,
|
||||
"id": message_id,
|
||||
"sent_at": sent_at,
|
||||
})
|
||||
.to_string();
|
||||
|
||||
for uid in &participant_ids {
|
||||
if let Some(sender) = hotel_users.get(uid) {
|
||||
let _ = sender.send(
|
||||
axum::extract::ws::Message::Text(update_msg.clone().into())
|
||||
Ok(true) => {
|
||||
// user is part of the conversation — continue
|
||||
}
|
||||
Ok(false) => {
|
||||
// early exit: not part of the conversation
|
||||
return (
|
||||
StatusCode::FORBIDDEN,
|
||||
"Not part of the conversation".to_string(),
|
||||
);
|
||||
}
|
||||
Err(_) => {
|
||||
// query failed
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"Query failed".to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(StatusCode::OK, format!("sent message: {}, to users:{:?}", payload.message, participant_ids))
|
||||
let (message_id, sent_at): (i64, String) = match conn.query_row(
|
||||
"INSERT INTO message (sender_id, content, conversation_id)
|
||||
VALUES (?1, ?2, ?3)
|
||||
RETURNING id, sent_at",
|
||||
params![user_id, payload.message, payload.conv_id],
|
||||
|row| Ok((row.get::<_, i64>(0)?, row.get::<_, String>(1)?)),
|
||||
) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("DB insert failed: {err}"),
|
||||
);
|
||||
}
|
||||
//Ok(_) => (StatusCode::NOT_FOUND, "Conversation not found".to_string()),
|
||||
};
|
||||
//let message_id = conn.last_insert_rowid();
|
||||
// FIXME: add sent_at and message id in the response.
|
||||
|
||||
// --- send to conversation participants ---
|
||||
let mut stmt_participants = conn
|
||||
.prepare("SELECT user_id FROM conversation_participants WHERE conversation_id = ?1")
|
||||
.expect("prepare participants failed");
|
||||
|
||||
let participant_ids: Vec<i32> = stmt_participants
|
||||
.query_map(params![payload.conv_id], |row| row.get(0))
|
||||
.expect("query_map failed")
|
||||
.filter_map(Result::ok)
|
||||
.collect();
|
||||
|
||||
if let Some(hotel_users) = state.ws_map.get(&hotel_id) {
|
||||
let update_msg = serde_json::json!({
|
||||
"event_type": "chat_message",
|
||||
"conv_id": payload.conv_id,
|
||||
"sender_id": user_id,
|
||||
"content": payload.message,
|
||||
"id": message_id,
|
||||
"sent_at": sent_at,
|
||||
})
|
||||
.to_string();
|
||||
|
||||
for uid in &participant_ids {
|
||||
if let Some(sender) = hotel_users.get(uid) {
|
||||
let _ = sender.send(axum::extract::ws::Message::Text(update_msg.clone().into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
StatusCode::OK,
|
||||
format!(
|
||||
"sent message: {}, to users:{:?}",
|
||||
payload.message, participant_ids
|
||||
),
|
||||
)
|
||||
}
|
||||
//Ok(_) => (StatusCode::NOT_FOUND, "Conversation not found".to_string()),
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct Message {
|
||||
@@ -369,16 +380,15 @@ struct Message {
|
||||
sent_at: String,
|
||||
}
|
||||
|
||||
|
||||
pub async fn get_message(
|
||||
State(state): State<AppState>,
|
||||
AuthClaims {user_id, hotel_id}: AuthClaims,
|
||||
GetMessagesPayload(payload):GetMessagesPayload
|
||||
AuthClaims { user_id, hotel_id }: AuthClaims,
|
||||
GetMessagesPayload(payload): GetMessagesPayload,
|
||||
) -> Result<impl IntoResponse, (StatusCode, String)> {
|
||||
|
||||
let pool = state.hotel_pools.get_pool(hotel_id);
|
||||
|
||||
let conn = pool.get()
|
||||
let conn = pool
|
||||
.get()
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Pool error".to_string()))?;
|
||||
|
||||
let from_time = match payload.timestamp.as_deref() {
|
||||
@@ -386,39 +396,51 @@ pub async fn get_message(
|
||||
Some(ts) => ts,
|
||||
};
|
||||
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT id, sender_id, content, sent_at
|
||||
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()))?;
|
||||
LIMIT 50",
|
||||
)
|
||||
.map_err(|_| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"Prepare failed".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
|
||||
let messages = stmt.query_map(
|
||||
params![payload.conv_id, from_time],
|
||||
|row| {
|
||||
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()))?;
|
||||
})
|
||||
.map_err(|_| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"Query failed".to_string(),
|
||||
)
|
||||
})?
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|_| {
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"Collect failed".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let response = serde_json::to_string(&messages)
|
||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Serialisation failed"));
|
||||
|
||||
Ok((StatusCode::OK, response ))
|
||||
|
||||
Ok((StatusCode::OK, response))
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct User {
|
||||
id: i32,
|
||||
@@ -426,22 +448,30 @@ struct User {
|
||||
//display_name: String,
|
||||
}
|
||||
|
||||
|
||||
pub async fn get_hotel_users(
|
||||
State(state): State<AppState>,
|
||||
AuthClaims { hotel_id, .. }: AuthClaims,
|
||||
) -> impl IntoResponse {
|
||||
let conn = match state.logs_pool.get() {
|
||||
Ok(c) => c,
|
||||
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB connection error".to_string()),
|
||||
Err(_) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"DB connection error".to_string(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let mut stmt = match conn.prepare(
|
||||
"SELECT user_id, username FROM hotel_user_link WHERE hotel_id = ?1",
|
||||
) {
|
||||
Ok(s) => s,
|
||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Prepare failed: {}", e)),
|
||||
};
|
||||
let mut stmt =
|
||||
match conn.prepare("SELECT user_id, username FROM hotel_user_link WHERE hotel_id = ?1") {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Prepare failed: {}", e),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let users_iter = match stmt.query_map(params![hotel_id], |row| {
|
||||
Ok(User {
|
||||
@@ -451,17 +481,30 @@ pub async fn get_hotel_users(
|
||||
})
|
||||
}) {
|
||||
Ok(iter) => iter,
|
||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Query failed: {}", e)),
|
||||
Err(e) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Query failed: {}", e),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let users: Vec<User> = match users_iter.collect::<Result<Vec<_>, _>>() {
|
||||
Ok(u) => u,
|
||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Collect failed: {}", e)),
|
||||
Err(e) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Collect failed: {}", e),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
match serde_json::to_string(&users) {
|
||||
Ok(json) => (StatusCode::OK, json),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("Serialization failed: {}", e)),
|
||||
Err(e) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Serialization failed: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,54 +518,71 @@ struct Conversation {
|
||||
pub async fn get_convs(
|
||||
State(state): State<AppState>,
|
||||
//Path((item_name, item_amount)): Path<(String, i32)>,
|
||||
AuthClaims{ user_id, hotel_id}: AuthClaims,
|
||||
AuthClaims { user_id, hotel_id }: AuthClaims,
|
||||
) -> impl IntoResponse {
|
||||
|
||||
let pool = state.hotel_pools.get_pool(hotel_id);
|
||||
|
||||
let conn = match pool.get(){
|
||||
let conn = match pool.get() {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error: {err}")),
|
||||
Err(err) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Pool error: {err}"),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let mut stmt = match conn.prepare(
|
||||
"SELECT conversation_id, name FROM conversation_participants WHERE user_id = ?1",
|
||||
) {
|
||||
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)),
|
||||
Err(e) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Prepare failed: {}", e),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let rows = match stmt.query_map(params![user_id], |row| {
|
||||
let conversation_id: i32 = row.get(0)?;
|
||||
let name: String = row.get(1)?;
|
||||
Ok(Conversation {
|
||||
id: row.get(0)?,
|
||||
title: row.get(1)?,
|
||||
})
|
||||
id: row.get(0)?,
|
||||
title: row.get(1)?,
|
||||
})
|
||||
}) {
|
||||
Ok(rows) => rows,
|
||||
//Ok(_) => {}, IMPLEMENT NO CONV ?
|
||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Query failed: {}", e)),
|
||||
Err(e) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Query failed: {}", e),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let convs: Vec<Conversation> = match rows.collect::<Result<Vec<_>, _>>() {
|
||||
Ok(u) => u,
|
||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Collect failed: {}", e)),
|
||||
Err(e) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Collect failed: {}", e),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
//.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, Json("error".to_string())));
|
||||
|
||||
match serde_json::to_string(&convs) {
|
||||
Ok(json) => (StatusCode::OK, json),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("Serialization failed: {}", e)),
|
||||
Err(e) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Serialization failed: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
pub async fn get_convs(
|
||||
State(state): State<AppState>,
|
||||
@@ -535,18 +595,18 @@ pub async fn get_convs(
|
||||
let conn = match pool.get(){
|
||||
Ok(conn) => conn,
|
||||
Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error: {}", err).into_response() )
|
||||
|
||||
|
||||
};
|
||||
|
||||
let mut stmt = match conn.prepare(
|
||||
"SELECT id, title FROM conversation WHERE creator_id = ?1",
|
||||
) {
|
||||
Ok(s) => s,
|
||||
Err(e) =>
|
||||
|
||||
Err(e) =>
|
||||
|
||||
return (StatusCode::INTERNAL_SERVER_ERROR, format!("Prepare failed: {}", e).into_response() )
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
let rows = match stmt.query_map(params![user_id], |row| {
|
||||
@@ -556,10 +616,10 @@ pub async fn get_convs(
|
||||
}) {
|
||||
Ok(rows) => rows,
|
||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Query failed: {}", e).into_response() )
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
let mut map = HashMap::new();
|
||||
|
||||
// ✅ Iterate through the row results
|
||||
@@ -569,7 +629,7 @@ pub async fn get_convs(
|
||||
map.insert(title, id);
|
||||
}
|
||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Row parsing failed: {}", e).into_response() )
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -578,7 +638,7 @@ pub async fn get_convs(
|
||||
Ok(c) => c,
|
||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("List unwrapping failed: {}", e).into_response() )
|
||||
};
|
||||
|
||||
|
||||
let conv_map_clean_json = serde_json::to_value(map)
|
||||
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Serialization failed: {}", e).into_response() ));
|
||||
|
||||
@@ -587,4 +647,4 @@ pub async fn get_convs(
|
||||
(StatusCode::OK, Json(conv_map_clean_json)).into_response()
|
||||
}
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod routes;
|
||||
|
||||
|
||||
mod extractor;
|
||||
mod handlers;
|
||||
mod handlers;
|
||||
|
||||
@@ -1,25 +1,15 @@
|
||||
use axum::{
|
||||
Router,
|
||||
routing::put,
|
||||
routing::post,
|
||||
routing::get,
|
||||
};
|
||||
use axum::{Router, routing::get, routing::post, routing::put};
|
||||
|
||||
use crate::utils::db_pool::AppState;
|
||||
use crate::chat::handlers::*;
|
||||
|
||||
|
||||
|
||||
|
||||
use crate::utils::db_pool::AppState;
|
||||
|
||||
pub fn chat_routes() -> Router<AppState> {
|
||||
|
||||
Router::new()
|
||||
.route("/create_conversation", post (create_conversation))
|
||||
.route("/create_conversation", post(create_conversation))
|
||||
.route("/add_users_conv", put(add_user_to_conv))
|
||||
.route("/send_message", post(send_message))
|
||||
.route("/get_conv", post(get_convs))
|
||||
.route("/get_message", post(get_message))
|
||||
.route("/hotel_users", post(get_hotel_users))
|
||||
.route("/get_conv_users/{conv_id}", post(get_conv_users))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,48 @@
|
||||
|
||||
use argon2::Params;
|
||||
use axum::{extract::{ws::{close_code::STATUS, Message}, Path, State}, http::StatusCode, response::IntoResponse};
|
||||
use axum::{
|
||||
extract::{
|
||||
Path, State,
|
||||
ws::{Message, close_code::STATUS},
|
||||
},
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
};
|
||||
use rusqlite::params;
|
||||
use serde::Serialize;
|
||||
use serde_json::json;
|
||||
|
||||
|
||||
|
||||
use crate::utils::{auth::AuthClaims, db_pool::AppState};
|
||||
|
||||
|
||||
|
||||
pub async fn create_inventory_item(
|
||||
State(state): State<AppState>,
|
||||
Path((item_name, item_amount)): Path<(String, i32)>,
|
||||
AuthClaims{ user_id, hotel_id}: AuthClaims,
|
||||
AuthClaims { user_id, hotel_id }: AuthClaims,
|
||||
) -> impl IntoResponse {
|
||||
|
||||
let pool = state.hotel_pools.get_pool(hotel_id);
|
||||
|
||||
let conn = match pool.get(){
|
||||
let conn = match pool.get() {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't open the connection"))
|
||||
Err(err) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("couldn't open the connection"),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let result = conn.execute(
|
||||
"INSERT INTO inventory (item_name, amount, user_id) VALUES (?1, ?2, ?3)",
|
||||
params![&item_name,&item_amount,&user_id]
|
||||
params![&item_name, &item_amount, &user_id],
|
||||
);
|
||||
match result {
|
||||
Ok(rows) => (StatusCode::OK, format!("inserted item {item_name}, with {item_amount} amount")),
|
||||
Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't add the new item, err: {}", err ))
|
||||
Ok(rows) => (
|
||||
StatusCode::OK,
|
||||
format!("inserted item {item_name}, with {item_amount} amount"),
|
||||
),
|
||||
Err(err) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("couldn't add the new item, err: {}", err),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +51,7 @@ pub struct InventoryItems {
|
||||
id: i32,
|
||||
amount: i32,
|
||||
name: String,
|
||||
user_id: i32,
|
||||
user_id: i32,
|
||||
updated_at: String,
|
||||
}
|
||||
|
||||
@@ -48,16 +60,20 @@ pub async fn update_inventory_item(
|
||||
Path((item_id, item_amount)): Path<(i32, i32)>,
|
||||
AuthClaims { user_id, hotel_id }: AuthClaims,
|
||||
) -> impl IntoResponse {
|
||||
|
||||
//TODO: make better error handling :
|
||||
// if wrong param collumn targeted,
|
||||
// if missing path param
|
||||
// if wrong param collumn targeted,
|
||||
// if missing path param
|
||||
|
||||
let pool = state.hotel_pools.get_pool(hotel_id);
|
||||
|
||||
let conn = match pool.get(){
|
||||
|
||||
let conn = match pool.get() {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error: {err}")),
|
||||
Err(err) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Pool error: {err}"),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let result: Result<String, rusqlite::Error> = conn.query_row(
|
||||
@@ -68,7 +84,7 @@ pub async fn update_inventory_item(
|
||||
|
||||
match result {
|
||||
Ok(item_name) => {
|
||||
if let Err(err) = conn.execute(
|
||||
if let Err(err) = conn.execute(
|
||||
"INSERT INTO inventory_history (item_id, amount, item_name, user_id) VALUES (?1,?2,?3,?4)",
|
||||
params![&item_id,&item_amount,&item_name, &user_id]
|
||||
){
|
||||
@@ -90,30 +106,29 @@ pub async fn update_inventory_item(
|
||||
let _ = sender.send(Message::Text(update_msg.clone().into()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
(StatusCode::OK, format!("updated item history"))
|
||||
}
|
||||
|
||||
Ok(_) => (StatusCode::NOT_FOUND, "No item found, err : {_}".to_string()),
|
||||
Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, format!("Error from DB: {err}")),
|
||||
|
||||
|
||||
Ok(_) => (
|
||||
StatusCode::NOT_FOUND,
|
||||
"No item found, err : {_}".to_string(),
|
||||
),
|
||||
Err(err) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Error from DB: {err}"),
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
match result {
|
||||
Ok(row) => (StatusCode::OK, format!("Items updated")),
|
||||
Ok(_) => (StatusCode::NOT_FOUND, format!("No item with this id exist")),
|
||||
Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, format!("error updating the item with id :{} with amount: {}", item_id, item_amount))
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
match result {
|
||||
Ok(row) => (StatusCode::OK, format!("Items updated")),
|
||||
Ok(_) => (StatusCode::NOT_FOUND, format!("No item with this id exist")),
|
||||
Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, format!("error updating the item with id :{} with amount: {}", item_id, item_amount))
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
pub async fn get_inventory_item(
|
||||
State(state): State<AppState>,
|
||||
AuthClaims { user_id, hotel_id }: AuthClaims,
|
||||
@@ -125,10 +140,16 @@ pub async fn get_inventory_item(
|
||||
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "Pool error".to_string()),
|
||||
};
|
||||
|
||||
let mut stmt = match conn.prepare("SELECT id, amount, item_name, user_id, updated_at FROM inventory") {
|
||||
Ok(s) => s,
|
||||
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "Statement error".to_string()),
|
||||
};
|
||||
let mut stmt =
|
||||
match conn.prepare("SELECT id, amount, item_name, user_id, updated_at FROM inventory") {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"Statement error".to_string(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let mut query_result = match stmt.query([]) {
|
||||
Ok(r) => r,
|
||||
@@ -151,8 +172,13 @@ pub async fn get_inventory_item(
|
||||
// Serialize to JSON
|
||||
let json = match serde_json::to_string(&items) {
|
||||
Ok(j) => j,
|
||||
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "Serialization error".to_string()),
|
||||
Err(_) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"Serialization error".to_string(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
(StatusCode::OK, json)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
mod handler;
|
||||
|
||||
pub mod routes;
|
||||
pub mod routes;
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
use axum::{
|
||||
routing::{get, put, post},
|
||||
Router,
|
||||
routing::{get, post, put},
|
||||
};
|
||||
|
||||
use crate::{inventory::handler::*, utils::db_pool::AppState};
|
||||
|
||||
pub fn inventory_routes() -> Router<AppState> {
|
||||
|
||||
Router::new()
|
||||
.route("/update_item/{item_id}/{item_amount}", put(update_inventory_item))
|
||||
.route("/add_item/{item_name}/{item_amount}", post(create_inventory_item))
|
||||
.route(
|
||||
"/update_item/{item_id}/{item_amount}",
|
||||
put(update_inventory_item),
|
||||
)
|
||||
.route(
|
||||
"/add_item/{item_name}/{item_amount}",
|
||||
post(create_inventory_item),
|
||||
)
|
||||
.route("/get_item/", get(get_inventory_item))
|
||||
}
|
||||
}
|
||||
|
||||
11
src/lib.rs
11
src/lib.rs
@@ -1,8 +1,7 @@
|
||||
#[allow(unused_imports)]
|
||||
#[allow(dead_code)]
|
||||
|
||||
pub mod utils;
|
||||
pub mod routes;
|
||||
pub mod rooms;
|
||||
pub mod chat;
|
||||
pub mod inventory;
|
||||
pub mod rooms;
|
||||
pub mod routes;
|
||||
#[allow(unused_imports)]
|
||||
#[allow(dead_code)]
|
||||
pub mod utils;
|
||||
|
||||
53
src/main.rs
53
src/main.rs
@@ -1,35 +1,37 @@
|
||||
use axum::serve;
|
||||
use axum::Extension;
|
||||
use axum::extract::{ws::{Message, WebSocket, WebSocketUpgrade}, State};
|
||||
use axum::extract::{
|
||||
State,
|
||||
ws::{Message, WebSocket, WebSocketUpgrade},
|
||||
};
|
||||
use axum::serve;
|
||||
|
||||
use jsonwebtoken::{DecodingKey, EncodingKey};
|
||||
use reqwest::header::AUTHORIZATION;
|
||||
use reqwest::header::CONTENT_TYPE;
|
||||
use reqwest::header::USER_AGENT;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use reqwest::Client;
|
||||
|
||||
mod utils;
|
||||
mod routes;
|
||||
mod rooms;
|
||||
|
||||
mod chat;
|
||||
mod inventory;
|
||||
use r2d2::{Pool};
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
mod rooms;
|
||||
mod routes;
|
||||
mod utils;
|
||||
use dashmap::DashMap;
|
||||
use r2d2::Pool;
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::utils::db_pool::{HotelPool,AppState};
|
||||
use routes::create_router;
|
||||
use crate::utils::auth::JwtKeys;
|
||||
use crate::utils::db_pool::{AppState, HotelPool};
|
||||
use routes::create_router;
|
||||
|
||||
use std::env;
|
||||
use dotenvy::dotenv;
|
||||
use std::env;
|
||||
//use tower_http::cors::Origin;
|
||||
use tower_http::cors::{CorsLayer, Any,};
|
||||
use axum::http::{Method, HeaderValue};
|
||||
use axum::http::{HeaderValue, Method};
|
||||
use tower_http::cors::{Any, CorsLayer};
|
||||
|
||||
pub async fn notify_discord(msg: &str) -> Result<(), reqwest::Error> {
|
||||
let payload = serde_json::json!({
|
||||
@@ -45,10 +47,8 @@ pub async fn notify_discord(msg: &str) -> Result<(), reqwest::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[tokio::main(flavor = "multi_thread", worker_threads = 8)]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
|
||||
dotenv().ok();
|
||||
|
||||
std::panic::set_hook(Box::new(|info| {
|
||||
@@ -78,39 +78,34 @@ async fn main() -> std::io::Result<()> {
|
||||
let state = AppState {
|
||||
hotel_pools,
|
||||
logs_pool,
|
||||
ws_map: Arc::new(DashMap::new()),
|
||||
ws_map: Arc::new(DashMap::new()),
|
||||
//jwt_secret: "your_jwt_secret_key s".to_string(), // better: load from env var
|
||||
};
|
||||
|
||||
//let jwt_secret = "your_jwt_secret_key".to_string();
|
||||
|
||||
|
||||
let jwt_secret = env::var("JWT_SECRET")
|
||||
.expect("JWT_SECRET must be set")
|
||||
.to_string();
|
||||
|
||||
|
||||
let jwt_keys = JwtKeys {
|
||||
encoding: EncodingKey::from_secret(jwt_secret.as_ref()),
|
||||
decoding: DecodingKey::from_secret(jwt_secret.as_ref()),
|
||||
};
|
||||
|
||||
let allowed_origins = vec![
|
||||
"http://82.66.253.209",
|
||||
"http://localhost:5173",
|
||||
];
|
||||
let allowed_origins = vec!["http://82.66.253.209", "http://localhost:5173"];
|
||||
|
||||
let cors = CorsLayer::very_permissive()
|
||||
.allow_credentials(true)
|
||||
.allow_methods([Method::GET, Method::POST, Method::PUT, Method::OPTIONS])
|
||||
.allow_headers([CONTENT_TYPE, AUTHORIZATION, USER_AGENT]);
|
||||
let app = create_router(state)
|
||||
.layer(Extension(jwt_keys))
|
||||
.layer(cors);
|
||||
let app = create_router(state).layer(Extension(jwt_keys)).layer(cors);
|
||||
|
||||
let listener = TcpListener::bind("0.0.0.0:7080").await?;
|
||||
serve(listener, app).into_future().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handler() -> &'static str {
|
||||
"Hiii from localhost"
|
||||
}
|
||||
async fn handler() -> &'static str {
|
||||
"Hiii from localhost"
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
#[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,9 +1,8 @@
|
||||
use axum::{
|
||||
extract::{Request, FromRequest, Path},
|
||||
body::{Body},
|
||||
|
||||
http::StatusCode,
|
||||
Json, Router,
|
||||
body::Body,
|
||||
extract::{FromRequest, Path, Request},
|
||||
http::StatusCode,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
@@ -16,7 +15,8 @@ pub struct UpdateRoomPayload(pub UpdateRoomValues);
|
||||
|
||||
//#[async_trait]
|
||||
impl<S> FromRequest<S> for UpdateRoomPayload
|
||||
where S: Send + Sync,
|
||||
where
|
||||
S: Send + Sync,
|
||||
{
|
||||
type Rejection = (StatusCode, String);
|
||||
|
||||
@@ -24,14 +24,14 @@ where S: Send + Sync,
|
||||
let Json(payload) = Json::<UpdateRoomValues>::from_request(req, state)
|
||||
.await
|
||||
.map_err(|err| (StatusCode::BAD_REQUEST, format!("Invalid body: {}", err)))?;
|
||||
|
||||
|
||||
Ok(UpdateRoomPayload(payload))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RoomIdValue {
|
||||
pub room_id: i32
|
||||
pub room_id: i32,
|
||||
}
|
||||
|
||||
pub type RoomIdPath = Path<i32>;
|
||||
pub type RoomIdPath = Path<i32>;
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
use axum::{Json, extract::Path, extract::State };
|
||||
use axum::response::IntoResponse;
|
||||
use axum::http::StatusCode;
|
||||
use axum::extract::ws::Message;
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
use axum::{Json, extract::Path, extract::State};
|
||||
use serde::Serialize;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::rooms::extractor::UpdateRoomPayload;
|
||||
//use crate::utils::db_pool::*;
|
||||
use crate::utils::auth::AuthClaims;
|
||||
use crate::utils::db_pool::{HotelPool,AppState};
|
||||
use crate::utils::db_pool::{AppState, HotelPool};
|
||||
|
||||
use std::sync::Arc;
|
||||
use r2d2::{Pool};
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use dashmap::DashMap;
|
||||
use r2d2::Pool;
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use rusqlite::params;
|
||||
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub async fn hello_rooms() -> String {
|
||||
"hello from rooms".to_string()
|
||||
@@ -28,11 +26,15 @@ pub async fn fake_db_update(
|
||||
Path(room_id): Path<i32>,
|
||||
UpdateRoomPayload(payload): UpdateRoomPayload,
|
||||
) -> impl IntoResponse {
|
||||
|
||||
let pool = state.hotel_pools.get_pool(hotel_id);
|
||||
let conn = match pool.get() {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error: {err}")),
|
||||
Err(err) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Pool error: {err}"),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let result = conn.execute(
|
||||
@@ -41,9 +43,15 @@ pub async fn fake_db_update(
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(rows) if rows > 0 => (StatusCode::OK, format!("Updated room {room_id} in hotel {}", hotel_id)),
|
||||
Ok(rows) if rows > 0 => (
|
||||
StatusCode::OK,
|
||||
format!("Updated room {room_id} in hotel {}", hotel_id),
|
||||
),
|
||||
Ok(_) => (StatusCode::NOT_FOUND, "No room found".to_string()),
|
||||
Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {err}")),
|
||||
Err(err) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("DB error: {err}"),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,16 +61,20 @@ pub async fn clean_db_update(
|
||||
AuthClaims { user_id, hotel_id }: AuthClaims,
|
||||
UpdateRoomPayload(payload): UpdateRoomPayload,
|
||||
) -> impl IntoResponse {
|
||||
|
||||
//TODO: make better error handling :
|
||||
// if wrong param collumn targeted,
|
||||
// if missing path param
|
||||
// if wrong param collumn targeted,
|
||||
// if missing path param
|
||||
|
||||
let pool = state.hotel_pools.get_pool(hotel_id);
|
||||
|
||||
let conn = match pool.get(){
|
||||
|
||||
let conn = match pool.get() {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error: {err}")),
|
||||
Err(err) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Pool error: {err}"),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let result: Result<String, rusqlite::Error> = conn.query_row(
|
||||
@@ -71,7 +83,6 @@ pub async fn clean_db_update(
|
||||
|row| row.get(0),
|
||||
);
|
||||
|
||||
|
||||
match result {
|
||||
Ok(room_number) => {
|
||||
// --- broadcast to all WS clients in the hotel ---
|
||||
@@ -79,7 +90,10 @@ pub async fn clean_db_update(
|
||||
"INSERT INTO room_history (room_id, room_number, status) VALUES (?1, ?2, ?3)",
|
||||
params![&room_id, &room_number, &payload.status],
|
||||
) {
|
||||
return (StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to insert history: {err}"));
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Failed to insert history: {err}"),
|
||||
);
|
||||
}
|
||||
if let Some(hotel_users) = state.ws_map.get(&hotel_id) {
|
||||
let update_msg = json!({
|
||||
@@ -97,10 +111,19 @@ pub async fn clean_db_update(
|
||||
}
|
||||
}
|
||||
|
||||
(StatusCode::OK, format!("updated room {room_id} in hotel {hotel_id}, with status: {}", payload.status))
|
||||
(
|
||||
StatusCode::OK,
|
||||
format!(
|
||||
"updated room {room_id} in hotel {hotel_id}, with status: {}",
|
||||
payload.status
|
||||
),
|
||||
)
|
||||
}
|
||||
Ok(_) => (StatusCode::NOT_FOUND, "No room found".to_string()),
|
||||
Err(err) => (StatusCode::INTERNAL_SERVER_ERROR, format!("Error from DB: {err}")),
|
||||
Err(err) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Error from DB: {err}"),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,50 +138,65 @@ pub async fn get_all_rooms(
|
||||
State(state): State<AppState>,
|
||||
AuthClaims { hotel_id, .. }: AuthClaims,
|
||||
) -> impl IntoResponse {
|
||||
|
||||
let pool = state.hotel_pools.get_pool(hotel_id);
|
||||
|
||||
let conn = match pool.get(){
|
||||
|
||||
let conn = match pool.get() {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Pool error: {err}")),
|
||||
};
|
||||
|
||||
let mut stmt = match conn.prepare(
|
||||
"SELECT id, number, status FROM rooms ",
|
||||
) {
|
||||
Ok(s) => s,
|
||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Prepare failed: {}", e)),
|
||||
};
|
||||
|
||||
let room_iter = match stmt.query_map(
|
||||
params![],|row| {
|
||||
Ok(Room {
|
||||
id: row.get(0)?,
|
||||
number: row.get(1)?,
|
||||
status: row.get(2)?,
|
||||
})
|
||||
Err(err) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Pool error: {err}"),
|
||||
);
|
||||
}
|
||||
) {
|
||||
};
|
||||
|
||||
let mut stmt = match conn.prepare("SELECT id, number, status FROM rooms ") {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Prepare failed: {}", e),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let room_iter = match stmt.query_map(params![], |row| {
|
||||
Ok(Room {
|
||||
id: row.get(0)?,
|
||||
number: row.get(1)?,
|
||||
status: row.get(2)?,
|
||||
})
|
||||
}) {
|
||||
Ok(iter) => iter,
|
||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Query failed: {}", e)),
|
||||
Err(e) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Query failed: {}", e),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let rooms: Vec<Room> = match room_iter.collect::<Result<Vec<_>, _>>() {
|
||||
Ok(u) => u,
|
||||
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Collect failed: {}", e)),
|
||||
Err(e) => {
|
||||
return (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Collect failed: {}", e),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
match serde_json::to_string(&rooms) {
|
||||
Ok(json) => (StatusCode::OK, json),
|
||||
Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("Serialization failed: {}", e)),
|
||||
Err(e) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Serialization failed: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct RoomRequest {
|
||||
item_id: i32,
|
||||
item_amount: i32,
|
||||
token: String,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//pub mod handler;
|
||||
//pub mod routes;
|
||||
|
||||
mod handler;
|
||||
mod extractor;
|
||||
mod handler;
|
||||
pub mod routes;
|
||||
|
||||
@@ -1,21 +1,15 @@
|
||||
use axum::{
|
||||
routing::{get, put,},
|
||||
Router,
|
||||
routing::{get, put},
|
||||
};
|
||||
|
||||
use crate::rooms::handler::*;
|
||||
use crate::utils::db_pool::{
|
||||
HotelPool,
|
||||
AppState,
|
||||
};
|
||||
|
||||
|
||||
use crate::utils::db_pool::{AppState, HotelPool};
|
||||
|
||||
// ROOTS
|
||||
pub fn rooms_routes() -> Router<AppState> {
|
||||
|
||||
Router::new()
|
||||
.route("/", get(hello_rooms) )
|
||||
.route("/", get(hello_rooms))
|
||||
.route("/clean_db_update/{room_id}", put(clean_db_update))
|
||||
.route("/rooms", get(get_all_rooms))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,23 @@
|
||||
use axum::{
|
||||
Router,
|
||||
};
|
||||
use axum::Router;
|
||||
use r2d2::Pool;
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use r2d2::{Pool};
|
||||
|
||||
use crate::rooms::routes::rooms_routes;
|
||||
use crate::utils::routes::utils_routes;
|
||||
use crate::chat::routes::chat_routes;
|
||||
use crate::inventory::routes::inventory_routes;
|
||||
use crate::rooms::routes::rooms_routes;
|
||||
use crate::utils::routes::utils_routes;
|
||||
|
||||
use crate::utils::db_pool::{AppState};
|
||||
//TODO: add secret fomr dotenv here
|
||||
use crate::utils::db_pool::AppState;
|
||||
//TODO: add secret fomr dotenv here
|
||||
/*
|
||||
Function to build our main router
|
||||
Function to build our main router
|
||||
that regroup all feature centered router
|
||||
*/
|
||||
pub fn create_router(state: AppState) -> Router {
|
||||
|
||||
Router::new()
|
||||
|
||||
.nest("/auth", utils_routes().with_state(state.clone()))
|
||||
.nest("/rooms", rooms_routes().with_state(state.clone()))
|
||||
.nest("/chat", chat_routes().with_state(state.clone()))
|
||||
.nest("/inventory", inventory_routes().with_state(state.clone()))
|
||||
|
||||
.with_state(state)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,21 @@
|
||||
use std::sync::Arc;
|
||||
use axum::extract::{
|
||||
State,
|
||||
ws::{Message, WebSocket, WebSocketUpgrade},
|
||||
};
|
||||
use dashmap::DashMap;
|
||||
use r2d2::{Pool};
|
||||
use r2d2::Pool;
|
||||
use r2d2_sqlite::SqliteConnectionManager;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
use axum::extract::{ws::{Message, WebSocket, WebSocketUpgrade}, State};
|
||||
|
||||
use crate::utils::websocket::WsMap;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub hotel_pools: HotelPool,
|
||||
pub logs_pool: Pool<SqliteConnectionManager>,
|
||||
pub ws_map: WsMap,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub hotel_pools: HotelPool,
|
||||
pub logs_pool: Pool<SqliteConnectionManager>,
|
||||
pub ws_map: WsMap,
|
||||
}
|
||||
|
||||
type HotelId = i32; // or i32 if you want numeric ids
|
||||
|
||||
@@ -44,4 +46,4 @@ impl HotelPool {
|
||||
self.hotel_pools.insert(hotel_id, db_pool.clone());
|
||||
db_pool
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pub mod db_pool;
|
||||
pub mod auth;
|
||||
pub mod db_pool;
|
||||
pub mod routes;
|
||||
pub mod websocket;
|
||||
pub mod websocket;
|
||||
|
||||
@@ -1,37 +1,29 @@
|
||||
use axum::{
|
||||
routing::{get, put, post},
|
||||
Router,
|
||||
routing::{get, post, put},
|
||||
};
|
||||
|
||||
use crate::utils::auth::*;
|
||||
use crate::utils::db_pool::{HotelPool, AppState, };
|
||||
use crate::utils::db_pool::{AppState, HotelPool};
|
||||
use crate::utils::websocket::ws_handler;
|
||||
use headers::UserAgent;
|
||||
use axum_extra::TypedHeader;
|
||||
|
||||
use headers::UserAgent;
|
||||
|
||||
// ROOTS
|
||||
pub fn utils_routes() -> Router<AppState> {
|
||||
|
||||
Router::new()
|
||||
.route("/login", put(clean_auth_loging))
|
||||
.route("/register", put(register_user))
|
||||
|
||||
.route("/tokentest", put(token_tester))
|
||||
.route("/update_password", put(update_password))
|
||||
|
||||
.route("/create_refresh", post(create_refresh_token))
|
||||
.route("/login_refresh_token", post(login_refresh_token))
|
||||
|
||||
.route("/logout_single_device", post(logout_from_single_device))
|
||||
.route("/logout_all_devices", post(logout_from_all_devices))
|
||||
|
||||
.route("/ws/{req_token}", get(ws_handler))
|
||||
|
||||
.route("/force_update_password", put(force_update_password))
|
||||
.route("/get_hotels", get(get_hotel))
|
||||
.route("/add_hotel_user", put(add_hotel_user))
|
||||
|
||||
|
||||
//.with_state(state)
|
||||
}
|
||||
//.with_state(state)
|
||||
}
|
||||
|
||||
@@ -1,32 +1,33 @@
|
||||
use axum::extract::Path;
|
||||
use axum::response::IntoResponse;
|
||||
use axum::{
|
||||
Extension,
|
||||
extract::{
|
||||
State,
|
||||
ws::{Message, WebSocket, WebSocketUpgrade},
|
||||
},
|
||||
};
|
||||
use dashmap::DashMap;
|
||||
use reqwest::StatusCode;
|
||||
use std::sync::Arc;
|
||||
use axum::{Extension, extract::{State, ws::{Message, WebSocket, WebSocketUpgrade}}};
|
||||
use tokio::sync::mpsc;
|
||||
use axum::extract::Path;
|
||||
use axum::response::IntoResponse;
|
||||
//use futures_util::stream::stream::StreamExt;
|
||||
use futures_util::{StreamExt, SinkExt};
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
|
||||
use crate::utils::{auth::{AuthClaims, JwtKeys, auth_claims_from_token}, db_pool::{AppState, HotelPool}};
|
||||
use crate::utils::{
|
||||
auth::{AuthClaims, JwtKeys, auth_claims_from_token},
|
||||
db_pool::{AppState, HotelPool},
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// Type alias: user_id → sender to that user
|
||||
/// Type alias: user_id → sender to that user
|
||||
pub type UserMap = DashMap<i32, mpsc::UnboundedSender<Message>>;
|
||||
/// hotel_id → users
|
||||
pub type HotelMap = DashMap<i32, Arc<UserMap>>;
|
||||
/// global map of all hotels
|
||||
pub type WsMap = Arc<HotelMap>;
|
||||
/// Type alias: user_id → sender to that user
|
||||
/// Type alias: user_id → sender to that user
|
||||
|
||||
|
||||
async fn handle_socket(
|
||||
mut socket: WebSocket,
|
||||
state: AppState,
|
||||
hotel_id: i32,
|
||||
user_id: i32,
|
||||
) {
|
||||
async fn handle_socket(mut socket: WebSocket, state: AppState, hotel_id: i32, user_id: i32) {
|
||||
// channel for sending messages TO this client
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<Message>();
|
||||
|
||||
@@ -45,8 +46,6 @@ async fn handle_socket(
|
||||
// split socket into sender/receiver
|
||||
let (mut sender, mut receiver) = socket.split();
|
||||
|
||||
|
||||
|
||||
// task for sending messages from server to client
|
||||
let mut rx_task = tokio::spawn(async move {
|
||||
while let Some(msg) = rx.recv().await {
|
||||
@@ -96,21 +95,17 @@ pub async fn ws_handler(
|
||||
State(state): State<AppState>,
|
||||
Path((req_token)): Path<(String)>,
|
||||
) -> impl IntoResponse {
|
||||
|
||||
|
||||
let token = req_token;
|
||||
|
||||
let claims = match auth_claims_from_token(&token, &keys) {
|
||||
|
||||
let claims = match auth_claims_from_token(&token, &keys) {
|
||||
Err(_) => {
|
||||
print!("error during auth claims processing");
|
||||
return StatusCode::UNAUTHORIZED.into_response();
|
||||
|
||||
}
|
||||
Ok(c) => c
|
||||
|
||||
Ok(c) => c,
|
||||
};
|
||||
|
||||
print!("{token}, web socket tried to connect", );
|
||||
|
||||
print!("{token}, web socket tried to connect",);
|
||||
|
||||
/*
|
||||
let claims = match auth_claims_from_token(&token, &keys) {
|
||||
@@ -119,7 +114,6 @@ pub async fn ws_handler(
|
||||
};
|
||||
*/
|
||||
|
||||
|
||||
ws.on_upgrade(move |socket| handle_socket(socket, state, claims.hotel_id, claims.user_id))
|
||||
}
|
||||
|
||||
@@ -132,4 +126,4 @@ fn print_ws_state(state: &AppState) {
|
||||
println!("Hotel {hotel_id}: users {:?}", users);
|
||||
}
|
||||
println!("--------------------------------");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
docker build -t rust-api:1.0.5 .
|
||||
docker save -o rust-api-1-0-4.tar rust-api:1.0.4
|
||||
|
||||
|
||||
cross build --release --target aarch64-unknown-linux-gnu
|
||||
|
||||
docker run -p 8080:8080 \
|
||||
-v ${PWD}/db:/db \
|
||||
-e JWT_SECRET="my-dev-secret" \
|
||||
rust-api:1.0.0
|
||||
|
||||
GOOD
|
||||
|
||||
docker run
|
||||
--hostname=58ff54b2464c
|
||||
--env=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
||||
--volume=/w/DEV/hotel-api-rs/db:/app/db
|
||||
--network=bridge
|
||||
--workdir=/app
|
||||
-p 8080:8080
|
||||
--restart=no
|
||||
--runtime=runc
|
||||
-d rust-api:1.0.0
|
||||
|
||||
|
||||
|
||||
docker run `
|
||||
--name hotel-api `
|
||||
-e JWT_SECRET=your_jwt_secret_key `
|
||||
-v "/w/DEV/hotel-api-rs/db:/app/db" `
|
||||
-p 8080:8080 `
|
||||
rust-api:1.0.1
|
||||
|
||||
linux distro
|
||||
|
||||
docker run `
|
||||
--name hotel-api `
|
||||
-e JWT_SECRET=your_jwt_secret_key `
|
||||
-v "/var/lib/hotel-api/db:/app/db" `
|
||||
-p 8080:8080 `
|
||||
rust-api:1.0.2
|
||||
|
||||
docker run \
|
||||
--name hotel-api-2 \
|
||||
-e JWT_SECRET=your_jwt_secret_key \
|
||||
-v "/var/lib/hotel-api/db:/app/db" \
|
||||
-p 8080:8080 \
|
||||
rust-api:1.0.3
|
||||
|
||||
|
||||
FORM UBUNUTU REMOTE HOST
|
||||
|
||||
sudo docker run -d \
|
||||
--name hotel-demo \
|
||||
-e JWT_SECRET=your_jwt_secret_key \
|
||||
-v "/var/lib/hotel-api/db:/app/db" \
|
||||
-p 5090:7080 \
|
||||
hotel-demo:1.0.0
|
||||
|
||||
|
||||
FOR WINDOWS
|
||||
vvvv
|
||||
sudo docker run `
|
||||
--name hotel-demo `
|
||||
-e JWT_SECRET=your_jwt_secret_key `
|
||||
-v "/var/lib/hotel-api/db:/app/db" `
|
||||
-p 5090:5090 `
|
||||
hotel-demo:1.0.0
|
||||
|
||||
|
||||
|
||||
BAD
|
||||
|
||||
"Mounts": [
|
||||
{
|
||||
"Type": "bind",
|
||||
"Source": "/w/DEV/hotel-api-rs/db",
|
||||
"Destination": "/app/db ",
|
||||
"Mode": "",
|
||||
"RW": true,
|
||||
"Propagation": "rprivate"
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
"Mounts": [
|
||||
{
|
||||
"Type": "bind",
|
||||
"Source": "/w/DEV/hotel-api-rs/db",
|
||||
"Destination": "/app/db",
|
||||
"Mode": "",
|
||||
"RW": true,
|
||||
"Propagation": "rprivate"
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
"Mounts": [
|
||||
{
|
||||
"Type": "bind",
|
||||
"Source": "W:\\DEV\\hotel-api-rs\\db\\db.sqlite",
|
||||
"Destination": "/app/db/db.sqlite",
|
||||
"Mode": "",
|
||||
"RW": true,
|
||||
"Propagation": "rprivate"
|
||||
}
|
||||
],
|
||||
|
||||
GOOD
|
||||
|
||||
"Mounts": [
|
||||
{
|
||||
"Type": "bind",
|
||||
"Source": "/w/DEV/hotel-api-rs/db",
|
||||
"Destination": "/app/db",
|
||||
"Mode": "",
|
||||
"RW": true,
|
||||
"Propagation": "rprivate"
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user