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}