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::{Message, MessageDirection, Transaction, TransactionStatus, TransactionType};
81
82#[cfg(not(feature = "storage"))]
83pub use mock::*;
84
85#[cfg(not(feature = "storage"))]
86mod mock {
87 use serde::{Deserialize, Serialize};
88 use tap_msg::didcomm::PlainMessage;
89
90 #[derive(Debug, Clone)]
91 pub struct Storage;
92
93 #[derive(Debug, thiserror::Error)]
94 #[error("Storage is not available in this build")]
95 pub struct StorageError;
96
97 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
98 pub enum MessageDirection {
99 Incoming,
100 Outgoing,
101 }
102
103 impl Storage {
104 pub async fn new(_path: Option<std::path::PathBuf>) -> Result<Self, StorageError> {
105 Ok(Storage)
106 }
107
108 pub async fn insert_transaction(
109 &self,
110 _message: &PlainMessage,
111 ) -> Result<(), StorageError> {
112 Ok(())
113 }
114
115 pub async fn log_message(
116 &self,
117 _message: &PlainMessage,
118 _direction: MessageDirection,
119 ) -> Result<(), StorageError> {
120 Ok(())
121 }
122 }
123}