Skip to content

Commit 85b53cb

Browse files
authored
Merge pull request #19 from Power2All/v3.1.2
v3.1.2
2 parents 8febbf5 + 330eb7b commit 85b53cb

17 files changed

+562
-212
lines changed

Cargo.lock

+313-174
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "torrust-axum"
3-
version = "3.1.1"
3+
version = "3.1.2"
44
edition = "2021"
55
license = "AGPL-3.0"
66
authors = [
@@ -22,8 +22,8 @@ rpath = false
2222

2323
[dependencies]
2424
async-trait = "0.1.57"
25-
axum = { version = "0.5.16" }
26-
axum-client-ip = "0.2.0"
25+
axum = { version = "0.6.1" }
26+
axum-client-ip = "0.3.0"
2727
axum-server = { version = "0.4.1", features = ["tls-rustls"] }
2828
binascii = "0.1.4"
2929
bip_bencode = "0.4.4"
@@ -39,7 +39,7 @@ log = "0.4.17"
3939
mime_guess = "2.0.4"
4040
percent-encoding = "2.1.0"
4141
rustls = "0.20.6"
42-
scc = "0.8.3"
42+
scc = "0.12.3"
4343
serde = { version = "1.0.141", features = ["derive"] }
4444
serde_json = "1.0.82"
4545
sqlx = { version = "0.6.0", features = ["mysql", "postgres", "sqlite", "runtime-tokio-rustls"] }

README.md

+37-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ This project originated from Torrust Tracker code originally developed by Mick v
1313
* [X] Multiple UDP server and HTTP(S) server blocks for socket binding possibilities
1414
* [X] Full IPv4 and IPv6 support for both UDP and HTTP(S)
1515
* [X] Built-in API on a separate port in HTTP
16-
* [X] Persistence saving supported using SQLite3, MySQL or PostgreSQL database
16+
* [X] Toggle maintenance mode through API and WebGUI
17+
* [X] Persistence saving supported using SQLite3, MySQL or PostgresSQL database
1718
* [X] Customize table and database names in the configuration file for persistence
1819
* [X] Whitelist system, which can be used to make the tracker private
1920
* [X] Blacklist system, to block and ban hashes
@@ -70,6 +71,7 @@ whitelist = false
7071
blacklist = false
7172
keys = true
7273
keys_cleanup_interval = 10
74+
maintenance_mode_enabled = false
7375
interval = 1800
7476
interval_minimum = 1800
7577
interval_cleanup = 900
@@ -152,6 +154,7 @@ This will show statistics of the tracker in JSON format.
152154
"torrents":0,
153155
"torrents_updates":0,
154156
"torrents_shadow":0,
157+
"maintenance_mode":false,
155158
"seeds":0,
156159
"peers":0,
157160
"completed":0,
@@ -336,6 +339,39 @@ This will remove a key from the keys list, and returns status if successful or f
336339
}
337340
```
338341

342+
### ChangeLog
343+
344+
#### v3.1.2
345+
* Bumped library versions.
346+
* Added a Code of Conduct file, as some open source projects need this.
347+
* Added a Maintenance toggle function to API and WebGUI.
348+
* Configuration file is not generated when it doesn't exist, or has invalid data, unless forced with a '--create-config' argument.
349+
* Fixed various small bugs.
350+
351+
#### v3.1.1
352+
* Bumped library versions.
353+
* Database for SQLite3, MySQL and PostgreSQL now works properly with all the tables, and will be used if enabled.
354+
* UDP had a problem in IPv4, fixed the code for correctly parsing byte array.
355+
* Cleanup and refactoring of some redundant code.
356+
* Added some small checks where needed to prevent errors.
357+
358+
#### v3.1.0
359+
* Whitelist System: You can enable this to only allow torrent hashes to be used you specify in the database, or add them through the API.
360+
* Blacklist System: You can enable this to disallow torrent hashes to be used you specify in the database, or add them through the API.
361+
* Keys System: You can enable this to only allow tracking when an activated "key" hash (same as an info_hash, 20 bytes or 40 characters hex) is given. Keys with a timeout of zero "0" will be permanent and won't be purged by the cleanup.
362+
* WebGUI: The API has an available web interface, which can be accessed through https://your.api:8080/webgui/ and giving the correct API Key, which you configure in the configuration file.
363+
* Customizable database structure can be given in the configuration file.
364+
* The system is also now available through Docker Hub at https://hub.docker.com/r/power2all/torrust-axum
365+
366+
#### v3.0.1
367+
* Bugfixes
368+
* SQLite3 support added
369+
* MySQL support added
370+
* PostgresSQL support added
371+
372+
#### v3.0.0
373+
Initial version of Torrust-Axum.
374+
339375
### Credits
340376
This Torrust-Tracker was a joint effort by [Nautilus Cyberneering GmbH](https://nautilus-cyberneering.de/), [Dutch Bits](https://dutchbits.nl) and [Power2All](https://power2all.com).
341377
Also thanks to [Naim A.](https://github.com/naim94a/udpt) and [greatest-ape](https://github.com/greatest-ape/aquatic) for some parts in the Torrust-Tracker code.

bin/torrust-axum.exe

16.5 KB
Binary file not shown.
-150 KB
Binary file not shown.

docker/Dockerfile

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
FROM rust:alpine3.16
1+
FROM rust:alpine3.17
2+
3+
ARG FULL_CONFIG=""
4+
ENV FULL_CONFIG=$FULL_CONFIG
25

36
ARG LOG_LEVEL="info"
47
ARG LOG_CONSOLE_INTERVAL=10

docker/init.sh

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
#!/bin/sh
22

3-
cat << EOF > /root/torrust-axum/target/release/config.toml
3+
if [ ! -z "${FULL_CONFIG}" ]
4+
then
5+
cat << EOF > /root/torrust-axum/target/release/config.toml
6+
${FULL_CONFIG}
7+
EOF
8+
else
9+
cat << EOF > /root/torrust-axum/target/release/config.toml
410
log_level = "${LOG_LEVEL}"
511
log_console_interval = ${LOG_CONSOLE_INTERVAL}
612
statistics_enabled = ${STATISTICS_ENABLED}
@@ -67,6 +73,7 @@ db_keys = "${DB_STRUCTURE_DB_KEYS}"
6773
table_keys_hash = "${DB_STRUCTURE_TABLE_KEYS_HASH}"
6874
table_keys_timeout = "${DB_STRUCTURE_TABLE_KEYS_TIMEOUT}"
6975
EOF
76+
fi
7077

7178
echo "Configuration:"
7279
echo ""

src/common.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@ use std::error::Error;
22
use std::fmt;
33
use std::fmt::Formatter;
44
use std::net::{IpAddr, SocketAddr};
5+
use scc::ebr::Arc;
56
use scc::HashIndex;
67
use serde::{Deserialize, Serialize};
8+
use crate::tracker::TorrentTracker;
79
use crate::udp_common;
810
use crate::udp_common::AnnounceRequest;
911

1012
pub fn parse_query(query: Option<String>) -> Result<HashIndex<String, Vec<Vec<u8>>>, CustomError> {
11-
let queries: HashIndex<String, Vec<Vec<u8>>> = HashIndex::new(0, Default::default());
13+
let queries: HashIndex<String, Vec<Vec<u8>>> = HashIndex::new();
1214
match query {
1315
None => {}
1416
Some(result) => {
@@ -463,3 +465,12 @@ fn bin2hex(data: &[u8; 20], f: &mut Formatter) -> fmt::Result {
463465
binascii::bin2hex(data, &mut chars).expect("failed to hexlify");
464466
write!(f, "{}", std::str::from_utf8(&chars).unwrap())
465467
}
468+
469+
pub async fn maintenance_mode(tracker: Arc<TorrentTracker>) -> bool
470+
{
471+
let stats = tracker.clone().get_stats().await;
472+
if stats.maintenance_mode != 0 {
473+
return true;
474+
}
475+
false
476+
}

src/config.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ pub struct Configuration {
7676
pub keys: bool,
7777
pub keys_cleanup_interval: Option<u64>,
7878

79+
pub maintenance_mode_enabled: bool,
7980
pub interval: Option<u64>,
8081
pub interval_minimum: Option<u64>,
8182
pub interval_cleanup: Option<u64>,
@@ -131,6 +132,7 @@ impl Configuration {
131132
keys: false,
132133
keys_cleanup_interval: Some(60),
133134

135+
maintenance_mode_enabled: false,
134136
interval: Some(1800),
135137
interval_minimum: Some(1800),
136138
interval_cleanup: Some(900),
@@ -186,12 +188,17 @@ impl Configuration {
186188
}
187189
}
188190

189-
pub fn load_from_file() -> Result<Configuration, CustomError> {
191+
pub fn load_from_file(create: bool) -> Result<Configuration, CustomError> {
190192
let mut config = Configuration::default();
191193
match Configuration::load_file("config.toml") {
192194
Ok(c) => { config = c; }
193195
Err(_) => {
194196
eprintln!("No config file found.");
197+
198+
if !create {
199+
eprintln!("You can either create your own config.toml file, or start this app using '--create-config' as parameter.");
200+
return Err(CustomError::new("will not create automatically config.toml file"));
201+
}
195202
eprintln!("Creating config file..");
196203

197204
let config_toml = toml::to_string(&config).unwrap();

src/databases.rs

+12-12
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ impl DatabaseConnector {
561561
"INSERT OR REPLACE INTO {} ({}) VALUES ('{}')",
562562
self.config.db_structure.db_whitelist,
563563
self.config.db_structure.table_whitelist_info_hash,
564-
info_hash.to_string()
564+
info_hash
565565
))
566566
.execute(&mut whitelist_transaction)
567567
.await {
@@ -598,7 +598,7 @@ impl DatabaseConnector {
598598
"INSERT INTO {} ({}) VALUES (UNHEX('{}'))",
599599
self.config.db_structure.db_whitelist,
600600
self.config.db_structure.table_whitelist_info_hash,
601-
info_hash.to_string()
601+
info_hash
602602
))
603603
.execute(&mut whitelist_transaction)
604604
.await {
@@ -641,7 +641,7 @@ impl DatabaseConnector {
641641
"INSERT INTO {} ({}) VALUES (decode('{}','hex'))",
642642
self.config.db_structure.db_whitelist,
643643
self.config.db_structure.table_whitelist_info_hash,
644-
info_hash.to_string()
644+
info_hash
645645
))
646646
.execute(&mut whitelist_transaction)
647647
.await {
@@ -687,7 +687,7 @@ impl DatabaseConnector {
687687
"INSERT OR REPLACE INTO {} ({}) VALUES ('{}')",
688688
self.config.db_structure.db_blacklist,
689689
self.config.db_structure.table_blacklist_info_hash,
690-
info_hash.to_string()
690+
info_hash
691691
))
692692
.execute(&mut blacklist_transaction)
693693
.await {
@@ -724,7 +724,7 @@ impl DatabaseConnector {
724724
"INSERT INTO {} ({}) VALUES (UNHEX('{}'))",
725725
self.config.db_structure.db_blacklist,
726726
self.config.db_structure.table_blacklist_info_hash,
727-
info_hash.to_string()
727+
info_hash
728728
))
729729
.execute(&mut blacklist_transaction)
730730
.await {
@@ -761,7 +761,7 @@ impl DatabaseConnector {
761761
"INSERT INTO {} ({}) VALUES (decode('{}','hex'))",
762762
self.config.db_structure.db_blacklist,
763763
self.config.db_structure.table_blacklist_info_hash,
764-
info_hash.to_string()
764+
info_hash
765765
))
766766
.execute(&mut blacklist_transaction)
767767
.await {
@@ -808,7 +808,7 @@ impl DatabaseConnector {
808808
self.config.db_structure.db_keys,
809809
self.config.db_structure.table_keys_hash,
810810
self.config.db_structure.table_keys_timeout,
811-
hash.to_string(),
811+
hash,
812812
timeout.clone()
813813
))
814814
.execute(&mut keys_transaction)
@@ -846,7 +846,7 @@ impl DatabaseConnector {
846846
self.config.db_structure.db_keys,
847847
self.config.db_structure.table_keys_hash,
848848
self.config.db_structure.table_keys_timeout,
849-
hash.to_string(),
849+
hash,
850850
timeout.clone(),
851851
self.config.db_structure.table_keys_timeout,
852852
self.config.db_structure.table_keys_timeout
@@ -886,7 +886,7 @@ impl DatabaseConnector {
886886
self.config.db_structure.db_keys,
887887
self.config.db_structure.table_keys_hash,
888888
self.config.db_structure.table_keys_timeout,
889-
hash.to_string(),
889+
hash,
890890
timeout.clone(),
891891
self.config.db_structure.table_keys_hash,
892892
self.config.db_structure.table_keys_timeout,
@@ -937,7 +937,7 @@ impl DatabaseConnector {
937937
self.config.db_structure.db_torrents,
938938
self.config.db_structure.table_torrents_info_hash,
939939
self.config.db_structure.table_torrents_completed,
940-
info_hash.to_string(),
940+
info_hash,
941941
completed.clone()
942942
))
943943
.execute(&mut torrents_transaction)
@@ -975,7 +975,7 @@ impl DatabaseConnector {
975975
self.config.db_structure.db_torrents,
976976
self.config.db_structure.table_torrents_info_hash,
977977
self.config.db_structure.table_torrents_completed,
978-
info_hash.to_string(),
978+
info_hash,
979979
completed.clone(),
980980
self.config.db_structure.table_torrents_completed,
981981
self.config.db_structure.table_torrents_completed
@@ -1015,7 +1015,7 @@ impl DatabaseConnector {
10151015
self.config.db_structure.db_torrents,
10161016
self.config.db_structure.table_torrents_info_hash,
10171017
self.config.db_structure.table_torrents_completed,
1018-
info_hash.to_string(),
1018+
info_hash,
10191019
completed.clone(),
10201020
self.config.db_structure.table_torrents_info_hash,
10211021
self.config.db_structure.table_torrents_completed,

src/http_api.rs

+52
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ pub async fn http_api(handle: Handle, addr: SocketAddr, data: Arc<TorrentTracker
4242
.route("/api/keys/reload", get(http_api_keys_reload))
4343
.route("/api/keys/:key", get(http_api_keys_get).delete(http_api_keys_delete))
4444
.route("/api/keys/:key/:seconds_valid", post(http_api_keys_post).patch(http_api_keys_patch))
45+
.route("/api/maintenance/enable", get(http_api_maintenance_enable))
46+
.route("/api/maintenance/disable", get(http_api_maintenance_disable))
4547
.layer(CorsLayer::new()
4648
.allow_methods([Method::GET, Method::POST, Method::DELETE, Method::PATCH])
4749
.allow_origin(Any)
@@ -76,6 +78,8 @@ pub async fn https_api(handle: Handle, addr: SocketAddr, data: Arc<TorrentTracke
7678
.route("/api/keys/reload", get(http_api_keys_reload))
7779
.route("/api/keys/:key", get(http_api_keys_get).delete(http_api_keys_delete))
7880
.route("/api/keys/:key/:seconds_valid", post(http_api_keys_post).patch(http_api_keys_patch))
81+
.route("/api/maintenance/enable", get(http_api_maintenance_enable))
82+
.route("/api/maintenance/disable", get(http_api_maintenance_disable))
7983
.layer(CorsLayer::new()
8084
.allow_methods([Method::GET, Method::POST, Method::DELETE, Method::PATCH])
8185
.allow_origin(Any)
@@ -822,6 +826,54 @@ pub async fn http_api_keys_delete(ClientIp(ip): ClientIp, axum::extract::RawQuer
822826
(StatusCode::OK, headers, serde_json::to_string(&return_data).unwrap())
823827
}
824828

829+
pub async fn http_api_maintenance_enable(ClientIp(ip): ClientIp, axum::extract::RawQuery(params): axum::extract::RawQuery, Extension(state): Extension<Arc<TorrentTracker>>) -> (StatusCode, HeaderMap, String)
830+
{
831+
http_api_stats_log(ip, state.clone()).await;
832+
833+
let mut headers = HeaderMap::new();
834+
headers.insert(HeaderName::from_static("content-type"), HeaderValue::from_static("text/plain"));
835+
836+
let query_map_result = parse_query(params);
837+
let query_map = match api_query_hashing(query_map_result, headers.clone()) {
838+
Ok(result) => { result }
839+
Err(err) => { return err; }
840+
};
841+
842+
let check_token = check_api_token(state.clone().config.clone(), ip, query_map.clone(), headers.clone()).await;
843+
if check_token.is_some() {
844+
return check_token.unwrap();
845+
}
846+
847+
state.clone().set_stats(StatsEvent::MaintenanceMode, 1).await;
848+
849+
let return_data = json!({ "status": "ok"});
850+
(StatusCode::OK, headers, serde_json::to_string(&return_data).unwrap())
851+
}
852+
853+
pub async fn http_api_maintenance_disable(ClientIp(ip): ClientIp, axum::extract::RawQuery(params): axum::extract::RawQuery, Extension(state): Extension<Arc<TorrentTracker>>) -> (StatusCode, HeaderMap, String)
854+
{
855+
http_api_stats_log(ip, state.clone()).await;
856+
857+
let mut headers = HeaderMap::new();
858+
headers.insert(HeaderName::from_static("content-type"), HeaderValue::from_static("text/plain"));
859+
860+
let query_map_result = parse_query(params);
861+
let query_map = match api_query_hashing(query_map_result, headers.clone()) {
862+
Ok(result) => { result }
863+
Err(err) => { return err; }
864+
};
865+
866+
let check_token = check_api_token(state.clone().config.clone(), ip, query_map.clone(), headers.clone()).await;
867+
if check_token.is_some() {
868+
return check_token.unwrap();
869+
}
870+
871+
state.clone().set_stats(StatsEvent::MaintenanceMode, 0).await;
872+
873+
let return_data = json!({ "status": "ok"});
874+
(StatusCode::OK, headers, serde_json::to_string(&return_data).unwrap())
875+
}
876+
825877
pub async fn http_api_stats_log(ip: IpAddr, tracker: Arc<TorrentTracker>)
826878
{
827879
if ip.is_ipv4() {

0 commit comments

Comments
 (0)