some fixes, first push to ovh, discord webhook

This commit is contained in:
2025-11-21 14:05:47 +01:00
parent 359f7a4ad8
commit 7356689d29
19 changed files with 1127 additions and 23 deletions

786
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -29,6 +29,7 @@ rand_core = {version = "0.6.4", features = ["getrandom"]}
futures-util = {version = "0.3.31"}
uuid = {version = "1.18.1", features = ["serde"] }
base64 = "0.22.1"
reqwest = { version = "0.12.24", features = ["json","blocking"] }

View File

@@ -4,11 +4,18 @@ 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
# Create the directory where DB will be stored
RUN mkdir -p /db
# Switch to non-root user
USER 1001
# Expose API port
EXPOSE 8080

16
Dockerfile copy Normal file
View File

@@ -0,0 +1,16 @@
FROM rust:latest AS builder
WORKDIR /app
COPY . .
RUN cargo build --release
FROM debian:bookworm-slim
WORKDIR /app
COPY --from=builder /app/target/release/hotel-api-rs /usr/local/bin/hotel-api-rs
# Create the directory where DB will be stored
RUN mkdir -p /db
# Expose API port
EXPOSE 8080
CMD ["/usr/local/bin/hotel-api-rs"]

Binary file not shown.

BIN
db/auth copy.sqlite Normal file

Binary file not shown.

Binary file not shown.

View File

View File

BIN
db/test_backup/1.sqlite Normal file

Binary file not shown.

BIN
db/test_backup/auth.sqlite Normal file

Binary file not shown.

BIN
rust-api-1-0-2.tar Normal file

Binary file not shown.

View File

@@ -1,16 +1,18 @@
use std::collections::HashMap;
use axum::{
extract::{
Json, extract::{
FromRequest,FromRequestParts, State,
}, http::StatusCode, response::{sse::KeepAlive, IntoResponse},
}, http::StatusCode, response::{IntoResponse, sse::KeepAlive}
};
//use axum::extract::ws::Message;
use chrono::NaiveDateTime;
use rusqlite::params;
use rusqlite::{Name, params};
use serde::Serialize;
use serde_json::json;
use serde_json::{json, to_value};
use crate::chat::extractor::{
AddUserConversationPayload, CreateConversationPayload, GetMessagesPayload, SendMessagePayload
@@ -154,6 +156,7 @@ pub async fn send_message(
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": user_id,
"content": payload.message,
@@ -280,3 +283,140 @@ pub async fn get_hotel_users(
}
}
#[derive(Debug, Serialize)]
struct Conversation {
id: i32,
title: String,
}
pub async fn get_convs(
State(state): State<AppState>,
//Path((item_name, item_amount)): Path<(String, i32)>,
AuthClaims{ user_id, hotel_id}: AuthClaims,
) -> impl IntoResponse {
let pool = state.hotel_pools.get_pool(hotel_id);
let conn = match pool.get(){
Ok(conn) => conn,
Err(err) => {
let body = json!({ "error": format!("Pool error: {}", err) });
return (StatusCode::INTERNAL_SERVER_ERROR, Json(body));
}
};
let mut stmt = match conn.prepare(
"SELECT conversation_id, name FROM conversation_participants WHERE user_id = ?1",
) {
Ok(s) => s,
Err(e) => {
let body = json!({ "error": format!("Prepare failed: {}", e) });
return (StatusCode::INTERNAL_SERVER_ERROR, Json(body) )
}
};
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, name))
}) {
Ok(rows) => rows,
//Ok(_) => {}, IMPLEMENT NO CONV ?
Err(e) => {
let body = json!({ "error": format!("Query failed: {}", e) });
return (StatusCode::INTERNAL_SERVER_ERROR, Json(body));
}
};
let mut map = HashMap::new();
// ✅ Iterate through the row results
for row_result in rows {
match row_result {
Ok((id, name)) => {
map.insert(id, name);
}
Err(e) => {
let body = json!({ "error": format!("Row parsing failed: {}", e) });
return (StatusCode::INTERNAL_SERVER_ERROR, Json(body));
}
}
}
let convs_string = serde_json::to_string(&map)
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, Json("error".to_string())));
let conv_map_json = to_value(map).unwrap();
(StatusCode::OK, Json(conv_map_json))
}
/*
pub async fn get_convs(
State(state): State<AppState>,
//Path((item_name, item_amount)): Path<(String, i32)>,
AuthClaims{ user_id, hotel_id}: AuthClaims,
) -> 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).into_response() )
};
let mut stmt = match conn.prepare(
"SELECT id, title FROM conversation WHERE creator_id = ?1",
) {
Ok(s) => s,
Err(e) =>
return (StatusCode::INTERNAL_SERVER_ERROR, format!("Prepare failed: {}", e).into_response() )
};
let rows = match stmt.query_map(params![user_id], |row| {
let id: i32 = row.get(0)?;
let title: String = row.get(1)?;
Ok((title, id))
}) {
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
for row_result in rows {
match row_result {
Ok((title, id)) => {
map.insert(title, id);
}
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Row parsing failed: {}", e).into_response() )
}
}
let conv_map_json = match to_value(map) {
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() ));
(StatusCode::OK, Json(conv_map_clean_json)).into_response()
}
*/

