torrust_tracker/core/databases/
mod.rs

1//! The persistence module.
2//!
3//! Persistence is currently implemented with one [`Database`] trait.
4//!
5//! There are two implementations of the trait (two drivers):
6//!
7//! - [`Mysql`](crate::core::databases::mysql::Mysql)
8//! - [`Sqlite`](crate::core::databases::sqlite::Sqlite)
9//!
10//! > **NOTICE**: There are no database migrations. If there are any changes,
11//! > we will implemented them or provide a script to migrate to the new schema.
12//!
13//! The persistent objects are:
14//!
15//! - [Torrent metrics](#torrent-metrics)
16//! - [Torrent whitelist](torrent-whitelist)
17//! - [Authentication keys](authentication-keys)
18//!
19//! # Torrent metrics
20//!
21//!  Field         | Sample data                              | Description
22//! ---|---|---
23//!  `id`          | 1                                        | Autoincrement id
24//!  `info_hash`   | `c1277613db1d28709b034a017ab2cae4be07ae10` | `BitTorrent` infohash V1
25//!  `completed`   | 20                                       | The number of peers that have ever completed downloading the torrent associated to this entry. See [`Entry`](torrust_tracker_torrent_repository::entry::Entry) for more information.
26//!
27//! > **NOTICE**: The peer list for a torrent is not persisted. Since peer have to re-announce themselves on intervals, the data is be
28//! > regenerated again after some minutes.
29//!
30//! # Torrent whitelist
31//!
32//! Field         | Sample data                              | Description
33//! ---|---|---
34//! `id`          | 1                                        | Autoincrement id
35//! `info_hash`   | `c1277613db1d28709b034a017ab2cae4be07ae10` | `BitTorrent` infohash V1
36//!
37//! # Authentication keys
38//!
39//! Field         | Sample data                      | Description                  
40//! ---|---|---
41//! `id`          | 1                                | Autoincrement id             
42//! `key`         | `IrweYtVuQPGbG9Jzx1DihcPmJGGpVy82` | Token                        
43//! `valid_until` | 1672419840                       | Timestamp for the expiring date  
44//!
45//! > **NOTICE**: All keys must have an expiration date.
46pub mod driver;
47pub mod error;
48pub mod mysql;
49pub mod sqlite;
50
51use std::marker::PhantomData;
52
53use torrust_tracker_primitives::info_hash::InfoHash;
54use torrust_tracker_primitives::PersistentTorrents;
55
56use self::error::Error;
57use crate::core::auth::{self, Key};
58
59struct Builder<T>
60where
61    T: Database,
62{
63    phantom: PhantomData<T>,
64}
65
66impl<T> Builder<T>
67where
68    T: Database + 'static,
69{
70    /// .
71    ///
72    /// # Errors
73    ///
74    /// Will return `r2d2::Error` if `db_path` is not able to create a database.
75    pub(self) fn build(db_path: &str) -> Result<Box<dyn Database>, Error> {
76        Ok(Box::new(T::new(db_path)?))
77    }
78}
79
80/// The persistence trait. It contains all the methods to interact with the database.
81pub trait Database: Sync + Send {
82    /// It instantiates a new database driver.
83    ///
84    /// # Errors
85    ///
86    /// Will return `r2d2::Error` if `db_path` is not able to create a database.
87    fn new(db_path: &str) -> Result<Self, Error>
88    where
89        Self: std::marker::Sized;
90
91    // Schema
92
93    /// It generates the database tables. SQL queries are hardcoded in the trait
94    /// implementation.
95    ///
96    /// # Context: Schema
97    ///
98    /// # Errors
99    ///
100    /// Will return `Error` if unable to create own tables.
101    fn create_database_tables(&self) -> Result<(), Error>;
102
103    /// It drops the database tables.
104    ///
105    /// # Context: Schema
106    ///
107    /// # Errors
108    ///
109    /// Will return `Err` if unable to drop tables.
110    fn drop_database_tables(&self) -> Result<(), Error>;
111
112    // Torrent Metrics
113
114    /// It loads the torrent metrics data from the database.
115    ///
116    /// It returns an array of tuples with the torrent
117    /// [`InfoHash`] and the
118    /// [`downloaded`](torrust_tracker_torrent_repository::entry::Torrent::downloaded) counter
119    /// which is the number of times the torrent has been downloaded.
120    /// See [`Entry::downloaded`](torrust_tracker_torrent_repository::entry::Torrent::downloaded).
121    ///
122    /// # Context: Torrent Metrics
123    ///
124    /// # Errors
125    ///
126    /// Will return `Err` if unable to load.
127    fn load_persistent_torrents(&self) -> Result<PersistentTorrents, Error>;
128
129    /// It saves the torrent metrics data into the database.
130    ///
131    /// # Context: Torrent Metrics
132    ///
133    /// # Errors
134    ///
135    /// Will return `Err` if unable to save.
136    fn save_persistent_torrent(&self, info_hash: &InfoHash, downloaded: u32) -> Result<(), Error>;
137
138    // Whitelist
139
140    /// It loads the whitelisted torrents from the database.
141    ///
142    /// # Context: Whitelist
143    ///
144    /// # Errors
145    ///
146    /// Will return `Err` if unable to load.
147    fn load_whitelist(&self) -> Result<Vec<InfoHash>, Error>;
148
149    /// It checks if the torrent is whitelisted.
150    ///
151    /// It returns `Some(InfoHash)` if the torrent is whitelisted, `None` otherwise.
152    ///
153    /// # Context: Whitelist
154    ///
155    /// # Errors
156    ///
157    /// Will return `Err` if unable to load.
158    fn get_info_hash_from_whitelist(&self, info_hash: InfoHash) -> Result<Option<InfoHash>, Error>;
159
160    /// It adds the torrent to the whitelist.
161    ///
162    /// # Context: Whitelist
163    ///
164    /// # Errors
165    ///
166    /// Will return `Err` if unable to save.
167    fn add_info_hash_to_whitelist(&self, info_hash: InfoHash) -> Result<usize, Error>;
168
169    /// It checks if the torrent is whitelisted.
170    ///
171    /// # Context: Whitelist
172    ///
173    /// # Errors
174    ///
175    /// Will return `Err` if unable to load.
176    fn is_info_hash_whitelisted(&self, info_hash: InfoHash) -> Result<bool, Error> {
177        Ok(self.get_info_hash_from_whitelist(info_hash)?.is_some())
178    }
179
180    /// It removes the torrent from the whitelist.
181    ///
182    /// # Context: Whitelist
183    ///
184    /// # Errors
185    ///
186    /// Will return `Err` if unable to save.
187    fn remove_info_hash_from_whitelist(&self, info_hash: InfoHash) -> Result<usize, Error>;
188
189    // Authentication keys
190
191    /// It loads the expiring authentication keys from the database.
192    ///
193    /// # Context: Authentication Keys
194    ///
195    /// # Errors
196    ///
197    /// Will return `Err` if unable to load.
198    fn load_keys(&self) -> Result<Vec<auth::PeerKey>, Error>;
199
200    /// It gets an expiring authentication key from the database.
201    ///
202    /// It returns `Some(PeerKey)` if a [`PeerKey`](crate::core::auth::PeerKey)
203    /// with the input [`Key`] exists, `None` otherwise.
204    ///
205    /// # Context: Authentication Keys
206    ///
207    /// # Errors
208    ///
209    /// Will return `Err` if unable to load.
210    fn get_key_from_keys(&self, key: &Key) -> Result<Option<auth::PeerKey>, Error>;
211
212    /// It adds an expiring authentication key to the database.
213    ///
214    /// # Context: Authentication Keys
215    ///
216    /// # Errors
217    ///
218    /// Will return `Err` if unable to save.
219    fn add_key_to_keys(&self, auth_key: &auth::PeerKey) -> Result<usize, Error>;
220
221    /// It removes an expiring authentication key from the database.
222    ///
223    /// # Context: Authentication Keys
224    ///
225    /// # Errors
226    ///
227    /// Will return `Err` if unable to load.
228    fn remove_key_from_keys(&self, key: &Key) -> Result<usize, Error>;
229}