tap_node/storage/mod.rs
1//! Storage module for persisting TAP messages and transactions
2//!
3//! This module provides comprehensive persistent storage capabilities for the TAP Node,
4//! maintaining both business transaction records and a complete message audit trail
5//! in a SQLite database for compliance, debugging, and operational purposes.
6//!
7//! # Architecture
8//!
9//! The storage system uses a dual-table design:
10//! - **transactions**: Stores Transfer and Payment messages for business logic
11//! - **messages**: Stores all messages (incoming/outgoing) for audit trail
12//!
13//! # Features
14//!
15//! - **Automatic Schema Migration**: Database schema is created and migrated automatically
16//! - **Connection Pooling**: Uses sqlx's built-in async connection pool
17//! - **Async API**: Native async operations without blocking threads
18//! - **WASM Compatibility**: Storage is automatically disabled in WASM builds
19//! - **Idempotent Operations**: Duplicate messages are silently ignored
20//! - **Direction Tracking**: Messages are tagged as incoming or outgoing
21//! - **Thread Tracking**: Full support for DIDComm thread and parent thread IDs
22//!
23//! # Usage
24//!
25//! Storage is automatically initialized when creating a TapNode with the storage feature enabled:
26//!
27//! ```no_run
28//! use tap_node::{NodeConfig, TapNode};
29//! use tap_node::storage::MessageDirection;
30//! use std::path::PathBuf;
31//!
32//! # async fn example() -> Result<(), Box<dyn std::error::Error>> {
33//! let config = NodeConfig {
34//! #[cfg(feature = "storage")]
35//! storage_path: Some(PathBuf::from("./my-database.db")),
36//! ..Default::default()
37//! };
38//!
39//! let node = TapNode::new(config);
40//!
41//! // Access storage functionality
42//! if let Some(storage) = node.storage() {
43//! // Query transactions
44//! let txs = storage.list_transactions(10, 0).await?;
45//!
46//! // Query message audit trail
47//! let messages = storage.list_messages(20, 0, Some(MessageDirection::Incoming)).await?;
48//! }
49//! # Ok(())
50//! # }
51//! ```
52//!
53//! # Environment Variables
54//!
55//! - `TAP_NODE_DB_PATH`: Override the default database path
56//!
57//! # Automatic Message Logging
58//!
59//! The TapNode automatically logs all messages:
60//! - Incoming messages are logged when `receive_message()` is called
61//! - Outgoing messages are logged when `send_message()` is called
62//! - Transfer and Payment messages are additionally stored in the transactions table
63
64#[cfg(feature = "storage")]
65pub mod agent_storage_manager;
66#[cfg(feature = "storage")]
67pub mod db;
68#[cfg(feature = "storage")]
69pub mod error;
70#[cfg(feature = "storage")]
71pub mod models;
72
73#[cfg(feature = "storage")]
74pub use agent_storage_manager::AgentStorageManager;
75#[cfg(feature = "storage")]
76pub use db::Storage;
77#[cfg(feature = "storage")]
78pub use error::StorageError;
79#[cfg(feature = "storage")]
80pub use models::{
81 Customer, CustomerIdentifier, CustomerRelationship, DecisionLogEntry, DecisionStatus,
82 DecisionType, Delivery, DeliveryStatus, DeliveryType, IdentifierType, Message,
83 MessageDirection, Received, ReceivedStatus, SchemaType, SourceType, Transaction,
84 TransactionStatus, TransactionType,
85};
86
87#[cfg(not(feature = "storage"))]
88pub use mock::*;
89
90#[cfg(not(feature = "storage"))]
91mod mock {
92 use serde::{Deserialize, Serialize};
93 use tap_msg::didcomm::PlainMessage;
94
95 #[derive(Debug, Clone)]
96 pub struct Storage;
97
98 #[derive(Debug, thiserror::Error)]
99 #[error("Storage is not available in this build")]
100 pub struct StorageError;
101
102 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
103 pub enum MessageDirection {
104 Incoming,
105 Outgoing,
106 }
107
108 impl Storage {
109 pub async fn new(_path: Option<std::path::PathBuf>) -> Result<Self, StorageError> {
110 Ok(Storage)
111 }
112
113 pub async fn insert_transaction(
114 &self,
115 _message: &PlainMessage,
116 ) -> Result<(), StorageError> {
117 Ok(())
118 }
119
120 pub async fn log_message(
121 &self,
122 _message: &PlainMessage,
123 _direction: MessageDirection,
124 ) -> Result<(), StorageError> {
125 Ok(())
126 }
127 }
128}