use anyhow::{Context, Result as AnyhowResult}; use bcrypt::{hash, verify, DEFAULT_COST}; // Add bcrypt dependency for password hashing use rusqlite::{params, Connection, OptionalExtension}; use uuid::Uuid; // UUID for generating unique IDs // Import anyhow pub fn init_db() -> AnyhowResult<Connection> { let conn = Connection::open("form_data.db").context("Failed to open the database")?; // Create tables conn.execute( "CREATE TABLE IF NOT EXISTS forms ( id TEXT PRIMARY KEY, name TEXT NOT NULL, fields TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP )", [], )?; conn.execute( "CREATE TABLE IF NOT EXISTS submissions ( id TEXT PRIMARY KEY, form_id TEXT NOT NULL, data TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (form_id) REFERENCES forms (id) ON DELETE CASCADE )", [], )?; // Add a table for users conn.execute( "CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, username TEXT NOT NULL UNIQUE, password TEXT NOT NULL, -- Store a hashed password token TEXT UNIQUE -- Optional: For token-based auth )", [], )?; // Setup initial admin after creating the tables setup_initial_admin(&conn)?; Ok(conn) } pub fn setup_initial_admin(conn: &Connection) -> AnyhowResult<()> { add_admin_user(conn)?; Ok(()) } pub fn add_admin_user(conn: &Connection) -> AnyhowResult<()> { // Check if admin user already exists let mut stmt = conn .prepare("SELECT id FROM users WHERE username = ?1") .context("Failed to prepare query for checking admin user")?; if stmt.exists(params!["admin"])? { return Ok(()); } // Generate a UUID for the admin user let admin_id = Uuid::new_v4().to_string(); // Hash the password before storing it let hashed_password = hash("admin", DEFAULT_COST).context("Failed to hash password")?; // Add admin user with hashed password conn.execute( "INSERT INTO users (id, username, password) VALUES (?1, ?2, ?3)", params![admin_id, "admin", hashed_password], ) .context("Failed to insert admin user into the database")?; Ok(()) } // Add a function to validate a token pub fn validate_token(conn: &Connection, token: &str) -> AnyhowResult<Option<String>> { let mut stmt = conn .prepare("SELECT id FROM users WHERE token = ?1") .context("Failed to prepare query for validating token")?; let user_id: Option<String> = stmt .query_row(params![token], |row| row.get(0)) .optional() .context("Failed to retrieve user ID for the given token")?; Ok(user_id) } // Add a function to authenticate users (for login) pub fn authenticate_user( conn: &Connection, username: &str, password: &str, ) -> AnyhowResult<Option<String>> { let mut stmt = conn .prepare("SELECT id, password FROM users WHERE username = ?1") .context("Failed to prepare query for authenticating user")?; let mut rows = stmt .query(params![username]) .context("Failed to execute query for authenticating user")?; if let Some(row) = rows.next()? { let user_id: String = row.get(0)?; let stored_password: String = row.get(1)?; // Use bcrypt to verify the hashed password if verify(password, &stored_password).context("Failed to verify password")? { return Ok(Some(user_id)); } } Ok(None) } // Add a function to generate and save a token for a user pub fn generate_token_for_user(conn: &Connection, user_id: &str, token: &str) -> AnyhowResult<()> { conn.execute( "UPDATE users SET token = ?1 WHERE id = ?2", params![token, user_id], ) .context("Failed to update token for user")?; Ok(()) }