torrust_index/console/commands/seeder/
api.rs1use thiserror::Error;
3use tracing::debug;
4
5use crate::web::api::client::v1::client::Client;
6use crate::web::api::client::v1::contexts::category::forms::AddCategoryForm;
7use crate::web::api::client::v1::contexts::category::responses::{ListItem, ListResponse};
8use crate::web::api::client::v1::contexts::torrent::forms::UploadTorrentMultipartForm;
9use crate::web::api::client::v1::contexts::torrent::responses::{UploadedTorrent, UploadedTorrentResponse};
10use crate::web::api::client::v1::contexts::user::forms::LoginForm;
11use crate::web::api::client::v1::contexts::user::responses::{LoggedInUserData, SuccessfulLoginResponse};
12use crate::web::api::client::v1::responses::TextResponse;
13
14#[derive(Error, Debug)]
15pub enum Error {
16 #[error("Torrent with the same info-hash already exist in the database")]
17 TorrentInfoHashAlreadyExists,
18 #[error("Torrent with the same title already exist in the database")]
19 TorrentTitleAlreadyExists,
20}
21
22pub async fn upload_torrent(client: &Client, upload_torrent_form: UploadTorrentMultipartForm) -> Result<UploadedTorrent, Error> {
32 let categories = get_categories(client).await;
33
34 if !contains_category_with_name(&categories, &upload_torrent_form.category) {
35 add_category(client, &upload_torrent_form.category).await;
36 }
37
38 let response = client
42 .upload_torrent(upload_torrent_form.into())
43 .await
44 .expect("API should return a response");
45
46 debug!(target:"seeder", "response: {}", response.status);
47
48 if response.status == 400 {
49 if response.body.contains("This torrent already exists in our database") {
50 return Err(Error::TorrentInfoHashAlreadyExists);
51 }
52
53 if response.body.contains("This torrent title has already been used") {
54 return Err(Error::TorrentTitleAlreadyExists);
55 }
56 }
57
58 assert!(response.is_json_and_ok(), "Error uploading torrent: {}", response.body);
59
60 let uploaded_torrent_response: UploadedTorrentResponse =
61 serde_json::from_str(&response.body).expect("a valid JSON response should be returned from the Torrust Index API");
62
63 Ok(uploaded_torrent_response.data)
64}
65
66pub async fn login(client: &Client, username: &str, password: &str) -> LoggedInUserData {
72 let response = client
73 .login_user(LoginForm {
74 login: username.to_owned(),
75 password: password.to_owned(),
76 })
77 .await
78 .expect("API should return a response");
79
80 let res: SuccessfulLoginResponse = serde_json::from_str(&response.body).unwrap_or_else(|_| {
81 panic!(
82 "a valid JSON response should be returned after login. Received: {}",
83 response.body
84 )
85 });
86
87 res.data
88}
89
90pub async fn get_categories(client: &Client) -> Vec<ListItem> {
96 let response = client.get_categories().await.expect("API should return a response");
97
98 let res: ListResponse = serde_json::from_str(&response.body).unwrap();
99
100 res.data
101}
102
103pub async fn add_category(client: &Client, name: &str) -> TextResponse {
109 client
110 .add_category(AddCategoryForm {
111 name: name.to_owned(),
112 icon: None,
113 })
114 .await
115 .expect("API should return a response")
116}
117
118fn contains_category_with_name(items: &[ListItem], category_name: &str) -> bool {
120 items.iter().any(|item| item.name == category_name)
121}