From 85224ef1f88475ceb7725ea2567ba0c7e1f56a0e Mon Sep 17 00:00:00 2001 From: Greg Shuflin Date: Mon, 3 Feb 2025 02:14:49 -0800 Subject: [PATCH] Demo flag --- README.md | 18 +++++++++++++++++- src/demo.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 38 +++++++++++++++++++++++++------------- 3 files changed, 88 insertions(+), 14 deletions(-) create mode 100644 src/demo.rs diff --git a/README.md b/README.md index 05981cb..6dc366b 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,11 @@ This project is packaged with Nix flakes. To install and run it: Or run it directly: ```bash + # Run with a persistent database nix run git+https://code.everydayimshuflin.com/greg/rss-reader -- -d /path/to/database.sqlite + + # Or try it out in demo mode (uses in-memory database) + nix run git+https://code.everydayimshuflin.com/greg/rss-reader -- --demo ``` ### Development @@ -46,7 +50,19 @@ To set up a development environment: 3. Run the application: ```bash + # Run with a persistent database cargo run -- -d rss-reader.db + + # Or try it out in demo mode (uses in-memory database) + cargo run -- --demo ``` -The application will be available at `http://localhost:8000`. On first run, you'll be prompted to create an admin user. \ No newline at end of file +The application will be available at `http://localhost:8000`. + +### Demo Mode + +When running in demo mode (using the `--demo` flag), the application will: +- Use an in-memory SQLite database that is cleared when the application stops +- Create two pre-configured users: + - Admin user: username `admin`, password `admin` + - Regular user: username `demo`, password `demo` \ No newline at end of file diff --git a/src/demo.rs b/src/demo.rs new file mode 100644 index 0000000..8df271d --- /dev/null +++ b/src/demo.rs @@ -0,0 +1,46 @@ +use chrono; +use sqlx; +use uuid::Uuid; + +pub async fn setup_demo_data(pool: &sqlx::SqlitePool) { + // Create admin user + let admin_id = Uuid::new_v4().to_string(); + let admin_hash = bcrypt::hash("admin", bcrypt::DEFAULT_COST).unwrap(); + let now = chrono::Utc::now().to_rfc3339(); + + sqlx::query( + "INSERT INTO users (id, username, password_hash, email, display_name, created_at, admin) + VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)", + ) + .bind(&admin_id) + .bind("admin") + .bind(&admin_hash) + .bind(Option::::None) + .bind(Option::::None) + .bind(&now) + .bind(true) + .execute(pool) + .await + .expect("Failed to create admin user"); + + // Create demo user + let demo_id = Uuid::new_v4().to_string(); + let demo_hash = bcrypt::hash("demo", bcrypt::DEFAULT_COST).unwrap(); + + sqlx::query( + "INSERT INTO users (id, username, password_hash, email, display_name, created_at, admin) + VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)", + ) + .bind(&demo_id) + .bind("demo") + .bind(&demo_hash) + .bind(Option::::None) + .bind(Option::::None) + .bind(&now) + .bind(false) + .execute(pool) + .await + .expect("Failed to create demo user"); + + println!("Successfully set up demo data"); +} diff --git a/src/main.rs b/src/main.rs index e9eb4ac..e31439b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ extern crate rocket; use clap::Parser; +mod demo; mod feed_utils; mod feeds; mod poll; @@ -20,7 +21,11 @@ use user::AuthenticatedUser; struct Args { /// Path to the SQLite database file #[arg(short, long)] - database: String, + database: Option, + + /// Run in demo mode with an in-memory database + #[arg(long)] + demo: bool, } #[derive(Database)] @@ -58,15 +63,21 @@ fn login() -> Template { fn rocket() -> _ { let args = Args::parse(); - let db_url = format!("sqlite:{}", args.database); + let db_url = if args.demo { + "sqlite::memory:".to_string() + } else { + let database = args + .database + .expect("Database path is required when not in demo mode"); + // Check if database file exists, create it if it doesn't + if !std::path::Path::new(&database).exists() { + use std::fs::File; + File::create(&database).expect("Failed to create database file"); + } + format!("sqlite:{}", database) + }; - // Check if database file exists, create it if it doesn't - if !std::path::Path::new(&args.database).exists() { - use std::fs::File; - File::create(&args.database).expect("Failed to create database file"); - } - - // Run migrations before starting the server + // Run migrations and setup demo data if needed let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async { let pool = sqlx::SqlitePool::connect(&db_url).await.unwrap(); @@ -74,12 +85,13 @@ fn rocket() -> _ { .run(&pool) .await .expect("Failed to run database migrations"); + + if args.demo { + demo::setup_demo_data(&pool).await; + } }); - let figment = rocket::Config::figment().merge(( - "databases.rss_data.url", - format!("sqlite:{}", args.database), - )); + let figment = rocket::Config::figment().merge(("databases.rss_data.url", db_url)); rocket::custom(figment) .mount(