diff --git a/Cargo.toml b/Cargo.toml index c89e9050..0d4a4573 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,5 @@ rand = "0.9.0" bip_metainfo = "0.12.0" serde_bytes = "0.11" actix-multipart = "0.7.2" -utoipa = "5" +# serde_postgres = "0.2.0" +# utoipa = "5" diff --git a/migrations/20250312215600_initdb.sql b/migrations/20250312215600_initdb.sql index bfa5f927..d649011e 100644 --- a/migrations/20250312215600_initdb.sql +++ b/migrations/20250312215600_initdb.sql @@ -46,21 +46,22 @@ CREATE TABLE artists ( name VARCHAR(255) NOT NULL, description TEXT NOT NULL, pictures TEXT [], - created_by INT, + created_by_id INT, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), title_groups_amount INT NOT NULL DEFAULT 0, edition_groups_amount INT NOT NULL DEFAULT 0, torrents_amount INT NOT NULL DEFAULT 0, seeders_amount INT NOT NULL DEFAULT 0, leechers_amount INT NOT NULL DEFAULT 0, snatches_amount INT NOT NULL DEFAULT 0, - FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE + FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE similar_artists ( - artist_1 INT NOT NULL, - artist_2 INT NOT NULL, - PRIMARY KEY (artist_1, artist_2), - FOREIGN KEY (artist_1) REFERENCES artists(id) ON DELETE CASCADE, - FOREIGN KEY (artist_2) REFERENCES artists(id) ON DELETE CASCADE + artist_1_id INT NOT NULL, + artist_2_id INT NOT NULL, + PRIMARY KEY (artist_1_id, artist_2_id), + FOREIGN KEY (artist_1_id) REFERENCES artists(id) ON DELETE CASCADE, + FOREIGN KEY (artist_2_id) REFERENCES artists(id) ON DELETE CASCADE ); CREATE TABLE master_groups ( id SERIAL PRIMARY KEY, @@ -68,7 +69,7 @@ CREATE TABLE master_groups ( -- name_aliases VARCHAR(255)[], created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW(), - created_by INT NOT NULL, + created_by_id INT NOT NULL, -- description TEXT NOT NULL, -- original_language VARCHAR(50) NOT NULL, -- country_from VARCHAR(50) NOT NULL, @@ -77,15 +78,15 @@ CREATE TABLE master_groups ( -- covers TEXT[], -- banners TEXT[], -- fan_arts TEXT[], - FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE + FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE SET NULL ); CREATE TABLE similar_master_groups ( - group_1 INT NOT NULL, - group_2 INT NOT NULL, - PRIMARY KEY (group_1, group_2), - FOREIGN KEY (group_1) REFERENCES master_groups(id) ON DELETE CASCADE, - FOREIGN KEY (group_2) REFERENCES master_groups(id) ON DELETE CASCADE + group_1_id INT NOT NULL, + group_2_id INT NOT NULL, + PRIMARY KEY (group_1_id, group_2_id), + FOREIGN KEY (group_1_id) REFERENCES master_groups(id) ON DELETE CASCADE, + FOREIGN KEY (group_2_id) REFERENCES master_groups(id) ON DELETE CASCADE ); CREATE TABLE series ( id SERIAL PRIMARY KEY, @@ -95,14 +96,14 @@ CREATE TABLE series ( covers TEXT [], fanarts TEXT [], banners TEXT [], - created_by INT, - FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE + created_by_id INT, + FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TYPE content_type_enum AS ENUM ( 'Movie', 'TV-Show', 'Music', - 'Game', + 'Software', 'Book', 'SiteRip' ); @@ -131,12 +132,12 @@ CREATE TYPE category_enum AS ENUM ( ); CREATE TABLE title_groups ( id SERIAL PRIMARY KEY, - master_group INT, + master_group_id INT, name TEXT NOT NULL, name_aliases TEXT [], created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW(), - created_by INT NOT NULL, + created_by_id INT NOT NULL, description TEXT NOT NULL, original_language TEXT NOT NULL, original_release_date TIMESTAMP NOT NULL, @@ -149,31 +150,31 @@ CREATE TABLE title_groups ( category category_enum, content_type content_type_enum NOT NULL, public_ratings JSONB, - serie INT, - FOREIGN KEY (master_group) REFERENCES master_groups(id) ON DELETE + serie_id INT, + FOREIGN KEY (master_group_id) REFERENCES master_groups(id) ON DELETE SET NULL, - FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE + FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE SET NULL, - FOREIGN KEY (serie) REFERENCES series(id) ON DELETE + FOREIGN KEY (serie_id) REFERENCES series(id) ON DELETE SET NULL ); CREATE TABLE similar_title_groups ( - group_1 INT NOT NULL, - group_2 INT NOT NULL, - PRIMARY KEY (group_1, group_2), - FOREIGN KEY (group_1) REFERENCES title_groups(id) ON DELETE CASCADE, - FOREIGN KEY (group_2) REFERENCES title_groups(id) ON DELETE CASCADE + group_1_id INT NOT NULL, + group_2_id INT NOT NULL, + PRIMARY KEY (group_1_id, group_2_id), + FOREIGN KEY (group_1_id) REFERENCES title_groups(id) ON DELETE CASCADE, + FOREIGN KEY (group_2_id) REFERENCES title_groups(id) ON DELETE CASCADE ); CREATE TABLE affiliated_artists ( - title_group INT NOT NULL, - artist INT NOT NULL, + title_group_id INT NOT NULL, + artist_id INT NOT NULL, status TEXT NOT NULL, nickname TEXT, - created_by INT NOT NULL, + created_by_id INT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW(), - FOREIGN KEY (title_group) REFERENCES title_groups(id) ON DELETE CASCADE, - FOREIGN KEY (artist) REFERENCES artists(id) ON DELETE CASCADE, - FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE + FOREIGN KEY (title_group_id) REFERENCES title_groups(id) ON DELETE CASCADE, + FOREIGN KEY (artist_id) REFERENCES artists(id) ON DELETE CASCADE, + FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE SET NULL ); -- for web: if it is a DL or a RIP should be specified at the torrent level @@ -199,20 +200,20 @@ CREATE TYPE source_enum AS ENUM ( ); CREATE TABLE edition_groups ( id SERIAL PRIMARY KEY, - title_group INT NOT NULL, + title_group_id INT NOT NULL, name TEXT NOT NULL, release_date TIMESTAMP NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW(), - created_by INT NOT NULL, + created_by_id INT NOT NULL, description TEXT, distributor VARCHAR(255), covers TEXT [] NOT NULL, external_links TEXT [] NOT NULL, language TEXT, source source_enum NOT NULL, - FOREIGN KEY (title_group) REFERENCES title_groups(id) ON DELETE CASCADE, - FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE + FOREIGN KEY (title_group_id) REFERENCES title_groups(id) ON DELETE CASCADE, + FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE SET NULL ); CREATE TYPE audio_codec_enum AS ENUM ( @@ -259,10 +260,10 @@ CREATE TYPE video_codec_enum AS ENUM( CREATE TYPE features_enum AS ENUM('HDR', 'DV', 'Commentary', 'Remux', '3D'); CREATE TABLE torrents ( id SERIAL PRIMARY KEY, - edition_group INT NOT NULL, + edition_group_id INT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW(), - created_by INT NOT NULL, + created_by_id INT NOT NULL, release_name VARCHAR(500), -- maybe change the size release_group VARCHAR(30) NOT NULL, @@ -276,8 +277,8 @@ CREATE TABLE torrents ( staff_checked BOOLEAN NOT NULL DEFAULT FALSE, size BIGINT NOT NULL, -- in bytes - FOREIGN KEY (edition_group) REFERENCES edition_groups(id) ON DELETE CASCADE, - FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE + FOREIGN KEY (edition_group_id) REFERENCES edition_groups(id) ON DELETE CASCADE, + FOREIGN KEY (created_by_id) REFERENCES users(id) ON DELETE SET NULL, -- audio duration INT NOT NULL, diff --git a/src/handlers/title_group_handler.rs b/src/handlers/title_group_handler.rs index 038c4160..92b439f5 100644 --- a/src/handlers/title_group_handler.rs +++ b/src/handlers/title_group_handler.rs @@ -1,9 +1,11 @@ +use std::collections::HashMap; + use actix_web::{HttpResponse, web}; use sqlx::PgPool; use crate::{ models::{title_group::UserCreatedTitleGroup, user::User}, - repositories::title_group_repository::create_title_group, + repositories::title_group_repository::{create_title_group, find_title_group}, }; pub async fn add_title_group( @@ -18,3 +20,16 @@ pub async fn add_title_group( })), } } + +pub async fn get_title_group( + pool: web::Data, + query: web::Query>, +) -> HttpResponse { + let title_group_id = query.get("id").expect("id not found in query"); + match find_title_group(&pool, title_group_id.parse::().unwrap()).await { + Ok(title_group) => HttpResponse::Ok().json(title_group), + Err(err) => HttpResponse::InternalServerError().json(serde_json::json!({ + "error": err.to_string() + })), + } +} diff --git a/src/models/artist.rs b/src/models/artist.rs index aced6cdb..7da896e8 100644 --- a/src/models/artist.rs +++ b/src/models/artist.rs @@ -1,3 +1,4 @@ +use chrono::NaiveDateTime; use serde::{Deserialize, Serialize}; use sqlx::prelude::FromRow; @@ -5,6 +6,8 @@ use sqlx::prelude::FromRow; pub struct Artist { pub id: i32, pub name: String, + pub created_at: NaiveDateTime, + pub created_by_id: i32, pub description: String, pub pictures: Option>, pub title_groups_amount: i32, @@ -15,7 +18,7 @@ pub struct Artist { pub snatches_amount: i32, } -#[derive(Debug, Deserialize, FromRow)] +#[derive(Debug, Deserialize, Serialize, FromRow)] pub struct SimilarArtists { pub artist_1: i32, pub artist_2: i32, diff --git a/src/models/edition_group.rs b/src/models/edition_group.rs index 0437c833..141360ff 100644 --- a/src/models/edition_group.rs +++ b/src/models/edition_group.rs @@ -35,12 +35,12 @@ pub enum Source { #[derive(Debug, Serialize, Deserialize, FromRow)] pub struct EditionGroup { pub id: i32, - pub title_group: i32, + pub title_group_id: i32, pub name: String, // edition name, not title name pub release_date: NaiveDateTime, // public release pub created_at: NaiveDateTime, // database entry creation pub updated_at: NaiveDateTime, - pub created_by: i32, + pub created_by_id: i32, pub description: Option, // specific to the edition pub distributor: Option, // web: [web stores/distributors], physical: [shop if specific edition ?] pub covers: Vec, diff --git a/src/models/master_group.rs b/src/models/master_group.rs index c750ab0f..d0301f8c 100644 --- a/src/models/master_group.rs +++ b/src/models/master_group.rs @@ -22,7 +22,7 @@ pub struct MasterGroup { // pub name_aliases: Vec, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, - pub created_by: i32, + pub created_by_id: i32, // pub description: String, // pub original_language: String, // pub country_from: String, diff --git a/src/models/serie.rs b/src/models/serie.rs index b8d9c0a2..a09bf208 100644 --- a/src/models/serie.rs +++ b/src/models/serie.rs @@ -9,7 +9,7 @@ pub struct Serie { pub description: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, - pub created_by: i32, + pub created_by_id: i32, pub covers: Vec, pub fannarts: Vec, pub banners: Vec, diff --git a/src/models/title_group.rs b/src/models/title_group.rs index 534d95e6..b69c4e38 100644 --- a/src/models/title_group.rs +++ b/src/models/title_group.rs @@ -10,7 +10,7 @@ pub enum ContentType { #[sqlx(rename = "TV-Show")] TVShow, Music, - Game, + Software, Book, SiteRip, } @@ -54,12 +54,12 @@ pub enum Category { #[derive(Debug, Serialize, Deserialize, FromRow)] pub struct TitleGroup { pub id: i32, - pub master_group: Option, // only if master groups are needed for this type of content + pub master_group_id: Option, // only if master groups are needed for this type of content pub name: String, pub name_aliases: Vec, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, - pub created_by: i32, + pub created_by_id: i32, pub description: String, pub original_language: String, pub original_release_date: NaiveDateTime, @@ -71,11 +71,12 @@ pub struct TitleGroup { // pub main_artists // pub artists_affiliated (multiple categories, multiple in each category) (composer, remixer, actors, developers, etc.) // pub entities_affiliated (multiple categories, mutliple in each category) (publisher, record label, franchise, etc.) - pub category: i32, // ((movie: feature film, short film), (music: ep, album, compilation)) + pub category: Category, // ((movie: feature film, short film), (music: ep, album, compilation)) pub content_type: ContentType, // movies, tv shows, books, games, etc pub tags: Vec, pub public_ratings: Option>, // {service: rating} - pub serie: Option, + pub serie_id: Option, + // pub edition_groups: Option>, } #[derive(Debug, Serialize, Deserialize)] @@ -114,7 +115,7 @@ pub struct UserCreatedTitleGroup { pub embedded_links: Option>, // pub artists_affiliated: //(multiple categories, multiple in each category) (composer, remixer, actors, developers, etc.) // pub entities_affiliated (multiple categories, mutliple in each category) (publisher, record label, franchise, etc.) - pub category: i32, // ((movie: feature film, short film), (music: ep, album, compilation)) + pub category: Category, // ((movie: feature film, short film), (music: ep, album, compilation)) pub content_type: ContentType, // movies, tv shows, books, games, etc pub tags: Vec, pub tagline: Option, diff --git a/src/models/torrent.rs b/src/models/torrent.rs index 87e4500e..3a533f5c 100644 --- a/src/models/torrent.rs +++ b/src/models/torrent.rs @@ -106,10 +106,10 @@ impl FromStr for Features { #[derive(Debug, Serialize, FromRow)] pub struct Torrent { pub id: i32, - pub edition_group: i32, + pub edition_group_id: i32, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, - pub created_by: i32, + pub created_by_id: i32, pub release_name: Option, pub release_group: String, pub description: Option, // specific to the torrent diff --git a/src/repositories/artist_repository.rs b/src/repositories/artist_repository.rs index 98fabbc6..686a6506 100644 --- a/src/repositories/artist_repository.rs +++ b/src/repositories/artist_repository.rs @@ -13,7 +13,7 @@ pub async fn create_artist( current_user: &User, ) -> Result> { let create_artist_query = r#" - INSERT INTO artists (name, description, pictures, created_by) + INSERT INTO artists (name, description, pictures, created_by_id) VALUES ($1, $2, $3, $4) RETURNING *; "#; diff --git a/src/repositories/edition_group_repository.rs b/src/repositories/edition_group_repository.rs index cea546b3..91a6e2ee 100644 --- a/src/repositories/edition_group_repository.rs +++ b/src/repositories/edition_group_repository.rs @@ -12,7 +12,7 @@ pub async fn create_edition_group( current_user: &User, ) -> Result> { let create_title_group_query = r#" - INSERT INTO edition_groups (title_group, name, release_date, created_by, description, distributor, covers, external_links, language, source) + INSERT INTO edition_groups (title_group_id, name, release_date, created_by_id, description, distributor, covers, external_links, language, source) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10::source_enum) RETURNING *; "#; diff --git a/src/repositories/master_group_repository.rs b/src/repositories/master_group_repository.rs index 748986e9..6310c2ba 100644 --- a/src/repositories/master_group_repository.rs +++ b/src/repositories/master_group_repository.rs @@ -12,7 +12,7 @@ pub async fn create_master_group( current_user: &User, ) -> Result> { let create_master_group_query = r#" - INSERT INTO master_groups (name,created_by) + INSERT INTO master_groups (name,created_by_id) VALUES ($1, $2) RETURNING *; "#; diff --git a/src/repositories/title_group_repository.rs b/src/repositories/title_group_repository.rs index 4b5d3cef..64f4071e 100644 --- a/src/repositories/title_group_repository.rs +++ b/src/repositories/title_group_repository.rs @@ -1,13 +1,11 @@ -use std::error::Error; - -use actix_web::web; - -use sqlx::PgPool; - use crate::models::{ title_group::{TitleGroup, UserCreatedTitleGroup}, user::User, }; +use actix_web::web; +use serde_json::Value; +use sqlx::{PgPool, postgres::PgRow, types::JsonRawValue}; +use std::error::Error; pub async fn create_title_group( pool: &web::Data, @@ -15,8 +13,8 @@ pub async fn create_title_group( current_user: &User, ) -> Result> { let create_title_group_query = r#" - INSERT INTO title_groups (master_group,name,name_aliases,created_by,description,original_language,country_from,covers,external_links,embedded_links,category,content_type,original_release_date,tags,tagline) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12::content_type_enum, $13, $14, $15) + 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) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11::category_enum, $12::content_type_enum, $13, $14, $15) RETURNING *; "#; @@ -52,3 +50,50 @@ pub async fn create_title_group( } } } + +pub async fn find_title_group( + pool: &web::Data, + title_group_id: i32, +) -> Result> { + let title_group = sqlx::query!(r#"WITH torrent_data AS ( + SELECT + t.edition_group_id, + jsonb_agg( + to_jsonb(t) || jsonb_build_object('uploader', jsonb_build_object('id', u.id, 'username', u.username)) + ) AS torrents + FROM torrents t + LEFT JOIN users u ON u.id = t.created_by_id + GROUP BY t.edition_group_id +), +edition_data AS ( + SELECT + eg.title_group_id, + jsonb_agg( + to_jsonb(eg) || jsonb_build_object('torrents', COALESCE(td.torrents, '[]'::jsonb)) + ) AS edition_groups + FROM edition_groups eg + LEFT JOIN torrent_data td ON td.edition_group_id = eg.id + GROUP BY eg.title_group_id +) +SELECT jsonb_build_object( + 'title_group', to_jsonb(tg), + 'edition_groups', COALESCE(ed.edition_groups, '[]'::jsonb) +) +FROM title_groups tg +LEFT JOIN edition_data ed ON ed.title_group_id = tg.id +WHERE tg.id = $1"#, title_group_id) + .fetch_one(pool.get_ref()) + .await; + + match title_group { + Ok(_) => Ok(title_group.unwrap().jsonb_build_object.unwrap()), + Err(e) => { + println!("{:#?}", e); + match e { + sqlx::Error::Database(db_error) => db_error.message().to_string(), + _ => e.to_string(), + }; + Err(format!("could not find title group").into()) + } + } +} diff --git a/src/repositories/torrent_repository.rs b/src/repositories/torrent_repository.rs index c8a34a00..b2a88774 100644 --- a/src/repositories/torrent_repository.rs +++ b/src/repositories/torrent_repository.rs @@ -16,7 +16,7 @@ pub async fn create_torrent( ) -> Result> { let create_torrent_query = r#" INSERT INTO torrents ( - edition_group, created_by, release_name, + edition_group_id, created_by_id, release_name, release_group, description, file_amount_per_type, uploaded_as_anonymous, file_list, mediainfo, trumpable, staff_checked, size, duration, audio_codec, audio_bitrate, audio_bitrate_sampling, diff --git a/src/routes/routes.rs b/src/routes/routes.rs index 9a080562..4c6c8d4c 100644 --- a/src/routes/routes.rs +++ b/src/routes/routes.rs @@ -6,7 +6,7 @@ use crate::handlers::{ edition_group_handler::add_edition_group, invitation_handler::send_invitation, master_group_handler::add_master_group, - title_group_handler::add_title_group, + title_group_handler::{add_title_group, get_title_group}, torrent_handler::upload_torrent, }; @@ -21,6 +21,7 @@ pub fn init(cfg: &mut web::ServiceConfig) { // .route("/torrent", web::post().to(upload_torrent)) .route("/master-group", web::post().to(add_master_group)) .route("/title-group", web::post().to(add_title_group)) + .route("/title-group", web::get().to(get_title_group)) .route("/edition-group", web::post().to(add_edition_group)) .route("/torrent", web::post().to(upload_torrent)) .route("/artist", web::post().to(add_artist))