diff --git a/Cargo.lock b/Cargo.lock index 0603176..b9ca793 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,7 +127,7 @@ dependencies = [ "actix-utils", "futures-core", "futures-util", - "mio", + "mio 1.0.3", "socket2", "tokio", "tracing", @@ -409,7 +409,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -746,7 +746,9 @@ dependencies = [ "leptos_actix", "leptos_meta", "leptos_router", + "mio 0.8.11", "nostr-sdk", + "paste", "rusqlite", "secp256k1 0.30.0", "serde", @@ -1995,6 +1997,18 @@ dependencies = [ "adler2", ] +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + [[package]] name = "mio" version = "1.0.3" @@ -2167,7 +2181,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3063,7 +3077,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio", + "mio 1.0.3", "parking_lot", "pin-project-lite", "signal-hook-registry", @@ -3493,13 +3507,22 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3508,7 +3531,22 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -3517,28 +3555,46 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3551,24 +3607,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index db698c2..3794533 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ leptos = { version = "0.6" } leptos_meta = { version = "0.6" } leptos_actix = { version = "0.6", optional = true } leptos_router = { version = "0.6" } +paste = "1.0" wasm-bindgen = "=0.2.99" rusqlite = { version = "0.27.0", optional = true} serde = { version = "1.0", features = ["derive"] } @@ -29,6 +30,7 @@ wasm-bindgen-futures = "0.4" serde_json="1.0.133" thiserror = "2.0.9" zerofrom = "0.1" +mio = "0.8" [features] default = ["ssr"] diff --git a/items.db b/compareware.db similarity index 100% rename from items.db rename to compareware.db diff --git a/src/api.rs b/src/api.rs index bdb08fc..4ef7ea3 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1,46 +1,35 @@ #[cfg(feature = "ssr")] -use leptos::*; -#[cfg(feature = "ssr")] -use leptos::logging::log; +use actix_web::{web, HttpResponse}; #[cfg(feature = "ssr")] use crate::db::{Database, DbItem}; +#[cfg(feature = "ssr")] +use std::sync::Arc; +#[cfg(feature = "ssr")] +use tokio::sync::Mutex; #[cfg(feature = "ssr")] -#[server(UpdateItem, "/api")] -pub async fn update_item_db(client_db_item: ClientDbItem) -> Result<(), ServerFnError> { - - let db_item = DbItem { - id: client_db_item.id, - name: client_db_item.name, - description: client_db_item.description, - wikidata_id: client_db_item.wikidata_id, - custom_properties: client_db_item.custom_properties, - }; - - // Log the start of the function - log!("Starting update_item function for item: {:?}", db_item); - - // Open the database - let db = match Database::new("items.db") { - Ok(db) => { - log!("Database opened successfully"); - db +pub async fn get_items(db: web::Data>>) -> HttpResponse { + let db = db.lock().await; + match db.get_items().await { + Ok(items) => HttpResponse::Ok().json(items), + Err(err) => { + leptos::logging::error!("Failed to fetch items: {:?}", err); + HttpResponse::InternalServerError().body("Failed to fetch items") } - Err(e) => { - log!("Failed to open database: {}", e); - return Err(ServerFnError::ServerError(e.to_string())); - } - }; + } +} - // Insert the item into the database - match db.insert_item(&db_item) { - Ok(_) => { - log!("Item inserted successfully: {:?}", db_item); - Ok(()) - } - Err(e) => { - log!("Failed to insert item into database: {}", e); - Err(ServerFnError::ServerError(e.to_string())) +#[cfg(feature = "ssr")] +pub async fn create_item( + db: web::Data>>, + item: web::Json, +) -> HttpResponse { + let db = db.lock().await; + match db.insert_item(&item.into_inner()).await { + Ok(_) => HttpResponse::Ok().body("Item inserted"), + Err(err) => { + leptos::logging::error!("Failed to insert item: {:?}", err); + HttpResponse::InternalServerError().body("Failed to insert item") } } } \ No newline at end of file diff --git a/src/components/items_list.rs b/src/components/items_list.rs index 8916498..41dc946 100644 --- a/src/components/items_list.rs +++ b/src/components/items_list.rs @@ -1,17 +1,13 @@ use crate::components::editable_cell::EditableCell; use crate::components::editable_cell::InputType; use leptos::*; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; use uuid::Uuid; use leptos::logging::log; use crate::models::item::Item; use std::collections::HashMap; use std::sync::Arc; use wasm_bindgen::JsCast; -use crate::api::update_item_db; - -#[cfg(feature = "ssr")] -use crate::db::{Database, DbItem}; #[derive(Deserialize, Clone, Debug)] struct WikidataSuggestion { @@ -20,15 +16,6 @@ struct WikidataSuggestion { description: Option, } -#[derive(Debug, Serialize)] -struct ClientDbItem { - id: String, - name: String, - description: String, - wikidata_id: Option, - custom_properties: String, -} - #[component] pub fn ItemsList( items: ReadSignal>, @@ -49,23 +36,6 @@ pub fn ItemsList( //signal to store the fetched property labels let (property_labels, set_property_labels) = create_signal(HashMap::::new()); - #[cfg(feature = "ssr")] - { - log!("SSR feature is enabled, attempting to update database..."); - log!("Is SSR enabled? {}", cfg!(feature = "ssr")); - let db_path = "items.db"; // path to the database file - let db = Database::new(db_path).unwrap(); - db.create_schema().unwrap(); - - let db_items = db.get_items().unwrap(); - let loaded_items: Vec = db_items.into_iter().map(|db_item| { - serde_json::from_str(&db_item.custom_properties).unwrap() - }).collect(); - - spawn_local(async move { - set_items.set(loaded_items); - }); - } // Ensure there's an initial empty row if items.get().is_empty() { set_items.set(vec![Item { @@ -271,26 +241,26 @@ pub fn ItemsList( } } - // Send the updated item to the server using the API endpoint - let client_db_item = ClientDbItem { - id: item.id.clone(), - name: item.name.clone(), - description: item.description.clone(), - wikidata_id: item.wikidata_id.clone(), - custom_properties: serde_json::to_string(&item.custom_properties).unwrap(), - }; + // // Send the updated item to the server using the API endpoint + // let client_db_item = ClientDbItem { + // id: item.id.clone(), + // name: item.name.clone(), + // description: item.description.clone(), + // wikidata_id: item.wikidata_id.clone(), + // custom_properties: serde_json::to_string(&item.custom_properties).unwrap(), + // }; - spawn_local(async move { - match update_item_db(client_db_item).await { - Ok(_) => { - log!("Item updated successfully on the server"); - } - Err(e) => { - log!("Error updating item on the server: {}", e); - } - } - }); + // spawn_local(async move { + // match update_item_db(client_db_item).await { + // Ok(_) => { + // log!("Item updated successfully on the server"); + // } + // Err(e) => { + // log!("Error updating item on the server: {}", e); + // } + // } + // }); } // Automatically add a new row when editing the last row if index == items.len() - 1 && !value.is_empty() { diff --git a/src/db.rs b/src/db.rs index 2bf78a5..0f9047f 100644 --- a/src/db.rs +++ b/src/db.rs @@ -2,47 +2,64 @@ mod db_impl { use rusqlite::{Connection, Error}; use serde::{Deserialize, Serialize}; + use std::sync::Arc; + use tokio::sync::Mutex; + use leptos::logging; // Define a struct to represent a database connection #[derive(Debug)] pub struct Database { - conn: Connection, + conn: Arc>, } impl Database { // Create a new database connection pub fn new(db_path: &str) -> Result { let conn = Connection::open(db_path)?; - Ok(Database { conn }) + logging::log!("Database connection established at: {}", db_path); // Log with Leptos + Ok(Database { + conn: Arc::new(Mutex::new(conn)), + }) } // Create the database schema - pub fn create_schema(&self) -> Result<(), Error> { - self.conn.execute_batch(" - CREATE TABLE IF NOT EXISTS items ( + pub async fn create_schema(&self) -> Result<(), Error> { + let conn = self.conn.lock().await; + conn.execute_batch( + "CREATE TABLE IF NOT EXISTS items ( id TEXT PRIMARY KEY, name TEXT NOT NULL, description TEXT, wikidata_id TEXT, custom_properties TEXT - ); - ")?; + );", + )?; + logging::log!("Database schema created or verified"); // Log with Leptos Ok(()) } // Insert a new item into the database - pub fn insert_item(&self, item: &DbItem) -> Result<(), Error> { + pub async fn insert_item(&self, item: &DbItem) -> Result<(), Error> { + let conn = self.conn.lock().await; let wikidata_id = item.wikidata_id.as_ref().map(|s| s.as_str()).unwrap_or(""); - self.conn.execute( + conn.execute( "INSERT INTO items (id, name, description, wikidata_id, custom_properties) VALUES (?, ?, ?, ?, ?);", - &[&item.id, &item.name, &item.description, &wikidata_id.to_string(), &item.custom_properties], + &[ + &item.id, + &item.name, + &item.description, + &wikidata_id.to_string(), + &item.custom_properties, + ], )?; + logging::log!("Item inserted: {}", item.id); // Log with Leptos Ok(()) } // Retrieve all items from the database - pub fn get_items(&self) -> Result, Error> { - let mut stmt = self.conn.prepare("SELECT * FROM items;")?; + pub async fn get_items(&self) -> Result, Error> { + let conn = self.conn.lock().await; + let mut stmt = conn.prepare("SELECT * FROM items;")?; let items = stmt.query_map([], |row| { Ok(DbItem { id: row.get(0)?, @@ -56,6 +73,7 @@ mod db_impl { for item in items { result.push(item?); } + logging::log!("Fetched {} items from the database", result.len()); // Log with Leptos Ok(result) } } @@ -69,7 +87,29 @@ mod db_impl { pub wikidata_id: Option, pub custom_properties: String, } + + // Implement conversion from DbItem to a JSON-friendly format + #[derive(Debug, Deserialize, Serialize, Clone)] + pub struct ItemResponse { + pub id: String, + pub name: String, + pub description: String, + pub wikidata_id: Option, + pub custom_properties: String, + } + + impl From for ItemResponse { + fn from(item: DbItem) -> Self { + ItemResponse { + id: item.id, + name: item.name, + description: item.description, + wikidata_id: item.wikidata_id, + custom_properties: item.custom_properties, + } + } + } } #[cfg(feature = "ssr")] -pub use db_impl::{Database, DbItem}; \ No newline at end of file +pub use db_impl::{Database, DbItem, ItemResponse}; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 2abc297..2c34e5c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,3 @@ -use actix_web::{web, App, HttpServer}; -use compareware::api::update_item_db; // Corrected server function name -use compareware::db::Database; #[cfg(feature = "ssr")] #[actix_web::main] async fn main() -> std::io::Result<()> { @@ -9,11 +6,20 @@ async fn main() -> std::io::Result<()> { use leptos::*; use leptos_actix::{generate_route_list, LeptosRoutes}; use compareware::app::*; + use compareware::db::{Database, DbItem}; + use compareware::api::{get_items, create_item}; // Import API handlers + use std::sync::Arc; + use tokio::sync::Mutex; // Load configuration let conf = get_configuration(None).await.unwrap(); let addr = conf.leptos_options.site_addr; + // Initialize the database + let db = Database::new("compareware.db").unwrap(); + db.create_schema().await.unwrap(); // Ensure the schema is created + let db = Arc::new(Mutex::new(db)); // Wrap the database in an Arc> for shared state + // Generate the list of routes in your Leptos App let routes = generate_route_list(App); println!("listening on http://{}", &addr); @@ -22,6 +28,8 @@ async fn main() -> std::io::Result<()> { HttpServer::new(move || { let leptos_options = &conf.leptos_options; let site_root = &leptos_options.site_root; + let db = db.clone(); // Clone the Arc for each worker + App::new() // Register server functions @@ -37,6 +45,11 @@ async fn main() -> std::io::Result<()> { // Pass Leptos options to the app .app_data(web::Data::new(leptos_options.to_owned())) //.wrap(middleware::Compress::default()) + // Pass the database as shared state + .app_data(web::Data::new(db)) + // Register API endpoints + .route("/api/items", web::get().to(get_items)) + .route("/api/items", web::post().to(create_item)) }) .bind(&addr)? .run()