mirror of
https://github.com/Arcadia-Solutions/arcadia.git
synced 2025-12-16 23:14:15 -06:00
feat: insert torrent in tracker on torrent upload
This commit is contained in:
5
Cargo.lock
generated
5
Cargo.lock
generated
@@ -59,9 +59,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "actix-http"
|
||||
version = "3.11.0"
|
||||
version = "3.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44dfe5c9e0004c623edc65391dfd51daa201e7e30ebd9c9bedf873048ec32bc2"
|
||||
checksum = "7926860314cbe2fb5d1f13731e387ab43bd32bca224e82e6e2db85de0a3dba49"
|
||||
dependencies = [
|
||||
"actix-codec",
|
||||
"actix-rt",
|
||||
@@ -577,6 +577,7 @@ dependencies = [
|
||||
"serde",
|
||||
"sqlx",
|
||||
"thiserror 2.0.16",
|
||||
"utoipa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -36,3 +36,6 @@ REDIS_PORT=6379
|
||||
# Used for the backend to make requests to the tracker
|
||||
# and vice-versa
|
||||
ARCADIA_TRACKER_API_KEY=change_me
|
||||
# used to make requests from the backend to the tracker
|
||||
# ARCADIA_TRACKER_URL is used for public access, and this one only for the backend
|
||||
ARCADIA_TRACKER_URL_INTERNAL=http://localhost:8081
|
||||
|
||||
@@ -71,3 +71,6 @@ REDIS_PORT=6379
|
||||
# Used for the backend to make requests to the tracker
|
||||
# and vice-versa
|
||||
ARCADIA_TRACKER_API_KEY=change_me
|
||||
# used to make requests from the backend to the tracker
|
||||
# ARCADIA_TRACKER_URL is used for public access, and this one only for the backend
|
||||
ARCADIA_TRACKER_URL_INTERNAL=http://localhost:8081
|
||||
|
||||
@@ -58,6 +58,8 @@ pub struct TrackerConfig {
|
||||
pub name: String,
|
||||
#[envconfig(from = "ARCADIA_TRACKER_URL")]
|
||||
pub url: Url,
|
||||
#[envconfig(from = "ARCADIA_TRACKER_URL_INTERNAL")]
|
||||
pub url_internal: Url,
|
||||
|
||||
#[envconfig(from = "ARCADIA_TRACKER_API_KEY")]
|
||||
pub api_key: String,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use actix_multipart::form::MultipartForm;
|
||||
use actix_web::{web::Data, HttpResponse};
|
||||
use arcadia_shared::tracker::models::torrent::APIInsertTorrent;
|
||||
use log::debug;
|
||||
use reqwest::Client;
|
||||
|
||||
use crate::{middlewares::auth_middleware::Authdata, Arcadia};
|
||||
use arcadia_common::error::Result;
|
||||
@@ -30,5 +33,36 @@ pub async fn exec<R: RedisPoolInterface + 'static>(
|
||||
|
||||
let torrent = arc.pool.create_torrent(&form, user.sub).await?;
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
let mut url = arc.env.tracker.url_internal.clone();
|
||||
url.path_segments_mut()
|
||||
.unwrap()
|
||||
.push("api")
|
||||
.push("torrents");
|
||||
|
||||
let payload = APIInsertTorrent {
|
||||
id: torrent.id as u32,
|
||||
info_hash: torrent.info_hash,
|
||||
is_deleted: false,
|
||||
seeders: 0,
|
||||
leechers: 0,
|
||||
times_completed: 0,
|
||||
download_factor: torrent.upload_factor as u8,
|
||||
upload_factor: torrent.download_factor as u8,
|
||||
};
|
||||
|
||||
let res = client
|
||||
.put(url)
|
||||
.header("x-api-key", arc.env.tracker.api_key.clone())
|
||||
.json(&payload)
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
debug!(
|
||||
"Tried to insert new torrent into tracker's db and got: {:?}",
|
||||
res
|
||||
);
|
||||
|
||||
Ok(HttpResponse::Created().json(torrent))
|
||||
}
|
||||
|
||||
56
backend/storage/.sqlx/query-46d7eee133e0653d9f11ab67f5d6faec7050c9b4c6a8c78e2097015d3e0fb7fb.json
generated
Normal file
56
backend/storage/.sqlx/query-46d7eee133e0653d9f11ab67f5d6faec7050c9b4c6a8c78e2097015d3e0fb7fb.json
generated
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n id,\n upload_factor,\n download_factor,\n seeders,\n leechers,\n times_completed,\n CASE\n WHEN deleted_at IS NOT NULL THEN TRUE\n ELSE FALSE\n END AS \"is_deleted!\"\n FROM torrents\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "upload_factor",
|
||||
"type_info": "Int2"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "download_factor",
|
||||
"type_info": "Int2"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "seeders",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "leechers",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "times_completed",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "is_deleted!",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "46d7eee133e0653d9f11ab67f5d6faec7050c9b4c6a8c78e2097015d3e0fb7fb"
|
||||
}
|
||||
26
backend/storage/.sqlx/query-4edda78ffd766d9ec15eb015fe5b985755924b0f0b44d5cf9411059cfbc5c757.json
generated
Normal file
26
backend/storage/.sqlx/query-4edda78ffd766d9ec15eb015fe5b985755924b0f0b44d5cf9411059cfbc5c757.json
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n id,\n passkey as \"passkey: Passkey\"\n FROM users\n WHERE banned = FALSE\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "passkey: Passkey",
|
||||
"type_info": "Varchar"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "4edda78ffd766d9ec15eb015fe5b985755924b0f0b44d5cf9411059cfbc5c757"
|
||||
}
|
||||
26
backend/storage/.sqlx/query-599587c7ce69b090843274603171c411af859ae256fc01eaf66af2aa2a922900.json
generated
Normal file
26
backend/storage/.sqlx/query-599587c7ce69b090843274603171c411af859ae256fc01eaf66af2aa2a922900.json
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n INSERT INTO peers (\n peer_id,\n ip,\n port,\n agent,\n uploaded,\n downloaded,\n \"left\",\n active,\n seeder,\n created_at,\n updated_at,\n torrent_id,\n user_id\n )\n SELECT\n t.peer_id,\n t.ip,\n t.port,\n t.agent,\n t.uploaded,\n t.downloaded,\n t.\"left\",\n t.active,\n t.seeder,\n -- stored as timestamp without time zone in DB\n (t.created_at AT TIME ZONE 'UTC')::timestamp,\n (t.updated_at AT TIME ZONE 'UTC')::timestamp,\n t.torrent_id,\n t.user_id\n FROM (\n SELECT * FROM unnest(\n $1::bytea[],\n $2::inet[],\n $3::int[],\n $4::varchar[],\n $5::bigint[],\n $6::bigint[],\n $7::bigint[],\n $8::boolean[],\n $9::boolean[],\n $10::timestamptz[],\n $11::timestamptz[],\n $12::int[],\n $13::int[]\n ) AS t(\n peer_id,\n ip,\n port,\n agent,\n uploaded,\n downloaded,\n \"left\",\n active,\n seeder,\n created_at,\n updated_at,\n torrent_id,\n user_id\n )\n ) AS t\n ON CONFLICT (user_id, torrent_id, peer_id) DO UPDATE SET\n ip = EXCLUDED.ip,\n port = EXCLUDED.port,\n agent = EXCLUDED.agent,\n uploaded = EXCLUDED.uploaded,\n downloaded = EXCLUDED.downloaded,\n \"left\" = EXCLUDED.\"left\",\n active = EXCLUDED.active,\n seeder = EXCLUDED.seeder,\n updated_at = EXCLUDED.updated_at\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"ByteaArray",
|
||||
"InetArray",
|
||||
"Int4Array",
|
||||
"VarcharArray",
|
||||
"Int8Array",
|
||||
"Int8Array",
|
||||
"Int8Array",
|
||||
"BoolArray",
|
||||
"BoolArray",
|
||||
"TimestamptzArray",
|
||||
"TimestamptzArray",
|
||||
"Int4Array",
|
||||
"Int4Array"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "599587c7ce69b090843274603171c411af859ae256fc01eaf66af2aa2a922900"
|
||||
}
|
||||
18
backend/storage/.sqlx/query-68c566af855b2cb1e46b77cd829934488b1d4da1086f74b7491d7468753f539a.json
generated
Normal file
18
backend/storage/.sqlx/query-68c566af855b2cb1e46b77cd829934488b1d4da1086f74b7491d7468753f539a.json
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE users\n SET\n uploaded = uploaded + updates.uploaded_delta,\n downloaded = downloaded + updates.downloaded_delta,\n real_uploaded = real_uploaded + updates.uploaded_delta,\n real_downloaded = real_downloaded + updates.downloaded_delta\n FROM (\n SELECT * FROM unnest($1::int[], $2::bigint[], $3::bigint[], $4::bigint[], $5::bigint[]) AS\n t(user_id, uploaded_delta, downloaded_delta, real_uploaded_delta, real_downloaded_delta)\n ) AS updates\n WHERE users.id = updates.user_id\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4Array",
|
||||
"Int8Array",
|
||||
"Int8Array",
|
||||
"Int8Array",
|
||||
"Int8Array"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "68c566af855b2cb1e46b77cd829934488b1d4da1086f74b7491d7468753f539a"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE torrents\n SET\n release_name = $2,\n release_group = $3,\n description = $4,\n uploaded_as_anonymous = $5,\n mediainfo = $6,\n container = $7,\n duration = $8,\n audio_codec = $9,\n audio_bitrate = $10,\n audio_bitrate_sampling = $11,\n audio_channels = $12,\n video_codec = $13,\n features = $14,\n subtitle_languages = $15,\n video_resolution = $16,\n video_resolution_other_x = $17,\n video_resolution_other_y = $18,\n languages = $19,\n extras = $20,\n updated_at = NOW()\n WHERE id = $1 AND deleted_at IS NULL\n RETURNING\n id, upload_factor, download_factor, seeders, leechers,\n times_completed, snatched, edition_group_id, created_at, updated_at,\n created_by_id,\n deleted_at AS \"deleted_at!: _\",\n deleted_by_id AS \"deleted_by_id!: _\",\n extras AS \"extras!: _\",\n languages AS \"languages!: _\",\n release_name, release_group, description, file_amount_per_type,\n uploaded_as_anonymous, file_list, mediainfo, trumpable, staff_checked,\n container, size, duration,\n audio_codec AS \"audio_codec: _\",\n audio_bitrate,\n audio_bitrate_sampling AS \"audio_bitrate_sampling: _\",\n audio_channels AS \"audio_channels: _\",\n video_codec AS \"video_codec: _\",\n features AS \"features!: _\",\n subtitle_languages AS \"subtitle_languages!: _\",\n video_resolution AS \"video_resolution!: _\",\n video_resolution_other_x,\n video_resolution_other_y\n ",
|
||||
"query": "\n UPDATE torrents\n SET\n release_name = $2,\n release_group = $3,\n description = $4,\n uploaded_as_anonymous = $5,\n mediainfo = $6,\n container = $7,\n duration = $8,\n audio_codec = $9,\n audio_bitrate = $10,\n audio_bitrate_sampling = $11,\n audio_channels = $12,\n video_codec = $13,\n features = $14,\n subtitle_languages = $15,\n video_resolution = $16,\n video_resolution_other_x = $17,\n video_resolution_other_y = $18,\n languages = $19,\n extras = $20,\n updated_at = NOW()\n WHERE id = $1 AND deleted_at IS NULL\n RETURNING\n id, info_hash as \"info_hash: InfoHash\", upload_factor, download_factor, seeders, leechers,\n times_completed, snatched, edition_group_id, created_at, updated_at,\n created_by_id,\n deleted_at AS \"deleted_at!: _\",\n deleted_by_id AS \"deleted_by_id!: _\",\n extras AS \"extras!: _\",\n languages AS \"languages!: _\",\n release_name, release_group, description, file_amount_per_type,\n uploaded_as_anonymous, file_list, mediainfo, trumpable, staff_checked,\n container, size, duration,\n audio_codec AS \"audio_codec: _\",\n audio_bitrate,\n audio_bitrate_sampling AS \"audio_bitrate_sampling: _\",\n audio_channels AS \"audio_channels: _\",\n video_codec AS \"video_codec: _\",\n features AS \"features!: _\",\n subtitle_languages AS \"subtitle_languages!: _\",\n video_resolution AS \"video_resolution!: _\",\n video_resolution_other_x,\n video_resolution_other_y\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -10,66 +10,71 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "info_hash: InfoHash",
|
||||
"type_info": "Bytea"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "upload_factor",
|
||||
"type_info": "Int2"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"ordinal": 3,
|
||||
"name": "download_factor",
|
||||
"type_info": "Int2"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"ordinal": 4,
|
||||
"name": "seeders",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"ordinal": 5,
|
||||
"name": "leechers",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"ordinal": 6,
|
||||
"name": "times_completed",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"ordinal": 7,
|
||||
"name": "snatched",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 8,
|
||||
"name": "edition_group_id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 9,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 10,
|
||||
"name": "updated_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 11,
|
||||
"name": "created_by_id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 12,
|
||||
"name": "deleted_at!: _",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 13,
|
||||
"name": "deleted_by_id!: _",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 14,
|
||||
"name": "extras!: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -96,7 +101,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 15,
|
||||
"name": "languages!: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -166,67 +171,67 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 16,
|
||||
"name": "release_name",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 17,
|
||||
"name": "release_group",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"ordinal": 18,
|
||||
"name": "description",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"ordinal": 19,
|
||||
"name": "file_amount_per_type",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"ordinal": 20,
|
||||
"name": "uploaded_as_anonymous",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"ordinal": 21,
|
||||
"name": "file_list",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"ordinal": 22,
|
||||
"name": "mediainfo",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"ordinal": 23,
|
||||
"name": "trumpable",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"ordinal": 24,
|
||||
"name": "staff_checked",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"ordinal": 25,
|
||||
"name": "container",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"ordinal": 26,
|
||||
"name": "size",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"ordinal": 27,
|
||||
"name": "duration",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 27,
|
||||
"ordinal": 28,
|
||||
"name": "audio_codec: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -249,12 +254,12 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 28,
|
||||
"ordinal": 29,
|
||||
"name": "audio_bitrate",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 29,
|
||||
"ordinal": 30,
|
||||
"name": "audio_bitrate_sampling: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -284,7 +289,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 30,
|
||||
"ordinal": 31,
|
||||
"name": "audio_channels: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -303,7 +308,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 31,
|
||||
"ordinal": 32,
|
||||
"name": "video_codec: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -326,7 +331,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 32,
|
||||
"ordinal": 33,
|
||||
"name": "features!: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -355,7 +360,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 33,
|
||||
"ordinal": 34,
|
||||
"name": "subtitle_languages!: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -425,7 +430,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 34,
|
||||
"ordinal": 35,
|
||||
"name": "video_resolution!: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -449,12 +454,12 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 35,
|
||||
"ordinal": 36,
|
||||
"name": "video_resolution_other_x",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 36,
|
||||
"ordinal": 37,
|
||||
"name": "video_resolution_other_y",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
@@ -765,6 +770,7 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
@@ -793,5 +799,5 @@
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "bb6c8c54e372a54f7a8345ce2308f10a11e77d60305e1e865f2e2e28da9ee622"
|
||||
"hash": "780af85dcf7cbfead514338b6b0ef6039e8c0b82703143d854df8c0faba9f950"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n id, upload_factor, download_factor, seeders, leechers,\n times_completed, snatched, edition_group_id, created_at, updated_at,\n created_by_id,\n deleted_at AS \"deleted_at!: _\",\n deleted_by_id AS \"deleted_by_id!: _\",\n extras AS \"extras!: _\",\n languages AS \"languages!: _\",\n release_name, release_group, description, file_amount_per_type,\n uploaded_as_anonymous, file_list, mediainfo, trumpable, staff_checked,\n container, size, duration,\n audio_codec AS \"audio_codec: _\",\n audio_bitrate,\n audio_bitrate_sampling AS \"audio_bitrate_sampling: _\",\n audio_channels AS \"audio_channels: _\",\n video_codec AS \"video_codec: _\",\n features AS \"features!: _\",\n subtitle_languages AS \"subtitle_languages!: _\",\n video_resolution AS \"video_resolution!: _\",\n video_resolution_other_x,\n video_resolution_other_y\n FROM torrents\n WHERE id = $1 AND deleted_at is NULL\n ",
|
||||
"query": "\n SELECT\n id, info_hash as \"info_hash: InfoHash\", upload_factor, download_factor, seeders, leechers,\n times_completed, snatched, edition_group_id, created_at, updated_at,\n created_by_id,\n deleted_at AS \"deleted_at!: _\",\n deleted_by_id AS \"deleted_by_id!: _\",\n extras AS \"extras!: _\",\n languages AS \"languages!: _\",\n release_name, release_group, description, file_amount_per_type,\n uploaded_as_anonymous, file_list, mediainfo, trumpable, staff_checked,\n container, size, duration,\n audio_codec AS \"audio_codec: _\",\n audio_bitrate,\n audio_bitrate_sampling AS \"audio_bitrate_sampling: _\",\n audio_channels AS \"audio_channels: _\",\n video_codec AS \"video_codec: _\",\n features AS \"features!: _\",\n subtitle_languages AS \"subtitle_languages!: _\",\n video_resolution AS \"video_resolution!: _\",\n video_resolution_other_x,\n video_resolution_other_y\n FROM torrents\n WHERE id = $1 AND deleted_at is NULL\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -10,66 +10,71 @@
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "info_hash: InfoHash",
|
||||
"type_info": "Bytea"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "upload_factor",
|
||||
"type_info": "Int2"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"ordinal": 3,
|
||||
"name": "download_factor",
|
||||
"type_info": "Int2"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"ordinal": 4,
|
||||
"name": "seeders",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"ordinal": 5,
|
||||
"name": "leechers",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"ordinal": 6,
|
||||
"name": "times_completed",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"ordinal": 7,
|
||||
"name": "snatched",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"ordinal": 8,
|
||||
"name": "edition_group_id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"ordinal": 9,
|
||||
"name": "created_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"ordinal": 10,
|
||||
"name": "updated_at",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 10,
|
||||
"ordinal": 11,
|
||||
"name": "created_by_id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 11,
|
||||
"ordinal": 12,
|
||||
"name": "deleted_at!: _",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 12,
|
||||
"ordinal": 13,
|
||||
"name": "deleted_by_id!: _",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 13,
|
||||
"ordinal": 14,
|
||||
"name": "extras!: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -96,7 +101,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 14,
|
||||
"ordinal": 15,
|
||||
"name": "languages!: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -166,67 +171,67 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 15,
|
||||
"ordinal": 16,
|
||||
"name": "release_name",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 16,
|
||||
"ordinal": 17,
|
||||
"name": "release_group",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 17,
|
||||
"ordinal": 18,
|
||||
"name": "description",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 18,
|
||||
"ordinal": 19,
|
||||
"name": "file_amount_per_type",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 19,
|
||||
"ordinal": 20,
|
||||
"name": "uploaded_as_anonymous",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 20,
|
||||
"ordinal": 21,
|
||||
"name": "file_list",
|
||||
"type_info": "Jsonb"
|
||||
},
|
||||
{
|
||||
"ordinal": 21,
|
||||
"ordinal": 22,
|
||||
"name": "mediainfo",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 22,
|
||||
"ordinal": 23,
|
||||
"name": "trumpable",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 23,
|
||||
"ordinal": 24,
|
||||
"name": "staff_checked",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 24,
|
||||
"ordinal": 25,
|
||||
"name": "container",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 25,
|
||||
"ordinal": 26,
|
||||
"name": "size",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 26,
|
||||
"ordinal": 27,
|
||||
"name": "duration",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 27,
|
||||
"ordinal": 28,
|
||||
"name": "audio_codec: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -249,12 +254,12 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 28,
|
||||
"ordinal": 29,
|
||||
"name": "audio_bitrate",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 29,
|
||||
"ordinal": 30,
|
||||
"name": "audio_bitrate_sampling: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -284,7 +289,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 30,
|
||||
"ordinal": 31,
|
||||
"name": "audio_channels: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -303,7 +308,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 31,
|
||||
"ordinal": 32,
|
||||
"name": "video_codec: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -326,7 +331,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 32,
|
||||
"ordinal": 33,
|
||||
"name": "features!: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -355,7 +360,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 33,
|
||||
"ordinal": 34,
|
||||
"name": "subtitle_languages!: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -425,7 +430,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 34,
|
||||
"ordinal": 35,
|
||||
"name": "video_resolution!: _",
|
||||
"type_info": {
|
||||
"Custom": {
|
||||
@@ -449,12 +454,12 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"ordinal": 35,
|
||||
"ordinal": 36,
|
||||
"name": "video_resolution_other_x",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 36,
|
||||
"ordinal": 37,
|
||||
"name": "video_resolution_other_y",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
@@ -476,6 +481,7 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
@@ -504,5 +510,5 @@
|
||||
true
|
||||
]
|
||||
},
|
||||
"hash": "840f4fe4178c9746d4bb4258aed340b11fa64db9e1efe5e39cf4c927e0954c54"
|
||||
"hash": "79b272dba5250f458e020b5b9ded8ba18a7f8af45f619ee7771ad88c4ab9a532"
|
||||
}
|
||||
16
backend/storage/.sqlx/query-7da73662a96a68e239d011598ace3bc5b287a82c5b0c34ce9543842a1bed0ea4.json
generated
Normal file
16
backend/storage/.sqlx/query-7da73662a96a68e239d011598ace3bc5b287a82c5b0c34ce9543842a1bed0ea4.json
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n DELETE FROM peers\n WHERE (user_id, torrent_id, peer_id) IN (\n SELECT t.user_id, t.torrent_id, t.peer_id\n FROM (\n SELECT * FROM unnest(\n $1::int[],\n $2::int[],\n $3::bytea[]\n ) AS t(user_id, torrent_id, peer_id)\n ) AS t\n )\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4Array",
|
||||
"Int4Array",
|
||||
"ByteaArray"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "7da73662a96a68e239d011598ace3bc5b287a82c5b0c34ce9543842a1bed0ea4"
|
||||
}
|
||||
38
backend/storage/.sqlx/query-bb66b1b13123112781db47d98cd02b28d11470b7c84d7aec56ec102fee20264d.json
generated
Normal file
38
backend/storage/.sqlx/query-bb66b1b13123112781db47d98cd02b28d11470b7c84d7aec56ec102fee20264d.json
generated
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n id,\n passkey as \"passkey: Passkey\",\n 0::INT AS \"num_seeding!\",\n 0::INT AS \"num_leeching!\"\n FROM users\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "passkey: Passkey",
|
||||
"type_info": "Varchar"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "num_seeding!",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "num_leeching!",
|
||||
"type_info": "Int4"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "bb66b1b13123112781db47d98cd02b28d11470b7c84d7aec56ec102fee20264d"
|
||||
}
|
||||
17
backend/storage/.sqlx/query-c45f235654a1b2aa8c849c5644443fe34ea7a4dd976fe6b4405e7b4a585a1325.json
generated
Normal file
17
backend/storage/.sqlx/query-c45f235654a1b2aa8c849c5644443fe34ea7a4dd976fe6b4405e7b4a585a1325.json
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n UPDATE torrents\n SET\n seeders = seeders + updates.seeder_delta,\n leechers = leechers + updates.leecher_delta,\n times_completed = times_completed + updates.times_completed_delta\n FROM (\n SELECT * FROM unnest($1::int[], $2::bigint[], $3::bigint[], $4::bigint[]) AS\n t(torrent_id, seeder_delta, leecher_delta, times_completed_delta)\n ) AS updates\n WHERE torrents.id = updates.torrent_id\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int4Array",
|
||||
"Int8Array",
|
||||
"Int8Array",
|
||||
"Int8Array"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "c45f235654a1b2aa8c849c5644443fe34ea7a4dd976fe6b4405e7b4a585a1325"
|
||||
}
|
||||
26
backend/storage/.sqlx/query-d94c7cf9c02a4f060345d02ac4bd2434069fc46d43e6f3e7e3618737c2dcd547.json
generated
Normal file
26
backend/storage/.sqlx/query-d94c7cf9c02a4f060345d02ac4bd2434069fc46d43e6f3e7e3618737c2dcd547.json
generated
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n id,\n info_hash as \"info_hash: InfoHash\"\n FROM torrents\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "info_hash: InfoHash",
|
||||
"type_info": "Bytea"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "d94c7cf9c02a4f060345d02ac4bd2434069fc46d43e6f3e7e3618737c2dcd547"
|
||||
}
|
||||
74
backend/storage/.sqlx/query-f6d849721ff84614c129c14455d9a6adbe0ad29b7876963d5bd9015c0f73ba9d.json
generated
Normal file
74
backend/storage/.sqlx/query-f6d849721ff84614c129c14455d9a6adbe0ad29b7876963d5bd9015c0f73ba9d.json
generated
Normal file
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "\n SELECT\n peers.ip AS \"ip_address: IpAddr\",\n peers.user_id AS \"user_id\",\n peers.torrent_id AS \"torrent_id\",\n peers.port AS \"port\",\n peers.seeder AS \"is_seeder: bool\",\n peers.active AS \"is_active: bool\",\n peers.updated_at AS \"updated_at: DateTime<Utc>\",\n peers.uploaded AS \"uploaded\",\n peers.downloaded AS \"downloaded\",\n peers.peer_id AS \"peer_id: PeerId\"\n FROM peers\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "ip_address: IpAddr",
|
||||
"type_info": "Inet"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "user_id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "torrent_id",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "port",
|
||||
"type_info": "Int4"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "is_seeder: bool",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "is_active: bool",
|
||||
"type_info": "Bool"
|
||||
},
|
||||
{
|
||||
"ordinal": 6,
|
||||
"name": "updated_at: DateTime<Utc>",
|
||||
"type_info": "Timestamp"
|
||||
},
|
||||
{
|
||||
"ordinal": 7,
|
||||
"name": "uploaded",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "downloaded",
|
||||
"type_info": "Int8"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "peer_id: PeerId",
|
||||
"type_info": "Bytea"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": []
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "f6d849721ff84614c129c14455d9a6adbe0ad29b7876963d5bd9015c0f73ba9d"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use actix_multipart::form::{bytes::Bytes, text::Text, MultipartForm};
|
||||
use arcadia_shared::tracker::models::torrent::InfoHash;
|
||||
use chrono::{DateTime, Local};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
@@ -307,6 +308,7 @@ impl FromStr for Features {
|
||||
#[derive(Debug, Serialize, FromRow, ToSchema)]
|
||||
pub struct Torrent {
|
||||
pub id: i32,
|
||||
pub info_hash: InfoHash,
|
||||
pub upload_factor: i16,
|
||||
pub download_factor: i16,
|
||||
pub seeders: i64,
|
||||
|
||||
@@ -13,7 +13,8 @@ use arcadia_common::{
|
||||
error::{Error, Result},
|
||||
services::torrent_service::{get_announce_url, looks_like_url},
|
||||
};
|
||||
use bip_metainfo::{Info, InfoBuilder, InfoHash, Metainfo, MetainfoBuilder, PieceLength};
|
||||
use arcadia_shared::tracker::models::torrent::InfoHash;
|
||||
use bip_metainfo::{Info, InfoBuilder, Metainfo, MetainfoBuilder, PieceLength};
|
||||
use serde_json::{json, Value};
|
||||
use sqlx::PgPool;
|
||||
use std::{borrow::Borrow, str::FromStr};
|
||||
@@ -72,7 +73,7 @@ impl ConnectionPool {
|
||||
.build(1, info, |_| {})
|
||||
.map_err(|_| Error::TorrentFileInvalid)?;
|
||||
|
||||
let info_hash = InfoHash::from_bytes(&info_normalized);
|
||||
let info_hash = bip_metainfo::InfoHash::from_bytes(&info_normalized);
|
||||
|
||||
// TODO: torrent metadata extraction should be done on the client side
|
||||
let parent_folder = info.directory().map(|d| d.to_str().unwrap()).unwrap_or("");
|
||||
@@ -206,7 +207,7 @@ impl ConnectionPool {
|
||||
Torrent,
|
||||
r#"
|
||||
SELECT
|
||||
id, upload_factor, download_factor, seeders, leechers,
|
||||
id, info_hash as "info_hash: InfoHash", upload_factor, download_factor, seeders, leechers,
|
||||
times_completed, snatched, edition_group_id, created_at, updated_at,
|
||||
created_by_id,
|
||||
deleted_at AS "deleted_at!: _",
|
||||
@@ -270,7 +271,7 @@ impl ConnectionPool {
|
||||
updated_at = NOW()
|
||||
WHERE id = $1 AND deleted_at IS NULL
|
||||
RETURNING
|
||||
id, upload_factor, download_factor, seeders, leechers,
|
||||
id, info_hash as "info_hash: InfoHash", upload_factor, download_factor, seeders, leechers,
|
||||
times_completed, snatched, edition_group_id, created_at, updated_at,
|
||||
created_by_id,
|
||||
deleted_at AS "deleted_at!: _",
|
||||
|
||||
@@ -16,3 +16,4 @@ ringmap = { version = "0.2.0", features = ["serde"] }
|
||||
actix-web = "4"
|
||||
log = "0.4"
|
||||
parking_lot = "0.12.4"
|
||||
utoipa = { version = "5.3.1", features = ["actix_extras"] }
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
use anyhow::{bail, Context};
|
||||
use chrono::{DateTime, Utc};
|
||||
use indexmap::IndexMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{Database, Decode, PgPool};
|
||||
use sqlx::postgres::PgTypeInfo;
|
||||
use sqlx::{Database, Decode, PgPool, Postgres, Type};
|
||||
use std::net::IpAddr;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::str::FromStr;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::tracker::models::peer::{self, Peer};
|
||||
use crate::tracker::models::peer_id::PeerId;
|
||||
use crate::utils::hex_decode;
|
||||
|
||||
#[derive(Clone, Copy, Serialize, Deserialize, Debug, Eq, Hash, PartialEq)]
|
||||
#[derive(Clone, Copy, Serialize, Deserialize, Debug, Eq, Hash, PartialEq, ToSchema)]
|
||||
pub struct InfoHash(pub [u8; 20]);
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
@@ -22,6 +27,18 @@ pub struct Torrent {
|
||||
pub peers: peer::Map,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct APIInsertTorrent {
|
||||
pub id: u32,
|
||||
pub info_hash: InfoHash,
|
||||
pub is_deleted: bool,
|
||||
pub seeders: u32,
|
||||
pub leechers: u32,
|
||||
pub times_completed: u32,
|
||||
pub download_factor: u8,
|
||||
pub upload_factor: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Map(pub IndexMap<u32, Torrent>);
|
||||
|
||||
@@ -144,6 +161,12 @@ impl DerefMut for Map {
|
||||
}
|
||||
}
|
||||
|
||||
impl Type<Postgres> for InfoHash {
|
||||
fn type_info() -> PgTypeInfo {
|
||||
<Vec<u8> as Type<Postgres>>::type_info()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, DB: Database> Decode<'r, DB> for InfoHash
|
||||
where
|
||||
&'r [u8]: Decode<'r, DB>,
|
||||
@@ -165,3 +188,23 @@ where
|
||||
Ok(InfoHash(<[u8; 20]>::try_from(&value[0..20])?))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for InfoHash {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let bytes = s.as_bytes();
|
||||
let mut out = [0u8; 20];
|
||||
|
||||
if bytes.len() != 40 {
|
||||
bail!("`{s}` is not a valid infohash.");
|
||||
}
|
||||
|
||||
for pos in 0..20 {
|
||||
out[pos] = hex_decode([bytes[pos * 2], bytes[pos * 2 + 1]])
|
||||
.context("`{s}` is not a valid infohash")?;
|
||||
}
|
||||
|
||||
Ok(InfoHash(out))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use anyhow::{bail, Result};
|
||||
|
||||
/// Encodes one byte into 2 ascii-encoded hex digits.
|
||||
#[inline(always)]
|
||||
pub fn hex_encode(char: u8) -> [u8; 2] {
|
||||
@@ -16,3 +18,19 @@ pub fn hex_encode(char: u8) -> [u8; 2] {
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
/// Decodes two ascii-encoded hex digits into one byte.
|
||||
#[inline(always)]
|
||||
pub fn hex_decode(chars: [u8; 2]) -> Result<u8> {
|
||||
Ok(match chars[0] {
|
||||
digit @ b'0'..=b'9' => (digit - b'0') << 4,
|
||||
lower @ b'a'..=b'f' => (lower - b'a' + 0xA) << 4,
|
||||
upper @ b'A'..=b'F' => (upper - b'A' + 0xA) << 4,
|
||||
_ => bail!("Invalid URL encoding."),
|
||||
} + match chars[1] {
|
||||
digit @ b'0'..=b'9' => digit - b'0',
|
||||
lower @ b'a'..=b'f' => lower - b'a' + 0xA,
|
||||
upper @ b'A'..=b'F' => upper - b'A' + 0xA,
|
||||
_ => bail!("Invalid URL encoding."),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
pub mod announce;
|
||||
pub mod torrents;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub mod upsert_torrent;
|
||||
@@ -0,0 +1,34 @@
|
||||
use actix_web::{
|
||||
web::{Data, Json},
|
||||
HttpResponse,
|
||||
};
|
||||
use arcadia_shared::tracker::models::{
|
||||
peer,
|
||||
torrent::{APIInsertTorrent, Torrent},
|
||||
};
|
||||
use log::info;
|
||||
|
||||
use crate::Tracker;
|
||||
|
||||
pub async fn exec(arc: Data<Tracker>, torrent: Json<APIInsertTorrent>) -> HttpResponse {
|
||||
info!("Inserting torrent with id {}.", torrent.id);
|
||||
|
||||
arc.torrents.lock().insert(
|
||||
torrent.id,
|
||||
Torrent {
|
||||
is_deleted: torrent.is_deleted,
|
||||
seeders: torrent.seeders,
|
||||
leechers: torrent.leechers,
|
||||
times_completed: torrent.times_completed,
|
||||
download_factor: torrent.download_factor as i16,
|
||||
upload_factor: torrent.upload_factor as i16,
|
||||
peers: peer::Map::new(),
|
||||
},
|
||||
);
|
||||
|
||||
arc.infohash2id
|
||||
.write()
|
||||
.insert(torrent.info_hash, torrent.id);
|
||||
|
||||
HttpResponse::Ok().finish()
|
||||
}
|
||||
@@ -3,6 +3,8 @@ use std::{collections::HashSet, str::FromStr};
|
||||
|
||||
#[derive(Debug, Envconfig, Clone)]
|
||||
pub struct Env {
|
||||
#[envconfig(from = "API_KEY")]
|
||||
pub api_key: String,
|
||||
#[envconfig(from = "ALLOWED_TORRENT_CLIENTS")]
|
||||
pub allowed_torrent_clients: AllowedTorrentClientSet,
|
||||
#[envconfig(from = "NUMWANT_DEFAULT")]
|
||||
|
||||
@@ -1,35 +1,19 @@
|
||||
// use actix_web::{dev::ServiceRequest, error::ErrorUnauthorized, web::Data};
|
||||
// use actix_web::{Error, FromRequest, HttpRequest};
|
||||
// use futures::future::{ready, Ready};
|
||||
use actix_web::Error;
|
||||
use actix_web::{dev::ServiceRequest, error::ErrorUnauthorized, web::Data};
|
||||
|
||||
// pub struct Passkey(pub String);
|
||||
use crate::Tracker;
|
||||
|
||||
// impl FromRequest for Passkey {
|
||||
// type Error = Error;
|
||||
// type Future = Ready<Result<Self, Self::Error>>;
|
||||
|
||||
// fn from_request(req: &HttpRequest, _payload: &mut actix_web::dev::Payload) -> Self::Future {
|
||||
// let passkey = req.path().into_inner();
|
||||
|
||||
// match passkey {
|
||||
// Some(key) => ready(Ok(Passkey(key))),
|
||||
// None => ready(Err(actix_web::error::ErrorUnauthorized(
|
||||
// "authentication error: missing passkey",
|
||||
// ))),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// pub async fn authenticate_user(
|
||||
// req: ServiceRequest,
|
||||
// passkey: Passkey,
|
||||
// ) -> std::result::Result<ServiceRequest, (actix_web::Error, ServiceRequest)> {
|
||||
// // if passkey.0 != arc.env.passkey {
|
||||
// // Err((
|
||||
// // ErrorUnauthorized("authentication error: invalid API key"),
|
||||
// // req,
|
||||
// // ))
|
||||
// // } else {
|
||||
// Ok(req)
|
||||
// // }
|
||||
// }
|
||||
pub async fn authenticate_backend(
|
||||
req: ServiceRequest,
|
||||
_: (),
|
||||
) -> Result<ServiceRequest, (Error, ServiceRequest)> {
|
||||
let api_key = &req
|
||||
.app_data::<Data<Tracker>>()
|
||||
.expect("app data set")
|
||||
.env
|
||||
.api_key;
|
||||
match req.headers().get("x-api-key").and_then(|v| v.to_str().ok()) {
|
||||
Some(k) if k == api_key => Ok(req),
|
||||
_ => Err((ErrorUnauthorized("invalid or missing API key"), req)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
use actix_web::web::{self, scope};
|
||||
use actix_web::web::{self, put, resource, scope};
|
||||
|
||||
use crate::announce::handlers::announce::config as AnnouncesConfig;
|
||||
// use actix_web_httpauth::middleware::HttpAuthentication;
|
||||
|
||||
// use crate::middleware::authenticate_user;
|
||||
use crate::{
|
||||
announce::handlers::{announce::config as AnnouncesConfig, torrents::upsert_torrent},
|
||||
middleware::authenticate_backend,
|
||||
};
|
||||
use actix_web_httpauth::middleware::HttpAuthentication;
|
||||
|
||||
pub fn init(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(scope("{passkey}").configure(AnnouncesConfig));
|
||||
// TODO: protect by only allowing requests from backend's ip
|
||||
// cfg.service(
|
||||
// .wrap(HttpAuthentication::with_fn(authenticate_user(req, passkey))),
|
||||
// );
|
||||
cfg.service(
|
||||
web::scope("/api")
|
||||
.wrap(HttpAuthentication::with_fn(authenticate_backend))
|
||||
.service(resource("/torrents").route(put().to(upsert_torrent::exec))),
|
||||
);
|
||||
cfg.service(scope("{passkey}").configure(AnnouncesConfig));
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ pub async fn create_test_app(
|
||||
) -> impl Service<Request, Response = ServiceResponse, Error = Error> {
|
||||
// Create a default env for testing
|
||||
let env = Env {
|
||||
api_key: "amazing_api_key".to_owned(),
|
||||
allowed_torrent_clients: AllowedTorrentClientSet {
|
||||
clients: vec![b"lt0F01-".to_vec(), b"qB".to_vec(), b"UTorrent".to_vec()]
|
||||
.into_iter()
|
||||
|
||||
Reference in New Issue
Block a user