Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/labrinth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ path = "src/main.rs"
[dependencies]
actix-cors = { workspace = true }
actix-files = { workspace = true }
actix-http = { workspace = true, optional = true }
actix-http = { workspace = true }
actix-multipart = { workspace = true }
actix-rt = { workspace = true }
actix-web = { workspace = true }
Expand Down Expand Up @@ -149,7 +149,7 @@ tikv-jemallocator = { workspace = true, features = [
] }

[features]
test = ["dep:actix-http"]
test = []

[lints]
workspace = true
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
CREATE TABLE minecraft_server_projects (
id bigint PRIMARY KEY NOT NULL
REFERENCES mods(id)
ON DELETE CASCADE,
max_players int NOT NULL
);

CREATE TABLE minecraft_java_server_projects (
id bigint PRIMARY KEY NOT NULL
REFERENCES mods(id)
ON DELETE CASCADE,
address varchar(255) NOT NULL
);

CREATE TABLE minecraft_bedrock_server_projects (
id bigint PRIMARY KEY NOT NULL
REFERENCES mods(id)
ON DELETE CASCADE,
address varchar(255) NOT NULL
);
41 changes: 37 additions & 4 deletions apps/labrinth/src/database/models/project_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use crate::models::projects::{
MonetizationStatus, ProjectStatus, SideTypesMigrationReviewStatus,
};
use crate::models::v67;
use ariadne::ids::base62_impl::parse_base62;
use chrono::{DateTime, Utc};
use dashmap::{DashMap, DashSet};
Expand Down Expand Up @@ -767,7 +768,7 @@
.await?;

let projects = sqlx::query!(
"
r#"
SELECT m.id id, m.name name, m.summary summary, m.downloads downloads, m.follows follows,
m.icon_url icon_url, m.raw_icon_url raw_icon_url, m.description description, m.published published,
m.approved approved, m.queued, m.status status, m.requested_status requested_status,
Expand All @@ -777,14 +778,28 @@
t.id thread_id, m.monetization_status monetization_status,
m.side_types_migration_review_status side_types_migration_review_status,
ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is false) categories,
ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is true) additional_categories
ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is true) additional_categories,
-- components
COUNT(c1.id) > 0 AS minecraft_server_exists,
MAX(c1.max_players) AS minecraft_server_max_players,
COUNT(c2.id) > 0 AS minecraft_java_server_exists,
MAX(c2.address) AS minecraft_java_server_address,
COUNT(c3.id) > 0 AS minecraft_bedrock_server_exists,
MAX(c3.address) AS minecraft_bedrock_server_address

FROM mods m
INNER JOIN threads t ON t.mod_id = m.id
LEFT JOIN mods_categories mc ON mc.joining_mod_id = m.id
LEFT JOIN categories c ON mc.joining_category_id = c.id

-- components
LEFT JOIN minecraft_server_projects c1 ON c1.id = m.id
LEFT JOIN minecraft_java_server_projects c2 ON c2.id = m.id
LEFT JOIN minecraft_bedrock_server_projects c3 ON c3.id = m.id

WHERE m.id = ANY($1) OR m.slug = ANY($2)
GROUP BY t.id, m.id;
",
GROUP BY t.id, m.id
"#,
&project_ids_parsed,
&slugs,
)
Expand Down Expand Up @@ -858,6 +873,21 @@
urls,
aggregate_version_fields: VersionField::from_query_json(version_fields, &loader_fields, &loader_field_enum_values, true),
thread_id: DBThreadId(m.thread_id),
minecraft_server: if m.minecraft_server_exists.unwrap_or(false) {
Some(v67::minecraft::Server {
max_players: m.minecraft_server_max_players.map(|n| n.cast_unsigned()),

Check failure on line 878 in apps/labrinth/src/database/models/project_item.rs

View workflow job for this annotation

GitHub Actions / Lint and Test

mismatched types
})
} else { None },
minecraft_java_server: if m.minecraft_java_server_exists.unwrap_or(false) {
Some(v67::minecraft::JavaServer {
address: m.minecraft_java_server_address.unwrap(),
})
} else { None },
minecraft_bedrock_server: if m.minecraft_bedrock_server_exists.unwrap_or(false) {
Some(v67::minecraft::BedrockServer {
address: m.minecraft_bedrock_server_address.unwrap(),
})
} else { None },
};

