From 25983f701a2202002cfc6b4157d7e76d0b0c3c64 Mon Sep 17 00:00:00 2001 From: despiegk Date: Sun, 20 Apr 2025 08:29:35 +0200 Subject: [PATCH] ... --- herodb/src/cmd/dbexample_mcc/main.rs | 2 +- herodb/src/db/mod.rs | 3 - herodb/src/instructions.md | 0 herodb/src/models/biz/contract.rs | 13 +-- herodb/src/models/biz/customer.rs | 13 +-- herodb/src/models/biz/invoice.rs | 67 ++++++------ herodb/src/models/biz/lib.rs | 4 +- herodb/src/models/biz/sale.rs | 147 +++++++++++++++------------ herodb/src/models/biz/service.rs | 126 ++++++++++++----------- herodb/src/models/circle/circle.rs | 13 +-- herodb/src/models/circle/lib.rs | 2 +- herodb/src/models/circle/member.rs | 13 +-- herodb/src/models/circle/mod.rs | 2 +- herodb/src/models/circle/name.rs | 13 +-- herodb/src/models/circle/wallet.rs | 13 +-- herodb/src/models/instructions.md | 5 +- instructions.md | 126 +++++++++++++++++++++++ 17 files changed, 348 insertions(+), 214 deletions(-) delete mode 100644 herodb/src/instructions.md create mode 100644 instructions.md diff --git a/herodb/src/cmd/dbexample_mcc/main.rs b/herodb/src/cmd/dbexample_mcc/main.rs index 18a7d3a..302a241 100644 --- a/herodb/src/cmd/dbexample_mcc/main.rs +++ b/herodb/src/cmd/dbexample_mcc/main.rs @@ -365,7 +365,7 @@ fn main() -> Result<(), Box> { // Get the calendar for an event println!("\nGetting calendar for Family Dinner event (ID: {}):", family_dinner.id); - let event_calendar = db.get::(&family_dinner.calendar_id.to_string())?; + let event_calendar = db.get::(family_dinner.calendar_id)?; println!(" - Calendar: {} ({})", event_calendar.title, event_calendar.description); // Get events for a contact diff --git a/herodb/src/db/mod.rs b/herodb/src/db/mod.rs index 191cb70..37b8f11 100644 --- a/herodb/src/db/mod.rs +++ b/herodb/src/db/mod.rs @@ -14,8 +14,5 @@ pub use store::{DbOperations, OurDbStore}; pub mod db; pub use db::{DB, DBBuilder, ModelRegistration, ModelRegistrar}; -// Export the base module (compatibility layer for migration) -pub mod base; - // Export macros for model methods pub mod macros; diff --git a/herodb/src/instructions.md b/herodb/src/instructions.md deleted file mode 100644 index e69de29..0000000 diff --git a/herodb/src/models/biz/contract.rs b/herodb/src/models/biz/contract.rs index e4c4dcc..2babb22 100644 --- a/herodb/src/models/biz/contract.rs +++ b/herodb/src/models/biz/contract.rs @@ -1,4 +1,4 @@ -use crate::db::base::{SledModel, Storable}; // Import Sled traits from db module +use crate::db::{Model, Storable}; // Import Model trait from db module use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -235,13 +235,10 @@ impl ContractBuilder { } } -// Implement Storable trait (provides default dump/load) -impl Storable for Contract {} - -// Implement SledModel trait -impl SledModel for Contract { - fn get_id(&self) -> String { - self.id.to_string() +// Implement Model trait +impl Model for Contract { + fn get_id(&self) -> u32 { + self.id } fn db_prefix() -> &'static str { diff --git a/herodb/src/models/biz/customer.rs b/herodb/src/models/biz/customer.rs index 757b763..7cb68cf 100644 --- a/herodb/src/models/biz/customer.rs +++ b/herodb/src/models/biz/customer.rs @@ -1,4 +1,4 @@ -use crate::db::base::{SledModel, Storable}; // Import Sled traits from db module +use crate::db::{Model, Storable}; // Import Model trait from db module use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -133,13 +133,10 @@ impl CustomerBuilder { } } -// Implement Storable trait (provides default dump/load) -impl Storable for Customer {} - -// Implement SledModel trait -impl SledModel for Customer { - fn get_id(&self) -> String { - self.id.to_string() +// Implement Model trait +impl Model for Customer { + fn get_id(&self) -> u32 { + self.id } fn db_prefix() -> &'static str { diff --git a/herodb/src/models/biz/invoice.rs b/herodb/src/models/biz/invoice.rs index 5c66257..956eb81 100644 --- a/herodb/src/models/biz/invoice.rs +++ b/herodb/src/models/biz/invoice.rs @@ -1,5 +1,5 @@ use crate::models::biz::Currency; // Use crate:: for importing from the module -use crate::db::base::{SledModel, Storable}; // Import Sled traits from db module +use crate::db::{Model, Storable}; // Import Model trait from db module use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -185,10 +185,11 @@ impl Invoice { due_date: DateTime, ) -> Self { let now = Utc::now(); - let zero_amount = Currency { - amount: 0.0, - currency_code: currency_code.clone(), - }; + let zero_amount = Currency::new( + 0, // Use 0 as a temporary ID for zero amounts + 0.0, + currency_code.clone() + ); Self { id, @@ -214,14 +215,16 @@ impl Invoice { // Update the total amount if self.items.is_empty() { // First item, initialize the total amount with the same currency - self.total_amount = Currency { - amount: item.amount.amount, - currency_code: item.amount.currency_code.clone(), - }; - self.balance_due = Currency { - amount: item.amount.amount, - currency_code: item.amount.currency_code.clone(), - }; + self.total_amount = Currency::new( + 0, // Use 0 as a temporary ID + item.amount.amount, + item.amount.currency_code.clone() + ); + self.balance_due = Currency::new( + 0, // Use 0 as a temporary ID + item.amount.amount, + item.amount.currency_code.clone() + ); } else { // Add to the existing total // (Assumes all items have the same currency) @@ -252,10 +255,11 @@ impl Invoice { } // Update the total amount - self.total_amount = Currency { - amount: total, - currency_code: currency_code.clone(), - }; + self.total_amount = Currency::new( + 0, // Use 0 as a temporary ID + total, + currency_code.clone() + ); // Recalculate the balance due self.calculate_balance(); @@ -290,10 +294,11 @@ impl Invoice { } // Update the balance due - self.balance_due = Currency { - amount: balance, - currency_code: self.total_amount.currency_code.clone(), - }; + self.balance_due = Currency::new( + 0, // Use 0 as a temporary ID + balance, + self.total_amount.currency_code.clone() + ); // Update the payment status self.update_payment_status(); @@ -438,10 +443,11 @@ impl InvoiceBuilder { let currency_code = self.currency_code.ok_or("currency_code is required")?; // Initialize with empty total amount and balance due - let mut total_amount = Currency { - amount: 0.0, - currency_code: currency_code.clone(), - }; + let mut total_amount = Currency::new( + 0, // Use 0 as a temporary ID + 0.0, + currency_code.clone() + ); // Calculate total amount from items for item in &self.items { @@ -494,13 +500,10 @@ impl InvoiceBuilder { } } -// Implement Storable trait (provides default dump/load) -impl Storable for Invoice {} - -// Implement SledModel trait -impl SledModel for Invoice { - fn get_id(&self) -> String { - self.id.to_string() +// Implement Model trait +impl Model for Invoice { + fn get_id(&self) -> u32 { + self.id } fn db_prefix() -> &'static str { diff --git a/herodb/src/models/biz/lib.rs b/herodb/src/models/biz/lib.rs index 785a06f..407d18a 100644 --- a/herodb/src/models/biz/lib.rs +++ b/herodb/src/models/biz/lib.rs @@ -26,5 +26,5 @@ pub use product::Currency; pub use currency::CurrencyBuilder; // Re-export database components -// Re-export from core module -pub use crate::core::{SledDB, SledDBError, SledDBResult, Storable, SledModel, DB}; +// Re-export database components from db module +pub use crate::db::{DB, DBBuilder, Model, Storable, DbError, DbResult, ModelRegistration, ModelRegistrar}; diff --git a/herodb/src/models/biz/sale.rs b/herodb/src/models/biz/sale.rs index 00f7c43..cb9fbb3 100644 --- a/herodb/src/models/biz/sale.rs +++ b/herodb/src/models/biz/sale.rs @@ -1,5 +1,5 @@ -use crate::db::base::{SledModel, Storable}; -use crate::models::biz::Currency; // Use crate:: for importing from the module // Import Sled traits from db module +use crate::db::{Model, Storable}; +use crate::models::biz::Currency; // Use crate:: for importing from the module // use super::db::Model; // Removed old Model trait import use chrono::{DateTime, Utc}; use rhai::{CustomType, TypeBuilder}; @@ -47,17 +47,19 @@ impl SaleItem { ) -> Self { // Calculate subtotal (before tax) let amount = unit_price.amount * quantity as f64; - let subtotal = Currency { + let subtotal = Currency::new( + 0, // Use 0 as a temporary ID amount, - currency_code: unit_price.currency_code.clone(), - }; + unit_price.currency_code.clone() + ); // Calculate tax amount let tax_amount_value = subtotal.amount * (tax_rate / 100.0); - let tax_amount = Currency { - amount: tax_amount_value, - currency_code: unit_price.currency_code.clone(), - }; + let tax_amount = Currency::new( + 0, // Use 0 as a temporary ID + tax_amount_value, + unit_price.currency_code.clone() + ); Self { id, @@ -77,10 +79,11 @@ impl SaleItem { /// Get the total amount including tax pub fn total_with_tax(&self) -> Currency { - Currency { - amount: self.subtotal.amount + self.tax_amount.amount, - currency_code: self.subtotal.currency_code.clone(), - } + Currency::new( + 0, // Use 0 as a temporary ID + self.subtotal.amount + self.tax_amount.amount, + self.subtotal.currency_code.clone() + ) } } @@ -188,17 +191,19 @@ impl SaleItemBuilder { // Calculate subtotal let amount = unit_price.amount * quantity as f64; - let subtotal = Currency { + let subtotal = Currency::new( + 0, // Use 0 as a temporary ID amount, - currency_code: unit_price.currency_code.clone(), - }; + unit_price.currency_code.clone() + ); // Calculate tax amount let tax_amount_value = subtotal.amount * (tax_rate / 100.0); - let tax_amount = Currency { - amount: tax_amount_value, - currency_code: unit_price.currency_code.clone(), - }; + let tax_amount = Currency::new( + 0, // Use 0 as a temporary ID + tax_amount_value, + unit_price.currency_code.clone() + ); Ok(SaleItem { id: self.id.ok_or("id is required")?, @@ -250,10 +255,11 @@ impl Sale { status: SaleStatus, ) -> Self { let now = Utc::now(); - let zero_currency = Currency { - amount: 0.0, - currency_code: currency_code.clone(), - }; + let zero_currency = Currency::new( + 0, // Use 0 as a temporary ID + 0.0, + currency_code.clone() + ); Self { id, @@ -281,18 +287,21 @@ impl Sale { // Update the amounts if self.items.is_empty() { // First item, initialize the amounts with the same currency - self.subtotal_amount = Currency { - amount: item.subtotal.amount, - currency_code: item.subtotal.currency_code.clone(), - }; - self.tax_amount = Currency { - amount: item.tax_amount.amount, - currency_code: item.tax_amount.currency_code.clone(), - }; - self.total_amount = Currency { - amount: item.subtotal.amount + item.tax_amount.amount, - currency_code: item.subtotal.currency_code.clone(), - }; + self.subtotal_amount = Currency::new( + 0, // Use 0 as a temporary ID + item.subtotal.amount, + item.subtotal.currency_code.clone() + ); + self.tax_amount = Currency::new( + 0, // Use 0 as a temporary ID + item.tax_amount.amount, + item.tax_amount.currency_code.clone() + ); + self.total_amount = Currency::new( + 0, // Use 0 as a temporary ID + item.subtotal.amount + item.tax_amount.amount, + item.subtotal.currency_code.clone() + ); } else { // Add to the existing totals // (Assumes all items have the same currency) @@ -327,18 +336,21 @@ impl Sale { } // Update the amounts - self.subtotal_amount = Currency { - amount: subtotal, - currency_code: currency_code.clone(), - }; - self.tax_amount = Currency { - amount: tax_total, - currency_code: currency_code.clone(), - }; - self.total_amount = Currency { - amount: subtotal + tax_total, - currency_code, - }; + self.subtotal_amount = Currency::new( + 0, // Use 0 as a temporary ID + subtotal, + currency_code.clone() + ); + self.tax_amount = Currency::new( + 0, // Use 0 as a temporary ID + tax_total, + currency_code.clone() + ); + self.total_amount = Currency::new( + 0, // Use 0 as a temporary ID + subtotal + tax_total, + currency_code + ); // Update the timestamp self.updated_at = Utc::now(); @@ -505,18 +517,21 @@ impl SaleBuilder { let currency_code = self.currency_code.ok_or("currency_code is required")?; // Initialize with empty amounts - let mut subtotal_amount = Currency { - amount: 0.0, - currency_code: currency_code.clone(), - }; - let mut tax_amount = Currency { - amount: 0.0, - currency_code: currency_code.clone(), - }; - let mut total_amount = Currency { - amount: 0.0, - currency_code: currency_code.clone(), - }; + let mut subtotal_amount = Currency::new( + 0, // Use 0 as a temporary ID + 0.0, + currency_code.clone() + ); + let mut tax_amount = Currency::new( + 0, // Use 0 as a temporary ID + 0.0, + currency_code.clone() + ); + let mut total_amount = Currency::new( + 0, // Use 0 as a temporary ID + 0.0, + currency_code.clone() + ); // Calculate amounts from items for item in &self.items { @@ -551,16 +566,14 @@ impl SaleBuilder { } } -// Implement Storable trait (provides default dump/load) -impl Storable for Sale {} - -// Implement SledModel trait -impl SledModel for Sale { - fn get_id(&self) -> String { - self.id.to_string() +// Implement Model trait +impl Model for Sale { + fn get_id(&self) -> u32 { + self.id } fn db_prefix() -> &'static str { "sale" } } + diff --git a/herodb/src/models/biz/service.rs b/herodb/src/models/biz/service.rs index c22b57f..6a3a505 100644 --- a/herodb/src/models/biz/service.rs +++ b/herodb/src/models/biz/service.rs @@ -1,5 +1,5 @@ use crate::models::biz::Currency; // Use crate:: for importing from the module -use crate::db::base::{SledModel, Storable}; // Import Sled traits from db module +use crate::db::{Model, Storable}; // Import Model trait from db module use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; @@ -57,22 +57,25 @@ impl ServiceItem { ) -> Self { // Calculate subtotal let amount = unit_price.amount * quantity as f64; - let subtotal = Currency { + let subtotal = Currency::new( + 0, // Use 0 as a temporary ID amount, - currency_code: unit_price.currency_code.clone(), - }; + unit_price.currency_code.clone() + ); // Calculate tax amount if taxable let tax_amount = if is_taxable { - Currency { - amount: subtotal.amount * tax_rate, - currency_code: unit_price.currency_code.clone(), - } + Currency::new( + 0, // Use 0 as a temporary ID + subtotal.amount * tax_rate, + unit_price.currency_code.clone() + ) } else { - Currency { - amount: 0.0, - currency_code: unit_price.currency_code.clone(), - } + Currency::new( + 0, // Use 0 as a temporary ID + 0.0, + unit_price.currency_code.clone() + ) }; Self { @@ -95,24 +98,27 @@ impl ServiceItem { /// Calculate the subtotal based on quantity and unit price pub fn calculate_subtotal(&mut self) { let amount = self.unit_price.amount * self.quantity as f64; - self.subtotal = Currency { + self.subtotal = Currency::new( + 0, // Use 0 as a temporary ID amount, - currency_code: self.unit_price.currency_code.clone(), - }; + self.unit_price.currency_code.clone() + ); } /// Calculate the tax amount based on subtotal and tax rate pub fn calculate_tax(&mut self) { if self.is_taxable { - self.tax_amount = Currency { - amount: self.subtotal.amount * self.tax_rate, - currency_code: self.subtotal.currency_code.clone(), - }; + self.tax_amount = Currency::new( + 0, // Use 0 as a temporary ID + self.subtotal.amount * self.tax_rate, + self.subtotal.currency_code.clone() + ); } else { - self.tax_amount = Currency { - amount: 0.0, - currency_code: self.subtotal.currency_code.clone(), - }; + self.tax_amount = Currency::new( + 0, // Use 0 as a temporary ID + 0.0, + self.subtotal.currency_code.clone() + ); } } } @@ -229,22 +235,25 @@ impl ServiceItemBuilder { // Calculate subtotal let amount = unit_price.amount * quantity as f64; - let subtotal = Currency { + let subtotal = Currency::new( + 0, // Use 0 as a temporary ID amount, - currency_code: unit_price.currency_code.clone(), - }; + unit_price.currency_code.clone() + ); // Calculate tax amount if taxable let tax_amount = if is_taxable { - Currency { - amount: subtotal.amount * tax_rate, - currency_code: unit_price.currency_code.clone(), - } + Currency::new( + 0, // Use 0 as a temporary ID + subtotal.amount * tax_rate, + unit_price.currency_code.clone() + ) } else { - Currency { - amount: 0.0, - currency_code: unit_price.currency_code.clone(), - } + Currency::new( + 0, // Use 0 as a temporary ID + 0.0, + unit_price.currency_code.clone() + ) }; Ok(ServiceItem { @@ -292,7 +301,7 @@ impl Service { Self { id, customer_id, - total_amount: Currency { amount: 0.0, currency_code }, + total_amount: Currency::new(0, 0.0, currency_code), status, billing_frequency, service_date: now, @@ -310,10 +319,11 @@ impl Service { // Update the total amount if self.items.is_empty() { // First item, initialize the total amount with the same currency - self.total_amount = Currency { - amount: item.subtotal.amount + item.tax_amount.amount, - currency_code: item.subtotal.currency_code.clone(), - }; + self.total_amount = Currency::new( + 0, // Use 0 as a temporary ID + item.subtotal.amount + item.tax_amount.amount, + item.subtotal.currency_code.clone() + ); } else { // Add to the existing total // (Assumes all items have the same currency) @@ -343,10 +353,11 @@ impl Service { } // Update the total amount - self.total_amount = Currency { - amount: total, - currency_code, - }; + self.total_amount = Currency::new( + 0, // Use 0 as a temporary ID + total, + currency_code + ); // Update the service timestamp self.updated_at = Utc::now(); @@ -439,10 +450,11 @@ impl ServiceBuilder { let currency_code = self.currency_code.ok_or("currency_code is required")?; // Initialize with empty total amount - let mut total_amount = Currency { - amount: 0.0, - currency_code: currency_code.clone(), - }; + let mut total_amount = Currency::new( + 0, // Use 0 as a temporary ID + 0.0, + currency_code.clone() + ); // Calculate total amount from items for item in &self.items { @@ -453,10 +465,11 @@ impl ServiceBuilder { if total_amount.amount == 0.0 { // First item, initialize the total amount with the same currency - total_amount = Currency { - amount: item.subtotal.amount + item.tax_amount.amount, - currency_code: item.subtotal.currency_code.clone(), - }; + total_amount = Currency::new( + 0, // Use 0 as a temporary ID + item.subtotal.amount + item.tax_amount.amount, + item.subtotal.currency_code.clone() + ); } else { // Add to the existing total // (Assumes all items have the same currency) @@ -478,13 +491,10 @@ impl ServiceBuilder { } } -// Implement Storable trait (provides default dump/load) -impl Storable for Service {} - -// Implement SledModel trait -impl SledModel for Service { - fn get_id(&self) -> String { - self.id.to_string() +// Implement Model trait +impl Model for Service { + fn get_id(&self) -> u32 { + self.id } fn db_prefix() -> &'static str { diff --git a/herodb/src/models/circle/circle.rs b/herodb/src/models/circle/circle.rs index 62e1995..d33bfd8 100644 --- a/herodb/src/models/circle/circle.rs +++ b/herodb/src/models/circle/circle.rs @@ -1,5 +1,5 @@ use serde::{Deserialize, Serialize}; -use crate::db::{SledModel, Storable}; +use crate::db::{Model, Storable}; use std::collections::HashMap; /// Circle represents a collection of members (users or other circles) @@ -28,13 +28,10 @@ impl Circle { } } -// Implement Storable trait (provides default dump/load) -impl Storable for Circle {} - -// Implement SledModel trait -impl SledModel for Circle { - fn get_id(&self) -> String { - self.id.to_string() +// Implement Model trait +impl Model for Circle { + fn get_id(&self) -> u32 { + self.id } fn db_prefix() -> &'static str { diff --git a/herodb/src/models/circle/lib.rs b/herodb/src/models/circle/lib.rs index 61044c0..699f2cd 100644 --- a/herodb/src/models/circle/lib.rs +++ b/herodb/src/models/circle/lib.rs @@ -6,4 +6,4 @@ pub use circle::{Circle, Member, Role}; pub use name::{Name, Record, RecordType}; // Re-export database components -pub use crate::db::{SledDB, SledDBError, SledDBResult, Storable, SledModel, DB}; +pub use crate::db::{DB, DBBuilder, Model, Storable, DbError, DbResult, ModelRegistration, ModelRegistrar}; diff --git a/herodb/src/models/circle/member.rs b/herodb/src/models/circle/member.rs index c39fa1f..1d4d410 100644 --- a/herodb/src/models/circle/member.rs +++ b/herodb/src/models/circle/member.rs @@ -1,5 +1,5 @@ use serde::{Deserialize, Serialize}; -use crate::db::{SledModel, Storable}; +use crate::db::{Model, Storable}; use std::collections::HashMap; /// Role represents the role of a member in a circle @@ -67,13 +67,10 @@ impl Member { } } -// Implement Storable trait (provides default dump/load) -impl Storable for Member {} - -// Implement SledModel trait -impl SledModel for Member { - fn get_id(&self) -> String { - self.id.to_string() +// Implement Model trait +impl Model for Member { + fn get_id(&self) -> u32 { + self.id } fn db_prefix() -> &'static str { diff --git a/herodb/src/models/circle/mod.rs b/herodb/src/models/circle/mod.rs index 2f578c4..02d48d4 100644 --- a/herodb/src/models/circle/mod.rs +++ b/herodb/src/models/circle/mod.rs @@ -10,4 +10,4 @@ pub use name::{Name, Record, RecordType}; pub use wallet::{Wallet, Asset}; // Re-export database components from db module -pub use crate::db::{SledDB, SledDBError, SledDBResult, Storable, SledModel, DB}; +pub use crate::db::{DB, DBBuilder, Model, Storable, DbError, DbResult, ModelRegistration, ModelRegistrar}; diff --git a/herodb/src/models/circle/name.rs b/herodb/src/models/circle/name.rs index 953d115..0cc5db2 100644 --- a/herodb/src/models/circle/name.rs +++ b/herodb/src/models/circle/name.rs @@ -1,5 +1,5 @@ use serde::{Deserialize, Serialize}; -use crate::db::{SledModel, Storable}; +use crate::db::{Model, Storable}; /// Record types for a DNS record #[derive(Debug, Clone, Serialize, Deserialize)] @@ -57,13 +57,10 @@ impl Name { } } -// Implement Storable trait (provides default dump/load) -impl Storable for Name {} - -// Implement SledModel trait -impl SledModel for Name { - fn get_id(&self) -> String { - self.id.to_string() +// Implement Model trait +impl Model for Name { + fn get_id(&self) -> u32 { + self.id } fn db_prefix() -> &'static str { diff --git a/herodb/src/models/circle/wallet.rs b/herodb/src/models/circle/wallet.rs index ff4b5e1..da187e6 100644 --- a/herodb/src/models/circle/wallet.rs +++ b/herodb/src/models/circle/wallet.rs @@ -1,5 +1,5 @@ use serde::{Deserialize, Serialize}; -use crate::db::{SledModel, Storable}; +use crate::db::{Model, Storable}; use std::collections::HashMap; /// Asset represents a cryptocurrency asset in a wallet @@ -69,13 +69,10 @@ impl Wallet { } } -// Implement Storable trait (provides default dump/load) -impl Storable for Wallet {} - -// Implement SledModel trait -impl SledModel for Wallet { - fn get_id(&self) -> String { - self.id.to_string() +// Implement Model trait +impl Model for Wallet { + fn get_id(&self) -> u32 { + self.id } fn db_prefix() -> &'static str { diff --git a/herodb/src/models/instructions.md b/herodb/src/models/instructions.md index 3a2cfc7..164450e 100644 --- a/herodb/src/models/instructions.md +++ b/herodb/src/models/instructions.md @@ -2,7 +2,10 @@ in @src/models/circle/circle.rs - member us now new rootobject, check implementation - a member is linked to one or more contacts id's (from src/models/mcc/contacts.rs) -- a member has one or more wallets +- create a new rootobject called wallet + - has a name, description, blockchainname (string), pubkey +- a wallet has embedded struct for asset which is name e.g. USDC and float which is the amount of money in the asset +- a member has one or more wallets, in member link to the id's of the wallet in@src/models/biz add a ticket module diff --git a/instructions.md b/instructions.md new file mode 100644 index 0000000..de7617c --- /dev/null +++ b/instructions.md @@ -0,0 +1,126 @@ +# HeroDB: ACL Layer Implementation + +## Project Overview + +Create a new module that implements an Access Control List (ACL) layer on top of the existing `ourdb` and `tst` databases. This module will manage permissions and access control for data stored in the database system. + +## Architecture + +- The module will sit as a layer between client applications and the underlying `ourdb` & `tst` databases +- ACLs are defined at the circle level and stored in a special topic called "acl" +- Data in `ourdb` is stored at path: `~/hero/var/ourdb/$circleid/$topicid` +- `tst` is used to create mappings between keys and IDs in `ourdb` + +## ACL Structure + +Each ACL contains: +- A unique name (per circle) +- A list of public keys with associated permissions +- Rights are hierarchical: read → write → delete → execute → admin (each right includes all rights to its left) + +## Core Methods + +### ACL Management + +#### aclupdate +Updates or creates an ACL with specified permissions. + +**Parameters:** +- `callerpubkey`: Public key of the requesting user +- `circleid`: ID of the circle where the ACL exists +- `name`: Unique name for the ACL within the circle +- `pubkeys`: Array of public keys to grant permissions to +- `right`: Permission level (enum: read/write/delete/execute/admin) +#### aclremove +Removes specific public keys from an existing ACL. + +**Parameters:** +- `callerpubkey`: Public key of the requesting user +- `circleid`: ID of the circle where the ACL exists +- `name`: Name of the ACL to modify +- `pubkeys`: Array of public keys to remove from the ACL + +#### acldel +Deletes an entire ACL. + +**Parameters:** +- `callerpubkey`: Public key of the requesting user +- `circleid`: ID of the circle where the ACL exists +- `name`: Name of the ACL to delete + +### Data Operations + +#### set +Stores or updates data in the database with optional ACL protection. + +**Parameters:** +- `callerpubkey`: Public key of the requesting user +- `circleid`: ID of the circle where the data belongs +- `topic`: String identifier for the database category (e.g., "customer", "product") +- `key`: Optional string key for the record +- `id`: Optional numeric ID for direct access +- `value`: Binary blob of data to store +- `aclid`: ID of the ACL to protect this record (0 for public access) + +**Behavior:** +- If only `key` is provided, use `tst` to map the key to a new or existing ID +- If `id` is specified or derived from an existing key, update the corresponding record +- Returns the ID of the created/updated record + +#### del +Marks a record as deleted. + +**Parameters:** +- `callerpubkey`: Public key of the requesting user +- `circleid`: ID of the circle where the data belongs +- `topic`: String identifier for the database category +- `id` or `key`: Identifier for the record to delete + +**Behavior:** +- Deletes the mapping in `tst` if a key was used +- Marks the record as deleted in `ourdb` (not physically removed) + +#### get +Retrieves data from the database. + +**Parameters:** +- `callerpubkey`: Public key of the requesting user +- `circleid`: ID of the circle where the data belongs +- `topic`: String identifier for the database category +- `id` or `key`: Identifier for the record to retrieve + +**Returns:** +- The binary data stored in the record if the caller has access + +## Implementation Details + +### ACL Storage Format +- ACLs are stored in a special topic named "acl" within each circle +- Each ACL has a unique numeric ID within the circle + +### Record ACL Protection +- When a record uses ACL protection, the first 4 bytes of the stored data contain the ACL ID +- A new constructor in `ourdb` should be created to handle ACL-protected records +- Records with ACL ID of 0 are accessible to everyone + +## RPC Interface + +The module should expose its functionality through an RPC interface: + +1. Client sends: + - Method name (e.g., "del", "set", "get") + - JSON-encoded arguments + - Cryptographic signature of the JSON data + +2. Server: + - Verifies the signature is valid + - Extracts the caller's public key from the signature + - Checks permissions against applicable ACLs + - Executes the requested operation if authorized + - Returns appropriate response + +## Security Considerations + +- All operations must validate the caller has appropriate permissions +- ACL changes should be logged for audit purposes +- Consider implementing rate limiting to prevent abuse