View File

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

View File

@@ -2,6 +2,7 @@
use argon2::Params;
use axum::{extract::{ws::{close_code::STATUS, Message}, Path, State}, http::StatusCode, response::IntoResponse};
use rusqlite::params;
use serde::Serialize;
use serde_json::json;
@@ -33,6 +34,14 @@ pub async fn create_inventory_item(
}
}
#[derive(Serialize)]
pub struct InventoryItems {
id: i32,
amount: i32,
name: String,
user_id: i32,
updated_at: String,
}
pub async fn update_inventory_item(
State(state): State<AppState>,
@@ -82,3 +91,47 @@ pub async fn update_inventory_item(
*/
}
pub async fn get_inventory_item(
State(state): State<AppState>,
AuthClaims { user_id, hotel_id }: AuthClaims,
) -> impl IntoResponse {
let pool = state.hotel_pools.get_pool(hotel_id);
let conn = match pool.get() {
Ok(c) => c,
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "Pool error".to_string()),
};
let mut stmt = match conn.prepare("SELECT id, amount, item_name, user_id 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,
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "Query error".to_string()),
};
let mut items = Vec::new();
while let Ok(Some(row)) = query_result.next() {
let item = InventoryItems {
id: row.get("id").unwrap_or_default(),
amount: row.get("amount").unwrap_or_default(),
name: row.get("name").unwrap_or_default(),
user_id: row.get("user_id").unwrap_or_default(),
updated_at: row.get("updated_at").unwrap_or_default(),
};
items.push(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()),
};
(StatusCode::OK, json)
}

View File

@@ -10,4 +10,5 @@ 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("/get_item/", get(get_inventory_item))
}

View File

@@ -5,6 +5,8 @@ use jsonwebtoken::{DecodingKey, EncodingKey};
use tokio::net::TcpListener;
use tokio::sync::mpsc;
use reqwest::Client;
mod utils;
mod routes;
mod rooms;
@@ -23,12 +25,43 @@ use std::env;
use dotenvy::dotenv;
pub async fn notify_discord(msg: &str) -> Result<(), reqwest::Error> {
let payload = serde_json::json!({
"content": msg
});
reqwest::Client::new()
.post("https://discord.com/api/webhooks/1440912618205347891/Ekg89krDoPm41kA27LA3gXgNWmMWvCCtziYIUsjqaY22Jnw4a6IWhZOht0in5JjnPX-W")
.json(&payload)
.send()
.await?;
Ok(())
}
#[tokio::main(flavor = "multi_thread", worker_threads = 8)]
async fn main() -> std::io::Result<()> {
dotenv().ok();
std::panic::set_hook(Box::new(|info| {
let msg = format!("Rust panic: {}", info);
// Use blocking client so the process can't exit before sending
let payload = serde_json::json!({
"content": msg
});
let client = reqwest::blocking::Client::new();
let _ = client
.post("https://discord.com/api/webhooks/1440912618205347891/Ekg89krDoPm41kA27LA3gXgNWmMWvCCtziYIUsjqaY22Jnw4a6IWhZOht0in5JjnPX-W")
.json(&payload)
.send();
}));
panic!("crash-test");
let hotel_pools = HotelPool::new();
let logs_manager = SqliteConnectionManager::file("db/auth.sqlite");
let logs_pool = Pool::builder()

View File

@@ -22,8 +22,6 @@ pub async fn hello_rooms() -> String {
"hello from rooms".to_string()
}
pub async fn fake_db_update(
State(state): State<AppState>,
AuthClaims { user_id, hotel_id }: AuthClaims,
@@ -85,6 +83,7 @@ pub async fn clean_db_update(
}
if let Some(hotel_users) = state.ws_map.get(&hotel_id) {
let update_msg = json!({
"event-type": "room-update",
"room_id": room_id,
"status": payload.status,
"updated_by": user_id,
@@ -131,13 +130,15 @@ pub async fn get_all_rooms(
Err(e) => return (StatusCode::INTERNAL_SERVER_ERROR, format!("Prepare failed: {}", e)),
};
let room_iter = match stmt.query_map( params![],|row| {
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)),
};

View File

@@ -4,3 +4,80 @@ 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
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"
}
],