acc.insert(m.id, (m.slug, project));
Expand Down Expand Up @@ -983,4 +1013,7 @@
pub gallery_items: Vec<DBGalleryItem>,
pub thread_id: DBThreadId,
pub aggregate_version_fields: Vec<VersionField>,
pub minecraft_server: Option<v67::minecraft::Server>,
pub minecraft_java_server: Option<v67::minecraft::JavaServer>,
pub minecraft_bedrock_server: Option<v67::minecraft::BedrockServer>,
}
1 change: 1 addition & 0 deletions apps/labrinth/src/models/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod error;
pub mod v2;
pub mod v3;
pub mod v67;

pub use v3::analytics;
pub use v3::billing;
Expand Down
11 changes: 11 additions & 0 deletions apps/labrinth/src/models/v3/projects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::database::models::version_item::VersionQueryResult;
use crate::models::ids::{
FileId, OrganizationId, ProjectId, TeamId, ThreadId, VersionId,
};
use crate::models::v67;
use ariadne::ids::UserId;
use chrono::{DateTime, Utc};
use itertools::Itertools;
Expand Down Expand Up @@ -98,6 +99,13 @@ pub struct Project {
/// Aggregated loader-fields across its myriad of versions
#[serde(flatten)]
pub fields: HashMap<String, Vec<serde_json::Value>>,

#[serde(skip_serializing_if = "Option::is_none")]
pub minecraft_server: Option<v67::minecraft::Server>,
#[serde(skip_serializing_if = "Option::is_none")]
pub minecraft_java_server: Option<v67::minecraft::JavaServer>,
#[serde(skip_serializing_if = "Option::is_none")]
pub minecraft_bedrock_server: Option<v67::minecraft::BedrockServer>,
}

// This is a helper function to convert a list of VersionFields into a HashMap of field name to vecs of values
Expand Down Expand Up @@ -212,6 +220,9 @@ impl From<ProjectQueryResult> for Project {
side_types_migration_review_status: m
.side_types_migration_review_status,
fields,
minecraft_server: data.minecraft_server,
minecraft_java_server: data.minecraft_java_server,
minecraft_bedrock_server: data.minecraft_bedrock_server,
}
}
}
Expand Down
26 changes: 26 additions & 0 deletions apps/labrinth/src/models/v67/base.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use serde::{Deserialize, Serialize};
use validator::Validate;

define! {
#[derive(Debug, Clone, Serialize, Deserialize, Validate, utoipa::ToSchema)]
pub struct Project {
/// Human-readable friendly name of the project.
#[validate(
length(min = 3, max = 64),
custom(function = "crate::util::validate::validate_name")
)]
pub name: String,
/// Slug of the project, used in vanity URLs.
#[validate(
length(min = 3, max = 64),
regex(path = *crate::util::validate::RE_URL_SAFE)
)]
pub slug: String,
/// Short description of the project.
#[validate(length(min = 3, max = 255))]
pub summary: String,
/// A long description of the project, in markdown.
#[validate(length(max = 65536))]
pub description: String,
}
}
210 changes: 210 additions & 0 deletions apps/labrinth/src/models/v67/minecraft.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
use std::sync::LazyLock;

use serde::{Deserialize, Serialize};
use sqlx::{PgTransaction, postgres::PgQueryResult};
use validator::Validate;

use crate::{
database::models::DBProjectId,
models::v67::{
ComponentKindArrayExt, ComponentKindExt, ComponentRelation,
ProjectComponent, ProjectComponentEdit, ProjectComponentKind,
},
};

pub(super) static RELATIONS: LazyLock<Vec<ComponentRelation>> =
LazyLock::new(|| {
use ProjectComponentKind as C;

vec![
[C::MinecraftMod].only(),
[
C::MinecraftServer,
C::MinecraftJavaServer,
C::MinecraftBedrockServer,
]
.only(),
C::MinecraftJavaServer.requires(C::MinecraftServer),
C::MinecraftBedrockServer.requires(C::MinecraftServer),
]
});

