Add SQLite database support and user management functionality
This commit is contained in:
parent
6ebdef9a64
commit
8df02b7a5a
8 changed files with 1362 additions and 9 deletions
1277
Cargo.lock
generated
1277
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -18,3 +18,4 @@ toml = "0.8.19"
|
||||||
ratatui = "0.29.0"
|
ratatui = "0.29.0"
|
||||||
crossterm = "0.28.1"
|
crossterm = "0.28.1"
|
||||||
chrono = "0.4.39"
|
chrono = "0.4.39"
|
||||||
|
sqlx = { version = "0.8", features = [ "sqlite", "runtime-tokio", "tls-native-tls" ] }
|
25
README.md
Normal file
25
README.md
Normal 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
BIN
db.sqlite
Normal file
Binary file not shown.
9
migrations/001_create_users_table.sql
Normal file
9
migrations/001_create_users_table.sql
Normal 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);
|
|
@ -7,11 +7,13 @@ pub(crate) mod handlers {
|
||||||
use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
|
use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _};
|
||||||
use log::{debug, error, info};
|
use log::{debug, error, info};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use std::sync::Arc;
|
||||||
use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader};
|
use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader};
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
use x25519_dalek::{EphemeralSecret, PublicKey};
|
use x25519_dalek::{EphemeralSecret, PublicKey};
|
||||||
|
|
||||||
|
use crate::db::users::create_user;
|
||||||
/*
|
/*
|
||||||
Specifications of the packet
|
Specifications of the packet
|
||||||
32 bytes - Command name
|
32 bytes - Command name
|
||||||
|
@ -81,9 +83,11 @@ pub(crate) mod handlers {
|
||||||
let decrypted = cipher_reader
|
let decrypted = cipher_reader
|
||||||
.decrypt(&nonce_reader, decoded.as_ref())
|
.decrypt(&nonce_reader, decoded.as_ref())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let username = String::from_utf8(decrypted)?;
|
let username = Arc::new(String::from_utf8(decrypted)?);
|
||||||
let username_read = username.clone(); // Clone for read task
|
let username_read = Arc::clone(&username); // Clone the Arc for read task
|
||||||
let mut username_write = username.clone(); // Clone for write 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
|
// Read task for receiving messages from the client
|
||||||
let read_task = tokio::spawn(async move {
|
let read_task = tokio::spawn(async move {
|
||||||
|
@ -160,12 +164,20 @@ pub(crate) mod handlers {
|
||||||
}
|
}
|
||||||
let new_nickname = &parsed_message.argument[0];
|
let new_nickname = &parsed_message.argument[0];
|
||||||
info!("Changing nickname to: {}", new_nickname);
|
info!("Changing nickname to: {}", new_nickname);
|
||||||
username_write = new_nickname.clone();
|
|
||||||
// Here implement your nickname change logic
|
// Here implement your nickname change logic
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
error!("Unknown command: {}", parsed_message.command[0]);
|
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 {
|
} else {
|
||||||
|
|
30
src/db/mod.rs
Normal file
30
src/db/mod.rs
Normal 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(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,13 @@
|
||||||
mod client;
|
mod client;
|
||||||
mod tui; // Add this new module
|
mod tui; // Add this new module
|
||||||
|
mod db;
|
||||||
|
|
||||||
use client::handlers::handle_client;
|
use client::handlers::handle_client;
|
||||||
|
use db::users::create_db_pool;
|
||||||
use log::{error, info, Level, Log, Metadata, Record};
|
use log::{error, info, Level, Log, Metadata, Record};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::sync::mpsc;
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use std::{process::exit, sync::mpsc};
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tokio::sync::broadcast;
|
use tokio::sync::broadcast;
|
||||||
use tui::{run_app, LogEntry};
|
use tui::{run_app, LogEntry};
|
||||||
|
@ -43,6 +45,8 @@ impl Log for CustomLogger {
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
create_db_pool().await.unwrap();
|
||||||
|
|
||||||
// Create a channel for logging
|
// Create a channel for logging
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
|
@ -56,6 +60,9 @@ async fn main() {
|
||||||
if let Err(e) = run_app(rx) {
|
if let Err(e) = run_app(rx) {
|
||||||
eprintln!("Error running TUI: {:?}", e);
|
eprintln!("Error running TUI: {:?}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exit the process when the TUI closes
|
||||||
|
exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Load the configuration from config file
|
// Load the configuration from config file
|
||||||
|
|
Loading…
Add table
Reference in a new issue