turned on WAL on db, fix force update of passwords
This commit is contained in:
14
.vscode/tasks.json
vendored
Normal file
14
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "cargo",
|
||||||
|
"command": "build",
|
||||||
|
"problemMatcher": [
|
||||||
|
"$rustc"
|
||||||
|
],
|
||||||
|
"group": "build",
|
||||||
|
"label": "rust: cargo build"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
db/1.sqlite
BIN
db/1.sqlite
Binary file not shown.
BIN
db/auth.sqlite
BIN
db/auth.sqlite
Binary file not shown.
BIN
db/auth.sqlite-shm
Normal file
BIN
db/auth.sqlite-shm
Normal file
Binary file not shown.
BIN
db/auth.sqlite-wal
Normal file
BIN
db/auth.sqlite-wal
Normal file
Binary file not shown.
0
db/hotel_1.sqlite
Normal file
0
db/hotel_1.sqlite
Normal file
51
db/hotel_schema.sql
Normal file
51
db/hotel_schema.sql
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
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));
|
||||||
|
|
||||||
0
hotel_1.sqlite
Normal file
0
hotel_1.sqlite
Normal file
0
hotel_schema.sql
Normal file
0
hotel_schema.sql
Normal file
@@ -66,13 +66,19 @@ pub async fn add_user_to_conv(
|
|||||||
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "prepare failed".to_string())
|
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "prepare failed".to_string())
|
||||||
};
|
};
|
||||||
|
|
||||||
if !statement.exists(params![user_id, payload.conv_id])
|
match statement.exists(params![user_id, payload.conv_id]) {
|
||||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Query failed".to_string())).unwrap()
|
Ok(true) => {
|
||||||
{
|
//user is creator
|
||||||
// Early exit if not creator
|
}
|
||||||
return ((StatusCode::FORBIDDEN, "Not the creator".to_string()));
|
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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
for target_id in &payload.users {
|
for target_id in &payload.users {
|
||||||
let rows_inserted = match conn.execute(
|
let rows_inserted = match conn.execute(
|
||||||
@@ -114,12 +120,18 @@ pub async fn send_message(
|
|||||||
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "prepare failed".to_string())
|
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "prepare failed".to_string())
|
||||||
};
|
};
|
||||||
|
|
||||||
if !statement.exists(params![user_id, payload.conv_id])
|
match statement.exists(params![user_id, payload.conv_id]) {
|
||||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Query failed".to_string()))
|
Ok(true) => {
|
||||||
.unwrap()
|
// user is part of the conversation — continue
|
||||||
{
|
}
|
||||||
// Early exit if not creator
|
Ok(false) => {
|
||||||
return ((StatusCode::FORBIDDEN, "Not part of the conversation".to_string()));
|
// 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 result = conn.execute(
|
let result = conn.execute(
|
||||||
@@ -209,11 +221,15 @@ pub async fn get_message(
|
|||||||
sent_at: row.get(3)?,
|
sent_at: row.get(3)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
).map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Query failed".to_string()))?
|
)
|
||||||
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Query failed".to_string()))?
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect::<Result<Vec<_>, _>>()
|
||||||
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Collect failed".to_string()))?;
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Collect failed".to_string()))?;
|
||||||
|
|
||||||
Ok((StatusCode::OK, serde_json::to_string(&messages).unwrap()))
|
let response = serde_json::to_string(&messages)
|
||||||
|
.map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, "Serialisation failed"));
|
||||||
|
|
||||||
|
Ok((StatusCode::OK, response ))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ use crate::utils::auth::JwtKeys;
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main(flavor = "multi_thread", worker_threads = 8)]
|
||||||
async fn main() -> std::io::Result<()> {
|
async fn main() -> std::io::Result<()> {
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -77,7 +77,6 @@ pub async fn clean_db_update(
|
|||||||
match result {
|
match result {
|
||||||
Ok(room_number) => {
|
Ok(room_number) => {
|
||||||
// --- broadcast to all WS clients in the hotel ---
|
// --- broadcast to all WS clients in the hotel ---
|
||||||
|
|
||||||
if let Err(err) = conn.execute(
|
if let Err(err) = conn.execute(
|
||||||
"INSERT INTO room_history (room_id, room_number, status) VALUES (?1, ?2, ?3)",
|
"INSERT INTO room_history (room_id, room_number, status) VALUES (?1, ?2, ?3)",
|
||||||
params![&room_id, &room_number, &payload.status],
|
params![&room_id, &room_number, &payload.status],
|
||||||
|
|||||||
@@ -106,10 +106,15 @@ fn hash_password(password: &str) -> anyhow::Result<String> {
|
|||||||
|
|
||||||
// Verify an incoming password against stored hash
|
// Verify an incoming password against stored hash
|
||||||
fn verify_password(password: &str, stored_hash: &str) -> bool {
|
fn verify_password(password: &str, stored_hash: &str) -> bool {
|
||||||
let parsed_hash = PasswordHash::new(stored_hash).unwrap();
|
|
||||||
|
let parsed_hash = match PasswordHash::new(&stored_hash) {
|
||||||
|
Ok(hash) => hash,
|
||||||
|
Err(_) => return false,
|
||||||
|
};
|
||||||
|
|
||||||
Argon2::default()
|
Argon2::default()
|
||||||
.verify_password(password.as_bytes(), &parsed_hash)
|
.verify_password(password.as_bytes(), &parsed_hash).is_ok()
|
||||||
.is_ok()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
@@ -155,6 +160,133 @@ pub async fn register_user (
|
|||||||
Ok((StatusCode::CREATED, "User registered successfully"))
|
Ok((StatusCode::CREATED, "User registered successfully"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct ForceUpdatePasswordValues{
|
||||||
|
username: String,
|
||||||
|
newpassword: String,
|
||||||
|
hotel_id: i32,
|
||||||
|
admin_pass: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
//pub struct ForceUpdatePasswordPayload (pub ForceUpdatePasswordValues);
|
||||||
|
|
||||||
|
pub async fn ForceUpdatePassword(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Json(payload): Json<ForceUpdatePasswordValues>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
|
||||||
|
let conn = match state.logs_pool.get() {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB, conn failed").into_response()
|
||||||
|
};
|
||||||
|
|
||||||
|
let user_row = match conn.query_row(
|
||||||
|
"SELECT id FROM users WHERE username = ?1 AND hotel_id = ?2",
|
||||||
|
params![&payload.username, &payload.hotel_id],
|
||||||
|
|row|{
|
||||||
|
let user_id: i32 = row.get(0)?;
|
||||||
|
//let hotel_id: i32 = row.get(1)?;
|
||||||
|
Ok((user_id))
|
||||||
|
},
|
||||||
|
).optional() {
|
||||||
|
Ok(opt) => opt,
|
||||||
|
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB query error")
|
||||||
|
.into_response(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (user_id) = match user_row {
|
||||||
|
Some(u) => u,
|
||||||
|
None => return (StatusCode::UNAUTHORIZED, "Not correct user")
|
||||||
|
.into_response(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let admin_check: String = "my_admin_password".to_string();
|
||||||
|
|
||||||
|
if &payload.admin_pass != &admin_check {
|
||||||
|
return (StatusCode::UNAUTHORIZED, "Invalid Amin Password").into_response()
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let hashed_password = match hash_password(&payload.newpassword) {
|
||||||
|
Ok(h) => h,
|
||||||
|
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "Password hashing failed").into_response(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = conn.execute(
|
||||||
|
"UPDATE users SET password = ?1 WHERE id = ?2",
|
||||||
|
params![&hashed_password, &user_id],
|
||||||
|
);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(rows) if rows > 0 => (StatusCode::OK, "Password updated").into_response(),
|
||||||
|
Ok(_) => (StatusCode::NOT_FOUND, "User not found").into_response(),
|
||||||
|
Err(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Failed to update password").into_response(),
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct UpdatePasswordValues{
|
||||||
|
username: String,
|
||||||
|
current_password: String,
|
||||||
|
newpassword: String,
|
||||||
|
hotel_id: i32,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn UpdatePassword(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Json(payload): Json<UpdatePasswordValues>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
|
||||||
|
let conn = match state.logs_pool.get() {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB, conn failed").into_response()
|
||||||
|
};
|
||||||
|
|
||||||
|
let user_row = match conn.query_row(
|
||||||
|
"SELECT password, id FROM users WHERE username = ?1 AND current_password = ?2",
|
||||||
|
params![&payload.username, &payload.current_password],
|
||||||
|
|row|{
|
||||||
|
let password: String = row.get(0)?;
|
||||||
|
let id: i32 = row.get(1)?;
|
||||||
|
Ok((password, id))
|
||||||
|
},
|
||||||
|
).optional() {
|
||||||
|
Ok(opt) => opt,
|
||||||
|
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "DB query error")
|
||||||
|
.into_response(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (password, user_id) = match user_row {
|
||||||
|
Some(u) => u,
|
||||||
|
None => return (StatusCode::UNAUTHORIZED, "Not correct user")
|
||||||
|
.into_response(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if verify_password( &payload.current_password, &password ) {
|
||||||
|
return (StatusCode::UNAUTHORIZED, "Invalid Password").into_response()
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let hashed_password = match hash_password(&payload.newpassword) {
|
||||||
|
Ok(h) => h,
|
||||||
|
Err(_) => return (StatusCode::INTERNAL_SERVER_ERROR, "Password hashing failed").into_response(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = conn.execute(
|
||||||
|
"UPDATE users SET password = ?1 WHERE id = ?2",
|
||||||
|
params![&hashed_password, &user_id],
|
||||||
|
);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(rows) if rows > 0 => (StatusCode::OK, "Password updated").into_response(),
|
||||||
|
Ok(_) => (StatusCode::NOT_FOUND, "User not found").into_response(),
|
||||||
|
Err(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Failed to update password").into_response(),
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct LoginValues {
|
pub struct LoginValues {
|
||||||
username : String,
|
username : String,
|
||||||
@@ -227,11 +359,14 @@ pub async fn clean_auth_loging(
|
|||||||
return (StatusCode::UNAUTHORIZED, "Invelid credentials").into_response();
|
return (StatusCode::UNAUTHORIZED, "Invelid credentials").into_response();
|
||||||
}
|
}
|
||||||
|
|
||||||
let expiration = chrono::Utc::now()
|
|
||||||
.checked_add_signed(chrono::Duration::hours(15))
|
|
||||||
.unwrap()
|
|
||||||
.timestamp() as usize;
|
|
||||||
|
|
||||||
|
let expiration = match chrono::Utc::now().checked_add_signed(chrono::Duration::hours(15)) {
|
||||||
|
Some(time) => time.timestamp() as usize,
|
||||||
|
None => {
|
||||||
|
// Handle overflow — probably a 500, since this should never happen
|
||||||
|
return (StatusCode::INTERNAL_SERVER_ERROR, "Time overflow".to_string()).into_response();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let claims = serde_json::json!({
|
let claims = serde_json::json!({
|
||||||
"id": user_id,
|
"id": user_id,
|
||||||
@@ -251,6 +386,11 @@ pub async fn clean_auth_loging(
|
|||||||
Json(LoginResponse { token }).into_response()
|
Json(LoginResponse { token }).into_response()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn internal_error<E: std::fmt::Display>(err: E) -> (StatusCode, String) {
|
fn internal_error<E: std::fmt::Display>(err: E) -> (StatusCode, String) {
|
||||||
(StatusCode::INTERNAL_SERVER_ERROR, format!("Internal error: {}", err))
|
(StatusCode::INTERNAL_SERVER_ERROR, format!("Internal error: {}", err))
|
||||||
}
|
}
|
||||||
@@ -15,6 +15,8 @@ pub fn utils_routes() -> Router<AppState> {
|
|||||||
.route("/register", put(register_user))
|
.route("/register", put(register_user))
|
||||||
.route("/ws/", get(ws_handler))
|
.route("/ws/", get(ws_handler))
|
||||||
.route("/tokentest", put(token_tester))
|
.route("/tokentest", put(token_tester))
|
||||||
|
.route("/force_update_password", put(ForceUpdatePassword))
|
||||||
|
.route("/update_password", put(UpdatePassword))
|
||||||
|
|
||||||
//.with_state(state)
|
//.with_state(state)
|
||||||
}
|
}
|
||||||
1
utils command.txt
Normal file
1
utils command.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
cross build --release --target aarch64-unknown-linux-gnu
|
||||||
Reference in New Issue
Block a user