mirror of
https://github.com/Arcadia-Solutions/arcadia.git
synced 2025-12-16 15:04:22 -06:00
more foundations for the new tracker and changed user id to i32 (rust)
and INT (sql)
This commit is contained in:
25
Cargo.lock
generated
25
Cargo.lock
generated
@@ -460,6 +460,12 @@ dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.2"
|
||||
@@ -553,6 +559,16 @@ dependencies = [
|
||||
"tokio-cron-scheduler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arcadia-shared"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"indexmap",
|
||||
"serde",
|
||||
"sqlx",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arcadia-storage"
|
||||
version = "0.1.0"
|
||||
@@ -560,6 +576,7 @@ dependencies = [
|
||||
"actix-multipart",
|
||||
"actix-web",
|
||||
"arcadia-common",
|
||||
"arcadia-shared",
|
||||
"argon2",
|
||||
"bip_metainfo",
|
||||
"chrono 0.4.41",
|
||||
@@ -582,10 +599,18 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"actix-web-httpauth",
|
||||
"anyhow",
|
||||
"arcadia-shared",
|
||||
"dotenvy",
|
||||
"env_logger",
|
||||
"envconfig",
|
||||
"futures",
|
||||
"indexmap",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"serde",
|
||||
"serde_bencode",
|
||||
"strum",
|
||||
"thiserror 2.0.16",
|
||||
"tokio",
|
||||
"utoipa",
|
||||
|
||||
@@ -3,7 +3,7 @@ members = [
|
||||
"backend/api",
|
||||
"backend/common",
|
||||
"backend/periodic-tasks",
|
||||
"backend/storage", "tracker/arcadia_tracker",
|
||||
"backend/storage", "tracker/arcadia_tracker", "shared",
|
||||
]
|
||||
|
||||
resolver = "2"
|
||||
|
||||
@@ -42,3 +42,8 @@ TASK_INTERVAL_REMOVE_INACTIVE_PEERS="0 0 * * * *"
|
||||
# Required for TMDB access, must create a new account with themoviedb.org
|
||||
# TMDB_API_KEY="your token"
|
||||
# COMIC_VINCE_API_KEY="your api key"
|
||||
|
||||
# ----------- Tracker
|
||||
# Used for the backend to make requests to the tracker
|
||||
# and vice-versa
|
||||
ARCADIA_TRACKER_API_KEY=change_me
|
||||
|
||||
@@ -3,3 +3,8 @@ ENV=Docker
|
||||
# Docker buildkit support
|
||||
DOCKER_BUILDKIT=1
|
||||
COMPOSE_DOCKER_CLI_BUILD=1
|
||||
|
||||
# ----------- Tracker
|
||||
# Used for the backend to make requests to the tracker
|
||||
# and vice-versa
|
||||
ARCADIA_TRACKER_API_KEY=change_me
|
||||
|
||||
@@ -79,3 +79,8 @@ TASK_INTERVAL_REMOVE_INACTIVE_PEERS="0 0 * * * *"
|
||||
# SMTP_PASSWORD=your-app-password
|
||||
# SMTP_FROM_EMAIL=noreply@yourtracker.com
|
||||
# SMTP_FROM_NAME=Arcadia Tracker
|
||||
|
||||
# ----------- Tracker
|
||||
# Used for the backend to make requests to the tracker
|
||||
# and vice-versa
|
||||
ARCADIA_TRACKER_API_KEY=change_me
|
||||
|
||||
@@ -68,15 +68,7 @@ pub async fn exec<R: RedisPoolInterface>(
|
||||
return Err(AnnounceError::TorrentClientNotInWhitelist);
|
||||
}
|
||||
|
||||
let passkey = u128::from_str_radix(&passkey, 16).map_err(|_| AnnounceError::InvalidPassKey)?;
|
||||
|
||||
let passkey_upper = (passkey >> 64) as i64;
|
||||
let passkey_lower = passkey as i64;
|
||||
|
||||
let current_user = arc
|
||||
.pool
|
||||
.find_user_with_passkey(passkey_upper, passkey_lower)
|
||||
.await?;
|
||||
let current_user = arc.pool.find_user_with_passkey(&passkey).await?;
|
||||
|
||||
let torrent = arc.pool.find_torrent_with_id(&ann.info_hash).await?;
|
||||
|
||||
|
||||
@@ -28,11 +28,7 @@ pub async fn exec<R: RedisPoolInterface + 'static>(
|
||||
user: Authdata,
|
||||
) -> Result<HttpResponse> {
|
||||
let current_user = arc.pool.find_user_with_id(user.sub).await?;
|
||||
let announce_url = get_announce_url(
|
||||
current_user.passkey_upper,
|
||||
current_user.passkey_lower,
|
||||
arc.tracker.url.as_ref(),
|
||||
);
|
||||
let announce_url = get_announce_url(current_user.passkey, arc.tracker.url.as_ref());
|
||||
|
||||
Ok(HttpResponse::Ok().json(UploadInformation { announce_url }))
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ use utoipa::IntoParams;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct GetUserQuery {
|
||||
id: i64,
|
||||
id: i32,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
|
||||
@@ -15,7 +15,7 @@ use jsonwebtoken::{decode, errors::ErrorKind, DecodingKey, Validation};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Authdata {
|
||||
pub sub: i64,
|
||||
pub sub: i32,
|
||||
pub class: UserClass,
|
||||
}
|
||||
|
||||
|
||||
@@ -11,12 +11,12 @@ pub static AUTH_TOKEN_LONG_DURATION: LazyLock<Duration> = LazyLock::new(|| Durat
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct InvalidationEntry {
|
||||
user_id: i64,
|
||||
user_id: i32,
|
||||
token_invalidation_ts: i64,
|
||||
}
|
||||
|
||||
impl InvalidationEntry {
|
||||
pub fn new(user_id: i64) -> Self {
|
||||
pub fn new(user_id: i32) -> Self {
|
||||
let now = Utc::now();
|
||||
|
||||
Self {
|
||||
@@ -35,7 +35,7 @@ impl<R: RedisPoolInterface> Auth<R> {
|
||||
Self { redis_pool }
|
||||
}
|
||||
|
||||
pub async fn invalidate(&self, user_id: i64) -> Result<()> {
|
||||
pub async fn invalidate(&self, user_id: i32) -> Result<()> {
|
||||
let entry = InvalidationEntry::new(user_id);
|
||||
let mut redis = self.redis_pool.connection().await?;
|
||||
|
||||
@@ -51,7 +51,7 @@ impl<R: RedisPoolInterface> Auth<R> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn is_invalidated(&self, user_id: i64, iat: i64) -> Result<bool> {
|
||||
pub async fn is_invalidated(&self, user_id: i32, iat: i64) -> Result<bool> {
|
||||
let mut redis = self.redis_pool.connection().await?;
|
||||
let Some(entry) = redis.get(user_id).await? else {
|
||||
return Ok(false);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
INSERT INTO
|
||||
users (banned, username, email, password_hash, registered_from_ip, passkey_upper, passkey_lower)
|
||||
users (banned, username, email, password_hash, registered_from_ip, passkey)
|
||||
VALUES
|
||||
-- passkey d2037c66dd3e13044e0d2f9b891c3837
|
||||
(true, 'test_user', 'test_email@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', '-3313668119574211836', '5624203854722381879')
|
||||
(true, 'test_user', 'test_email@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'mqdfkjqmsdkf')
|
||||
|
||||
@@ -2,4 +2,4 @@ INSERT INTO
|
||||
users (username, email, password_hash, registered_from_ip, passkey_upper, passkey_lower, class)
|
||||
VALUES
|
||||
-- passkey d2037c66dd3e13044e0d2f9b891c3837
|
||||
('test_user', 'test_email@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', '-3313668119574211836', '5624203854722381879', 'newbie')
|
||||
('test_user', 'test_email@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'mqdslkfmkldf', 'newbie')
|
||||
|
||||
@@ -2,4 +2,4 @@ INSERT INTO
|
||||
users (username, email, password_hash, registered_from_ip, passkey_upper, passkey_lower, class)
|
||||
VALUES
|
||||
-- passkey d2037c66dd3e13044e0d2f9b891c3838
|
||||
('test_user2', 'test_email2@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', '-3313668119574211836', '5624203854722381880', 'staff')
|
||||
('test_user2', 'test_email2@testdomain.com', '$argon2id$v=19$m=19456,t=2,p=1$WM6V9pJ2ya7+N+NNIUtolg$n128u9idizCHLwZ9xhKaxOttLaAVZZgvfRZlRAnfyKk', '10.10.4.88', 'cmqklsdfmj', 'staff')
|
||||
|
||||
@@ -1,401 +1,401 @@
|
||||
pub mod common;
|
||||
pub mod mocks;
|
||||
|
||||
use std::sync::Arc;
|
||||
// use std::sync::Arc;
|
||||
|
||||
use actix_web::test;
|
||||
use arcadia_api::OpenSignups;
|
||||
use arcadia_common::models::tracker::announce;
|
||||
use arcadia_storage::connection_pool::ConnectionPool;
|
||||
use mocks::mock_redis::MockRedisPool;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use sqlx::PgPool;
|
||||
// use actix_web::test;
|
||||
// use arcadia_api::OpenSignups;
|
||||
// use arcadia_common::models::tracker::announce;
|
||||
// use arcadia_storage::connection_pool::ConnectionPool;
|
||||
// use mocks::mock_redis::MockRedisPool;
|
||||
// use serde::Deserialize;
|
||||
// use serde_json::Value;
|
||||
// use sqlx::PgPool;
|
||||
|
||||
use crate::common::auth_header;
|
||||
// use crate::common::auth_header;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct WrappedError {
|
||||
#[serde(rename = "failure reason")]
|
||||
_failure_reason: String,
|
||||
}
|
||||
// #[derive(Debug, Deserialize)]
|
||||
// struct WrappedError {
|
||||
// #[serde(rename = "failure reason")]
|
||||
// _failure_reason: String,
|
||||
// }
|
||||
|
||||
#[sqlx::test(fixtures("with_test_user"), migrations = "../storage/migrations")]
|
||||
async fn test_announce_unknown_passkey(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let service = common::create_test_app(
|
||||
pool,
|
||||
MockRedisPool::default(),
|
||||
OpenSignups::Enabled,
|
||||
1.0,
|
||||
1.0,
|
||||
)
|
||||
.await;
|
||||
// #[sqlx::test(fixtures("with_test_user"), migrations = "../storage/migrations")]
|
||||
// async fn test_announce_unknown_passkey(pool: PgPool) {
|
||||
// let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
// let service = common::create_test_app(
|
||||
// pool,
|
||||
// MockRedisPool::default(),
|
||||
// OpenSignups::Enabled,
|
||||
// 1.0,
|
||||
// 1.0,
|
||||
// )
|
||||
// .await;
|
||||
|
||||
let req = test::TestRequest::get()
|
||||
.uri(concat!(
|
||||
"/announce/33333333333333333333333333333333?",
|
||||
"info_hash=%7C%B3%C6y%9A%FFm%5C%3B%10%A6S%1FF%07%D9%C9%0E%C0%A7&",
|
||||
"peer_id=-lt0F01-%3D%91%BB%AC%5C%C69%C0%EDmux&",
|
||||
"key=1ab4e687&",
|
||||
"compact=1&",
|
||||
"port=6968&",
|
||||
"uploaded=0&",
|
||||
"downloaded=0&",
|
||||
"left=14&",
|
||||
"event=started"
|
||||
))
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.to_request();
|
||||
// let req = test::TestRequest::get()
|
||||
// .uri(concat!(
|
||||
// "/announce/33333333333333333333333333333333?",
|
||||
// "info_hash=%7C%B3%C6y%9A%FFm%5C%3B%10%A6S%1FF%07%D9%C9%0E%C0%A7&",
|
||||
// "peer_id=-lt0F01-%3D%91%BB%AC%5C%C69%C0%EDmux&",
|
||||
// "key=1ab4e687&",
|
||||
// "compact=1&",
|
||||
// "port=6968&",
|
||||
// "uploaded=0&",
|
||||
// "downloaded=0&",
|
||||
// "left=14&",
|
||||
// "event=started"
|
||||
// ))
|
||||
// .insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
// .to_request();
|
||||
|
||||
let resp = test::call_service(&service, req).await;
|
||||
// let resp = test::call_service(&service, req).await;
|
||||
|
||||
// Should fail because the passkey is invalid
|
||||
assert!(
|
||||
resp.status().is_client_error(),
|
||||
"status {} is not client error",
|
||||
resp.status()
|
||||
);
|
||||
// // Should fail because the passkey is invalid
|
||||
// assert!(
|
||||
// resp.status().is_client_error(),
|
||||
// "status {} is not client error",
|
||||
// resp.status()
|
||||
// );
|
||||
|
||||
// Any error is okay, as long as it has "failure reason" populated.
|
||||
common::read_body_bencode::<WrappedError, _>(resp)
|
||||
.await
|
||||
.expect("expected failure message");
|
||||
}
|
||||
// // Any error is okay, as long as it has "failure reason" populated.
|
||||
// common::read_body_bencode::<WrappedError, _>(resp)
|
||||
// .await
|
||||
// .expect("expected failure message");
|
||||
// }
|
||||
|
||||
#[sqlx::test(fixtures("with_test_user"), migrations = "../storage/migrations")]
|
||||
async fn test_announce_unknown_torrent(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let service = common::create_test_app(
|
||||
pool,
|
||||
MockRedisPool::default(),
|
||||
OpenSignups::Enabled,
|
||||
1.0,
|
||||
1.0,
|
||||
)
|
||||
.await;
|
||||
// #[sqlx::test(fixtures("with_test_user"), migrations = "../storage/migrations")]
|
||||
// async fn test_announce_unknown_torrent(pool: PgPool) {
|
||||
// let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
// let service = common::create_test_app(
|
||||
// pool,
|
||||
// MockRedisPool::default(),
|
||||
// OpenSignups::Enabled,
|
||||
// 1.0,
|
||||
// 1.0,
|
||||
// )
|
||||
// .await;
|
||||
|
||||
let req = test::TestRequest::get()
|
||||
.uri(concat!(
|
||||
"/announce/d2037c66dd3e13044e0d2f9b891c3837?",
|
||||
"info_hash=%7C%B3%C6y%9A%FFm%5C%3B%10%A6S%1FF%07%D9%C9%0E%C0%A7&",
|
||||
"peer_id=-lt0F01-%3D%91%BB%AC%5C%C69%C0%EDmux&",
|
||||
"key=1ab4e687&",
|
||||
"compact=1&",
|
||||
"port=6968&",
|
||||
"uploaded=0&",
|
||||
"downloaded=0&",
|
||||
"left=14&",
|
||||
"event=started"
|
||||
))
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.to_request();
|
||||
// let req = test::TestRequest::get()
|
||||
// .uri(concat!(
|
||||
// "/announce/d2037c66dd3e13044e0d2f9b891c3837?",
|
||||
// "info_hash=%7C%B3%C6y%9A%FFm%5C%3B%10%A6S%1FF%07%D9%C9%0E%C0%A7&",
|
||||
// "peer_id=-lt0F01-%3D%91%BB%AC%5C%C69%C0%EDmux&",
|
||||
// "key=1ab4e687&",
|
||||
// "compact=1&",
|
||||
// "port=6968&",
|
||||
// "uploaded=0&",
|
||||
// "downloaded=0&",
|
||||
// "left=14&",
|
||||
// "event=started"
|
||||
// ))
|
||||
// .insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
// .to_request();
|
||||
|
||||
let resp = test::call_service(&service, req).await;
|
||||
// let resp = test::call_service(&service, req).await;
|
||||
|
||||
// Should fail because there is no torrent matching infohash.
|
||||
assert!(
|
||||
resp.status().is_client_error(),
|
||||
"status {} is not client error",
|
||||
resp.status()
|
||||
);
|
||||
// // Should fail because there is no torrent matching infohash.
|
||||
// assert!(
|
||||
// resp.status().is_client_error(),
|
||||
// "status {} is not client error",
|
||||
// resp.status()
|
||||
// );
|
||||
|
||||
// Any error is okay, as long as it has "failure reason" populated.
|
||||
common::read_body_bencode::<WrappedError, _>(resp)
|
||||
.await
|
||||
.expect("expected failure message");
|
||||
}
|
||||
// // Any error is okay, as long as it has "failure reason" populated.
|
||||
// common::read_body_bencode::<WrappedError, _>(resp)
|
||||
// .await
|
||||
// .expect("expected failure message");
|
||||
// }
|
||||
|
||||
#[sqlx::test(
|
||||
fixtures(
|
||||
"with_test_user",
|
||||
"with_test_title_group",
|
||||
"with_test_edition_group",
|
||||
"with_test_torrent"
|
||||
),
|
||||
migrations = "../storage/migrations"
|
||||
)]
|
||||
async fn test_announce_known_torrent(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let service = common::create_test_app(
|
||||
pool,
|
||||
MockRedisPool::default(),
|
||||
OpenSignups::Enabled,
|
||||
1.0,
|
||||
1.0,
|
||||
)
|
||||
.await;
|
||||
let req = test::TestRequest::get()
|
||||
.uri(concat!(
|
||||
"/announce/d2037c66dd3e13044e0d2f9b891c3837?",
|
||||
"info_hash=%11%223DUfw%88%99%AA%BB%CC%DD%EE%FF%00%11%223D&",
|
||||
"peer_id=-lt0F01-%3D%91%BB%AC%5C%C69%C0%EDmux&",
|
||||
"key=1ab4e687&",
|
||||
"compact=1&",
|
||||
"port=6968&",
|
||||
"uploaded=0&",
|
||||
"downloaded=0&",
|
||||
"left=14&",
|
||||
"event=started"
|
||||
))
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.to_request();
|
||||
// #[sqlx::test(
|
||||
// fixtures(
|
||||
// "with_test_user",
|
||||
// "with_test_title_group",
|
||||
// "with_test_edition_group",
|
||||
// "with_test_torrent"
|
||||
// ),
|
||||
// migrations = "../storage/migrations"
|
||||
// )]
|
||||
// async fn test_announce_known_torrent(pool: PgPool) {
|
||||
// let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
// let service = common::create_test_app(
|
||||
// pool,
|
||||
// MockRedisPool::default(),
|
||||
// OpenSignups::Enabled,
|
||||
// 1.0,
|
||||
// 1.0,
|
||||
// )
|
||||
// .await;
|
||||
// let req = test::TestRequest::get()
|
||||
// .uri(concat!(
|
||||
// "/announce/d2037c66dd3e13044e0d2f9b891c3837?",
|
||||
// "info_hash=%11%223DUfw%88%99%AA%BB%CC%DD%EE%FF%00%11%223D&",
|
||||
// "peer_id=-lt0F01-%3D%91%BB%AC%5C%C69%C0%EDmux&",
|
||||
// "key=1ab4e687&",
|
||||
// "compact=1&",
|
||||
// "port=6968&",
|
||||
// "uploaded=0&",
|
||||
// "downloaded=0&",
|
||||
// "left=14&",
|
||||
// "event=started"
|
||||
// ))
|
||||
// .insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
// .to_request();
|
||||
|
||||
let resp = test::call_service(&service, req).await;
|
||||
// let resp = test::call_service(&service, req).await;
|
||||
|
||||
// Should succeed because there is both a matching user and info hash.
|
||||
assert!(
|
||||
resp.status().is_success(),
|
||||
"status {} is not success",
|
||||
resp.status()
|
||||
);
|
||||
// // Should succeed because there is both a matching user and info hash.
|
||||
// assert!(
|
||||
// resp.status().is_success(),
|
||||
// "status {} is not success",
|
||||
// resp.status()
|
||||
// );
|
||||
|
||||
let resp = common::read_body_bencode::<announce::AnnounceResponse, _>(resp)
|
||||
.await
|
||||
.expect("could not deserialize announce response");
|
||||
// let resp = common::read_body_bencode::<announce::AnnounceResponse, _>(resp)
|
||||
// .await
|
||||
// .expect("could not deserialize announce response");
|
||||
|
||||
// There are no peers, so should be empty.
|
||||
assert!(resp.peers.is_empty());
|
||||
}
|
||||
// // There are no peers, so should be empty.
|
||||
// assert!(resp.peers.is_empty());
|
||||
// }
|
||||
|
||||
#[sqlx::test(
|
||||
fixtures(
|
||||
"with_test_user",
|
||||
"with_test_title_group",
|
||||
"with_test_edition_group",
|
||||
"with_test_torrent",
|
||||
"with_test_user2",
|
||||
"with_test_peers"
|
||||
),
|
||||
migrations = "../storage/migrations"
|
||||
)]
|
||||
async fn test_announce_known_torrent_with_peers(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let (service, user) =
|
||||
common::create_test_app_and_login(pool, MockRedisPool::default(), 1.0, 1.0).await;
|
||||
// #[sqlx::test(
|
||||
// fixtures(
|
||||
// "with_test_user",
|
||||
// "with_test_title_group",
|
||||
// "with_test_edition_group",
|
||||
// "with_test_torrent",
|
||||
// "with_test_user2",
|
||||
// "with_test_peers"
|
||||
// ),
|
||||
// migrations = "../storage/migrations"
|
||||
// )]
|
||||
// async fn test_announce_known_torrent_with_peers(pool: PgPool) {
|
||||
// let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
// let (service, user) =
|
||||
// common::create_test_app_and_login(pool, MockRedisPool::default(), 1.0, 1.0).await;
|
||||
|
||||
let req = test::TestRequest::get()
|
||||
.uri(concat!(
|
||||
"/announce/d2037c66dd3e13044e0d2f9b891c3837?",
|
||||
"info_hash=%11%223DUfw%88%99%AA%BB%CC%DD%EE%FF%00%11%223D&",
|
||||
"peer_id=-lt0F01-%3D%91%BB%AC%5C%C69%C0%EDmux&",
|
||||
"key=1ab4e687&",
|
||||
"compact=1&",
|
||||
"port=6968&",
|
||||
"uploaded=42&",
|
||||
"downloaded=43&",
|
||||
"left=14&",
|
||||
"event=started"
|
||||
))
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.to_request();
|
||||
// let req = test::TestRequest::get()
|
||||
// .uri(concat!(
|
||||
// "/announce/d2037c66dd3e13044e0d2f9b891c3837?",
|
||||
// "info_hash=%11%223DUfw%88%99%AA%BB%CC%DD%EE%FF%00%11%223D&",
|
||||
// "peer_id=-lt0F01-%3D%91%BB%AC%5C%C69%C0%EDmux&",
|
||||
// "key=1ab4e687&",
|
||||
// "compact=1&",
|
||||
// "port=6968&",
|
||||
// "uploaded=42&",
|
||||
// "downloaded=43&",
|
||||
// "left=14&",
|
||||
// "event=started"
|
||||
// ))
|
||||
// .insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
// .to_request();
|
||||
|
||||
let resp = test::call_service(&service, req).await;
|
||||
// let resp = test::call_service(&service, req).await;
|
||||
|
||||
// Should succeed because there is both a matching user and info hash.
|
||||
assert!(
|
||||
resp.status().is_success(),
|
||||
"status {} is not success",
|
||||
resp.status()
|
||||
);
|
||||
// // Should succeed because there is both a matching user and info hash.
|
||||
// assert!(
|
||||
// resp.status().is_success(),
|
||||
// "status {} is not success",
|
||||
// resp.status()
|
||||
// );
|
||||
|
||||
let resp = common::read_body_bencode::<announce::AnnounceResponse, _>(resp)
|
||||
.await
|
||||
.expect("could not deserialize announce response");
|
||||
// let resp = common::read_body_bencode::<announce::AnnounceResponse, _>(resp)
|
||||
// .await
|
||||
// .expect("could not deserialize announce response");
|
||||
|
||||
// Fixture sets up two non-self peers.
|
||||
assert!(resp.peers.len() == 2);
|
||||
// // Fixture sets up two non-self peers.
|
||||
// assert!(resp.peers.len() == 2);
|
||||
|
||||
for announce::Peer { ip, port } in &resp.peers {
|
||||
assert_ne!(
|
||||
(ip, port),
|
||||
(&std::net::Ipv4Addr::new(10, 10, 4, 88), &6968),
|
||||
"announce response contains self in peer list"
|
||||
);
|
||||
// for announce::Peer { ip, port } in &resp.peers {
|
||||
// assert_ne!(
|
||||
// (ip, port),
|
||||
// (&std::net::Ipv4Addr::new(10, 10, 4, 88), &6968),
|
||||
// "announce response contains self in peer list"
|
||||
// );
|
||||
|
||||
assert_ne!(
|
||||
(ip, port),
|
||||
(&std::net::Ipv4Addr::new(10, 10, 4, 91), &26),
|
||||
"peer by the same user is included in peer list"
|
||||
);
|
||||
}
|
||||
// assert_ne!(
|
||||
// (ip, port),
|
||||
// (&std::net::Ipv4Addr::new(10, 10, 4, 91), &26),
|
||||
// "peer by the same user is included in peer list"
|
||||
// );
|
||||
// }
|
||||
|
||||
let req = test::TestRequest::get()
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.insert_header(auth_header(&user.token))
|
||||
.uri("/api/users/me")
|
||||
.to_request();
|
||||
// let req = test::TestRequest::get()
|
||||
// .insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
// .insert_header(auth_header(&user.token))
|
||||
// .uri("/api/users/me")
|
||||
// .to_request();
|
||||
|
||||
let body = common::call_and_read_body_json::<Value, _>(&service, req).await;
|
||||
// let body = common::call_and_read_body_json::<Value, _>(&service, req).await;
|
||||
|
||||
assert_eq!(body["user"]["real_uploaded"].as_u64().unwrap(), 42);
|
||||
// should be 44 because users start with 1 byte downloaded at account creation
|
||||
assert_eq!(body["user"]["real_downloaded"].as_u64().unwrap(), 44);
|
||||
}
|
||||
// assert_eq!(body["user"]["real_uploaded"].as_u64().unwrap(), 42);
|
||||
// // should be 44 because users start with 1 byte downloaded at account creation
|
||||
// assert_eq!(body["user"]["real_downloaded"].as_u64().unwrap(), 44);
|
||||
// }
|
||||
|
||||
#[sqlx::test(
|
||||
fixtures(
|
||||
"with_test_user",
|
||||
"with_test_title_group",
|
||||
"with_test_edition_group",
|
||||
"with_test_torrent",
|
||||
"with_test_user2",
|
||||
"with_test_peers"
|
||||
),
|
||||
migrations = "../storage/migrations"
|
||||
)]
|
||||
async fn test_announce_global_factor_manipulation(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let (service, user) =
|
||||
common::create_test_app_and_login(pool, MockRedisPool::default(), 2.0, 0.5).await;
|
||||
let req = test::TestRequest::get()
|
||||
.uri(concat!(
|
||||
"/announce/d2037c66dd3e13044e0d2f9b891c3837?",
|
||||
"info_hash=%11%223DUfw%88%99%AA%BB%CC%DD%EE%FF%00%11%223D&",
|
||||
"peer_id=-lt0F01-%3D%91%BB%AC%5C%C69%C0%EDmux&",
|
||||
"key=1ab4e687&",
|
||||
"compact=1&",
|
||||
"port=6968&",
|
||||
"uploaded=10&",
|
||||
"downloaded=10&",
|
||||
"left=14&",
|
||||
"event=started"
|
||||
))
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.to_request();
|
||||
// #[sqlx::test(
|
||||
// fixtures(
|
||||
// "with_test_user",
|
||||
// "with_test_title_group",
|
||||
// "with_test_edition_group",
|
||||
// "with_test_torrent",
|
||||
// "with_test_user2",
|
||||
// "with_test_peers"
|
||||
// ),
|
||||
// migrations = "../storage/migrations"
|
||||
// )]
|
||||
// async fn test_announce_global_factor_manipulation(pool: PgPool) {
|
||||
// let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
// let (service, user) =
|
||||
// common::create_test_app_and_login(pool, MockRedisPool::default(), 2.0, 0.5).await;
|
||||
// let req = test::TestRequest::get()
|
||||
// .uri(concat!(
|
||||
// "/announce/d2037c66dd3e13044e0d2f9b891c3837?",
|
||||
// "info_hash=%11%223DUfw%88%99%AA%BB%CC%DD%EE%FF%00%11%223D&",
|
||||
// "peer_id=-lt0F01-%3D%91%BB%AC%5C%C69%C0%EDmux&",
|
||||
// "key=1ab4e687&",
|
||||
// "compact=1&",
|
||||
// "port=6968&",
|
||||
// "uploaded=10&",
|
||||
// "downloaded=10&",
|
||||
// "left=14&",
|
||||
// "event=started"
|
||||
// ))
|
||||
// .insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
// .to_request();
|
||||
|
||||
let _ = test::call_service(&service, req).await;
|
||||
// let _ = test::call_service(&service, req).await;
|
||||
|
||||
let req = test::TestRequest::get()
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.insert_header(auth_header(&user.token))
|
||||
.uri("/api/users/me")
|
||||
.to_request();
|
||||
// let req = test::TestRequest::get()
|
||||
// .insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
// .insert_header(auth_header(&user.token))
|
||||
// .uri("/api/users/me")
|
||||
// .to_request();
|
||||
|
||||
let body = common::call_and_read_body_json::<Value, _>(&service, req).await;
|
||||
// let body = common::call_and_read_body_json::<Value, _>(&service, req).await;
|
||||
|
||||
assert_eq!(body["user"]["uploaded"].as_u64().unwrap(), 20);
|
||||
// should be 6 because users start with 1 byte downloaded at account creation
|
||||
assert_eq!(body["user"]["downloaded"].as_u64().unwrap(), 6);
|
||||
}
|
||||
// assert_eq!(body["user"]["uploaded"].as_u64().unwrap(), 20);
|
||||
// // should be 6 because users start with 1 byte downloaded at account creation
|
||||
// assert_eq!(body["user"]["downloaded"].as_u64().unwrap(), 6);
|
||||
// }
|
||||
|
||||
#[sqlx::test(
|
||||
fixtures(
|
||||
"with_test_user",
|
||||
"with_test_title_group",
|
||||
"with_test_edition_group",
|
||||
"with_test_torrent_custom_up_down_factors",
|
||||
"with_test_user2",
|
||||
"with_test_peers"
|
||||
),
|
||||
migrations = "../storage/migrations"
|
||||
)]
|
||||
async fn test_announce_torrent_specific_factor_manipulation(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let (service, user) =
|
||||
common::create_test_app_and_login(pool, MockRedisPool::default(), 1.0, 1.0).await;
|
||||
let req = test::TestRequest::get()
|
||||
.uri(concat!(
|
||||
"/announce/d2037c66dd3e13044e0d2f9b891c3837?",
|
||||
"info_hash=%11%223DUfw%88%99%AA%BB%CC%DD%EE%FF%00%11%223D&",
|
||||
"peer_id=-lt0F01-%3D%91%BB%AC%5C%C69%C0%EDmux&",
|
||||
"key=1ab4e687&",
|
||||
"compact=1&",
|
||||
"port=6968&",
|
||||
"uploaded=10&",
|
||||
"downloaded=10&",
|
||||
"left=14&",
|
||||
"event=started"
|
||||
))
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.to_request();
|
||||
// #[sqlx::test(
|
||||
// fixtures(
|
||||
// "with_test_user",
|
||||
// "with_test_title_group",
|
||||
// "with_test_edition_group",
|
||||
// "with_test_torrent_custom_up_down_factors",
|
||||
// "with_test_user2",
|
||||
// "with_test_peers"
|
||||
// ),
|
||||
// migrations = "../storage/migrations"
|
||||
// )]
|
||||
// async fn test_announce_torrent_specific_factor_manipulation(pool: PgPool) {
|
||||
// let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
// let (service, user) =
|
||||
// common::create_test_app_and_login(pool, MockRedisPool::default(), 1.0, 1.0).await;
|
||||
// let req = test::TestRequest::get()
|
||||
// .uri(concat!(
|
||||
// "/announce/d2037c66dd3e13044e0d2f9b891c3837?",
|
||||
// "info_hash=%11%223DUfw%88%99%AA%BB%CC%DD%EE%FF%00%11%223D&",
|
||||
// "peer_id=-lt0F01-%3D%91%BB%AC%5C%C69%C0%EDmux&",
|
||||
// "key=1ab4e687&",
|
||||
// "compact=1&",
|
||||
// "port=6968&",
|
||||
// "uploaded=10&",
|
||||
// "downloaded=10&",
|
||||
// "left=14&",
|
||||
// "event=started"
|
||||
// ))
|
||||
// .insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
// .to_request();
|
||||
|
||||
let _ = test::call_service(&service, req).await;
|
||||
// let _ = test::call_service(&service, req).await;
|
||||
|
||||
let req = test::TestRequest::get()
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.insert_header(auth_header(&user.token))
|
||||
.uri("/api/users/me")
|
||||
.to_request();
|
||||
// let req = test::TestRequest::get()
|
||||
// .insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
// .insert_header(auth_header(&user.token))
|
||||
// .uri("/api/users/me")
|
||||
// .to_request();
|
||||
|
||||
let body = common::call_and_read_body_json::<Value, _>(&service, req).await;
|
||||
// let body = common::call_and_read_body_json::<Value, _>(&service, req).await;
|
||||
|
||||
assert_eq!(body["user"]["uploaded"].as_u64().unwrap(), 20);
|
||||
// should be 6 because users start with 1 byte downloaded at account creation
|
||||
assert_eq!(body["user"]["downloaded"].as_u64().unwrap(), 6);
|
||||
}
|
||||
// assert_eq!(body["user"]["uploaded"].as_u64().unwrap(), 20);
|
||||
// // should be 6 because users start with 1 byte downloaded at account creation
|
||||
// assert_eq!(body["user"]["downloaded"].as_u64().unwrap(), 6);
|
||||
// }
|
||||
|
||||
#[sqlx::test(
|
||||
fixtures(
|
||||
"with_test_user",
|
||||
"with_test_title_group",
|
||||
"with_test_edition_group",
|
||||
"with_test_torrent"
|
||||
),
|
||||
migrations = "../storage/migrations"
|
||||
)]
|
||||
async fn test_peers_after_announce(pool: PgPool) {
|
||||
let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
let (service, user) =
|
||||
common::create_test_app_and_login(pool, MockRedisPool::default(), 1.0, 1.0).await;
|
||||
// #[sqlx::test(
|
||||
// fixtures(
|
||||
// "with_test_user",
|
||||
// "with_test_title_group",
|
||||
// "with_test_edition_group",
|
||||
// "with_test_torrent"
|
||||
// ),
|
||||
// migrations = "../storage/migrations"
|
||||
// )]
|
||||
// async fn test_peers_after_announce(pool: PgPool) {
|
||||
// let pool = Arc::new(ConnectionPool::with_pg_pool(pool));
|
||||
// let (service, user) =
|
||||
// common::create_test_app_and_login(pool, MockRedisPool::default(), 1.0, 1.0).await;
|
||||
|
||||
let req = test::TestRequest::get()
|
||||
.uri(concat!(
|
||||
"/announce/d2037c66dd3e13044e0d2f9b891c3837?",
|
||||
"info_hash=%11%223DUfw%88%99%AA%BB%CC%DD%EE%FF%00%11%223D&",
|
||||
"peer_id=-lt0F01-%3D%91%BB%AC%5C%C69%C0%EDmux&",
|
||||
"key=1ab4e687&",
|
||||
"compact=1&",
|
||||
"port=6968&",
|
||||
"uploaded=100&",
|
||||
"downloaded=100&",
|
||||
"left=14&",
|
||||
"event=started"
|
||||
))
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.to_request();
|
||||
// let req = test::TestRequest::get()
|
||||
// .uri(concat!(
|
||||
// "/announce/d2037c66dd3e13044e0d2f9b891c3837?",
|
||||
// "info_hash=%11%223DUfw%88%99%AA%BB%CC%DD%EE%FF%00%11%223D&",
|
||||
// "peer_id=-lt0F01-%3D%91%BB%AC%5C%C69%C0%EDmux&",
|
||||
// "key=1ab4e687&",
|
||||
// "compact=1&",
|
||||
// "port=6968&",
|
||||
// "uploaded=100&",
|
||||
// "downloaded=100&",
|
||||
// "left=14&",
|
||||
// "event=started"
|
||||
// ))
|
||||
// .insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
// .to_request();
|
||||
|
||||
let resp = test::call_service(&service, req).await;
|
||||
// let resp = test::call_service(&service, req).await;
|
||||
|
||||
// Should succeed because there is both a matching user and info hash.
|
||||
assert!(
|
||||
resp.status().is_success(),
|
||||
"status {} is not success",
|
||||
resp.status()
|
||||
);
|
||||
// // Should succeed because there is both a matching user and info hash.
|
||||
// assert!(
|
||||
// resp.status().is_success(),
|
||||
// "status {} is not success",
|
||||
// resp.status()
|
||||
// );
|
||||
|
||||
let _ = common::read_body_bencode::<announce::AnnounceResponse, _>(resp)
|
||||
.await
|
||||
.expect("could not deserialize announce response");
|
||||
// let _ = common::read_body_bencode::<announce::AnnounceResponse, _>(resp)
|
||||
// .await
|
||||
// .expect("could not deserialize announce response");
|
||||
|
||||
let req = test::TestRequest::get()
|
||||
.uri("/api/users/me")
|
||||
.insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
.insert_header(auth_header(&user.token))
|
||||
.to_request();
|
||||
// let req = test::TestRequest::get()
|
||||
// .uri("/api/users/me")
|
||||
// .insert_header(("X-Forwarded-For", "10.10.4.88"))
|
||||
// .insert_header(auth_header(&user.token))
|
||||
// .to_request();
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
struct Peer {
|
||||
pub ip: String,
|
||||
pub port: i16,
|
||||
pub real_uploaded: i64,
|
||||
pub real_downloaded: i64,
|
||||
}
|
||||
// #[derive(Debug, PartialEq, Deserialize)]
|
||||
// struct Peer {
|
||||
// pub ip: String,
|
||||
// pub port: i16,
|
||||
// pub real_uploaded: i64,
|
||||
// pub real_downloaded: i64,
|
||||
// }
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
struct Profile {
|
||||
pub peers: Vec<Peer>,
|
||||
}
|
||||
// #[derive(Debug, PartialEq, Deserialize)]
|
||||
// struct Profile {
|
||||
// pub peers: Vec<Peer>,
|
||||
// }
|
||||
|
||||
let resp = common::call_and_read_body_json::<Profile, _>(&service, req).await;
|
||||
// let resp = common::call_and_read_body_json::<Profile, _>(&service, req).await;
|
||||
|
||||
assert_eq!(
|
||||
resp.peers,
|
||||
vec![Peer {
|
||||
ip: String::from("10.10.4.88/32"),
|
||||
port: 6968,
|
||||
real_uploaded: 100,
|
||||
real_downloaded: 100,
|
||||
}]
|
||||
);
|
||||
}
|
||||
// assert_eq!(
|
||||
// resp.peers,
|
||||
// vec![Peer {
|
||||
// ip: String::from("10.10.4.88/32"),
|
||||
// port: 6968,
|
||||
// real_uploaded: 100,
|
||||
// real_downloaded: 100,
|
||||
// }]
|
||||
// );
|
||||
// }
|
||||
|
||||
@@ -122,7 +122,7 @@ async fn test_upload_torrent(pool: PgPool) {
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Torrent {
|
||||
edition_group_id: i64,
|
||||
created_by_id: i64,
|
||||
created_by_id: i32,
|
||||
}
|
||||
|
||||
let torrent = common::call_and_read_body_json_with_status::<Torrent, _>(
|
||||
|
||||
@@ -114,7 +114,7 @@ pub enum Error {
|
||||
UserNotFound(String),
|
||||
|
||||
#[error("user with id '{0}' not found")]
|
||||
UserWithIdNotFound(i64),
|
||||
UserWithIdNotFound(i32),
|
||||
|
||||
#[error("wrong username or password")]
|
||||
WrongUsernameOrPassword,
|
||||
|
||||
@@ -3,7 +3,7 @@ use chrono::{DateTime, Local};
|
||||
#[derive(Debug)]
|
||||
pub struct Peer {
|
||||
pub id: i64,
|
||||
pub user_id: i64,
|
||||
pub user_id: i32,
|
||||
pub torrent_id: i64,
|
||||
pub peer_id: [u8; 20],
|
||||
pub ip: Option<std::net::Ipv4Addr>,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
pub fn get_announce_url(passkey_upper: i64, passkey_lower: i64, tracker_url: &str) -> String {
|
||||
let passkey = ((passkey_upper as u64 as u128) << 64) | (passkey_lower as u64 as u128);
|
||||
|
||||
format!("{tracker_url}announce/{passkey:x}")
|
||||
pub fn get_announce_url(passkey: String, tracker_url: &str) -> String {
|
||||
format!("{tracker_url}announce/{passkey}")
|
||||
}
|
||||
|
||||
pub fn looks_like_url(s: &str) -> bool {
|
||||
|
||||
@@ -21,3 +21,4 @@ strum = { version = "0.27", features = ["derive"] }
|
||||
musicbrainz_rs = "0.9.1"
|
||||
rand = "0.9.0"
|
||||
utoipa = { version = "5.3.1", features = ["actix_extras"] }
|
||||
arcadia-shared = { path = "../../shared" }
|
||||
|
||||
@@ -5,7 +5,7 @@ CREATE TYPE user_class_enum AS ENUM (
|
||||
);
|
||||
|
||||
CREATE TABLE users (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
id SERIAL PRIMARY KEY,
|
||||
username VARCHAR(20) UNIQUE NOT NULL,
|
||||
avatar TEXT,
|
||||
email VARCHAR(255) UNIQUE NOT NULL,
|
||||
@@ -40,22 +40,21 @@ CREATE TABLE users (
|
||||
bonus_points BIGINT NOT NULL DEFAULT 0,
|
||||
freeleech_tokens INT NOT NULL DEFAULT 0,
|
||||
settings JSONB NOT NULL DEFAULT '{}',
|
||||
passkey_upper BIGINT NOT NULL,
|
||||
passkey_lower BIGINT NOT NULL,
|
||||
passkey VARCHAR(33) NOT NULL,
|
||||
warned BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
banned BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
staff_note TEXT NOT NULL DEFAULT '',
|
||||
|
||||
UNIQUE(passkey_upper, passkey_lower)
|
||||
UNIQUE(passkey)
|
||||
);
|
||||
INSERT INTO users (username, email, password_hash, registered_from_ip, settings, passkey_upper, passkey_lower)
|
||||
VALUES ('creator', 'none@domain.com', 'none', '127.0.0.1', '{}'::jsonb, '1', '1');
|
||||
INSERT INTO users (username, email, password_hash, registered_from_ip, settings, passkey)
|
||||
VALUES ('creator', 'none@domain.com', 'none', '127.0.0.1', '{}'::jsonb, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
|
||||
CREATE TABLE api_keys (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
name VARCHAR(30) NOT NULL,
|
||||
value VARCHAR(40) NOT NULL UNIQUE,
|
||||
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE
|
||||
user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TYPE user_application_status_enum AS ENUM (
|
||||
'pending',
|
||||
@@ -77,27 +76,27 @@ CREATE TABLE invitations (
|
||||
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
invitation_key VARCHAR(50) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
sender_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
sender_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
receiver_email VARCHAR(255) NOT NULL,
|
||||
user_application_id BIGINT REFERENCES user_applications(id) ON DELETE SET NULL,
|
||||
receiver_id BIGINT REFERENCES users(id) ON DELETE SET NULL
|
||||
receiver_id INT REFERENCES users(id) ON DELETE SET NULL
|
||||
);
|
||||
|
||||
CREATE TABLE user_warnings (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
expires_at TIMESTAMP WITH TIME ZONE,
|
||||
reason TEXT NOT NULL,
|
||||
ban boolean NOT NULL,
|
||||
created_by_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE
|
||||
created_by_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE
|
||||
);
|
||||
CREATE TABLE gifts (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
sent_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
message TEXT NOT NULL,
|
||||
sender_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
receiver_id BIGINT NOT NULL REFERENCES users(id) ON DELETE SET NULL,
|
||||
sender_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
receiver_id INT NOT NULL REFERENCES users(id) ON DELETE SET NULL,
|
||||
bonus_points BIGINT NOT NULL DEFAULT 0,
|
||||
freeleech_tokens INT NOT NULL DEFAULT 0
|
||||
);
|
||||
@@ -106,7 +105,7 @@ CREATE TABLE artists (
|
||||
name VARCHAR(255) UNIQUE NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
pictures TEXT [] NOT NULL,
|
||||
created_by_id BIGINT NOT NULL,
|
||||
created_by_id INT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
title_groups_amount INT NOT NULL DEFAULT 0,
|
||||
edition_groups_amount INT NOT NULL DEFAULT 0,
|
||||
@@ -129,7 +128,7 @@ CREATE TABLE master_groups (
|
||||
-- name_aliases VARCHAR(255)[],
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
created_by_id BIGINT NOT NULL,
|
||||
created_by_id INT NOT NULL,
|
||||
-- description TEXT NOT NULL,
|
||||
-- original_language VARCHAR(50) NOT NULL,
|
||||
-- country_from VARCHAR(50) NOT NULL,
|
||||
@@ -155,7 +154,7 @@ CREATE TABLE series (
|
||||
tags TEXT [] NOT NULL,
|
||||
covers TEXT [] NOT NULL,
|
||||
banners TEXT [] NOT NULL,
|
||||
created_by_id BIGINT NOT NULL,
|
||||
created_by_id INT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
@@ -258,7 +257,7 @@ CREATE TABLE title_groups (
|
||||
name_aliases TEXT [],
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
created_by_id BIGINT NOT NULL,
|
||||
created_by_id INT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
platform platform_enum,
|
||||
original_language language_enum,
|
||||
@@ -314,7 +313,7 @@ CREATE TABLE affiliated_artists (
|
||||
artist_id BIGINT NOT NULL,
|
||||
roles artist_role_enum[] NOT NULL,
|
||||
nickname VARCHAR(255),
|
||||
created_by_id BIGINT NOT NULL,
|
||||
created_by_id INT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
FOREIGN KEY (title_group_id) REFERENCES title_groups(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (artist_id) REFERENCES artists(id) ON DELETE CASCADE,
|
||||
@@ -349,7 +348,7 @@ CREATE TABLE edition_groups (
|
||||
release_date TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
created_by_id BIGINT NOT NULL,
|
||||
created_by_id INT NOT NULL,
|
||||
description TEXT,
|
||||
distributor VARCHAR(255),
|
||||
covers TEXT [] NOT NULL,
|
||||
@@ -439,7 +438,7 @@ CREATE TABLE torrents (
|
||||
edition_group_id BIGINT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
created_by_id BIGINT NOT NULL,
|
||||
created_by_id INT NOT NULL,
|
||||
info_hash BYTEA NOT NULL CHECK(octet_length(info_hash) = 20),
|
||||
info_dict BYTEA NOT NULL,
|
||||
languages language_enum[] NOT NULL,
|
||||
@@ -485,7 +484,7 @@ CREATE TABLE torrents (
|
||||
CREATE TABLE deleted_torrents (
|
||||
LIKE torrents INCLUDING CONSTRAINTS, -- INCLUDING DEFAULTS INCLUDING INDEXES,
|
||||
deleted_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
deleted_by_id BIGINT NOT NULL,
|
||||
deleted_by_id INT NOT NULL,
|
||||
reason TEXT NOT NULL,
|
||||
|
||||
FOREIGN KEY (deleted_by_id) REFERENCES users(id)
|
||||
@@ -495,7 +494,7 @@ CREATE TABLE title_group_comments (
|
||||
content TEXT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
created_by_id BIGINT NOT NULL,
|
||||
created_by_id INT NOT NULL,
|
||||
title_group_id BIGINT NOT NULL,
|
||||
refers_to_torrent_id BIGINT,
|
||||
answers_to_comment_id BIGINT,
|
||||
@@ -509,8 +508,8 @@ CREATE TABLE torrent_requests (
|
||||
title_group_id BIGINT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
created_by_id BIGINT NOT NULL,
|
||||
filled_by_user_id BIGINT,
|
||||
created_by_id INT NOT NULL,
|
||||
filled_by_user_id INT,
|
||||
filled_by_torrent_id BIGINT,
|
||||
filled_at TIMESTAMP WITH TIME ZONE,
|
||||
edition_name TEXT,
|
||||
@@ -539,7 +538,7 @@ CREATE TABLE torrent_request_votes(
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
torrent_request_id BIGINT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
created_by_id BIGINT NOT NULL,
|
||||
created_by_id INT NOT NULL,
|
||||
bounty_upload BIGINT NOT NULL DEFAULT 0,
|
||||
bounty_bonus_points BIGINT NOT NULL DEFAULT 0,
|
||||
FOREIGN KEY (torrent_request_id) REFERENCES torrent_requests(id) ON DELETE CASCADE,
|
||||
@@ -548,7 +547,7 @@ CREATE TABLE torrent_request_votes(
|
||||
CREATE TABLE torrent_reports (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
reported_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
reported_by_id BIGINT NOT NULL,
|
||||
reported_by_id INT NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
reported_torrent_id BIGINT NOT NULL,
|
||||
FOREIGN KEY (reported_by_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
@@ -558,7 +557,7 @@ CREATE TABLE torrent_reports (
|
||||
CREATE TYPE peer_status_enum AS ENUM('seeding', 'leeching');
|
||||
CREATE TABLE peers (
|
||||
id BIGINT GENERATED ALWAYS AS IDENTITY,
|
||||
user_id BIGINT NOT NULL,
|
||||
user_id INT NOT NULL,
|
||||
torrent_id BIGINT NOT NULL,
|
||||
peer_id BYTEA NOT NULL CHECK(octet_length(peer_id) = 20),
|
||||
ip INET NOT NULL,
|
||||
@@ -580,7 +579,7 @@ CREATE TABLE peers (
|
||||
CREATE TABLE torrent_activities (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
torrent_id BIGINT NOT NULL,
|
||||
user_id BIGINT NOT NULL,
|
||||
user_id INT NOT NULL,
|
||||
snatched_at TIMESTAMP WITH TIME ZONE,
|
||||
first_seen_seeding_at TIMESTAMP WITH TIME ZONE,
|
||||
last_seen_seeding_at TIMESTAMP WITH TIME ZONE,
|
||||
@@ -596,7 +595,7 @@ CREATE TABLE entities (
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT NOT NULL,
|
||||
pictures TEXT[] NOT NULL,
|
||||
created_by_id BIGINT NOT NULL,
|
||||
created_by_id INT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
title_groups_amount INT NOT NULL DEFAULT 0,
|
||||
edition_groups_amount INT NOT NULL DEFAULT 0,
|
||||
@@ -616,7 +615,7 @@ CREATE TABLE affiliated_entities (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
title_group_id BIGINT NOT NULL,
|
||||
entity_id BIGINT NOT NULL,
|
||||
created_by_id BIGINT NOT NULL,
|
||||
created_by_id INT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
roles entity_role_enum[] NOT NULL,
|
||||
FOREIGN KEY (title_group_id) REFERENCES title_groups(id) ON DELETE CASCADE,
|
||||
@@ -638,7 +637,7 @@ CREATE TYPE collage_type_enum AS ENUM (
|
||||
CREATE TABLE collage (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
created_by_id BIGINT NOT NULL,
|
||||
created_by_id INT NOT NULL,
|
||||
name VARCHAR NOT NULL,
|
||||
cover TEXT,
|
||||
description TEXT NOT NULL,
|
||||
@@ -650,7 +649,7 @@ CREATE TABLE collage (
|
||||
CREATE TABLE collage_entry (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
created_by_id BIGINT NOT NULL REFERENCES users(id),
|
||||
created_by_id INT NOT NULL REFERENCES users(id),
|
||||
collage_id BIGINT NOT NULL REFERENCES collage(id),
|
||||
artist_id BIGINT REFERENCES artists(id),
|
||||
entity_id BIGINT REFERENCES entities(id),
|
||||
@@ -717,7 +716,7 @@ CREATE TABLE forum_categories (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
created_by_id BIGINT NOT NULL,
|
||||
created_by_id INT NOT NULL,
|
||||
|
||||
FOREIGN KEY (created_by_id) REFERENCES users(id)
|
||||
);
|
||||
@@ -727,7 +726,7 @@ CREATE TABLE forum_sub_categories (
|
||||
forum_category_id INT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
created_by_id BIGINT,
|
||||
created_by_id INT,
|
||||
threads_amount BIGINT NOT NULL DEFAULT 0,
|
||||
posts_amount BIGINT NOT NULL DEFAULT 0,
|
||||
forbidden_classes VARCHAR(50) [] NOT NULL DEFAULT ARRAY[]::VARCHAR(50)[],
|
||||
@@ -741,7 +740,7 @@ CREATE TABLE forum_threads (
|
||||
forum_sub_category_id INT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
created_by_id BIGINT NOT NULL,
|
||||
created_by_id INT NOT NULL,
|
||||
posts_amount BIGINT NOT NULL DEFAULT 0,
|
||||
sticky BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
locked BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
@@ -755,7 +754,7 @@ CREATE TABLE forum_posts (
|
||||
forum_thread_id BIGINT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
created_by_id BIGINT NOT NULL,
|
||||
created_by_id INT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
sticky BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
@@ -767,9 +766,9 @@ CREATE TABLE wiki_articles (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
title TEXT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
created_by_id BIGINT NOT NULL,
|
||||
created_by_id INT NOT NULL,
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_by_id BIGINT NOT NULL,
|
||||
updated_by_id INT NOT NULL,
|
||||
body TEXT NOT NULL,
|
||||
|
||||
FOREIGN KEY (created_by_id) REFERENCES users(id)
|
||||
@@ -778,8 +777,8 @@ CREATE TABLE conversations (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
|
||||
subject VARCHAR(255) NOT NULL,
|
||||
sender_id BIGINT NOT NULL,
|
||||
receiver_id BIGINT NOT NULL,
|
||||
sender_id INT NOT NULL,
|
||||
receiver_id INT NOT NULL,
|
||||
sender_last_seen_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
|
||||
receiver_last_seen_at TIMESTAMP WITH TIME ZONE,
|
||||
|
||||
@@ -790,7 +789,7 @@ CREATE TABLE conversation_messages (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
conversation_id BIGINT NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL,
|
||||
created_by_id BIGINT NOT NULL,
|
||||
created_by_id INT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
|
||||
FOREIGN KEY (conversation_id) REFERENCES conversations(id),
|
||||
@@ -800,14 +799,14 @@ CREATE TABLE staff_pms (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
subject TEXT NOT NULL,
|
||||
created_by_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
created_by_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
resolved BOOLEAN NOT NULL DEFAULT FALSE
|
||||
);
|
||||
CREATE TABLE staff_pm_messages (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
staff_pm_id BIGINT NOT NULL REFERENCES staff_pms(id) ON DELETE CASCADE,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
created_by_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
content TEXT NOT NULL
|
||||
);
|
||||
CREATE TYPE notification_reason_enum AS ENUM (
|
||||
@@ -835,7 +834,7 @@ CREATE TABLE subscriptions (
|
||||
CREATE TABLE notifications (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
receiver_id BIGINT NOT NULL,
|
||||
receiver_id INT NOT NULL,
|
||||
reason notification_reason_enum NOT NULL,
|
||||
message TEXT,
|
||||
read_status BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
@@ -927,9 +926,9 @@ ORDER BY
|
||||
p_order TEXT DEFAULT 'desc',
|
||||
p_limit BIGINT DEFAULT NULL,
|
||||
p_offset BIGINT DEFAULT NULL,
|
||||
p_torrent_created_by_id BIGINT DEFAULT NULL,
|
||||
p_torrent_snatched_by_id BIGINT DEFAULT NULL,
|
||||
p_requesting_user_id BIGINT DEFAULT NULL,
|
||||
p_torrent_created_by_id INT DEFAULT NULL,
|
||||
p_torrent_snatched_by_id INT DEFAULT NULL,
|
||||
p_requesting_user_id INT DEFAULT NULL,
|
||||
p_external_link TEXT DEFAULT NULL
|
||||
)
|
||||
RETURNS TABLE (
|
||||
|
||||
@@ -27,11 +27,11 @@ INSERT INTO public._sqlx_migrations VALUES (20250312215600, 'initdb', '2025-09-1
|
||||
-- Data for Name: users; Type: TABLE DATA; Schema: public; Owner: arcadia
|
||||
--
|
||||
|
||||
INSERT INTO public.users VALUES (1, 'creator', NULL, 'none@domain.com', 'none', '127.0.0.1', '2025-09-17 12:42:13.702455+00', '', 0, 0, 1, 1, 0, 0, '2025-09-17 12:42:13.702455+00', 'newbie', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '{}', 1, 1, false, false, '');
|
||||
INSERT INTO public.users VALUES (5, 'waterbottle', 'https://i.pinimg.com/736x/a6/27/12/a6271204df8d387c3e614986c106f549.jpg', 'user2@example.com', 'hashedpassword2', '192.168.1.2', '2025-03-30 16:24:57.388152+00', '', 0, 0, 1, 1, 0, 0, '2025-03-30 16:24:57.388152+00', 'newbie', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '{"site_appearance": {"item_detail_layout": "sidebar_right"}}', 5493004881313328037, 2566432999990446913, false, false, '''''');
|
||||
INSERT INTO public.users VALUES (3, 'coolguy', 'https://i.pinimg.com/474x/c1/5a/6c/c15a6c91515e22f6ea8b766f89c12f0c.jpg', 'user3@example.com', 'hashedpassword3', '192.168.1.3', '2025-03-30 16:24:57.388152+00', '', 0, 0, 1, 1, 0, 0, '2025-03-30 16:24:57.388152+00', 'newbie', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '{"site_appearance": {"item_detail_layout": "sidebar_right"}}', 2274483400846363122, 1270934296711348124, false, false, '''''');
|
||||
INSERT INTO public.users VALUES (2, 'picolo', 'https://img.freepik.com/premium-vector/random-people-line-art-vector_567805-63.jpg', 'user1@example.com', '$argon2id$v=19$m=19456,t=2,p=1$s4XJtCUk9IrGgNsTfP6Ofw$ktoGbBEoFaVgdiTn19Gh9h45LjFiv7AUEL5KHhzm4d0', '192.168.1.1', '2025-03-30 16:24:57.388152+00', '', 10000, 0, 1, 1, 0, 0, '2025-09-17 09:27:11.336576+00', 'staff', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 999999410, 0, '{"site_appearance": {"item_detail_layout": "sidebar_right"}}', -197409747985172542, 1837889239438807682, false, false, '''''');
|
||||
INSERT INTO public.users VALUES (4, 'test', NULL, 'test@test.tsttt', '$argon2id$v=19$m=19456,t=2,p=1$yaA+WqA4OfSyAqR3iXhDng$/Ngv7VeJvVNHli9rBgQG0d/O2W+qoI2yHhQxZSxxW2M', '127.0.0.1', '2025-04-10 19:15:51.036818+00', '', 979900000000, 0, 1, 1, 0, 0, '2025-09-17 09:15:44.322914+00', 'newbie', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99999000, 0, '{"site_appearance": {"item_detail_layout": "sidebar_right"}}', -7167291202215854785, 1526268353104531819, false, false, '''''');
|
||||
INSERT INTO public.users VALUES (1, 'creator', NULL, 'none@domain.com', 'none', '127.0.0.1', '2025-09-17 12:42:13.702455+00', '', 0, 0, 1, 1, 0, 0, '2025-09-17 12:42:13.702455+00', 'newbie', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '{}', 'aa', false, false, '');
|
||||
INSERT INTO public.users VALUES (5, 'waterbottle', 'https://i.pinimg.com/736x/a6/27/12/a6271204df8d387c3e614986c106f549.jpg', 'user2@example.com', 'hashedpassword2', '192.168.1.2', '2025-03-30 16:24:57.388152+00', '', 0, 0, 1, 1, 0, 0, '2025-03-30 16:24:57.388152+00', 'newbie', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '{"site_appearance": {"item_detail_layout": "sidebar_right"}}', 'fqmslfjqmlsfj', false, false, '''''');
|
||||
INSERT INTO public.users VALUES (3, 'coolguy', 'https://i.pinimg.com/474x/c1/5a/6c/c15a6c91515e22f6ea8b766f89c12f0c.jpg', 'user3@example.com', 'hashedpassword3', '192.168.1.3', '2025-03-30 16:24:57.388152+00', '', 0, 0, 1, 1, 0, 0, '2025-03-30 16:24:57.388152+00', 'newbie', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '{"site_appearance": {"item_detail_layout": "sidebar_right"}}', 'qnsvmqfmlqsdm', false, false, '''''');
|
||||
INSERT INTO public.users VALUES (2, 'picolo', 'https://img.freepik.com/premium-vector/random-people-line-art-vector_567805-63.jpg', 'user1@example.com', '$argon2id$v=19$m=19456,t=2,p=1$s4XJtCUk9IrGgNsTfP6Ofw$ktoGbBEoFaVgdiTn19Gh9h45LjFiv7AUEL5KHhzm4d0', '192.168.1.1', '2025-03-30 16:24:57.388152+00', '', 10000, 0, 1, 1, 0, 0, '2025-09-17 09:27:11.336576+00', 'staff', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100, 999999410, 0, '{"site_appearance": {"item_detail_layout": "sidebar_right"}}', 'qmofqmlskdfnnns', false, false, '''''');
|
||||
INSERT INTO public.users VALUES (4, 'test', NULL, 'test@test.tsttt', '$argon2id$v=19$m=19456,t=2,p=1$yaA+WqA4OfSyAqR3iXhDng$/Ngv7VeJvVNHli9rBgQG0d/O2W+qoI2yHhQxZSxxW2M', '127.0.0.1', '2025-04-10 19:15:51.036818+00', '', 979900000000, 0, 1, 1, 0, 0, '2025-09-17 09:15:44.322914+00', 'newbie', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 99999000, 0, '{"site_appearance": {"item_detail_layout": "sidebar_right"}}', 'mqnmnqmlngqsklf', false, false, '''''');
|
||||
|
||||
|
||||
--
|
||||
|
||||
@@ -11,7 +11,7 @@ pub struct Artist {
|
||||
pub name: String,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub description: String,
|
||||
pub pictures: Vec<String>,
|
||||
pub title_groups_amount: i32,
|
||||
@@ -110,7 +110,7 @@ pub struct AffiliatedArtist {
|
||||
pub nickname: Option<String>, // for example: name of the character the actor is playing
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, FromRow, ToSchema)]
|
||||
@@ -145,6 +145,6 @@ pub struct AffiliatedArtistHierarchy {
|
||||
pub nickname: Option<String>,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub artist: Artist,
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ pub struct Collage {
|
||||
pub id: i64,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub name: String,
|
||||
pub cover: Option<String>,
|
||||
pub description: String,
|
||||
@@ -57,7 +57,7 @@ pub struct CollageEntry {
|
||||
pub id: i64,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub artist_id: Option<i64>,
|
||||
pub entity_id: Option<i64>,
|
||||
pub title_group_id: Option<i64>,
|
||||
@@ -81,7 +81,7 @@ pub struct CollageEntryHierarchy {
|
||||
pub id: i64,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub artist_id: Option<i64>,
|
||||
pub artist: Option<ArtistLite>,
|
||||
pub entity_id: Option<i64>,
|
||||
@@ -105,7 +105,7 @@ pub struct CollageSearchResult {
|
||||
pub id: i64,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub created_by: UserLite,
|
||||
pub name: String,
|
||||
pub cover: Option<String>,
|
||||
|
||||
@@ -13,8 +13,8 @@ pub struct Conversation {
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub subject: String,
|
||||
pub sender_id: i64,
|
||||
pub receiver_id: i64,
|
||||
pub sender_id: i32,
|
||||
pub receiver_id: i32,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub sender_last_seen_at: DateTime<Utc>,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
@@ -24,7 +24,7 @@ pub struct Conversation {
|
||||
#[derive(Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct UserCreatedConversation {
|
||||
pub subject: String,
|
||||
pub receiver_id: i64,
|
||||
pub receiver_id: i32,
|
||||
pub first_message: UserCreatedConversationMessage,
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ pub struct ConversationMessage {
|
||||
pub conversation_id: i64,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
@@ -81,8 +81,8 @@ pub struct ConversationOverview {
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub subject: String,
|
||||
pub sender_id: i64,
|
||||
pub receiver_id: i64,
|
||||
pub sender_id: i32,
|
||||
pub receiver_id: i32,
|
||||
pub correspondant: UserLite,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub sender_last_seen_at: DateTime<Utc>,
|
||||
|
||||
@@ -68,7 +68,7 @@ pub struct EditionGroup {
|
||||
pub created_at: DateTime<Utc>, // database entry creation
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub updated_at: DateTime<Utc>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub description: Option<String>, // specific to the edition
|
||||
pub distributor: Option<String>, // web: [web stores/distributors], physical: [shop if specific edition ?]
|
||||
pub covers: Vec<String>,
|
||||
@@ -123,7 +123,7 @@ pub struct EditionGroupHierarchy {
|
||||
pub created_at: DateTime<Utc>,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub updated_at: DateTime<Utc>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub description: Option<String>,
|
||||
pub distributor: Option<String>,
|
||||
pub covers: Vec<String>,
|
||||
|
||||
@@ -28,7 +28,7 @@ pub struct Entity {
|
||||
pub name: String,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub description: String,
|
||||
pub pictures: Vec<String>,
|
||||
}
|
||||
@@ -43,7 +43,7 @@ pub struct AffiliatedEntity {
|
||||
pub id: i64,
|
||||
pub title_group_id: i64,
|
||||
pub entity_id: i64,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Local>,
|
||||
pub roles: Vec<EntityRole>,
|
||||
@@ -53,7 +53,7 @@ pub struct AffiliatedEntityHierarchy {
|
||||
pub id: i64,
|
||||
pub title_group_id: i64,
|
||||
pub entity_id: i64,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Local>,
|
||||
pub roles: Vec<EntityRole>,
|
||||
|
||||
@@ -11,7 +11,7 @@ pub struct ForumCategory {
|
||||
pub name: String,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, FromRow, ToSchema)]
|
||||
@@ -21,7 +21,7 @@ pub struct ForumSubCategory {
|
||||
pub name: String,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub threads_amount: i64,
|
||||
pub posts_amount: i64,
|
||||
pub forbidden_classes: Vec<String>,
|
||||
@@ -34,7 +34,7 @@ pub struct ForumThread {
|
||||
pub name: String,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub posts_amount: i64,
|
||||
pub sticky: bool,
|
||||
pub locked: bool,
|
||||
@@ -55,7 +55,7 @@ pub struct ForumPost {
|
||||
pub created_at: DateTime<Local>,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub updated_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub content: String,
|
||||
pub sticky: bool,
|
||||
}
|
||||
@@ -125,7 +125,7 @@ pub struct ForumThreadAndPosts {
|
||||
pub name: String,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub posts_amount: i64,
|
||||
pub sticky: bool,
|
||||
pub locked: bool,
|
||||
@@ -153,7 +153,7 @@ pub struct ForumPostAndThreadName {
|
||||
pub created_at: DateTime<Local>,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub updated_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub content: String,
|
||||
pub sticky: bool,
|
||||
pub forum_thread_name: String,
|
||||
|
||||
@@ -9,8 +9,8 @@ pub struct Gift {
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub sent_at: DateTime<Utc>,
|
||||
pub message: String,
|
||||
pub sender_id: i64,
|
||||
pub receiver_id: i64,
|
||||
pub sender_id: i32,
|
||||
pub receiver_id: i32,
|
||||
pub bonus_points: i64,
|
||||
pub freeleech_tokens: i32,
|
||||
}
|
||||
@@ -18,7 +18,7 @@ pub struct Gift {
|
||||
#[derive(Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct UserCreatedGift {
|
||||
pub message: String,
|
||||
pub receiver_id: i64,
|
||||
pub receiver_id: i32,
|
||||
pub bonus_points: i64,
|
||||
pub freeleech_tokens: i32,
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@ pub struct Invitation {
|
||||
pub expires_at: DateTime<Local>,
|
||||
pub message: String,
|
||||
pub invitation_key: String,
|
||||
pub sender_id: i64,
|
||||
pub sender_id: i32,
|
||||
pub receiver_email: String,
|
||||
pub receiver_id: Option<i64>,
|
||||
pub receiver_id: Option<i32>,
|
||||
pub user_application_id: Option<i64>,
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ pub struct MasterGroup {
|
||||
pub created_at: DateTime<Local>,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub updated_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
// pub description: String,
|
||||
// pub original_language: String,
|
||||
// pub country_from: String,
|
||||
|
||||
@@ -16,7 +16,7 @@ pub enum NotificationReason {
|
||||
pub struct Notification {
|
||||
pub id: i64,
|
||||
pub created_at: DateTime<Local>,
|
||||
pub receiver_id: i64,
|
||||
pub receiver_id: i32,
|
||||
pub reason: NotificationReason,
|
||||
pub message: Option<String>,
|
||||
pub read_status: bool,
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct Series {
|
||||
pub created_at: DateTime<Local>,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub updated_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub covers: Vec<String>,
|
||||
pub banners: Option<Vec<String>>,
|
||||
pub tags: Vec<String>,
|
||||
@@ -47,7 +47,7 @@ pub struct SeriesSearchResult {
|
||||
pub name: String,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub covers: Vec<String>,
|
||||
pub banners: Option<Vec<String>>,
|
||||
pub tags: Vec<String>,
|
||||
|
||||
@@ -9,7 +9,7 @@ pub struct StaffPm {
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub subject: String,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub resolved: bool,
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ pub struct StaffPmMessage {
|
||||
pub staff_pm_id: i64,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ pub struct TitleGroup {
|
||||
pub created_at: DateTime<Utc>,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub updated_at: DateTime<Utc>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub description: String,
|
||||
pub platform: Option<Platform>,
|
||||
pub original_language: Option<Language>,
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct TitleGroupComment {
|
||||
pub created_at: DateTime<Local>,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub updated_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub title_group_id: i64,
|
||||
pub refers_to_torrent_id: Option<i64>,
|
||||
pub answers_to_comment_id: Option<i64>,
|
||||
@@ -35,7 +35,7 @@ pub struct TitleGroupCommentHierarchy {
|
||||
pub created_at: DateTime<Local>,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub updated_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub title_group_id: i64,
|
||||
pub refers_to_torrent_id: Option<i64>,
|
||||
pub answers_to_comment_id: Option<i64>,
|
||||
|
||||
@@ -318,7 +318,7 @@ pub struct Torrent {
|
||||
pub created_at: DateTime<Local>,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub updated_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub extras: Vec<Extras>,
|
||||
pub release_name: Option<String>,
|
||||
pub release_group: Option<String>,
|
||||
@@ -431,8 +431,8 @@ pub struct TorrentSearchTitleGroup {
|
||||
pub struct TorrentSearchTorrent {
|
||||
pub reported: Option<bool>,
|
||||
pub staff_checked: Option<bool>,
|
||||
pub created_by_id: Option<i64>,
|
||||
pub snatched_by_id: Option<i64>,
|
||||
pub created_by_id: Option<i32>,
|
||||
pub snatched_by_id: Option<i32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, ToSchema, Display)]
|
||||
@@ -532,7 +532,7 @@ pub struct TorrentHierarchy {
|
||||
pub created_at: DateTime<Local>,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub updated_at: DateTime<Local>,
|
||||
pub created_by_id: Option<i64>,
|
||||
pub created_by_id: Option<i32>,
|
||||
pub created_by: Option<UserLite>,
|
||||
pub extras: Vec<Extras>,
|
||||
pub release_name: Option<String>,
|
||||
|
||||
@@ -7,7 +7,7 @@ use utoipa::ToSchema;
|
||||
pub struct TorrentActivity {
|
||||
pub id: i64,
|
||||
pub torrent_id: i64,
|
||||
pub user_id: i64,
|
||||
pub user_id: i32,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub snatched_at: DateTime<Local>,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
|
||||
@@ -8,7 +8,7 @@ pub struct TorrentReport {
|
||||
pub id: i64,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub reported_at: DateTime<Local>,
|
||||
pub reported_by_id: i64,
|
||||
pub reported_by_id: i32,
|
||||
pub reported_torrent_id: i64,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ pub struct TorrentRequest {
|
||||
pub created_at: DateTime<Utc>,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub updated_at: DateTime<Utc>,
|
||||
pub created_by_id: i64,
|
||||
pub filled_by_user_id: Option<i64>,
|
||||
pub created_by_id: i32,
|
||||
pub filled_by_user_id: Option<i32>,
|
||||
pub filled_by_torrent_id: Option<i64>,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub filled_at: Option<DateTime<Utc>>,
|
||||
|
||||
@@ -11,7 +11,7 @@ pub struct TorrentRequestVote {
|
||||
pub torrent_request_id: i64,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub bounty_upload: i64,
|
||||
pub bounty_bonus_points: i64,
|
||||
}
|
||||
@@ -29,7 +29,7 @@ pub struct TorrentRequestVoteHierarchy {
|
||||
pub torrent_request_id: i64,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Local>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub created_by: UserLite,
|
||||
pub bounty_upload: i64,
|
||||
pub bounty_bonus_points: i64,
|
||||
|
||||
@@ -40,7 +40,7 @@ use super::title_group::TitleGroupHierarchyLite;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, FromRow, ToSchema)]
|
||||
pub struct User {
|
||||
pub id: i64,
|
||||
pub id: i32,
|
||||
pub username: String,
|
||||
pub avatar: Option<String>,
|
||||
pub email: String,
|
||||
@@ -80,8 +80,7 @@ pub struct User {
|
||||
pub warned: bool,
|
||||
pub banned: bool,
|
||||
pub staff_note: String,
|
||||
pub passkey_upper: i64,
|
||||
pub passkey_lower: i64,
|
||||
pub passkey: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::Type, ToSchema, PartialEq, Eq)]
|
||||
@@ -116,7 +115,7 @@ pub struct LoginResponse {
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Claims {
|
||||
pub sub: i64,
|
||||
pub sub: i32,
|
||||
pub exp: i64,
|
||||
pub iat: i64,
|
||||
pub class: UserClass,
|
||||
@@ -136,7 +135,7 @@ pub struct EditedUser {
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, FromRow, ToSchema)]
|
||||
pub struct PublicUser {
|
||||
pub id: i64,
|
||||
pub id: i32,
|
||||
pub username: String,
|
||||
pub avatar: Option<String>,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
@@ -173,7 +172,7 @@ pub struct PublicUser {
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, FromRow, ToSchema, Decode)]
|
||||
pub struct UserLite {
|
||||
pub id: i64,
|
||||
pub id: i32,
|
||||
pub username: String,
|
||||
pub warned: bool,
|
||||
pub banned: bool,
|
||||
@@ -181,7 +180,7 @@ pub struct UserLite {
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, FromRow, ToSchema)]
|
||||
pub struct UserLiteAvatar {
|
||||
pub id: i64,
|
||||
pub id: i32,
|
||||
pub username: String,
|
||||
pub banned: bool,
|
||||
pub avatar: Option<String>,
|
||||
@@ -207,19 +206,19 @@ pub struct PublicProfile {
|
||||
#[derive(Debug, Serialize, Deserialize, ToSchema, FromRow)]
|
||||
pub struct UserWarning {
|
||||
pub id: i64,
|
||||
pub user_id: i64,
|
||||
pub user_id: i32,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Utc>,
|
||||
#[schema(value_type = Option<String>, format = DateTime)]
|
||||
pub expires_at: Option<DateTime<Utc>>,
|
||||
pub reason: String,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
pub ban: bool, // wether or not this warning bans the user
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct UserCreatedUserWarning {
|
||||
pub user_id: i64,
|
||||
pub user_id: i32,
|
||||
#[schema(value_type = Option<String>, format = DateTime)]
|
||||
pub expires_at: Option<DateTime<Utc>>,
|
||||
pub reason: String,
|
||||
@@ -234,7 +233,7 @@ pub struct APIKey {
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
pub user_id: i64,
|
||||
pub user_id: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, FromRow, ToSchema)]
|
||||
@@ -244,7 +243,6 @@ pub struct UserCreatedAPIKey {
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct UserMinimal {
|
||||
pub id: i64,
|
||||
pub passkey_upper: i64,
|
||||
pub passkey_lower: i64,
|
||||
pub id: i32,
|
||||
pub passkey: String,
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@ pub struct WikiArticle {
|
||||
pub title: String,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub created_by_id: i64,
|
||||
pub created_by_id: i32,
|
||||
#[schema(value_type = String, format = DateTime)]
|
||||
pub updated_at: DateTime<Utc>,
|
||||
pub updated_by_id: i64,
|
||||
pub updated_by_id: i32,
|
||||
pub body: String,
|
||||
}
|
||||
|
||||
|
||||
@@ -6,23 +6,18 @@ use crate::connection_pool::ConnectionPool;
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
pub struct UserCompact {
|
||||
pub id: i64,
|
||||
pub id: i32,
|
||||
}
|
||||
|
||||
impl ConnectionPool {
|
||||
pub async fn find_user_with_passkey(
|
||||
&self,
|
||||
passkey_upper: i64,
|
||||
passkey_lower: i64,
|
||||
) -> Result<UserCompact, Error> {
|
||||
pub async fn find_user_with_passkey(&self, passkey: &str) -> Result<UserCompact, Error> {
|
||||
sqlx::query_as!(
|
||||
UserCompact,
|
||||
r#"
|
||||
SELECT id FROM users
|
||||
WHERE (passkey_upper, passkey_lower) = ($1, $2)
|
||||
WHERE passkey = $1
|
||||
"#,
|
||||
passkey_upper,
|
||||
passkey_lower
|
||||
passkey
|
||||
)
|
||||
.fetch_one(self.borrow())
|
||||
.await
|
||||
@@ -52,7 +47,7 @@ impl ConnectionPool {
|
||||
downloaded: i64,
|
||||
real_uploaded: i64,
|
||||
real_downloaded: i64,
|
||||
user_id: i64,
|
||||
user_id: i32,
|
||||
) -> Result<PgQueryResult, Error> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
@@ -76,7 +71,7 @@ impl ConnectionPool {
|
||||
|
||||
pub async fn update_total_seedtime(
|
||||
&self,
|
||||
user_id: i64,
|
||||
user_id: i32,
|
||||
torrent_id: i64,
|
||||
announce_interval: u32,
|
||||
grace_period: u32,
|
||||
|
||||
@@ -14,7 +14,7 @@ impl ConnectionPool {
|
||||
pub async fn create_artists(
|
||||
&self,
|
||||
artists: &Vec<UserCreatedArtist>,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<Vec<Artist>> {
|
||||
let mut tx = <ConnectionPool as Borrow<PgPool>>::borrow(self)
|
||||
.begin()
|
||||
@@ -53,7 +53,7 @@ impl ConnectionPool {
|
||||
pub async fn create_artists_affiliation(
|
||||
&self,
|
||||
artists: &Vec<UserCreatedAffiliatedArtist>,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<Vec<AffiliatedArtistHierarchy>> {
|
||||
let values: Vec<String> = (0..artists.len())
|
||||
.map(|i| {
|
||||
|
||||
@@ -37,12 +37,14 @@ impl ConnectionPool {
|
||||
invitation: &Invitation,
|
||||
open_signups: &bool,
|
||||
) -> Result<User> {
|
||||
let mut rng = rand::rng();
|
||||
let rng = rand::rng();
|
||||
|
||||
let passkey = rng.random::<u128>();
|
||||
|
||||
let passkey_upper = (passkey >> 64) as i64;
|
||||
let passkey_lower = passkey as i64;
|
||||
// TODO: check if the passkey already exists
|
||||
let passkey: String = rng
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(33)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
|
||||
// Check username availability first
|
||||
if self.does_username_exist(&user.username).await? {
|
||||
@@ -55,8 +57,8 @@ impl ConnectionPool {
|
||||
let registered_user = sqlx::query_as_unchecked!(
|
||||
User,
|
||||
r#"
|
||||
INSERT INTO users (username, email, password_hash, registered_from_ip, settings, passkey_upper, passkey_lower)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
INSERT INTO users (username, email, password_hash, registered_from_ip, settings, passkey)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING *
|
||||
"#,
|
||||
&user.username,
|
||||
@@ -64,8 +66,7 @@ impl ConnectionPool {
|
||||
password_hash,
|
||||
from_ip,
|
||||
settings,
|
||||
passkey_upper,
|
||||
passkey_lower,
|
||||
passkey
|
||||
)
|
||||
.fetch_one(self.borrow())
|
||||
.await
|
||||
@@ -127,7 +128,7 @@ impl ConnectionPool {
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub async fn find_user_with_id(&self, id: i64) -> Result<User> {
|
||||
pub async fn find_user_with_id(&self, id: i32) -> Result<User> {
|
||||
sqlx::query_as_unchecked!(
|
||||
User,
|
||||
r#"
|
||||
@@ -144,7 +145,7 @@ impl ConnectionPool {
|
||||
pub async fn create_api_key(
|
||||
&self,
|
||||
created_api_key: &UserCreatedAPIKey,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<APIKey> {
|
||||
let mut tx = <ConnectionPool as Borrow<PgPool>>::borrow(self)
|
||||
.begin()
|
||||
|
||||
@@ -14,7 +14,7 @@ impl ConnectionPool {
|
||||
pub async fn create_collage(
|
||||
&self,
|
||||
collage: &UserCreatedCollage,
|
||||
user_id: i64,
|
||||
user_id: i32,
|
||||
) -> Result<Collage> {
|
||||
let created_collage = sqlx::query_as!(
|
||||
Collage,
|
||||
@@ -42,7 +42,7 @@ impl ConnectionPool {
|
||||
pub async fn create_collage_entries(
|
||||
&self,
|
||||
collage_entries: &[UserCreatedCollageEntry],
|
||||
user_id: i64,
|
||||
user_id: i32,
|
||||
) -> Result<Vec<CollageEntry>> {
|
||||
let mut created_entries = Vec::with_capacity(collage_entries.len());
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ impl ConnectionPool {
|
||||
pub async fn create_conversation(
|
||||
&self,
|
||||
conversation: &mut UserCreatedConversation,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<Conversation> {
|
||||
//TODO: make transactional
|
||||
let created_conversation = sqlx::query_as!(
|
||||
@@ -40,7 +40,7 @@ impl ConnectionPool {
|
||||
pub async fn create_conversation_message(
|
||||
&self,
|
||||
message: &UserCreatedConversationMessage,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<ConversationMessage> {
|
||||
let message = sqlx::query_as!(
|
||||
ConversationMessage,
|
||||
@@ -60,7 +60,7 @@ impl ConnectionPool {
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
pub async fn find_user_conversations(&self, user_id: i64) -> Result<Value> {
|
||||
pub async fn find_user_conversations(&self, user_id: i32) -> Result<Value> {
|
||||
let conversations = sqlx::query!(
|
||||
r#"
|
||||
SELECT
|
||||
@@ -127,7 +127,7 @@ impl ConnectionPool {
|
||||
pub async fn find_conversation(
|
||||
&self,
|
||||
conversation_id: i64,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
update_last_seen_at: bool,
|
||||
) -> Result<Value> {
|
||||
let conversation_with_messages = sqlx::query!(
|
||||
@@ -215,7 +215,7 @@ impl ConnectionPool {
|
||||
Ok(conversation_with_messages.conversation_details.unwrap())
|
||||
}
|
||||
|
||||
pub async fn find_unread_conversations_amount(&self, user_id: i64) -> Result<u32> {
|
||||
pub async fn find_unread_conversations_amount(&self, user_id: i32) -> Result<u32> {
|
||||
let amount = sqlx::query_scalar!(
|
||||
r#"
|
||||
SELECT
|
||||
|
||||
@@ -9,10 +9,10 @@ impl ConnectionPool {
|
||||
pub async fn create_edition_group(
|
||||
&self,
|
||||
edition_group_form: &UserCreatedEditionGroup,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<EditionGroup> {
|
||||
const CREATE_EDITION_GROUPS_QUERY: &str = r#"
|
||||
INSERT INTO edition_groups (title_group_id, name, release_date, created_by_id, description, distributor, covers, external_links, source, additional_information)
|
||||
INSERT INTO edition_groups (title_group_id, name, release_date, created_by_id, description, distributor, covers, external_links, source, additional_information)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9::source_enum, $10)
|
||||
RETURNING *;
|
||||
"#;
|
||||
|
||||
@@ -14,7 +14,7 @@ impl ConnectionPool {
|
||||
pub async fn create_forum_post(
|
||||
&self,
|
||||
forum_post: &UserCreatedForumPost,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<ForumPost> {
|
||||
let mut tx = <ConnectionPool as Borrow<PgPool>>::borrow(self)
|
||||
.begin()
|
||||
@@ -67,7 +67,7 @@ impl ConnectionPool {
|
||||
pub async fn create_forum_thread(
|
||||
&self,
|
||||
forum_thread: &mut UserCreatedForumThread,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<ForumThread> {
|
||||
let mut tx = <ConnectionPool as Borrow<PgPool>>::borrow(self)
|
||||
.begin()
|
||||
|
||||
@@ -7,7 +7,7 @@ use sqlx::{PgPool, Postgres, Transaction};
|
||||
use std::borrow::Borrow;
|
||||
|
||||
impl ConnectionPool {
|
||||
pub async fn create_gift(&self, gift: &UserCreatedGift, current_user_id: i64) -> Result<Gift> {
|
||||
pub async fn create_gift(&self, gift: &UserCreatedGift, current_user_id: i32) -> Result<Gift> {
|
||||
let mut tx = <ConnectionPool as Borrow<PgPool>>::borrow(self)
|
||||
.begin()
|
||||
.await?;
|
||||
@@ -44,7 +44,7 @@ impl ConnectionPool {
|
||||
|
||||
pub async fn decrement_bonus_points_and_freeleech_tokens(
|
||||
tx: &mut Transaction<'_, Postgres>,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
bonus_points: i64,
|
||||
freeleech_tokens: i32,
|
||||
) -> Result<()> {
|
||||
|
||||
@@ -14,7 +14,7 @@ impl ConnectionPool {
|
||||
pub async fn create_invitation(
|
||||
&self,
|
||||
invitation: &SentInvitation,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<Invitation> {
|
||||
// TODO: retry if invitation_key already exists
|
||||
let invitation_key: String = Alphanumeric.sample_string(&mut rng(), 50);
|
||||
@@ -85,7 +85,7 @@ impl ConnectionPool {
|
||||
|
||||
pub async fn decrement_invitations_available(
|
||||
tx: &mut Transaction<'_, Postgres>,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<()> {
|
||||
sqlx::query!(
|
||||
r#"
|
||||
|
||||
@@ -9,7 +9,7 @@ impl ConnectionPool {
|
||||
pub async fn create_master_group(
|
||||
&self,
|
||||
master_group_form: &UserCreatedMasterGroup,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<MasterGroup> {
|
||||
let created_master_group = sqlx::query_as!(
|
||||
MasterGroup,
|
||||
|
||||
@@ -20,6 +20,7 @@ pub mod torrent_report_repository;
|
||||
pub mod torrent_repository;
|
||||
pub mod torrent_request_repository;
|
||||
pub mod torrent_request_vote_repository;
|
||||
pub mod tracker_repository;
|
||||
pub mod user_application_repository;
|
||||
pub mod user_repository;
|
||||
pub mod wiki_repository;
|
||||
|
||||
@@ -79,11 +79,11 @@ impl ConnectionPool {
|
||||
|
||||
pub async fn find_unread_notifications_amount(
|
||||
&self,
|
||||
user_id: i64,
|
||||
user_id: i32,
|
||||
) -> Result<HashMap<NotificationReason, i64>> {
|
||||
let rows = sqlx::query!(
|
||||
r#"
|
||||
SELECT reason as "reason: NotificationReason",
|
||||
SELECT reason as "reason: NotificationReason",
|
||||
COUNT(*) as "count!"
|
||||
FROM notifications
|
||||
WHERE receiver_id = $1 AND read_status = FALSE
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::borrow::Borrow;
|
||||
use crate::models;
|
||||
|
||||
impl ConnectionPool {
|
||||
pub async fn get_user_peers(&self, user_id: i64) -> Vec<models::peer::Peer> {
|
||||
pub async fn get_user_peers(&self, user_id: i32) -> Vec<models::peer::Peer> {
|
||||
sqlx::query_as!(
|
||||
models::peer::Peer,
|
||||
r#"
|
||||
@@ -59,7 +59,7 @@ impl ConnectionPool {
|
||||
&self,
|
||||
torrent_id: &i64,
|
||||
ip: &IpNetwork,
|
||||
user_id: &i64,
|
||||
user_id: &i32,
|
||||
ann: &Announce,
|
||||
user_agent: Option<&str>,
|
||||
) -> (i64, i64) {
|
||||
@@ -115,7 +115,7 @@ impl ConnectionPool {
|
||||
.unwrap_or((0, 0))
|
||||
}
|
||||
|
||||
pub async fn find_torrent_peers(&self, torrent_id: &i64, user_id: &i64) -> Vec<Peer> {
|
||||
pub async fn find_torrent_peers(&self, torrent_id: &i64, user_id: &i32) -> Vec<Peer> {
|
||||
let peers = sqlx::query!(
|
||||
r#"
|
||||
SELECT peers.ip AS ip, peers.port AS port
|
||||
|
||||
@@ -10,7 +10,7 @@ use sqlx::{query_as_unchecked, query_scalar};
|
||||
use std::borrow::Borrow;
|
||||
|
||||
impl ConnectionPool {
|
||||
pub async fn create_series(&self, series: &UserCreatedSeries, user_id: i64) -> Result<Series> {
|
||||
pub async fn create_series(&self, series: &UserCreatedSeries, user_id: i32) -> Result<Series> {
|
||||
let created_series = sqlx::query_as!(
|
||||
Series,
|
||||
r#"
|
||||
|
||||
@@ -10,7 +10,7 @@ impl ConnectionPool {
|
||||
pub async fn create_staff_pm(
|
||||
&self,
|
||||
conversation: &mut UserCreatedStaffPm,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<StaffPm> {
|
||||
let created_conversation = sqlx::query_as!(
|
||||
StaffPm,
|
||||
@@ -36,7 +36,7 @@ impl ConnectionPool {
|
||||
pub async fn create_staff_pm_message(
|
||||
&self,
|
||||
message: &UserCreatedStaffPmMessage,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<StaffPmMessage> {
|
||||
let message = sqlx::query_as!(
|
||||
StaffPmMessage,
|
||||
@@ -59,7 +59,7 @@ impl ConnectionPool {
|
||||
pub async fn resolve_staff_pm(
|
||||
&self,
|
||||
staff_pm_id: i64,
|
||||
_current_user_id: i64,
|
||||
_current_user_id: i32,
|
||||
) -> Result<StaffPm> {
|
||||
let updated = sqlx::query_as!(
|
||||
StaffPm,
|
||||
@@ -78,7 +78,7 @@ impl ConnectionPool {
|
||||
Ok(updated)
|
||||
}
|
||||
|
||||
pub async fn list_staff_pms(&self, current_user_id: i64, is_staff: bool) -> Result<Value> {
|
||||
pub async fn list_staff_pms(&self, current_user_id: i32, is_staff: bool) -> Result<Value> {
|
||||
let row = sqlx::query_unchecked!(
|
||||
r#"
|
||||
SELECT
|
||||
@@ -134,7 +134,7 @@ impl ConnectionPool {
|
||||
pub async fn get_staff_pm(
|
||||
&self,
|
||||
staff_pm_id: i64,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
is_staff: bool,
|
||||
) -> Result<Value> {
|
||||
let row = sqlx::query_unchecked!(
|
||||
|
||||
@@ -7,7 +7,7 @@ impl ConnectionPool {
|
||||
&self,
|
||||
item_id: i64,
|
||||
item: &str, // TODO: should only be one of the existing columns of the table
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<()> {
|
||||
sqlx::query(&format!(
|
||||
"
|
||||
@@ -28,7 +28,7 @@ impl ConnectionPool {
|
||||
&self,
|
||||
item_id: i64,
|
||||
item: &str,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<()> {
|
||||
let _ = sqlx::query(&format!(
|
||||
"
|
||||
|
||||
@@ -9,7 +9,7 @@ impl ConnectionPool {
|
||||
pub async fn create_title_group_comment(
|
||||
&self,
|
||||
title_group_comment: &UserCreatedTitleGroupComment,
|
||||
user_id: i64,
|
||||
user_id: i32,
|
||||
) -> Result<TitleGroupComment> {
|
||||
let created_title_group_comment = sqlx::query_as!(
|
||||
TitleGroupComment,
|
||||
|
||||
@@ -25,7 +25,7 @@ impl ConnectionPool {
|
||||
&self,
|
||||
title_group_form: &UserCreatedTitleGroup,
|
||||
public_ratings: &Vec<PublicRating>,
|
||||
user_id: i64,
|
||||
user_id: i32,
|
||||
) -> Result<TitleGroup> {
|
||||
let create_title_group_query = r#"
|
||||
INSERT INTO title_groups (master_group_id,name,name_aliases,created_by_id,description,original_language,country_from,covers,external_links,embedded_links,category,content_type,original_release_date,tags,tagline,platform,screenshots,public_ratings)
|
||||
@@ -65,7 +65,7 @@ impl ConnectionPool {
|
||||
pub async fn find_title_group_hierarchy(
|
||||
&self,
|
||||
title_group_id: i64,
|
||||
user_id: i64,
|
||||
user_id: i32,
|
||||
) -> Result<Value> {
|
||||
let title_group = sqlx::query!(r#"WITH torrent_data AS (
|
||||
SELECT
|
||||
|
||||
@@ -9,7 +9,7 @@ impl ConnectionPool {
|
||||
pub async fn report_torrent(
|
||||
&self,
|
||||
form: &UserCreatedTorrentReport,
|
||||
user_id: i64,
|
||||
user_id: i32,
|
||||
) -> Result<TorrentReport> {
|
||||
let torrent_report = sqlx::query_as!(
|
||||
TorrentReport,
|
||||
|
||||
@@ -34,7 +34,7 @@ impl ConnectionPool {
|
||||
pub async fn create_torrent(
|
||||
&self,
|
||||
torrent_form: &UploadedTorrent,
|
||||
user_id: i64,
|
||||
user_id: i32,
|
||||
) -> Result<Torrent> {
|
||||
let mut tx = <ConnectionPool as Borrow<PgPool>>::borrow(self)
|
||||
.begin()
|
||||
@@ -317,7 +317,7 @@ impl ConnectionPool {
|
||||
|
||||
pub async fn get_torrent(
|
||||
&self,
|
||||
user_id: i64,
|
||||
user_id: i32,
|
||||
torrent_id: i64,
|
||||
tracker_name: &str,
|
||||
frontend_url: &str,
|
||||
@@ -346,7 +346,7 @@ impl ConnectionPool {
|
||||
let info = Info::from_bytes(torrent.info_dict).map_err(|_| Error::TorrentFileInvalid)?;
|
||||
|
||||
let user = self.find_user_with_id(user_id).await?;
|
||||
let announce_url = get_announce_url(user.passkey_upper, user.passkey_lower, tracker_url);
|
||||
let announce_url = get_announce_url(user.passkey, tracker_url);
|
||||
|
||||
let frontend_url = format!("{frontend_url}torrent/{torrent_id}");
|
||||
|
||||
@@ -384,7 +384,7 @@ impl ConnectionPool {
|
||||
pub async fn search_torrents(
|
||||
&self,
|
||||
torrent_search: &TorrentSearch,
|
||||
requesting_user_id: Option<i64>,
|
||||
requesting_user_id: Option<i32>,
|
||||
) -> Result<Value> {
|
||||
let input = torrent_search.title_group.name.trim();
|
||||
|
||||
@@ -468,7 +468,7 @@ impl ConnectionPool {
|
||||
pub async fn remove_torrent(
|
||||
&self,
|
||||
torrent_to_delete: &TorrentToDelete,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<()> {
|
||||
let mut tx = <ConnectionPool as Borrow<PgPool>>::borrow(self)
|
||||
.begin()
|
||||
|
||||
@@ -11,7 +11,7 @@ impl ConnectionPool {
|
||||
pub async fn create_torrent_request(
|
||||
&self,
|
||||
torrent_request: &mut UserCreatedTorrentRequest,
|
||||
user_id: i64,
|
||||
user_id: i32,
|
||||
) -> Result<TorrentRequest> {
|
||||
//TODO: make those requests transactional
|
||||
let create_torrent_request_query = r#"
|
||||
@@ -60,7 +60,7 @@ impl ConnectionPool {
|
||||
&self,
|
||||
torrent_id: i64,
|
||||
torrent_request_id: i64,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<()> {
|
||||
let is_torrent_in_requested_title_group = sqlx::query_scalar!(
|
||||
r#"
|
||||
@@ -125,7 +125,7 @@ impl ConnectionPool {
|
||||
let upload_share = (bounty_summary.total_upload as f32 / 2.0).round() as i32;
|
||||
let bonus_share = (bounty_summary.total_bonus as f32 / 2.0).round() as i32;
|
||||
|
||||
let torrent_uploader_id: i64 = query_scalar!(
|
||||
let torrent_uploader_id: i32 = query_scalar!(
|
||||
r#"
|
||||
SELECT created_by_id FROM torrents WHERE id = $1
|
||||
"#,
|
||||
|
||||
@@ -9,7 +9,7 @@ impl ConnectionPool {
|
||||
pub async fn create_torrent_request_vote(
|
||||
&self,
|
||||
torrent_request_vote: &UserCreatedTorrentRequestVote,
|
||||
user_id: i64,
|
||||
user_id: i32,
|
||||
) -> Result<TorrentRequestVote> {
|
||||
let current_user = self.find_user_with_id(user_id).await?;
|
||||
if current_user.bonus_points - torrent_request_vote.bounty_bonus_points < 0 {
|
||||
|
||||
45
backend/storage/src/repositories/tracker_repository.rs
Normal file
45
backend/storage/src/repositories/tracker_repository.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use crate::connection_pool::ConnectionPool;
|
||||
use arcadia_common::error::Result;
|
||||
use arcadia_shared::tracker::models::user::{Passkey, User};
|
||||
use std::borrow::Borrow;
|
||||
|
||||
// This file contains functions for Arcadia's tracker
|
||||
// but not necessarily related to the tracker itself directly
|
||||
|
||||
impl ConnectionPool {
|
||||
pub async fn find_users(&self) -> Result<Vec<User>> {
|
||||
// TODO: fix this
|
||||
// query_as!() doesn't work as it requires the FromString trait
|
||||
// which is implemented, but somehow still throws an error
|
||||
let rows = sqlx::query!(
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
passkey,
|
||||
TRUE AS "can_download!",
|
||||
0::int4 AS "num_seeding!",
|
||||
0::int4 AS "num_leeching!"
|
||||
FROM users
|
||||
"#
|
||||
)
|
||||
.fetch_all(self.borrow())
|
||||
.await
|
||||
.expect("could not get users");
|
||||
|
||||
let users = rows
|
||||
.into_iter()
|
||||
.map(|r| User {
|
||||
id: r.id as u32,
|
||||
passkey: r
|
||||
.passkey
|
||||
.parse::<Passkey>()
|
||||
.expect("invalid passkey in database"),
|
||||
can_download: r.can_download,
|
||||
num_seeding: r.num_seeding as u32,
|
||||
num_leeching: r.num_leeching as u32,
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(users)
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ use sqlx::PgPool;
|
||||
use std::borrow::Borrow;
|
||||
|
||||
impl ConnectionPool {
|
||||
pub async fn find_user_profile(&self, id: &i64) -> Result<PublicUser> {
|
||||
pub async fn find_user_profile(&self, id: &i32) -> Result<PublicUser> {
|
||||
sqlx::query_as!(
|
||||
PublicUser,
|
||||
r#"
|
||||
@@ -55,7 +55,7 @@ impl ConnectionPool {
|
||||
.map_err(|_| Error::UserWithIdNotFound(*id))
|
||||
}
|
||||
|
||||
pub async fn update_last_seen(&self, id: i64) -> Result<()> {
|
||||
pub async fn update_last_seen(&self, id: i32) -> Result<()> {
|
||||
let _ = sqlx::query!(
|
||||
r#"
|
||||
UPDATE users
|
||||
@@ -70,7 +70,7 @@ impl ConnectionPool {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_user(&self, user_id: i64, edited_user: &EditedUser) -> Result<()> {
|
||||
pub async fn update_user(&self, user_id: i32, edited_user: &EditedUser) -> Result<()> {
|
||||
let _ = sqlx::query!(
|
||||
r#"
|
||||
UPDATE users
|
||||
@@ -90,7 +90,7 @@ impl ConnectionPool {
|
||||
|
||||
pub async fn create_user_warning(
|
||||
&self,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
user_warning: &UserCreatedUserWarning,
|
||||
) -> Result<UserWarning> {
|
||||
let mut tx = <ConnectionPool as Borrow<PgPool>>::borrow(self)
|
||||
@@ -135,7 +135,7 @@ impl ConnectionPool {
|
||||
Ok(user_warning)
|
||||
}
|
||||
|
||||
pub async fn find_user_warnings(&self, user_id: i64) -> Vec<UserWarning> {
|
||||
pub async fn find_user_warnings(&self, user_id: i32) -> Vec<UserWarning> {
|
||||
sqlx::query_as!(
|
||||
UserWarning,
|
||||
r#"
|
||||
@@ -149,7 +149,7 @@ impl ConnectionPool {
|
||||
.expect("failed to get user warnings")
|
||||
}
|
||||
|
||||
pub async fn is_user_banned(&self, user_id: i64) -> Result<bool> {
|
||||
pub async fn is_user_banned(&self, user_id: i32) -> Result<bool> {
|
||||
let result = sqlx::query_scalar!("SELECT banned FROM users WHERE id = $1", user_id)
|
||||
.fetch_optional(self.borrow())
|
||||
.await?;
|
||||
@@ -165,7 +165,7 @@ impl ConnectionPool {
|
||||
let users = sqlx::query_as!(
|
||||
UserMinimal,
|
||||
r#"
|
||||
SELECT id, passkey_upper, passkey_lower FROM users
|
||||
SELECT id, passkey FROM users
|
||||
"#
|
||||
)
|
||||
.fetch_all(self.borrow())
|
||||
|
||||
@@ -10,7 +10,7 @@ impl ConnectionPool {
|
||||
pub async fn create_wiki_article(
|
||||
&self,
|
||||
article: &UserCreatedWikiArticle,
|
||||
current_user_id: i64,
|
||||
current_user_id: i32,
|
||||
) -> Result<WikiArticle> {
|
||||
let created_article = sqlx::query_as!(
|
||||
WikiArticle,
|
||||
|
||||
@@ -20,8 +20,7 @@ const initialState: User = {
|
||||
invited: 0,
|
||||
last_seen: '',
|
||||
leeching: 0,
|
||||
passkey_lower: 0,
|
||||
passkey_upper: 0,
|
||||
passkey: 'aaaaaaaaaaaa',
|
||||
password_hash: '',
|
||||
ratio: 0.0,
|
||||
real_downloaded: 0,
|
||||
|
||||
10
shared/Cargo.toml
Normal file
10
shared/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "arcadia-shared"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
indexmap = { version = "2.11.0", default-features = false, features = ["std", "serde"] }
|
||||
anyhow = { version = "1.0.99", default-features = true, features = ["std"] }
|
||||
sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-native-tls", "postgres", "chrono", "ipnetwork" ] }
|
||||
1
shared/src/lib.rs
Normal file
1
shared/src/lib.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod tracker;
|
||||
1
shared/src/tracker/mod.rs
Normal file
1
shared/src/tracker/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod models;
|
||||
1
shared/src/tracker/models/mod.rs
Normal file
1
shared/src/tracker/models/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod user;
|
||||
63
shared/src/tracker/models/user.rs
Normal file
63
shared/src/tracker/models/user.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
use anyhow::bail;
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
use sqlx::{Database, Decode};
|
||||
use std::{fmt::Display, str::FromStr};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq)]
|
||||
pub struct Passkey(pub [u8; 32]);
|
||||
|
||||
#[derive(Clone, Deserialize, Serialize)]
|
||||
pub struct User {
|
||||
pub id: u32,
|
||||
pub passkey: Passkey,
|
||||
pub can_download: bool,
|
||||
pub num_seeding: u32,
|
||||
pub num_leeching: u32,
|
||||
}
|
||||
|
||||
impl FromStr for Passkey {
|
||||
type Err = anyhow::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut bytes = s.bytes();
|
||||
|
||||
if bytes.len() != 32 {
|
||||
bail!("Invalid passkey length.");
|
||||
}
|
||||
|
||||
let array = [(); 32].map(|_| bytes.next().unwrap());
|
||||
|
||||
Ok(Passkey(array))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, DB: Database> Decode<'r, DB> for Passkey
|
||||
where
|
||||
&'r str: Decode<'r, DB>,
|
||||
{
|
||||
fn decode(
|
||||
value: <DB as Database>::ValueRef<'r>,
|
||||
) -> Result<Passkey, Box<dyn std::error::Error + 'static + Send + Sync>> {
|
||||
let value = <&str as Decode<DB>>::decode(value)?;
|
||||
let mut bytes = value.bytes();
|
||||
|
||||
let array = [(); 32].map(|_| bytes.next().expect("Invalid passkey length."));
|
||||
|
||||
Ok(Passkey(array))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Passkey {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&String::from_utf8_lossy(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Passkey {
|
||||
fn serialize<S>(&self, serializer: S) -> std::prelude::v1::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
@@ -15,3 +15,11 @@ env_logger = "0.11.8"
|
||||
thiserror = "2.0.12"
|
||||
actix-web-httpauth = "0.8.2"
|
||||
futures = "0.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
strum = "0.27"
|
||||
log = "0.4"
|
||||
serde_bencode = "0.2.4"
|
||||
indexmap = { version = "2.11.0", default-features = false, features = ["std", "serde"] }
|
||||
anyhow = { version = "1.0.99", default-features = true, features = ["std"] }
|
||||
arcadia-shared = { path = "../../shared" }
|
||||
parking_lot = "0.12.4"
|
||||
|
||||
43
tracker/arcadia_tracker/src/announce/error.rs
Normal file
43
tracker/arcadia_tracker/src/announce/error.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::announce::HttpResponseBuilderExt;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, AnnounceError>;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum AnnounceError {
|
||||
#[error("invalid passkey")]
|
||||
InvalidPassKey,
|
||||
|
||||
#[error("invalid info_hash")]
|
||||
InvalidInfoHash,
|
||||
|
||||
#[error("invalid user id")]
|
||||
InvalidUserId,
|
||||
|
||||
#[error("invalid user id or torrent id")]
|
||||
InvalidUserIdOrTorrentId,
|
||||
|
||||
#[error("torrent client not in whitelist")]
|
||||
TorrentClientNotInWhitelist,
|
||||
}
|
||||
|
||||
impl actix_web::ResponseError for AnnounceError {
|
||||
#[inline]
|
||||
fn status_code(&self) -> actix_web::http::StatusCode {
|
||||
actix_web::http::StatusCode::BAD_REQUEST
|
||||
}
|
||||
|
||||
fn error_response(&self) -> actix_web::HttpResponse {
|
||||
log::error!("The request generated this error: {self}");
|
||||
#[derive(Debug, Serialize)]
|
||||
struct WrappedError {
|
||||
#[serde(rename = "failure reason")]
|
||||
failure_reason: String,
|
||||
}
|
||||
|
||||
actix_web::HttpResponse::build(self.status_code()).bencode(WrappedError {
|
||||
failure_reason: self.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
21
tracker/arcadia_tracker/src/announce/handlers/announce.rs
Normal file
21
tracker/arcadia_tracker/src/announce/handlers/announce.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
use actix_web::{
|
||||
dev,
|
||||
web::{Data, Path},
|
||||
FromRequest, HttpRequest, HttpResponse, ResponseError,
|
||||
};
|
||||
|
||||
use crate::announce::error::{AnnounceError, Result};
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
operation_id = "Announce",
|
||||
tag = "Announce",
|
||||
path = "/{passkey}/announce",
|
||||
responses(
|
||||
(status = 200, description = "Announce"),
|
||||
)
|
||||
)]
|
||||
pub async fn exec(arc: Data<Arcadia>, passkey: Path<String>) -> Result<HttpResponse> {
|
||||
let passkey = u128::from_str_radix(&passkey, 16).map_err(|_| AnnounceError::InvalidPassKey)?;
|
||||
Ok(HttpResponse::Ok())
|
||||
}
|
||||
1
tracker/arcadia_tracker/src/announce/handlers/mod.rs
Normal file
1
tracker/arcadia_tracker/src/announce/handlers/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod announce;
|
||||
19
tracker/arcadia_tracker/src/announce/mod.rs
Normal file
19
tracker/arcadia_tracker/src/announce/mod.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use actix_web::{HttpResponse, HttpResponseBuilder};
|
||||
use serde::Serialize;
|
||||
|
||||
pub mod error;
|
||||
pub mod handlers;
|
||||
pub mod models;
|
||||
|
||||
pub trait HttpResponseBuilderExt {
|
||||
fn bencode(&mut self, val: impl Serialize) -> HttpResponse;
|
||||
}
|
||||
|
||||
impl HttpResponseBuilderExt for HttpResponseBuilder {
|
||||
fn bencode(&mut self, val: impl Serialize) -> HttpResponse {
|
||||
match serde_bencode::to_bytes(&val) {
|
||||
Ok(data) => self.body(data),
|
||||
Err(_) => HttpResponse::BadRequest().body("Failed to bencode"),
|
||||
}
|
||||
}
|
||||
}
|
||||
1
tracker/arcadia_tracker/src/announce/models/mod.rs
Normal file
1
tracker/arcadia_tracker/src/announce/models/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod torrent;
|
||||
34
tracker/arcadia_tracker/src/announce/models/torrent.rs
Normal file
34
tracker/arcadia_tracker/src/announce/models/torrent.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use serde::Deserialize;
|
||||
use strum::{Display, EnumString};
|
||||
|
||||
#[derive(Clone, Copy, Deserialize, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||
pub struct InfoHash(pub [u8; 20]);
|
||||
|
||||
#[derive(Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)]
|
||||
pub struct PeerId(pub [u8; 20]);
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Display, EnumString)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum Event {
|
||||
#[strum(to_string = "completed")]
|
||||
Completed,
|
||||
#[strum(to_string = "")]
|
||||
Empty,
|
||||
#[strum(to_string = "started")]
|
||||
Started,
|
||||
#[strum(to_string = "stopped")]
|
||||
Stopped,
|
||||
}
|
||||
|
||||
pub struct Announce {
|
||||
info_hash: InfoHash,
|
||||
peer_id: PeerId,
|
||||
port: u16,
|
||||
uploaded: u64,
|
||||
downloaded: u64,
|
||||
left: u64,
|
||||
event: Event,
|
||||
numwant: usize,
|
||||
corrupt: Option<u64>,
|
||||
key: Option<String>,
|
||||
}
|
||||
1
tracker/arcadia_tracker/src/common/mod.rs
Normal file
1
tracker/arcadia_tracker/src/common/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod models;
|
||||
1
tracker/arcadia_tracker/src/common/models/mod.rs
Normal file
1
tracker/arcadia_tracker/src/common/models/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod user;
|
||||
7
tracker/arcadia_tracker/src/common/models/user.rs
Normal file
7
tracker/arcadia_tracker/src/common/models/user.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use indexmap::IndexMap;
|
||||
use serde::Serialize;
|
||||
|
||||
pub use arcadia_shared::tracker::models::user::{Passkey, User};
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct Map(IndexMap<u32, User>);
|
||||
@@ -1,13 +1,19 @@
|
||||
use crate::env::Env;
|
||||
use std::ops::Deref;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use crate::env::Env;
|
||||
use std::{io::Write, ops::Deref};
|
||||
|
||||
pub mod announce;
|
||||
pub mod api_doc;
|
||||
pub mod common;
|
||||
pub mod env;
|
||||
pub mod middleware;
|
||||
pub mod routes;
|
||||
|
||||
pub struct Tracker {
|
||||
env: Env,
|
||||
|
||||
pub users: RwLock<common::models::user::Map>,
|
||||
}
|
||||
|
||||
impl Deref for Tracker {
|
||||
@@ -20,6 +26,14 @@ impl Deref for Tracker {
|
||||
|
||||
impl Tracker {
|
||||
pub fn new(env: Env) -> Self {
|
||||
Self { env }
|
||||
print!("Getting users...");
|
||||
std::io::stdout().flush().unwrap();
|
||||
let users = .await?;
|
||||
println!("[Finished] Records: {:?}", users.len());
|
||||
|
||||
Self {
|
||||
env,
|
||||
users: RwLock::new(common::models::user::Map::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +1,35 @@
|
||||
use actix_web::{dev::ServiceRequest, error::ErrorUnauthorized, web::Data};
|
||||
use actix_web::{Error, FromRequest, HttpRequest};
|
||||
use futures::future::{ready, Ready};
|
||||
// use actix_web::{dev::ServiceRequest, error::ErrorUnauthorized, web::Data};
|
||||
// use actix_web::{Error, FromRequest, HttpRequest};
|
||||
// use futures::future::{ready, Ready};
|
||||
|
||||
pub struct Passkey(pub String);
|
||||
// pub struct Passkey(pub String);
|
||||
|
||||
impl FromRequest for Passkey {
|
||||
type Error = Error;
|
||||
type Future = Ready<Result<Self, Self::Error>>;
|
||||
// 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
|
||||
.headers()
|
||||
.get("api-key")
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.map(|s| s.to_owned());
|
||||
// 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",
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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_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)
|
||||
// // }
|
||||
// }
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use actix_web::web::{self, scope};
|
||||
use actix_web_httpauth::middleware::HttpAuthentication;
|
||||
// use actix_web_httpauth::middleware::HttpAuthentication;
|
||||
|
||||
use crate::middleware::authenticate_user;
|
||||
// use crate::middleware::authenticate_user;
|
||||
|
||||
pub fn init(cfg: &mut web::ServiceConfig) {
|
||||
// cfg.service(
|
||||
// web::scope("/{passkey}").wrap(HttpAuthentication::with_fn(authenticate_user(req, passkey))),
|
||||
// );
|
||||
cfg.service(
|
||||
web::scope("/{passkey}"), //.wrap(HttpAuthentication::with_fn(authenticate_user(req, passkey))),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user