Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cSpell.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"Hydranode",
"incompletei",
"infohash",
"infohashes",
"infoschema",
"intervali",
"leecher",
Expand Down Expand Up @@ -58,6 +59,7 @@
"sharktorrent",
"socketaddr",
"sqllite",
"subsec",
"Swatinem",
"Swiftbit",
"thiserror",
Expand Down
20 changes: 10 additions & 10 deletions src/apis/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ pub async fn add_torrent_to_whitelist_handler(
match InfoHash::from_str(&info_hash.0) {
Err(_) => invalid_info_hash_param_response(&info_hash.0),
Ok(info_hash) => match tracker.add_torrent_to_whitelist(&info_hash).await {
Ok(..) => ok_response(),
Err(..) => failed_to_whitelist_torrent_response(),
Ok(_) => ok_response(),
Err(e) => failed_to_whitelist_torrent_response(e),
},
}
}
Expand All @@ -79,24 +79,24 @@ pub async fn remove_torrent_from_whitelist_handler(
match InfoHash::from_str(&info_hash.0) {
Err(_) => invalid_info_hash_param_response(&info_hash.0),
Ok(info_hash) => match tracker.remove_torrent_from_whitelist(&info_hash).await {
Ok(..) => ok_response(),
Err(..) => failed_to_remove_torrent_from_whitelist_response(),
Ok(_) => ok_response(),
Err(e) => failed_to_remove_torrent_from_whitelist_response(e),
},
}
}

pub async fn reload_whitelist_handler(State(tracker): State<Arc<Tracker>>) -> Response {
match tracker.load_whitelist().await {
Ok(..) => ok_response(),
Err(..) => failed_to_reload_whitelist_response(),
Ok(_) => ok_response(),
Err(e) => failed_to_reload_whitelist_response(e),
}
}

pub async fn generate_auth_key_handler(State(tracker): State<Arc<Tracker>>, Path(seconds_valid_or_key): Path<u64>) -> Response {
let seconds_valid = seconds_valid_or_key;
match tracker.generate_auth_key(Duration::from_secs(seconds_valid)).await {
Ok(auth_key) => auth_key_response(&AuthKey::from(auth_key)),
Err(_) => failed_to_generate_key_response(),
Err(e) => failed_to_generate_key_response(e),
}
}

Expand All @@ -111,15 +111,15 @@ pub async fn delete_auth_key_handler(
Err(_) => invalid_auth_key_param_response(&seconds_valid_or_key.0),
Ok(key_id) => match tracker.remove_auth_key(&key_id.to_string()).await {
Ok(_) => ok_response(),
Err(_) => failed_to_delete_key_response(),
Err(e) => failed_to_delete_key_response(e),
},
}
}

pub async fn reload_keys_handler(State(tracker): State<Arc<Tracker>>) -> Response {
match tracker.load_keys().await {
Ok(..) => ok_response(),
Err(..) => failed_to_reload_keys_response(),
Ok(_) => ok_response(),
Err(e) => failed_to_reload_keys_response(e),
}
}

Expand Down
26 changes: 14 additions & 12 deletions src/apis/responses.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::error::Error;

use axum::http::{header, StatusCode};
use axum::response::{IntoResponse, Json, Response};
use serde::Serialize;
Expand Down Expand Up @@ -110,33 +112,33 @@ pub fn torrent_not_known_response() -> Response {
}

