Add SQLite database support and user management functionality

This commit is contained in:
Andrea Moro 2025-01-31 13:43:35 +01:00
parent 6ebdef9a64
commit 8df02b7a5a
8 changed files with 1362 additions and 9 deletions

1277
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -18,3 +18,4 @@ toml = "0.8.19"
ratatui = "0.29.0"
crossterm = "0.28.1"
chrono = "0.4.39"
sqlx = { version = "0.8", features = [ "sqlite", "runtime-tokio", "tls-native-tls" ] }

25
README.md Normal file
View file

@ -0,0 +1,25 @@
# SRC (Simple Rust Chat)
Simple Rust Chat è una chat Client/Server che permette di fare le seguenti azioni:
- Chattare con altri utenti in canali per topic
- Chattare con una persona sola (DMs)
- Inviare i file tra utenti
- Possibilità di amministrare la chat con comandi di /kick o /ban
La chat è basata molto sull'idea di una chat IRC (inizialmente il progetto aveva come scopo la creazione di un server IRC da utilizzare con dei clienti IRC come Halloy o mIRC)
## Protocolli utilizzati
Il server utilizza TCP/IP come protocollo per la trasmissione dei dati in rete. I pacchetti sono composti da un pacchetto prestabilito
```
/*
Specifications of the packet
32 bytes - Command name
512 bytes - Command argument
if command is empty then it is a message
*/
```
La chat è sicura usando x25519-dalek e AES-128 per criptare i messaggi e i dati dei file che vengono inviati. Lo scambio di chiavi viene effettuato con Diffie Hellman

BIN
db.sqlite Normal file

Binary file not shown.

View file

@ -0,0 +1,9 @@
-- Create the users table
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT, -- Unique ID for each user
username TEXT NOT NULL UNIQUE, -- Username, must be unique
password_hash TEXT NOT NULL -- Hashed password for security
);
-- Create an index on the username and email columns for faster lookups
CREATE INDEX IF NOT EXISTS idx_users_username ON users (username);

View file

@ -7,11 +7,13 @@ pub(crate) mod handlers {
use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
use log::{debug, error, info};
use serde::Deserialize;
use std::sync::Arc;
use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader};
use tokio::net::TcpStream;
use tokio::sync::broadcast;
use x25519_dalek::{EphemeralSecret, PublicKey};
use crate::db::users::create_user;
/*
Specifications of the packet
32 bytes - Command name
@ -81,9 +83,11 @@ pub(crate) mod handlers {
let decrypted = cipher_reader
.decrypt(&nonce_reader, decoded.as_ref())
.unwrap();
let username = String::from_utf8(decrypted)?;
let username_read = username.clone(); // Clone for read task
let mut username_write = username.clone(); // Clone for write task
let username = Arc::new(String::from_utf8(decrypted)?);
let username_read = Arc::clone(&username); // Clone the Arc for read task
let username_write = Arc::clone(&username); // Clone the Arc for write task
create_user(&username, "1234").await?;
// Read task for receiving messages from the client
let read_task = tokio::spawn(async move {
@ -160,12 +164,20 @@ pub(crate) mod handlers {
}
let new_nickname = &parsed_message.argument[0];
info!("Changing nickname to: {}", new_nickname);
username_write = new_nickname.clone();
// Here implement your nickname change logic
}
_ => {
error!("Unknown command: {}", parsed_message.command[0]);
match tx.send("Error! Unknown command".to_string()) {
Ok(_) => {
info!("Error message sent to client {}", username_write)
}
Err(e) => {
error!("Failed to send error message: {:?}", e);
break;
}
}
}
}
} else {

30
src/db/mod.rs Normal file
View file

@ -0,0 +1,30 @@
pub(crate) mod users {
use sqlx::sqlite::SqlitePool;
pub async fn connect_to_db() -> Result<SqlitePool, sqlx::Error> {
let pool = SqlitePool::connect("sqlite:./db.sqlite").await?;
Ok(pool)
}
pub async fn create_db_pool() -> Result<SqlitePool, sqlx::Error> {
let pool = connect_to_db().await?;
sqlx::migrate!("./migrations").run(&pool).await?;
Ok(pool)
}
pub async fn create_user(username: &str, password_hash: &str) -> Result<(), sqlx::Error> {
let pool = create_db_pool().await?;
sqlx::query(
r#"
INSERT INTO users (username, password_hash)
VALUES (?, ?)
"#,
)
.bind(username)
.bind(password_hash)
.execute(&pool)
.await?;
Ok(())
}
}

View file

@ -1,11 +1,13 @@
mod client;
mod tui; // Add this new module
mod db;
use client::handlers::handle_client;
use db::users::create_db_pool;
use log::{error, info, Level, Log, Metadata, Record};
use serde::Deserialize;
use std::sync::mpsc;
use std::thread;
use std::{process::exit, sync::mpsc};
use tokio::net::TcpListener;
use tokio::sync::broadcast;
use tui::{run_app, LogEntry};
@ -43,6 +45,8 @@ impl Log for CustomLogger {
#[tokio::main]
async fn main() {
create_db_pool().await.unwrap();
// Create a channel for logging
let (tx, rx) = mpsc::channel();
@ -56,6 +60,9 @@ async fn main() {
if let Err(e) = run_app(rx) {
eprintln!("Error running TUI: {:?}", e);
}
// Exit the process when the TUI closes
exit(0);
});
// Load the configuration from config file