1use async_trait::async_trait;
2use chrono::{DateTime, NaiveDateTime, Utc};
3use serde::{Deserialize, Serialize};
4use url::Url;
5
6use crate::databases::mysql::Mysql;
7use crate::databases::sqlite::Sqlite;
8use crate::models::category::CategoryId;
9use crate::models::info_hash::InfoHash;
10use crate::models::response::TorrentsResponse;
11use crate::models::torrent::{Metadata, TorrentListing};
12use crate::models::torrent_file::{DbTorrent, Torrent, TorrentFile};
13use crate::models::torrent_tag::{TagId, TorrentTag};
14use crate::models::tracker_key::TrackerKey;
15use crate::models::user::{User, UserAuthentication, UserCompact, UserId, UserProfile};
16use crate::services::torrent::CanonicalInfoHashGroup;
17
18pub const TABLES_TO_TRUNCATE: &[&str] = &[
21 "torrust_torrent_announce_urls",
22 "torrust_torrent_files",
23 "torrust_torrent_info",
24 "torrust_torrent_tag_links",
25 "torrust_torrent_tracker_stats",
26 "torrust_torrents",
27 "torrust_tracker_keys",
28 "torrust_user_authentication",
29 "torrust_user_bans",
30 "torrust_user_invitation_uses",
31 "torrust_user_invitations",
32 "torrust_user_profiles",
33 "torrust_user_public_keys",
34 "torrust_users",
35 "torrust_categories",
36 "torrust_torrent_tags",
37];
38
39#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
41pub enum Driver {
42 Sqlite3,
43 Mysql,
44}
45
46#[derive(Debug, Serialize, sqlx::FromRow)]
48pub struct TorrentCompact {
49 pub torrent_id: i64,
50 pub info_hash: String,
51}
52
53#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)]
55pub struct Category {
56 pub category_id: i64,
57 pub name: String,
58 pub num_torrents: i64,
59}
60
61#[derive(Clone, Copy, Debug, Deserialize)]
63pub enum Sorting {
64 UploadedAsc,
65 UploadedDesc,
66 SeedersAsc,
67 SeedersDesc,
68 LeechersAsc,
69 LeechersDesc,
70 NameAsc,
71 NameDesc,
72 SizeAsc,
73 SizeDesc,
74}
75
76#[derive(Debug)]
78pub enum Error {
79 Error,
80 ErrorWithText(String),
81 UnrecognizedDatabaseDriver, UsernameTaken,
83 EmailTaken,
84 UserNotFound,
85 CategoryNotFound,
86 TagAlreadyExists,
87 TagNotFound,
88 TorrentNotFound,
89 TorrentAlreadyExists, TorrentTitleAlreadyExists,
91 TorrentInfoHashNotFound,
92}
93
94pub fn get_driver(db_path: &str) -> Result<Driver, Error> {
100 match &db_path.chars().collect::<Vec<char>>() as &[char] {
101 ['s', 'q', 'l', 'i', 't', 'e', ..] => Ok(Driver::Sqlite3),
102 ['m', 'y', 's', 'q', 'l', ..] => Ok(Driver::Mysql),
103 _ => Err(Error::UnrecognizedDatabaseDriver),
104 }
105}
106
107pub async fn connect(db_path: &str) -> Result<Box<dyn Database>, Error> {
113 let db_driver = self::get_driver(db_path)?;
114
115 Ok(match db_driver {
116 self::Driver::Sqlite3 => Box::new(Sqlite::new(db_path).await),
117 self::Driver::Mysql => Box::new(Mysql::new(db_path).await),
118 })
119}
120
121#[async_trait]
123pub trait Database: Sync + Send {
124 fn get_database_driver(&self) -> Driver;
126
127 async fn new(db_path: &str) -> Self
128 where
129 Self: Sized;
130
131 async fn insert_user_and_get_id(&self, username: &str, email: &str, password: &str) -> Result<UserId, Error>;
133
134 async fn change_user_password(&self, user_id: i64, new_password: &str) -> Result<(), Error>;
136
137 async fn get_user_from_id(&self, user_id: i64) -> Result<User, Error>;
139
140 async fn get_user_authentication_from_id(&self, user_id: UserId) -> Result<UserAuthentication, Error>;
142
143 async fn get_user_profile_from_username(&self, username: &str) -> Result<UserProfile, Error>;
145
146 async fn get_user_compact_from_id(&self, user_id: i64) -> Result<UserCompact, Error>;
148
149 async fn get_user_tracker_key(&self, user_id: i64) -> Option<TrackerKey>;
151
152 async fn count_users(&self) -> Result<i64, Error>;
154
155 async fn ban_user(&self, user_id: i64, reason: &str, date_expiry: NaiveDateTime) -> Result<(), Error>;
157
158 async fn grant_admin_role(&self, user_id: i64) -> Result<(), Error>;
160
161 async fn verify_email(&self, user_id: i64) -> Result<(), Error>;
163
164 async fn add_tracker_key(&self, user_id: i64, tracker_key: &TrackerKey) -> Result<(), Error>;
166
167 async fn delete_user(&self, user_id: i64) -> Result<(), Error>;
169
170 async fn insert_category_and_get_id(&self, category_name: &str) -> Result<i64, Error>;
172
173 async fn get_category_from_id(&self, category_id: i64) -> Result<Category, Error>;
175
176 async fn get_category_from_name(&self, category_name: &str) -> Result<Category, Error>;
178
179 async fn get_categories(&self) -> Result<Vec<Category>, Error>;
181
182 async fn delete_category(&self, category_name: &str) -> Result<(), Error>;
184
185 async fn get_torrents_search_sorted_paginated(
187 &self,
188 search: &Option<String>,
189 categories: &Option<Vec<String>>,
190 tags: &Option<Vec<String>>,
191 sort: &Sorting,
192 offset: u64,
193 page_size: u8,
194 ) -> Result<TorrentsResponse, Error>;
195
196 async fn insert_torrent_and_get_id(
198 &self,
199 original_info_hash: &InfoHash,
200 torrent: &Torrent,
201 uploader_id: UserId,
202 metadata: &Metadata,
203 ) -> Result<i64, Error>;
204
205 async fn get_torrent_from_info_hash(&self, info_hash: &InfoHash) -> Result<Torrent, Error> {
207 let db_torrent = self.get_torrent_info_from_info_hash(info_hash).await?;
208
209 let torrent_files = self.get_torrent_files_from_id(db_torrent.torrent_id).await?;
210
211 let torrent_announce_urls = self.get_torrent_announce_urls_from_id(db_torrent.torrent_id).await?;
212
213 let torrent_http_seed_urls = self.get_torrent_http_seed_urls_from_id(db_torrent.torrent_id).await?;
214
215 let torrent_nodes = self.get_torrent_nodes_from_id(db_torrent.torrent_id).await?;
216
217 Ok(Torrent::from_database(
218 &db_torrent,
219 &torrent_files,
220 torrent_announce_urls,
221 torrent_http_seed_urls,
222 torrent_nodes,
223 ))
224 }
225
226 async fn get_torrent_from_id(&self, torrent_id: i64) -> Result<Torrent, Error> {
228 let db_torrent = self.get_torrent_info_from_id(torrent_id).await?;
229
230 let torrent_files = self.get_torrent_files_from_id(torrent_id).await?;
231
232 let torrent_announce_urls = self.get_torrent_announce_urls_from_id(torrent_id).await?;
233
234 let torrent_http_seed_urls = self.get_torrent_http_seed_urls_from_id(db_torrent.torrent_id).await?;
235
236 let torrent_nodes = self.get_torrent_nodes_from_id(db_torrent.torrent_id).await?;
237
238 Ok(Torrent::from_database(
239 &db_torrent,
240 &torrent_files,
241 torrent_announce_urls,
242 torrent_http_seed_urls,
243 torrent_nodes,
244 ))
245 }
246
247 async fn get_torrent_canonical_info_hash_group(&self, canonical: &InfoHash) -> Result<CanonicalInfoHashGroup, Error>;
256
257 async fn find_canonical_info_hash_for(&self, info_hash: &InfoHash) -> Result<Option<InfoHash>, Error>;
264
265 async fn add_info_hash_to_canonical_info_hash_group(&self, original: &InfoHash, canonical: &InfoHash) -> Result<(), Error>;
271
272 async fn get_torrent_info_from_id(&self, torrent_id: i64) -> Result<DbTorrent, Error>;
274
275 async fn get_torrent_info_from_info_hash(&self, info_hash: &InfoHash) -> Result<DbTorrent, Error>;
277
278 async fn get_torrent_files_from_id(&self, torrent_id: i64) -> Result<Vec<TorrentFile>, Error>;
280
281 async fn get_torrent_announce_urls_from_id(&self, torrent_id: i64) -> Result<Vec<Vec<String>>, Error>;
283
284 async fn get_torrent_http_seed_urls_from_id(&self, torrent_id: i64) -> Result<Vec<String>, Error>;
286
287 async fn get_torrent_nodes_from_id(&self, torrent_id: i64) -> Result<Vec<(String, i64)>, Error>;
289
290 async fn get_torrent_listing_from_id(&self, torrent_id: i64) -> Result<TorrentListing, Error>;
292
293 async fn get_torrent_listing_from_info_hash(&self, info_hash: &InfoHash) -> Result<TorrentListing, Error>;
295
296 async fn get_all_torrents_compact(&self) -> Result<Vec<TorrentCompact>, Error>;
298
299 async fn get_torrents_with_stats_not_updated_since(
301 &self,
302 datetime: DateTime<Utc>,
303 limit: i64,
304 ) -> Result<Vec<TorrentCompact>, Error>;
305
306 async fn update_torrent_title(&self, torrent_id: i64, title: &str) -> Result<(), Error>;
308
309 async fn update_torrent_description(&self, torrent_id: i64, description: &str) -> Result<(), Error>;
311
312 async fn update_torrent_category(&self, torrent_id: i64, category_id: CategoryId) -> Result<(), Error>;
314
315 async fn insert_tag_and_get_id(&self, name: &str) -> Result<i64, Error>;
317
318 async fn delete_tag(&self, tag_id: TagId) -> Result<(), Error>;
320
321 async fn add_torrent_tag_link(&self, torrent_id: i64, tag_id: TagId) -> Result<(), Error>;
323
324 async fn add_torrent_tag_links(&self, torrent_id: i64, tag_ids: &[TagId]) -> Result<(), Error>;
326
327 async fn delete_torrent_tag_link(&self, torrent_id: i64, tag_id: TagId) -> Result<(), Error>;
329
330 async fn delete_all_torrent_tag_links(&self, torrent_id: i64) -> Result<(), Error>;
332
333 async fn get_tag_from_name(&self, name: &str) -> Result<TorrentTag, Error>;
335
336 async fn get_tags(&self) -> Result<Vec<TorrentTag>, Error>;
338
339 async fn get_tags_for_torrent_id(&self, torrent_id: i64) -> Result<Vec<TorrentTag>, Error>;
341
342 async fn update_tracker_info(&self, torrent_id: i64, tracker_url: &Url, seeders: i64, leechers: i64) -> Result<(), Error>;
344
345 async fn delete_torrent(&self, torrent_id: i64) -> Result<(), Error>;
347
348 async fn delete_all_database_rows(&self) -> Result<(), Error>;
350}