#[must_use]
pub fn failed_to_remove_torrent_from_whitelist_response() -> Response {
unhandled_rejection_response("failed to remove torrent from whitelist".to_string())
pub fn failed_to_remove_torrent_from_whitelist_response<E: Error>(e: E) -> Response {
unhandled_rejection_response(format!("failed to remove torrent from whitelist: {e}"))
}

#[must_use]
pub fn failed_to_whitelist_torrent_response() -> Response {
unhandled_rejection_response("failed to whitelist torrent".to_string())
pub fn failed_to_whitelist_torrent_response<E: Error>(e: E) -> Response {
unhandled_rejection_response(format!("failed to whitelist torrent: {e}"))
}

#[must_use]
pub fn failed_to_reload_whitelist_response() -> Response {
unhandled_rejection_response("failed to reload whitelist".to_string())
pub fn failed_to_reload_whitelist_response<E: Error>(e: E) -> Response {
unhandled_rejection_response(format!("failed to reload whitelist: {e}"))
}

#[must_use]
pub fn failed_to_generate_key_response() -> Response {
unhandled_rejection_response("failed to generate key".to_string())
pub fn failed_to_generate_key_response<E: Error>(e: E) -> Response {
unhandled_rejection_response(format!("failed to generate key: {e}"))
}

#[must_use]
pub fn failed_to_delete_key_response() -> Response {
unhandled_rejection_response("failed to delete key".to_string())
pub fn failed_to_delete_key_response<E: Error>(e: E) -> Response {
unhandled_rejection_response(format!("failed to delete key: {e}"))
}

#[must_use]
pub fn failed_to_reload_keys_response() -> Response {
unhandled_rejection_response("failed to reload keys".to_string())
pub fn failed_to_reload_keys_response<E: Error>(e: E) -> Response {
unhandled_rejection_response(format!("failed to reload keys: {e}"))
}

/// This error response is to keep backward compatibility with the old Warp API.
Expand Down
87 changes: 42 additions & 45 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
use std::collections::{HashMap, HashSet};
use std::net::IpAddr;
use std::panic::Location;
use std::path::Path;
use std::str::FromStr;
use std::sync::Arc;
use std::{env, fs};

use config::{Config, ConfigError, File, FileFormat};
use log::warn;
use rand::{thread_rng, Rng};
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, NoneAsEmptyString};
use thiserror::Error;
use {std, toml};

use crate::databases::driver::Driver;
use crate::located_error::{Located, LocatedError};
use crate::tracker::mode;

#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
Expand Down Expand Up @@ -74,13 +79,30 @@ pub struct Configuration {
pub http_api: HttpApi,
}

#[derive(Debug)]
#[derive(Error, Debug)]
pub enum Error {
Message(String),
ConfigError(ConfigError),
IOError(std::io::Error),
ParseError(toml::de::Error),
TrackerModeIncompatible,
#[error("Unable to load from Environmental Variable: {source}")]
UnableToLoadFromEnvironmentVariable {
source: LocatedError<'static, dyn std::error::Error + Send + Sync>,
},

#[error("Default configuration created at: `{path}`, please review and reload tracker, {location}")]
CreatedNewConfigHalt {
location: &'static Location<'static>,
path: String,
},

#[error("Failed processing the configuration: {source}")]
ConfigError { source: LocatedError<'static, ConfigError> },
}

impl From<ConfigError> for Error {
#[track_caller]
fn from(err: ConfigError) -> Self {
Self::ConfigError {
source: Located(err).into(),
}
}
}

/// This configuration is used for testing. It generates random config values so they do not collide
Expand Down Expand Up @@ -129,20 +151,6 @@ fn random_port() -> u16 {
rng.gen_range(49152..65535)
}

impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Error::Message(e) => e.fmt(f),
Error::ConfigError(e) => e.fmt(f),
Error::IOError(e) => e.fmt(f),
Error::ParseError(e) => e.fmt(f),
Error::TrackerModeIncompatible => write!(f, "{self:?}"),
}
}
}

impl std::error::Error for Error {}

impl Default for Configuration {
fn default() -> Self {
let mut configuration = Configuration {
Expand Down Expand Up @@ -210,21 +218,19 @@ impl Configuration {
let mut config = Config::default();

if Path::new(path).exists() {
config = config_builder
.add_source(File::with_name(path))
.build()
.map_err(Error::ConfigError)?;
config = config_builder.add_source(File::with_name(path)).build()?;
} else {
eprintln!("No config file found.");
eprintln!("Creating config file..");
warn!("No config file found.");
warn!("Creating config file..");
let config = Configuration::default();
config.save_to_file(path)?;
return Err(Error::Message(
"Please edit the config.TOML and restart the tracker.".to_string(),
));
return Err(Error::CreatedNewConfigHalt {
location: Location::caller(),
path: path.to_string(),
});
}

let torrust_config: Configuration = config.try_deserialize().map_err(Error::ConfigError)?;
let torrust_config: Configuration = config.try_deserialize()?;

Ok(torrust_config)
}
Expand All @@ -237,15 +243,13 @@ impl Configuration {
Ok(config_toml) => {
let config_builder = Config::builder()
.add_source(File::from_str(&config_toml, FileFormat::Toml))
.build()
.map_err(Error::ConfigError)?;
let config = config_builder.try_deserialize().map_err(Error::ConfigError)?;
.build()?;
let config = config_builder.try_deserialize()?;
Ok(config)
}
Err(_) => Err(Error::Message(format!(
"No environment variable for configuration found: {}",
&config_env_var_name
))),
Err(e) => Err(Error::UnableToLoadFromEnvironmentVariable {
source: (Arc::new(e) as Arc<dyn std::error::Error + Send + Sync>).into(),
}),
}
}

Expand All @@ -262,7 +266,7 @@ impl Configuration {

#[cfg(test)]
mod tests {
use crate::config::{Configuration, Error};
use crate::config::Configuration;

#[cfg(test)]
fn default_config_toml() -> String {
Expand Down Expand Up @@ -381,13 +385,6 @@ mod tests {
assert_eq!(configuration, Configuration::default());
}

#[test]
fn configuration_error_could_be_displayed() {
let error = Error::TrackerModeIncompatible;

assert_eq!(format!("{error}"), "TrackerModeIncompatible");
}

#[test]
fn http_api_configuration_should_check_if_it_contains_a_token() {
let configuration = Configuration::default();
Expand Down
25 changes: 24 additions & 1 deletion src/databases/driver.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,30 @@
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)]
use super::error::Error;
use super::mysql::Mysql;
use super::sqlite::Sqlite;
use super::{Builder, Database};

#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, derive_more::Display, Clone)]
pub enum Driver {
Sqlite3,
MySQL,
}

impl Driver {
/// .
///
/// # Errors
///
/// This function will return an error if unable to connect to the database.
pub fn build(&self, db_path: &str) -> Result<Box<dyn Database>, Error> {
let database = match self {
Driver::Sqlite3 => Builder::<Sqlite>::build(db_path),
Driver::MySQL => Builder::<Mysql>::build(db_path),
}?;

database.create_database_tables().expect("Could not create database tables.");

Ok(database)
}
}
Loading