...
This commit is contained in:
parent
a8ef07bb3f
commit
25983f701a
@ -365,7 +365,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
// Get the calendar for an event
|
||||
println!("\nGetting calendar for Family Dinner event (ID: {}):", family_dinner.id);
|
||||
let event_calendar = db.get::<Calendar>(&family_dinner.calendar_id.to_string())?;
|
||||
let event_calendar = db.get::<Calendar>(family_dinner.calendar_id)?;
|
||||
println!(" - Calendar: {} ({})", event_calendar.title, event_calendar.description);
|
||||
|
||||
// Get events for a contact
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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<Utc>,
|
||||
) -> 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 {
|
||||
|
@ -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};
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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};
|
||||
|
@ -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 {
|
||||
|
@ -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};
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
126
instructions.md
Normal file
126
instructions.md
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user