Technical review queue (#4775)

* chore: fix typo in status message

* feat(labrinth): overhaul malware scanner report storage and routes

* chore: address some review comments

* feat: add Delphi to Docker Compose `with-delphi` profile

* chore: fix unused import Clippy lint

* feat(labrinth/delphi): use PAT token authorization with project read scopes

* chore: expose file IDs in version queries

* fix: accept null decompiled source payloads from Delphi

* tweak(labrinth): expose base62 file IDs more consistently for Delphi

* feat(labrinth/delphi): support new Delphi report severity field

* chore(labrinth): run `cargo sqlx prepare` to fix Docker build errors

* tweak: add route for fetching Delphi issue type schema, abstract Labrinth away from issue types

* chore: run `cargo sqlx prepare`

* chore: fix typo on frontend generated state file message

* feat: update to use new Delphi issue schema

* wip: tech review endpoints

* wip: add ToSchema for dependent types

* wip: report issues return

* wip

* wip: returning more data

* wip

* Fix up db query

* Delphi configuration to talk to Labrinth

* Get Delphi working with Labrinth

* Add Delphi dummy fixture

* Better Delphi logging

* Improve utoipa for tech review routes

* Add more sorting options for tech review queue

* Oops join

* New routes for fetching issues and reports

* Fix which kind of ID is returned in tech review endpoints

* Deduplicate tech review report rows

* Reduce info sent for projects

* Fetch more thread info

* Address PR comments

* fix ci

* fix postgres version mismatch

* fix version creation

* Implement routes

* fix up tech review

* Allow adding a moderation comment to Delphi rejections

* fix up rebase

* exclude rejected projects from tech review

* add status change msg to tech review thread

* cargo sqlx prepare

* also ignore withheld projects

* More filtering on issue search

* wip: report routes

* Fix up for build

* cargo sqlx prepare

* fix thread message privacy

* New tech review search route

* submit route

* details have statuses now

* add default to drid status

* dedup issue details

* fix sqlx query on empty files

* fixes

* Dedupe issue detail statuses and message on entering tech rev

* Fix qa issues

* Fix qa issues

* fix review comments

* typos

* fix ci

* feat: tech review frontend (#4781)

* chore: fix typo in status message

* feat(labrinth): overhaul malware scanner report storage and routes

* chore: address some review comments

* feat: add Delphi to Docker Compose `with-delphi` profile

* chore: fix unused import Clippy lint

* feat(labrinth/delphi): use PAT token authorization with project read scopes

* chore: expose file IDs in version queries

* fix: accept null decompiled source payloads from Delphi

* tweak(labrinth): expose base62 file IDs more consistently for Delphi

* feat(labrinth/delphi): support new Delphi report severity field

* chore(labrinth): run `cargo sqlx prepare` to fix Docker build errors

* tweak: add route for fetching Delphi issue type schema, abstract Labrinth away from issue types

* chore: run `cargo sqlx prepare`

* chore: fix typo on frontend generated state file message

* feat: update to use new Delphi issue schema

* wip: tech review endpoints

* wip: add ToSchema for dependent types

* wip: report issues return

* wip

* wip: returning more data

* wip

* Fix up db query

* Delphi configuration to talk to Labrinth

* Get Delphi working with Labrinth

* Add Delphi dummy fixture

* Better Delphi logging

* Improve utoipa for tech review routes

* Add more sorting options for tech review queue

* Oops join

* New routes for fetching issues and reports

* Fix which kind of ID is returned in tech review endpoints

* Deduplicate tech review report rows

* Reduce info sent for projects

* Fetch more thread info

* Address PR comments

* fix ci

* fix ci

* fix postgres version mismatch

* fix version creation

* Implement routes

* feat: batch scan alert

* feat: layout

* feat: introduce surface variables

* fix: theme selector

* feat: rough draft of tech review card

* feat: tab switcher

* feat: batch scan btn

* feat: api-client module for tech review

* draft: impl

* feat: auto icons

* fix: layout issues

* feat: fixes to code blocks + flag labels

* feat: temp remove mock data

* fix: search sort types

* fix: intl & lint

* chore: re-enable mock data

* fix: flag badges + auto open first issue in file tab

* feat: update for new routes

* fix: more qa issues

* feat: lazy load sources

* fix: re-enable auth middleware

* feat: impl threads

* fix: lint & severity

* feat: download btn + switch to using NavTabs with new local mode option

* feat: re-add toplevel btns

* feat: reports page consistency

* fix: consistency on project queue

* fix: icons + sizing

* fix: colors and gaps

* fix: impl endpoints

* feat: load all flags on file tab

* feat: thread generics changes

* feat: more qa

* feat: fix collapse

* fix: qa

* feat: msg modal

* fix: ISO import

* feat: qa fixes

* fix: empty state basic

* fix: collapsible region

* fix: collapse thread by default

* feat: rough draft of new process/flow

* fix labrinth build

* fix thread message privacy

* New tech review search route

* feat: qa fixes

* feat: QA changes

* fix: verdict on detail not whole issue

* fix: lint + intl

* fix: lint

* fix: thread message for tech rev verdict

* feat: use anim frames

* fix: exports + typecheck

* polish: qa changes

* feat: qa

* feat: qa polish

* feat: fix malic modal

* fix: lint

* fix: qa + lint

* fix: pagination

* fix: lint

* fix: qa

* intl extract

* fix ci

---------

Signed-off-by: Calum H. <contact@cal.engineer>
Co-authored-by: Alejandro González <me@alegon.dev>
Co-authored-by: aecsocket <aecsocket@tutanota.com>

---------

Signed-off-by: Calum H. <contact@cal.engineer>
Co-authored-by: Alejandro González <me@alegon.dev>
Co-authored-by: Calum H. <contact@cal.engineer>
This commit is contained in:
aecsocket
2025-12-20 11:43:04 +00:00
committed by GitHub
parent 1e9e13aebb
commit 39f2b0ecb6
109 changed files with 6281 additions and 2017 deletions

View File

@@ -94,6 +94,32 @@ impl From<crate::models::v3::threads::MessageBody> for LegacyMessageBody {
new_status,
old_status,
},
crate::models::v3::threads::MessageBody::TechReview { verdict } => {
LegacyMessageBody::Text {
body: format!(
"(legacy) Reviewed technical report and gave verdict {verdict:?}"
),
private: true,
replying_to: None,
associated_images: Vec::new(),
}
}
crate::models::v3::threads::MessageBody::TechReviewEntered => {
LegacyMessageBody::Text {
body: "(legacy) Entered technical review".into(),
private: true,
replying_to: None,
associated_images: Vec::new(),
}
}
crate::models::v3::threads::MessageBody::TechReviewExitFileDeleted => {
LegacyMessageBody::Text {
body: "(legacy) Exited technical review because file was deleted".into(),
private: true,
replying_to: None,
associated_images: Vec::new(),
}
}
crate::models::v3::threads::MessageBody::ThreadClosure => {
LegacyMessageBody::ThreadClosure
}

View File

@@ -5,7 +5,7 @@ use crate::database::models::loader_fields::VersionField;
use crate::database::models::project_item::{LinkUrl, ProjectQueryResult};
use crate::database::models::version_item::VersionQueryResult;
use crate::models::ids::{
OrganizationId, ProjectId, TeamId, ThreadId, VersionId,
FileId, OrganizationId, ProjectId, TeamId, ThreadId, VersionId,
};
use ariadne::ids::UserId;
use chrono::{DateTime, Utc};
@@ -731,6 +731,7 @@ impl From<VersionQueryResult> for Version {
.files
.into_iter()
.map(|f| VersionFile {
id: Some(FileId(f.id.0 as u64)),
url: f.url,
filename: f.filename,
hashes: f.hashes,
@@ -855,6 +856,10 @@ impl VersionStatus {
/// A single project file, with a url for the file and the file's hash
#[derive(Serialize, Deserialize, Clone)]
pub struct VersionFile {
/// The ID of the file. Every file has an ID once created, but it
/// is not known until it indeed has been created.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<FileId>,
/// A map of hashes of the file. The key is the hashing algorithm
/// and the value is the string version of the hash.
pub hashes: std::collections::HashMap<String, String>,

View File

@@ -1,3 +1,4 @@
use crate::database::models::delphi_report_item::DelphiVerdict;
use crate::models::ids::{
ImageId, ProjectId, ReportId, ThreadId, ThreadMessageId,
};
@@ -7,7 +8,7 @@ use ariadne::ids::UserId;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct Thread {
pub id: ThreadId,
#[serde(rename = "type")]
@@ -18,7 +19,7 @@ pub struct Thread {
pub members: Vec<User>,
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct ThreadMessage {
pub id: ThreadMessageId,
pub author_id: Option<UserId>,
@@ -27,7 +28,7 @@ pub struct ThreadMessage {
pub hide_identity: bool,
}
#[derive(Serialize, Deserialize, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum MessageBody {
Text {
@@ -42,6 +43,11 @@ pub enum MessageBody {
new_status: ProjectStatus,
old_status: ProjectStatus,
},
TechReview {
verdict: DelphiVerdict,
},
TechReviewEntered,
TechReviewExitFileDeleted,
ThreadClosure,
ThreadReopen,
Deleted {
@@ -50,7 +56,23 @@ pub enum MessageBody {
},
}
#[derive(Serialize, Deserialize, Eq, PartialEq, Copy, Clone)]
impl MessageBody {
pub fn is_private(&self) -> bool {
match self {
Self::Text { private, .. } | Self::Deleted { private } => *private,
Self::TechReview { .. }
| Self::TechReviewEntered
| Self::TechReviewExitFileDeleted => true,
Self::StatusChange { .. }
| Self::ThreadClosure
| Self::ThreadReopen => false,
}
}
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, utoipa::ToSchema,
)]
#[serde(rename_all = "snake_case")]
pub enum ThreadType {
Report,
@@ -100,16 +122,7 @@ impl Thread {
messages: data
.messages
.into_iter()
.filter(|x| {
if let MessageBody::Text { private, .. } = x.body {
!private || user.role.is_mod()
} else if let MessageBody::Deleted { private, .. } = x.body
{
!private || user.role.is_mod()
} else {
true
}
})
.filter(|x| user.role.is_mod() || !x.body.is_private())
.map(|x| ThreadMessage::from(x, user))
.collect(),
members: users,

View File

@@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
pub const DELETED_USER: UserId = UserId(127155982985829);
bitflags::bitflags! {
#[derive(Copy, Clone, Debug)]
#[derive(Debug, Clone, Copy)]
pub struct Badges: u64 {
const MIDAS = 1 << 0;
const EARLY_MODPACK_ADOPTER = 1 << 1;
@@ -21,6 +21,23 @@ bitflags::bitflags! {
}
}
impl utoipa::PartialSchema for Badges {
fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
u64::schema()
}
}
impl utoipa::ToSchema for Badges {
fn schemas(
schemas: &mut Vec<(
String,
utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>,
)>,
) {
u64::schemas(schemas);
}
}
bitflags_serde_impl!(Badges, u64);
impl Default for Badges {
@@ -29,7 +46,7 @@ impl Default for Badges {
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct User {
pub id: UserId,
pub username: String,
@@ -52,7 +69,7 @@ pub struct User {
pub github_id: Option<u64>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct UserPayoutData {
pub paypal_address: Option<String>,
pub paypal_country: Option<String>,
@@ -137,7 +154,9 @@ impl User {
}
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
#[derive(
Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, utoipa::ToSchema,
)]
#[serde(rename_all = "lowercase")]
pub enum Role {
Developer,