mirror of
https://github.com/Arcadia-Solutions/arcadia.git
synced 2025-12-20 08:49:36 -06:00
save dottorrent file to disk, added route to download dottorrent file
This commit is contained in:
2
.env
2
.env
@@ -22,6 +22,8 @@ DATABASE_URL=postgresql://${POSTGRESQL_USER}:${POSTGRESQL_PASSWORD}@${POSTGRESQL
|
|||||||
# arcadia config
|
# arcadia config
|
||||||
|
|
||||||
ARCADIA_OPEN_SIGNUPS=true
|
ARCADIA_OPEN_SIGNUPS=true
|
||||||
|
ARCADIA_DOTTORRENT_FILES_PATH=/absolute/path/to/dottorrent_files
|
||||||
|
ARCADIA_FRONTEND_URL=https://site.com
|
||||||
|
|
||||||
# Required for TMDB access, must create a new account with themoviedb.org
|
# Required for TMDB access, must create a new account with themoviedb.org
|
||||||
# TMDB_API_TOKEN="your token"
|
# TMDB_API_TOKEN="your token"
|
||||||
|
|||||||
@@ -75,3 +75,5 @@ You can also hop on the [discord server](https://discord.gg/amYWVk7pS3) to chat
|
|||||||
Arcadia-index has a [board](https://github.com/orgs/Arcadia-Solutions/projects/1) to track the existing issues and features that need to be worked on. Feel free to claim one that isn't claimed yet before starting to work on it.
|
Arcadia-index has a [board](https://github.com/orgs/Arcadia-Solutions/projects/1) to track the existing issues and features that need to be worked on. Feel free to claim one that isn't claimed yet before starting to work on it.
|
||||||
|
|
||||||
To claim a github issue, simply leave a comment on it saying that you are working on it.
|
To claim a github issue, simply leave a comment on it saying that you are working on it.
|
||||||
|
|
||||||
|
You can also search for `TODO` in the code and pick one of those tasks. If you decide to do this, please open an issue first and clam it before working on the task.
|
||||||
@@ -24,6 +24,7 @@ rand = "0.9.0"
|
|||||||
bip_metainfo = "0.12.0"
|
bip_metainfo = "0.12.0"
|
||||||
serde_bytes = "0.11"
|
serde_bytes = "0.11"
|
||||||
actix-multipart = "0.7.2"
|
actix-multipart = "0.7.2"
|
||||||
|
actix-files = "0.6.6"
|
||||||
reqwest = { version = "0.12", features = ["json"] }
|
reqwest = { version = "0.12", features = ["json"] }
|
||||||
actix-cors = "0.7.1"
|
actix-cors = "0.7.1"
|
||||||
actix-http = "3.10.0"
|
actix-http = "3.10.0"
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
use utoipa::OpenApi;
|
use utoipa::OpenApi;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
handlers::{artist_handler::GetArtistPublicationsQuery, auth_handler::RegisterQuery},
|
handlers::{
|
||||||
|
artist_handler::GetArtistPublicationsQuery, auth_handler::RegisterQuery,
|
||||||
|
torrent_handler::DownloadTorrentQuery,
|
||||||
|
},
|
||||||
models::user::{Login, Register},
|
models::user::{Login, Register},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -12,7 +15,14 @@ use crate::{
|
|||||||
crate::handlers::auth_handler::register,
|
crate::handlers::auth_handler::register,
|
||||||
crate::handlers::auth_handler::login,
|
crate::handlers::auth_handler::login,
|
||||||
crate::handlers::artist_handler::get_artist_publications,
|
crate::handlers::artist_handler::get_artist_publications,
|
||||||
|
crate::handlers::torrent_handler::download_dottorrent_file,
|
||||||
),
|
),
|
||||||
components(schemas(Register, RegisterQuery, Login, GetArtistPublicationsQuery),)
|
components(schemas(
|
||||||
|
Register,
|
||||||
|
RegisterQuery,
|
||||||
|
Login,
|
||||||
|
GetArtistPublicationsQuery,
|
||||||
|
DownloadTorrentQuery
|
||||||
|
),)
|
||||||
)]
|
)]
|
||||||
pub struct ApiDoc;
|
pub struct ApiDoc;
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
use actix_files::NamedFile;
|
||||||
use actix_multipart::form::MultipartForm;
|
use actix_multipart::form::MultipartForm;
|
||||||
use actix_web::{HttpResponse, web};
|
use actix_web::{HttpResponse, web};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use utoipa::{IntoParams, ToSchema};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Arcadia, Result,
|
Arcadia, Error, Result,
|
||||||
models::{torrent::UploadedTorrent, user::User},
|
models::{torrent::UploadedTorrent, user::User},
|
||||||
repositories::torrent_repository::create_torrent,
|
repositories::torrent_repository::create_torrent,
|
||||||
};
|
};
|
||||||
@@ -14,7 +17,50 @@ pub async fn upload_torrent(
|
|||||||
) -> Result<HttpResponse> {
|
) -> Result<HttpResponse> {
|
||||||
// TODO : check if user can upload
|
// TODO : check if user can upload
|
||||||
|
|
||||||
let torrent = create_torrent(&arc.pool, &form, ¤t_user).await?;
|
let torrent = create_torrent(
|
||||||
|
&arc.pool,
|
||||||
|
&form,
|
||||||
|
¤t_user,
|
||||||
|
&arc.frontend_url,
|
||||||
|
&arc.dottorrent_files_path,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(HttpResponse::Created().json(torrent))
|
Ok(HttpResponse::Created().json(torrent))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams, ToSchema)]
|
||||||
|
pub struct DownloadTorrentQuery {
|
||||||
|
id: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/torrent",
|
||||||
|
params (DownloadTorrentQuery),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Successfully downloaded the torrent file"),
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn download_dottorrent_file(
|
||||||
|
query: web::Query<DownloadTorrentQuery>,
|
||||||
|
arc: web::Data<Arcadia>,
|
||||||
|
current_user: User,
|
||||||
|
) -> Result<NamedFile> {
|
||||||
|
let file_path = &arc
|
||||||
|
.dottorrent_files_path
|
||||||
|
.join(query.id.to_string() + ".torrent");
|
||||||
|
|
||||||
|
// should never happen as query.id is an int, but we never know...
|
||||||
|
if !file_path.starts_with(&arc.dottorrent_files_path) {
|
||||||
|
println!(
|
||||||
|
"User(username: {}, id: {}) attempted path traversal: {:#?}",
|
||||||
|
current_user.username,
|
||||||
|
current_user.id,
|
||||||
|
file_path.to_str()
|
||||||
|
);
|
||||||
|
return Err(Error::DottorrentFileNotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
actix_files::NamedFile::open(&file_path).map_err(|_| Error::DottorrentFileNotFound)
|
||||||
|
}
|
||||||
|
|||||||
11
src/lib.rs
11
src/lib.rs
@@ -1,3 +1,6 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use reqwest::Url;
|
||||||
pub mod api_doc;
|
pub mod api_doc;
|
||||||
pub mod handlers;
|
pub mod handlers;
|
||||||
pub mod models;
|
pub mod models;
|
||||||
@@ -13,6 +16,8 @@ pub enum OpenSignups {
|
|||||||
pub struct Arcadia {
|
pub struct Arcadia {
|
||||||
pub pool: sqlx::PgPool,
|
pub pool: sqlx::PgPool,
|
||||||
pub open_signups: OpenSignups,
|
pub open_signups: OpenSignups,
|
||||||
|
pub dottorrent_files_path: PathBuf,
|
||||||
|
pub frontend_url: Url,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Arcadia {
|
impl Arcadia {
|
||||||
@@ -105,6 +110,12 @@ pub enum Error {
|
|||||||
#[error("not enough upload to place this bounty")]
|
#[error("not enough upload to place this bounty")]
|
||||||
InsufficientUploadForBounty,
|
InsufficientUploadForBounty,
|
||||||
|
|
||||||
|
#[error("dottorrent file not found")]
|
||||||
|
DottorrentFileNotFound,
|
||||||
|
|
||||||
|
#[error("could not save torrent file in path: '{0}'\n'{1}'")]
|
||||||
|
CouldNotSaveTorrentFile(String, String),
|
||||||
|
|
||||||
#[error("unexpected third party response")]
|
#[error("unexpected third party response")]
|
||||||
UnexpectedThirdPartyResponse(#[from] reqwest::Error),
|
UnexpectedThirdPartyResponse(#[from] reqwest::Error),
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/main.rs
14
src/main.rs
@@ -6,9 +6,10 @@ mod routes;
|
|||||||
use actix_cors::Cors;
|
use actix_cors::Cors;
|
||||||
use actix_web::{App, HttpServer, middleware, web::Data};
|
use actix_web::{App, HttpServer, middleware, web::Data};
|
||||||
use dotenv;
|
use dotenv;
|
||||||
|
use reqwest::Url;
|
||||||
use routes::init;
|
use routes::init;
|
||||||
use sqlx::postgres::PgPoolOptions;
|
use sqlx::postgres::PgPoolOptions;
|
||||||
use std::env;
|
use std::{env, path::PathBuf, str::FromStr};
|
||||||
use utoipa::OpenApi;
|
use utoipa::OpenApi;
|
||||||
use utoipa_swagger_ui::SwaggerUi;
|
use utoipa_swagger_ui::SwaggerUi;
|
||||||
|
|
||||||
@@ -37,6 +38,12 @@ async fn main() -> std::io::Result<()> {
|
|||||||
OpenSignups::Disabled
|
OpenSignups::Disabled
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let dottorrent_files_path = env::var("ARCADIA_DOTTORRENT_FILES_PATH")
|
||||||
|
.expect("ARCADIA_DOTTORRENT_FILES_PATH env var is not set");
|
||||||
|
|
||||||
|
let frontend_url =
|
||||||
|
env::var("ARCADIA_FRONTEND_URL").expect("ARCADIA_FRONTEND_URL env var is not set");
|
||||||
|
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
let cors = Cors::permissive();
|
let cors = Cors::permissive();
|
||||||
App::new()
|
App::new()
|
||||||
@@ -44,7 +51,10 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.wrap(cors)
|
.wrap(cors)
|
||||||
.app_data(Data::new(Arcadia {
|
.app_data(Data::new(Arcadia {
|
||||||
pool: pool.clone(),
|
pool: pool.clone(),
|
||||||
open_signups,
|
open_signups: open_signups,
|
||||||
|
dottorrent_files_path: PathBuf::from(&dottorrent_files_path),
|
||||||
|
frontend_url: Url::from_str(&frontend_url)
|
||||||
|
.expect("ARCADIA_FRONTEND_URL env var malformed"),
|
||||||
}))
|
}))
|
||||||
.configure(init) // Initialize routes
|
.configure(init) // Initialize routes
|
||||||
.service(
|
.service(
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ use crate::{
|
|||||||
user::User,
|
user::User,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use bip_metainfo::Metainfo;
|
use bip_metainfo::Metainfo;
|
||||||
|
use reqwest::Url;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::str::FromStr;
|
use std::{fs, path::PathBuf, str::FromStr};
|
||||||
|
|
||||||
use super::notification_repository::notify_users;
|
use super::notification_repository::notify_users;
|
||||||
|
|
||||||
@@ -22,6 +24,8 @@ pub async fn create_torrent(
|
|||||||
pool: &PgPool,
|
pool: &PgPool,
|
||||||
torrent_form: &UploadedTorrent,
|
torrent_form: &UploadedTorrent,
|
||||||
current_user: &User,
|
current_user: &User,
|
||||||
|
frontend_url: &Url,
|
||||||
|
dottorrent_files_path: &PathBuf,
|
||||||
) -> Result<Torrent> {
|
) -> Result<Torrent> {
|
||||||
let create_torrent_query = r#"
|
let create_torrent_query = r#"
|
||||||
INSERT INTO torrents (
|
INSERT INTO torrents (
|
||||||
@@ -131,8 +135,6 @@ pub async fn create_torrent(
|
|||||||
.await
|
.await
|
||||||
.map_err(Error::CouldNotCreateTorrent)?;
|
.map_err(Error::CouldNotCreateTorrent)?;
|
||||||
|
|
||||||
// TODO: edit the torrent file with proper flags, remove announce url and store it to the disk
|
|
||||||
|
|
||||||
let title_group_info = sqlx::query_as!(
|
let title_group_info = sqlx::query_as!(
|
||||||
LiteTitleGroupInfo,
|
LiteTitleGroupInfo,
|
||||||
r#"
|
r#"
|
||||||
@@ -146,6 +148,17 @@ pub async fn create_torrent(
|
|||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
//TODO: edit torrent file : remove announce url, add comment with torrent url, etc.
|
||||||
|
let output_path = format!(
|
||||||
|
"{}",
|
||||||
|
dottorrent_files_path
|
||||||
|
.join(format!("{}{}", uploaded_torrent.id, ".torrent"))
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
fs::write(&output_path, &torrent_form.torrent_file.data)
|
||||||
|
.map_err(|error| Error::CouldNotSaveTorrentFile(output_path, error.to_string()))?;
|
||||||
|
|
||||||
let _ = notify_users(
|
let _ = notify_users(
|
||||||
pool,
|
pool,
|
||||||
&"torrent_uploaded",
|
&"torrent_uploaded",
|
||||||
|
|||||||
@@ -6,13 +6,15 @@ use crate::handlers::{
|
|||||||
edition_group_handler::add_edition_group,
|
edition_group_handler::add_edition_group,
|
||||||
invitation_handler::send_invitation,
|
invitation_handler::send_invitation,
|
||||||
master_group_handler::add_master_group,
|
master_group_handler::add_master_group,
|
||||||
scrapers::open_library::get_open_library_data,
|
scrapers::{
|
||||||
scrapers::tmdb::{get_tmdb_movie_data, get_tmdb_tv_data},
|
open_library::get_open_library_data,
|
||||||
|
tmdb::{get_tmdb_movie_data, get_tmdb_tv_data},
|
||||||
|
},
|
||||||
series_handler::{add_series, get_series},
|
series_handler::{add_series, get_series},
|
||||||
subscriptions_handler::{add_subscription, remove_subscription},
|
subscriptions_handler::{add_subscription, remove_subscription},
|
||||||
title_group_comment_handler::add_title_group_comment,
|
title_group_comment_handler::add_title_group_comment,
|
||||||
title_group_handler::{add_title_group, get_lite_title_group_info, get_title_group},
|
title_group_handler::{add_title_group, get_lite_title_group_info, get_title_group},
|
||||||
torrent_handler::upload_torrent,
|
torrent_handler::{download_dottorrent_file, upload_torrent},
|
||||||
torrent_request_handler::add_torrent_request,
|
torrent_request_handler::add_torrent_request,
|
||||||
torrent_request_vote_handler::add_torrent_request_vote,
|
torrent_request_vote_handler::add_torrent_request_vote,
|
||||||
user_handler::{get_me, get_user},
|
user_handler::{get_me, get_user},
|
||||||
@@ -38,6 +40,7 @@ pub fn init(cfg: &mut web::ServiceConfig) {
|
|||||||
)
|
)
|
||||||
.route("/edition-group", web::post().to(add_edition_group))
|
.route("/edition-group", web::post().to(add_edition_group))
|
||||||
.route("/torrent", web::post().to(upload_torrent))
|
.route("/torrent", web::post().to(upload_torrent))
|
||||||
|
.route("/torrent", web::get().to(download_dottorrent_file))
|
||||||
.route("/artist", web::post().to(add_artist))
|
.route("/artist", web::post().to(add_artist))
|
||||||
.route("/artist", web::get().to(get_artist_publications))
|
.route("/artist", web::get().to(get_artist_publications))
|
||||||
.route(
|
.route(
|
||||||
|
|||||||
Reference in New Issue
Block a user