Skip to content

Commit

Permalink
feat: torrent tags
Browse files Browse the repository at this point in the history
  • Loading branch information
mickvandijke committed May 24, 2023
1 parent 8748f93 commit 7ce3d5e
Show file tree
Hide file tree
Showing 17 changed files with 457 additions and 13 deletions.
5 changes: 5 additions & 0 deletions migrations/mysql/20230321122049_torrust_torrent_tags.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS torrust_torrent_tags (
tag_id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
date_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
7 changes: 7 additions & 0 deletions migrations/mysql/20230321122825_torrust_torrent_tag_links.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CREATE TABLE IF NOT EXISTS torrust_torrent_tag_links (
torrent_id INTEGER NOT NULL,
tag_id INTEGER NOT NULL,
FOREIGN KEY (torrent_id) REFERENCES torrust_torrents(torrent_id) ON DELETE CASCADE,
FOREIGN KEY (tag_id) REFERENCES torrust_torrent_tags(tag_id) ON DELETE CASCADE,
PRIMARY KEY (torrent_id, tag_id)
);
5 changes: 5 additions & 0 deletions migrations/sqlite3/20230321122049_torrust_torrent_tags.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE TABLE IF NOT EXISTS torrust_torrent_tags (
tag_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name VARCHAR(255) NOT NULL,
date_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CREATE TABLE IF NOT EXISTS torrust_torrent_tag_links (
torrent_id INTEGER NOT NULL,
tag_id INTEGER NOT NULL,
FOREIGN KEY (torrent_id) REFERENCES torrust_torrents(torrent_id) ON DELETE CASCADE,
FOREIGN KEY (tag_id) REFERENCES torrust_torrent_tags(tag_id) ON DELETE CASCADE,
PRIMARY KEY (torrent_id, tag_id)
);
8 changes: 4 additions & 4 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ use crate::config::Configuration;
use crate::databases::database;
use crate::services::authentication::{DbUserAuthenticationRepository, JsonWebToken, Service};
use crate::services::category::{self, DbCategoryRepository};
use crate::services::torrent::{
DbTorrentAnnounceUrlRepository, DbTorrentFileRepository, DbTorrentInfoRepository, DbTorrentListingGenerator,
DbTorrentRepository,
};
use crate::services::torrent::{DbTorrentAnnounceUrlRepository, DbTorrentFileRepository, DbTorrentInfoRepository, DbTorrentListingGenerator, DbTorrentRepository, DbTorrentTagRepository};
use crate::services::user::{self, DbBannedUserList, DbUserProfileRepository, DbUserRepository};
use crate::services::{proxy, settings, torrent};
use crate::tracker::statistics_importer::StatisticsImporter;
Expand Down Expand Up @@ -63,6 +60,7 @@ pub async fn run(configuration: Configuration) -> Running {
let torrent_info_repository = Arc::new(DbTorrentInfoRepository::new(database.clone()));
let torrent_file_repository = Arc::new(DbTorrentFileRepository::new(database.clone()));
let torrent_announce_url_repository = Arc::new(DbTorrentAnnounceUrlRepository::new(database.clone()));
let torrent_tag_repository = Arc::new(DbTorrentTagRepository::new(database.clone()));
let torrent_listing_generator = Arc::new(DbTorrentListingGenerator::new(database.clone()));
let banned_user_list = Arc::new(DbBannedUserList::new(database.clone()));

Expand All @@ -85,6 +83,7 @@ pub async fn run(configuration: Configuration) -> Running {
torrent_info_repository.clone(),
torrent_file_repository.clone(),
torrent_announce_url_repository.clone(),
torrent_tag_repository.clone(),
torrent_listing_generator.clone(),
));
let registration_service = Arc::new(user::RegistrationService::new(
Expand Down Expand Up @@ -126,6 +125,7 @@ pub async fn run(configuration: Configuration) -> Running {
torrent_info_repository,
torrent_file_repository,
torrent_announce_url_repository,
torrent_tag_repository,
torrent_listing_generator,
banned_user_list,
category_service,
Expand Down
8 changes: 4 additions & 4 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ use crate::config::Configuration;
use crate::databases::database::Database;
use crate::services::authentication::{DbUserAuthenticationRepository, JsonWebToken, Service};
use crate::services::category::{self, DbCategoryRepository};
use crate::services::torrent::{
DbTorrentAnnounceUrlRepository, DbTorrentFileRepository, DbTorrentInfoRepository, DbTorrentListingGenerator,
DbTorrentRepository,
};
use crate::services::torrent::{DbTorrentAnnounceUrlRepository, DbTorrentFileRepository, DbTorrentInfoRepository, DbTorrentListingGenerator, DbTorrentRepository, DbTorrentTagRepository};
use crate::services::user::{self, DbBannedUserList, DbUserProfileRepository, DbUserRepository};
use crate::services::{proxy, settings, torrent};
use crate::tracker::statistics_importer::StatisticsImporter;
Expand Down Expand Up @@ -37,6 +34,7 @@ pub struct AppData {
pub torrent_info_repository: Arc<DbTorrentInfoRepository>,
pub torrent_file_repository: Arc<DbTorrentFileRepository>,
pub torrent_announce_url_repository: Arc<DbTorrentAnnounceUrlRepository>,
pub torrent_tag_repository: Arc<DbTorrentTagRepository>,
pub torrent_listing_generator: Arc<DbTorrentListingGenerator>,
pub banned_user_list: Arc<DbBannedUserList>,
// Services
Expand Down Expand Up @@ -69,6 +67,7 @@ impl AppData {
torrent_info_repository: Arc<DbTorrentInfoRepository>,
torrent_file_repository: Arc<DbTorrentFileRepository>,
torrent_announce_url_repository: Arc<DbTorrentAnnounceUrlRepository>,
torrent_tag_repository: Arc<DbTorrentTagRepository>,
torrent_listing_generator: Arc<DbTorrentListingGenerator>,
banned_user_list: Arc<DbBannedUserList>,
// Services
Expand Down Expand Up @@ -98,6 +97,7 @@ impl AppData {
torrent_info_repository,
torrent_file_repository,
torrent_announce_url_repository,
torrent_tag_repository,
torrent_listing_generator,
banned_user_list,
// Services
Expand Down
26 changes: 26 additions & 0 deletions src/databases/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::models::info_hash::InfoHash;
use crate::models::response::TorrentsResponse;
use crate::models::torrent::TorrentListing;
use crate::models::torrent_file::{DbTorrentInfo, Torrent, TorrentFile};
use crate::models::torrent_tag::{TagId, TorrentTag};
use crate::models::tracker_key::TrackerKey;
use crate::models::user::{User, UserAuthentication, UserCompact, UserId, UserProfile};

Expand Down Expand Up @@ -52,6 +53,7 @@ pub enum Sorting {
#[derive(Debug)]
pub enum Error {
Error,
ErrorWithText(String),
UnrecognizedDatabaseDriver, // when the db path does not start with sqlite or mysql
UsernameTaken,
EmailTaken,
Expand Down Expand Up @@ -228,6 +230,30 @@ pub trait Database: Sync + Send {
/// Update a torrent's description with `torrent_id` and `description`.
async fn update_torrent_description(&self, torrent_id: i64, description: &str) -> Result<(), Error>;

/// Add a new tag.
async fn add_tag(&self, name: &str) -> Result<TorrentTag, Error>;

/// Delete a tag.
async fn delete_tag(&self, tag_id: TagId) -> Result<TorrentTag, Error>;

/// Add a tag to torrent.
async fn add_torrent_tag_link(&self, torrent_id: i64, tag_id: TagId) -> Result<(), Error>;

/// Add multiple tags to a torrent at once.
async fn add_torrent_tag_links(&self, torrent_id: i64, tag_ids: &Vec<TagId>) -> Result<(), Error>;

/// Remove a tag from torrent.
async fn delete_torrent_tag_link(&self, torrent_id: i64, tag_id: TagId) -> Result<(), Error>;

/// Remove all tags from torrent.
async fn delete_all_torrent_tag_links(&self, torrent_id: i64) -> Result<(), Error>;

/// Get all tags as `Vec<TorrentTag>`.
async fn get_tags(&self) -> Result<Vec<TorrentTag>, Error>;

/// Get tags for `torrent_id`.
async fn get_tags_for_torrent_id(&self, torrent_id: i64) -> Result<Vec<TorrentTag>, Error>;

/// Update the seeders and leechers info for a torrent with `torrent_id`, `tracker_url`, `seeders` and `leechers`.
async fn update_tracker_info(&self, torrent_id: i64, tracker_url: &str, seeders: i64, leechers: i64) -> Result<(), Error>;

Expand Down
91 changes: 91 additions & 0 deletions src/databases/mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::models::info_hash::InfoHash;
use crate::models::response::TorrentsResponse;
use crate::models::torrent::TorrentListing;
use crate::models::torrent_file::{DbTorrentAnnounceUrl, DbTorrentFile, DbTorrentInfo, Torrent, TorrentFile};
use crate::models::torrent_tag::{TagId, TorrentTag};
use crate::models::tracker_key::TrackerKey;
use crate::models::user::{User, UserAuthentication, UserCompact, UserId, UserProfile};
use crate::utils::clock;
Expand Down Expand Up @@ -669,6 +670,96 @@ impl Database for Mysql {
})
}

async fn add_tag(&self, name: &str) -> Result<TorrentTag, database::Error> {
println!("inserting tag: {}", name);

query_as("INSERT INTO torrust_torrent_tags (name) VALUES (?) RETURNING id, name")
.bind(name)
.fetch_one(&self.pool)
.await
.map_err(|err| database::Error::ErrorWithText(err.to_string()))
}

async fn delete_tag(&self, tag_id: TagId) -> Result<TorrentTag, database::Error> {
println!("deleting tag: {}", tag_id);

query_as("DELETE FROM torrust_torrent_tags WHERE tag_id = ? RETURNING id, name")
.bind(tag_id)
.fetch_one(&self.pool)
.await
.map_err(|err| database::Error::ErrorWithText(err.to_string()))
}

async fn add_torrent_tag_link(&self, torrent_id: i64, tag_id: TagId) -> Result<(), database::Error> {
query("INSERT INTO torrust_torrent_tag_links (torrent_id, tag_id) VALUES (?, ?)")
.bind(torrent_id)
.bind(tag_id)
.execute(&self.pool)
.await
.map(|_| ())
.map_err(|_| database::Error::Error)
}

async fn add_torrent_tag_links(&self, torrent_id: i64, tag_ids: &Vec<TagId>) -> Result<(), database::Error> {
let mut transaction = self.pool.begin()
.await
.map_err(|err| database::Error::ErrorWithText(err.to_string()))?;

for tag_id in tag_ids {
query("INSERT INTO torrust_torrent_tag_links (torrent_id, tag_id) VALUES (?, ?)")
.bind(torrent_id)
.bind(tag_id)
.execute(&mut transaction)
.await
.map_err(|err| database::Error::ErrorWithText(err.to_string()))?;
}

transaction.commit()
.await
.map_err(|err| database::Error::ErrorWithText(err.to_string()))
}

async fn delete_torrent_tag_link(&self, torrent_id: i64, tag_id: TagId) -> Result<(), database::Error> {
query("DELETE FROM torrust_torrent_tag_links WHERE torrent_id = ? AND tag_id = ?")
.bind(torrent_id)
.bind(tag_id)
.execute(&self.pool)
.await
.map(|_| ())
.map_err(|_| database::Error::Error)
}

async fn delete_all_torrent_tag_links(&self, torrent_id: i64) -> Result<(), database::Error> {
query("DELETE FROM torrust_torrent_tag_links WHERE torrent_id = ?")
.bind(torrent_id)
.execute(&self.pool)
.await
.map(|_| ())
.map_err(|err| database::Error::ErrorWithText(err.to_string()))
}

async fn get_tags(&self) -> Result<Vec<TorrentTag>, database::Error> {
query_as::<_, TorrentTag>(
"SELECT tag_id, name FROM torrust_torrent_tags"
)
.fetch_all(&self.pool)
.await
.map_err(|_| database::Error::Error)
}

async fn get_tags_for_torrent_id(&self, torrent_id: i64) -> Result<Vec<TorrentTag>, database::Error> {
query_as::<_, TorrentTag>(
"SELECT torrust_torrent_tags.tag_id, torrust_torrent_tags.name
FROM torrust_torrent_tags
JOIN torrust_torrent_tag_links ON torrust_torrent_tags.tag_id = torrust_torrent_tag_links.tag_id
WHERE torrust_torrent_tag_links.torrent_id = ?"
)
.bind(torrent_id)
.fetch_all(&self.pool)
.await
.map_err(|_| database::Error::Error)
}

async fn update_tracker_info(
&self,
torrent_id: i64,
Expand Down
87 changes: 87 additions & 0 deletions src/databases/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::models::info_hash::InfoHash;
use crate::models::response::TorrentsResponse;
use crate::models::torrent::TorrentListing;
use crate::models::torrent_file::{DbTorrentAnnounceUrl, DbTorrentFile, DbTorrentInfo, Torrent, TorrentFile};
use crate::models::torrent_tag::{TagId, TorrentTag};
use crate::models::tracker_key::TrackerKey;
use crate::models::user::{User, UserAuthentication, UserCompact, UserId, UserProfile};
use crate::utils::clock;
Expand Down Expand Up @@ -659,6 +660,92 @@ impl Database for Sqlite {
})
}

async fn add_tag(&self, name: &str) -> Result<TorrentTag, database::Error> {
query_as("INSERT INTO torrust_torrent_tags (name) VALUES (?) RETURNING id, name")
.bind(name)
.fetch_one(&self.pool)
.await
.map_err(|err| database::Error::ErrorWithText(err.to_string()))
}

async fn delete_tag(&self, tag_id: TagId) -> Result<TorrentTag, database::Error> {
query_as("DELETE FROM torrust_torrent_tags WHERE tag_id = ? RETURNING id, name")
.bind(tag_id)
.fetch_one(&self.pool)
.await
.map_err(|err| database::Error::ErrorWithText(err.to_string()))
}

async fn add_torrent_tag_link(&self, torrent_id: i64, tag_id: TagId) -> Result<(), database::Error> {
query("INSERT INTO torrust_torrent_tag_links (torrent_id, tag_id) VALUES (?, ?)")
.bind(torrent_id)
.bind(tag_id)
.execute(&self.pool)
.await
.map(|_| ())
.map_err(|_| database::Error::Error)
}

async fn add_torrent_tag_links(&self, torrent_id: i64, tag_ids: &Vec<TagId>) -> Result<(), database::Error> {
let mut transaction = self.pool.begin()
.await
.map_err(|err| database::Error::ErrorWithText(err.to_string()))?;

for tag_id in tag_ids {
query("INSERT INTO torrust_torrent_tag_links (torrent_id, tag_id) VALUES (?, ?)")
.bind(torrent_id)
.bind(tag_id)
.execute(&mut transaction)
.await
.map_err(|err| database::Error::ErrorWithText(err.to_string()))?;
}

transaction.commit()
.await
.map_err(|err| database::Error::ErrorWithText(err.to_string()))
}

async fn delete_torrent_tag_link(&self, torrent_id: i64, tag_id: TagId) -> Result<(), database::Error> {
query("DELETE FROM torrust_torrent_tag_links WHERE torrent_id = ? AND tag_id = ?")
.bind(torrent_id)
.bind(tag_id)
.execute(&self.pool)
.await
.map(|_| ())
.map_err(|_| database::Error::Error)
}

async fn delete_all_torrent_tag_links(&self, torrent_id: i64) -> Result<(), database::Error> {
query("DELETE FROM torrust_torrent_tag_links WHERE torrent_id = ?")
.bind(torrent_id)
.execute(&self.pool)
.await
.map(|_| ())
.map_err(|err| database::Error::ErrorWithText(err.to_string()))
}

async fn get_tags(&self) -> Result<Vec<TorrentTag>, database::Error> {
query_as::<_, TorrentTag>(
"SELECT tag_id, name FROM torrust_torrent_tags"
)
.fetch_all(&self.pool)
.await
.map_err(|_| database::Error::Error)
}

async fn get_tags_for_torrent_id(&self, torrent_id: i64) -> Result<Vec<TorrentTag>, database::Error> {
query_as::<_, TorrentTag>(
"SELECT torrust_torrent_tags.tag_id, torrust_torrent_tags.name
FROM torrust_torrent_tags
JOIN torrust_torrent_tag_links ON torrust_torrent_tags.tag_id = torrust_torrent_tag_links.tag_id
WHERE torrust_torrent_tag_links.torrent_id = ?"
)
.bind(torrent_id)
.fetch_all(&self.pool)
.await
.map_err(|_| database::Error::Error)
}

async fn update_tracker_info(
&self,
torrent_id: i64,
Expand Down
1 change: 1 addition & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ impl From<database::Error> for ServiceError {
#[allow(clippy::match_same_arms)]
match e {
database::Error::Error => ServiceError::InternalServerError,
database::Error::ErrorWithText(_) => ServiceError::InternalServerError,
database::Error::UsernameTaken => ServiceError::UsernameTaken,
database::Error::EmailTaken => ServiceError::EmailTaken,
database::Error::UserNotFound => ServiceError::UserNotFound,
Expand Down
1 change: 1 addition & 0 deletions src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pub mod torrent;
pub mod torrent_file;
pub mod tracker_key;
pub mod user;
pub mod torrent_tag;
3 changes: 3 additions & 0 deletions src/models/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use super::torrent::TorrentId;
use crate::databases::database::Category;
use crate::models::torrent::TorrentListing;
use crate::models::torrent_file::TorrentFile;
use crate::models::torrent_tag::TorrentTag;

pub enum OkResponses {
TokenResponse(TokenResponse),
Expand Down Expand Up @@ -59,6 +60,7 @@ pub struct TorrentResponse {
pub files: Vec<TorrentFile>,
pub trackers: Vec<String>,
pub magnet_link: String,
pub tags: Vec<TorrentTag>,
}

impl TorrentResponse {
Expand All @@ -78,6 +80,7 @@ impl TorrentResponse {
files: vec![],
trackers: vec![],
magnet_link: String::new(),
tags: vec![],
}
}
}
Expand Down
Loading

0 comments on commit 7ce3d5e

Please sign in to comment.