Skip to content

Commit 7b9d5a4

Browse files
committed
feat: [#438] persist metainfo field nodes. BEP 5
1 parent 0cd58e4 commit 7b9d5a4

File tree

7 files changed

+106
-4
lines changed

7 files changed

+106
-4
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CREATE TABLE IF NOT EXISTS torrust_torrent_nodes (
2+
node_id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
3+
torrent_id INTEGER NOT NULL,
4+
node_ip VARCHAR(256) NOT NULL,
5+
node_port INTEGER NOT NULL,
6+
FOREIGN KEY(torrent_id) REFERENCES torrust_torrents(torrent_id) ON DELETE CASCADE
7+
)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CREATE TABLE IF NOT EXISTS torrust_torrent_nodes (
2+
node_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
3+
torrent_id INTEGER NOT NULL,
4+
node_ip TEXT NOT NULL,
5+
node_port INTEGER NOT NULL,
6+
FOREIGN KEY(torrent_id) REFERENCES torrust_torrents(torrent_id) ON DELETE CASCADE
7+
)

share/default/config/index.development.sqlite3.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
log_level = "info"
1+
log_level = "debug"
22

33
[website]
44
name = "Torrust"

src/databases/database.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,11 +208,14 @@ pub trait Database: Sync + Send {
208208

209209
let torrent_http_seed_urls = self.get_torrent_http_seed_urls_from_id(db_torrent.torrent_id).await?;
210210

211+
let torrent_nodes = self.get_torrent_nodes_from_id(db_torrent.torrent_id).await?;
212+
211213
Ok(Torrent::from_database(
212214
&db_torrent,
213215
&torrent_files,
214216
torrent_announce_urls,
215217
torrent_http_seed_urls,
218+
torrent_nodes,
216219
))
217220
}
218221

@@ -226,11 +229,14 @@ pub trait Database: Sync + Send {
226229

227230
let torrent_http_seed_urls = self.get_torrent_http_seed_urls_from_id(db_torrent.torrent_id).await?;
228231

232+
let torrent_nodes = self.get_torrent_nodes_from_id(db_torrent.torrent_id).await?;
233+
229234
Ok(Torrent::from_database(
230235
&db_torrent,
231236
&torrent_files,
232237
torrent_announce_urls,
233238
torrent_http_seed_urls,
239+
torrent_nodes,
234240
))
235241
}
236242

@@ -274,6 +280,9 @@ pub trait Database: Sync + Send {
274280
/// Get all torrent's HTTP seed urls as `Vec<Vec<String>>` from `torrent_id`.
275281
async fn get_torrent_http_seed_urls_from_id(&self, torrent_id: i64) -> Result<Vec<String>, Error>;
276282

283+
/// Get all torrent's nodes as `Vec<(String, i64)>` from `torrent_id`.
284+
async fn get_torrent_nodes_from_id(&self, torrent_id: i64) -> Result<Vec<(String, i64)>, Error>;
285+
277286
/// Get `TorrentListing` from `torrent_id`.
278287
async fn get_torrent_listing_from_id(&self, torrent_id: i64) -> Result<TorrentListing, Error>;
279288

src/databases/mysql.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ use crate::models::category::CategoryId;
1313
use crate::models::info_hash::InfoHash;
1414
use crate::models::response::TorrentsResponse;
1515
use crate::models::torrent::{Metadata, TorrentListing};
16-
use crate::models::torrent_file::{DbTorrent, DbTorrentAnnounceUrl, DbTorrentFile, DbTorrentHttpSeedUrl, Torrent, TorrentFile};
16+
use crate::models::torrent_file::{
17+
DbTorrent, DbTorrentAnnounceUrl, DbTorrentFile, DbTorrentHttpSeedUrl, DbTorrentNode, Torrent, TorrentFile,
18+
};
1719
use crate::models::torrent_tag::{TagId, TorrentTag};
1820
use crate::models::tracker_key::TrackerKey;
1921
use crate::models::user::{User, UserAuthentication, UserCompact, UserId, UserProfile};
@@ -606,6 +608,31 @@ impl Database for Mysql {
606608
return Err(e);
607609
}
608610

611+
// add nodes
612+
613+
let insert_torrent_nodes_result: Result<(), database::Error> = if let Some(nodes) = &torrent.nodes {
614+
for node in nodes {
615+
let () = query("INSERT INTO torrust_torrent_nodes (torrent_id, node_ip, node_port) VALUES (?, ?, ?)")
616+
.bind(torrent_id)
617+
.bind(node.0.clone())
618+
.bind(node.1)
619+
.execute(&mut *tx)
620+
.await
621+
.map(|_| ())
622+
.map_err(|_| database::Error::Error)?;
623+
}
624+
625+
Ok(())
626+
} else {
627+
Ok(())
628+
};
629+
630+
// rollback transaction on error
631+
if let Err(e) = insert_torrent_nodes_result {
632+
drop(tx.rollback().await);
633+
return Err(e);
634+
}
635+
609636
// add tags
610637

611638
for tag_id in &metadata.tags {
@@ -773,6 +800,15 @@ impl Database for Mysql {
773800
.map_err(|_| database::Error::TorrentNotFound)
774801
}
775802

803+
async fn get_torrent_nodes_from_id(&self, torrent_id: i64) -> Result<Vec<(String, i64)>, database::Error> {
804+
query_as::<_, DbTorrentNode>("SELECT node_ip, node_port FROM torrust_torrent_nodes WHERE torrent_id = ?")
805+
.bind(torrent_id)
806+
.fetch_all(&self.pool)
807+
.await
808+
.map(|v| v.iter().map(|a| (a.node_ip.to_string(), a.node_port)).collect())
809+
.map_err(|_| database::Error::TorrentNotFound)
810+
}
811+
776812
async fn get_torrent_listing_from_id(&self, torrent_id: i64) -> Result<TorrentListing, database::Error> {
777813
query_as::<_, TorrentListing>(
778814
"SELECT

src/databases/sqlite.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ use crate::models::category::CategoryId;
1313
use crate::models::info_hash::InfoHash;
1414
use crate::models::response::TorrentsResponse;
1515
use crate::models::torrent::{Metadata, TorrentListing};
16-
use crate::models::torrent_file::{DbTorrent, DbTorrentAnnounceUrl, DbTorrentFile, DbTorrentHttpSeedUrl, Torrent, TorrentFile};
16+
use crate::models::torrent_file::{
17+
DbTorrent, DbTorrentAnnounceUrl, DbTorrentFile, DbTorrentHttpSeedUrl, DbTorrentNode, Torrent, TorrentFile,
18+
};
1719
use crate::models::torrent_tag::{TagId, TorrentTag};
1820
use crate::models::tracker_key::TrackerKey;
1921
use crate::models::user::{User, UserAuthentication, UserCompact, UserId, UserProfile};
@@ -600,6 +602,31 @@ impl Database for Sqlite {
600602
return Err(e);
601603
}
602604

605+
// add nodes
606+
607+
let insert_torrent_nodes_result: Result<(), database::Error> = if let Some(nodes) = &torrent.nodes {
608+
for node in nodes {
609+
let () = query("INSERT INTO torrust_torrent_nodes (torrent_id, node_ip, node_port) VALUES (?, ?, ?)")
610+
.bind(torrent_id)
611+
.bind(node.0.clone())
612+
.bind(node.1)
613+
.execute(&mut *tx)
614+
.await
615+
.map(|_| ())
616+
.map_err(|_| database::Error::Error)?;
617+
}
618+
619+
Ok(())
620+
} else {
621+
Ok(())
622+
};
623+
624+
// rollback transaction on error
625+
if let Err(e) = insert_torrent_nodes_result {
626+
drop(tx.rollback().await);
627+
return Err(e);
628+
}
629+
603630
// add tags
604631

605632
for tag_id in &metadata.tags {
@@ -767,6 +794,15 @@ impl Database for Sqlite {
767794
.map_err(|_| database::Error::TorrentNotFound)
768795
}
769796

797+
async fn get_torrent_nodes_from_id(&self, torrent_id: i64) -> Result<Vec<(String, i64)>, database::Error> {
798+
query_as::<_, DbTorrentNode>("SELECT node_ip, node_port FROM torrust_torrent_nodes WHERE torrent_id = ?")
799+
.bind(torrent_id)
800+
.fetch_all(&self.pool)
801+
.await
802+
.map(|v| v.iter().map(|a| (a.node_ip.to_string(), a.node_port)).collect())
803+
.map_err(|_| database::Error::TorrentNotFound)
804+
}
805+
770806
async fn get_torrent_listing_from_id(&self, torrent_id: i64) -> Result<TorrentListing, database::Error> {
771807
query_as::<_, TorrentListing>(
772808
"SELECT

src/models/torrent_file.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ impl Torrent {
7575
torrent_files: &[TorrentFile],
7676
torrent_announce_urls: Vec<Vec<String>>,
7777
torrent_http_seed_urls: Vec<String>,
78+
torrent_nodes: Vec<(String, i64)>,
7879
) -> Self {
7980
let info_dict = TorrentInfoDictionary::with(
8081
&db_torrent.name,
@@ -88,7 +89,7 @@ impl Torrent {
8889
Self {
8990
info: info_dict,
9091
announce: None,
91-
nodes: None,
92+
nodes: if torrent_nodes.is_empty() { None } else { Some(torrent_nodes) },
9293
encoding: db_torrent.encoding.clone(),
9394
httpseeds: if torrent_http_seed_urls.is_empty() {
9495
None
@@ -359,6 +360,12 @@ pub struct DbTorrentHttpSeedUrl {
359360
pub seed_url: String,
360361
}
361362

363+
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
364+
pub struct DbTorrentNode {
365+
pub node_ip: String,
366+
pub node_port: i64,
367+
}
368+
362369
#[cfg(test)]
363370
mod tests {
364371

0 commit comments

Comments
 (0)