define! {
#[derive(Debug, Clone, Serialize, Deserialize, Validate, utoipa::ToSchema)]
pub struct Mod {}

#[derive(Debug, Clone, Serialize, Deserialize, Validate, utoipa::ToSchema)]
pub struct Server {
pub max_players: u32,
}

#[derive(Debug, Clone, Serialize, Deserialize, Validate, utoipa::ToSchema)]
pub struct JavaServer {
#[validate(length(max = 255))]
pub address: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, Validate, utoipa::ToSchema)]
pub struct BedrockServer {
#[validate(length(max = 255))]
pub address: String,
}
}

// impl

impl ProjectComponent for Mod {
fn kind() -> ProjectComponentKind {
ProjectComponentKind::MinecraftMod
}

async fn insert(
&self,
_txn: &mut PgTransaction<'_>,
_project_id: DBProjectId,
) -> Result<(), sqlx::Error> {
unimplemented!();
}
}

impl ProjectComponentEdit for ModEdit {
async fn update(
&self,
_txn: &mut PgTransaction<'_>,
_project_id: DBProjectId,
) -> Result<PgQueryResult, sqlx::Error> {
unimplemented!();
}
}

impl ProjectComponent for Server {
fn kind() -> ProjectComponentKind {
ProjectComponentKind::MinecraftServer
}

async fn insert(
&self,
txn: &mut PgTransaction<'_>,
project_id: DBProjectId,
) -> Result<(), sqlx::Error> {
sqlx::query!(
"
INSERT INTO minecraft_server_projects (id, max_players)
VALUES ($1, $2)
",
project_id as _,
self.max_players.cast_signed(),
)
.execute(&mut **txn)
.await?;
Ok(())
}
}

impl ProjectComponentEdit for ServerEdit {
async fn update(
&self,
txn: &mut PgTransaction<'_>,
project_id: DBProjectId,
) -> Result<PgQueryResult, sqlx::Error> {
sqlx::query!(
"
UPDATE minecraft_server_projects
SET max_players = COALESCE($2, max_players)
WHERE id = $1
",
project_id as _,
self.max_players.map(|n| n.cast_signed()),
)
.execute(&mut **txn)
.await
}
}

impl ProjectComponent for JavaServer {
fn kind() -> ProjectComponentKind {
ProjectComponentKind::MinecraftJavaServer
}

async fn insert(
&self,
txn: &mut PgTransaction<'_>,
project_id: DBProjectId,
) -> Result<(), sqlx::Error> {
sqlx::query!(
"
INSERT INTO minecraft_java_server_projects (id, address)
VALUES ($1, $2)
",
project_id as _,
self.address,
)
.execute(&mut **txn)
.await?;
Ok(())
}
}

impl ProjectComponentEdit for JavaServerEdit {
async fn update(
&self,
txn: &mut PgTransaction<'_>,
project_id: DBProjectId,
) -> Result<PgQueryResult, sqlx::Error> {
sqlx::query!(
"
UPDATE minecraft_java_server_projects
SET address = COALESCE($2, address)
WHERE id = $1
",
project_id as _,
self.address,
)
.execute(&mut **txn)
.await
}
}

impl ProjectComponent for BedrockServer {
fn kind() -> ProjectComponentKind {
ProjectComponentKind::MinecraftBedrockServer
}

async fn insert(
&self,
txn: &mut PgTransaction<'_>,
project_id: DBProjectId,
) -> Result<(), sqlx::Error> {
sqlx::query!(
"
INSERT INTO minecraft_bedrock_server_projects (id, address)
VALUES ($1, $2)
",
project_id as _,
self.address,
)
.execute(&mut **txn)
.await?;
Ok(())
}
}

impl ProjectComponentEdit for BedrockServerEdit {
async fn update(
&self,
txn: &mut PgTransaction<'_>,
project_id: DBProjectId,
) -> Result<PgQueryResult, sqlx::Error> {
sqlx::query!(
"
UPDATE minecraft_bedrock_server_projects
SET address = COALESCE($2, address)
WHERE id = $1
",
project_id as _,
self.address,
)
.execute(&mut **txn)
.await
}
}
Loading
Loading