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: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
/bin/
/config-idx-back.local.toml
/config-tracker.local.toml
/config.local.toml
/config.toml
/config.toml.local
/cspell.json
/data_v2.db*
/data.db
Expand Down
2 changes: 1 addition & 1 deletion bin/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# Generate the default settings file if it does not exist
if ! [ -f "./config.toml" ]; then
cp ./config.toml.local ./config.toml
cp ./config.local.toml ./config.toml
fi

# Generate storage directory if it does not exist
Expand Down
5 changes: 5 additions & 0 deletions config-idx-back.local.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,8 @@ capacity = 128000000
entry_size_limit = 4000000
user_quota_period_seconds = 3600
user_quota_bytes = 64000000

[api]
default_torrent_page_size = 10
max_torrent_page_size = 30

4 changes: 4 additions & 0 deletions config.toml.local → config.local.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ capacity = 128000000
entry_size_limit = 4000000
user_quota_period_seconds = 3600
user_quota_bytes = 64000000

[api]
default_torrent_page_size = 10
max_torrent_page_size = 30
51 changes: 30 additions & 21 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,25 @@ pub struct ImageCache {
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TorrustConfig {
pub struct Api {
pub default_torrent_page_size: u8,
pub max_torrent_page_size: u8,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AppConfiguration {
pub website: Website,
pub tracker: Tracker,
pub net: Network,
pub auth: Auth,
pub database: Database,
pub mail: Mail,
pub image_cache: ImageCache,
pub api: Api,
}

impl TorrustConfig {
pub fn default() -> Self {
impl Default for AppConfiguration {
fn default() -> Self {
Self {
website: Website {
name: "Torrust".to_string(),
Expand Down Expand Up @@ -121,9 +128,9 @@ impl TorrustConfig {
email_verification_enabled: false,
from: "[email protected]".to_string(),
reply_to: "[email protected]".to_string(),
username: "".to_string(),
password: "".to_string(),
server: "".to_string(),
username: String::new(),
password: String::new(),
server: String::new(),
port: 25,
},
image_cache: ImageCache {
Expand All @@ -133,22 +140,28 @@ impl TorrustConfig {
user_quota_period_seconds: 3600,
user_quota_bytes: 64_000_000,
},
api: Api {
default_torrent_page_size: 10,
max_torrent_page_size: 30,
},
}
}
}

#[derive(Debug)]
pub struct Configuration {
pub settings: RwLock<TorrustConfig>,
pub settings: RwLock<AppConfiguration>,
}

impl Configuration {
pub fn default() -> Configuration {
Configuration {
settings: RwLock::new(TorrustConfig::default()),
impl Default for Configuration {
fn default() -> Self {
Self {
settings: RwLock::new(AppConfiguration::default()),
}
}
}

impl Configuration {
/// Loads the configuration from the configuration file.
pub async fn load_from_file(config_path: &str) -> Result<Configuration, ConfigError> {
let config_builder = Config::builder();
Expand All @@ -168,7 +181,7 @@ impl Configuration {
));
}

let torrust_config: TorrustConfig = match config.try_deserialize() {
let torrust_config: AppConfiguration = match config.try_deserialize() {
Ok(data) => Ok(data),
Err(e) => Err(ConfigError::Message(format!("Errors while processing config: {}.", e))),
}?;
Expand All @@ -191,16 +204,14 @@ impl Configuration {
let config_builder = Config::builder()
.add_source(File::from_str(&config_toml, FileFormat::Toml))
.build()?;
let torrust_config: TorrustConfig = config_builder.try_deserialize()?;
let torrust_config: AppConfiguration = config_builder.try_deserialize()?;
Ok(Configuration {
settings: RwLock::new(torrust_config),
})
}
Err(_) => {
return Err(ConfigError::Message(
"Unable to load configuration from the configuration environment variable.".to_string(),
))
}
Err(_) => Err(ConfigError::Message(
"Unable to load configuration from the configuration environment variable.".to_string(),
)),
}
}

Expand All @@ -215,7 +226,7 @@ impl Configuration {
Ok(())
}

pub async fn update_settings(&self, new_settings: TorrustConfig, config_path: &str) -> Result<(), ()> {
pub async fn update_settings(&self, new_settings: AppConfiguration, config_path: &str) -> Result<(), ()> {
let mut settings = self.settings.write().await;
*settings = new_settings;

Expand All @@ -225,9 +236,7 @@ impl Configuration {

Ok(())
}
}

impl Configuration {
pub async fn get_public(&self) -> ConfigurationPublic {
let settings_lock = self.settings.read().await;

Expand Down
4 changes: 2 additions & 2 deletions src/databases/mysql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ impl Database for MysqlDatabase {
categories: &Option<Vec<String>>,
sort: &Sorting,
offset: u64,
page_size: u8,
limit: u8,
) -> Result<TorrentsResponse, DatabaseError> {
let title = match search {
None => "%".to_string(),
Expand Down Expand Up @@ -365,7 +365,7 @@ impl Database for MysqlDatabase {
let res: Vec<TorrentListing> = sqlx::query_as::<_, TorrentListing>(&query_string)
.bind(title)
.bind(offset as i64)
.bind(page_size)
.bind(limit)
.fetch_all(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;
Expand Down
4 changes: 2 additions & 2 deletions src/databases/sqlite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ impl Database for SqliteDatabase {
categories: &Option<Vec<String>>,
sort: &Sorting,
offset: u64,
page_size: u8,
limit: u8,
) -> Result<TorrentsResponse, DatabaseError> {
let title = match search {
None => "%".to_string(),
Expand Down Expand Up @@ -360,7 +360,7 @@ impl Database for SqliteDatabase {
let res: Vec<TorrentListing> = sqlx::query_as::<_, TorrentListing>(&query_string)
.bind(title)
.bind(offset as i64)
.bind(page_size)
.bind(limit)
.fetch_all(&self.pool)
.await
.map_err(|_| DatabaseError::Error)?;
Expand Down
6 changes: 3 additions & 3 deletions src/routes/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use actix_web::{web, HttpRequest, HttpResponse, Responder};

use crate::bootstrap::config::ENV_VAR_DEFAULT_CONFIG_PATH;
use crate::common::WebAppData;
use crate::config::TorrustConfig;
use crate::config::AppConfiguration;
use crate::errors::{ServiceError, ServiceResult};
use crate::models::response::OkResponse;

Expand All @@ -28,7 +28,7 @@ pub async fn get_settings(req: HttpRequest, app_data: WebAppData) -> ServiceResu
return Err(ServiceError::Unauthorized);
}

let settings: tokio::sync::RwLockReadGuard<TorrustConfig> = app_data.cfg.settings.read().await;
let settings: tokio::sync::RwLockReadGuard<AppConfiguration> = app_data.cfg.settings.read().await;

Ok(HttpResponse::Ok().json(OkResponse { data: &*settings }))
}
Expand All @@ -49,7 +49,7 @@ pub async fn get_site_name(app_data: WebAppData) -> ServiceResult<impl Responder

pub async fn update_settings(
req: HttpRequest,
payload: web::Json<TorrustConfig>,
payload: web::Json<AppConfiguration>,
app_data: WebAppData,
) -> ServiceResult<impl Responder> {
// check for user
Expand Down
29 changes: 20 additions & 9 deletions src/routes/torrent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub fn init_routes(cfg: &mut web::ServiceConfig) {
.route(web::delete().to(delete_torrent_handler)),
),
);
cfg.service(web::scope("/torrents").service(web::resource("").route(web::get().to(get_torrents))));
cfg.service(web::scope("/torrents").service(web::resource("").route(web::get().to(get_torrents_handler))));
}

#[derive(FromRow)]
Expand Down Expand Up @@ -321,25 +321,36 @@ pub async fn delete_torrent_handler(req: HttpRequest, app_data: WebAppData) -> S
}))
}

// eg: /torrents?categories=music,other,movie&search=bunny&sort=size_DESC
pub async fn get_torrents(params: Query<TorrentSearch>, app_data: WebAppData) -> ServiceResult<impl Responder> {
/// It returns a list of torrents matching the search criteria.
/// Eg: `/torrents?categories=music,other,movie&search=bunny&sort=size_DESC`
///
/// # Errors
///
/// Returns a `ServiceError::DatabaseError` if the database query fails.
pub async fn get_torrents_handler(params: Query<TorrentSearch>, app_data: WebAppData) -> ServiceResult<impl Responder> {
let settings = app_data.cfg.settings.read().await;

let sort = params.sort.unwrap_or(Sorting::UploadedDesc);

let page = params.page.unwrap_or(0);

// make sure the min page size = 10
let page_size = match params.page_size.unwrap_or(30) {
0..=9 => 10,
v => v,
let page_size = params.page_size.unwrap_or(settings.api.default_torrent_page_size);

// Guard that page size does not exceed the maximum
let max_torrent_page_size = settings.api.max_torrent_page_size;
let page_size = if page_size > max_torrent_page_size {
max_torrent_page_size
} else {
page_size
};

let offset = (page * page_size as u32) as u64;
let offset = u64::from(page * u32::from(page_size));

let categories = params.categories.as_csv::<String>().unwrap_or(None);

let torrents_response = app_data
.database
.get_torrents_search_sorted_paginated(&params.search, &categories, &sort, offset, page_size as u8)
.get_torrents_search_sorted_paginated(&params.search, &categories, &sort, offset, page_size)
.await?;

Ok(HttpResponse::Ok().json(OkResponse { data: torrents_response }))
Expand Down
4 changes: 2 additions & 2 deletions tests/common/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ impl Client {

// Context: torrent

pub async fn get_torrents(&self) -> TextResponse {
self.http_client.get("torrents", Query::empty()).await
pub async fn get_torrents(&self, params: Query) -> TextResponse {
self.http_client.get("torrents", params).await
}

pub async fn get_torrent(&self, infohash: &InfoHash) -> TextResponse {
Expand Down
Loading