feat: better api docs (#6586)

* feat: docs

* fix tombi

* chore: fix some of the routes rendering with missing /

* response schemas

* fix: restore labrinth docs routes

* Fix path parameter docs in routes

* remove utoipa-actix-web

* consistency

* improve version intros

* improve formatting, examples

* better hash examples, move openapi stuff to openapi.rs

* more utoipa param fixes

* request body docs

* chore: remove moderation route from v2, remove ingest & webhooks from v3 spec

* fixes

* chore: tweak sources titles

* fix

* fix test

* improve examples

* increase compiler spawned thread stack size

* remove unused tests & script

* test

* bro what

* fix

---------

Co-authored-by: aecsocket <43144841+aecsocket@users.noreply.github.com>
This commit is contained in:
François-Xavier Talbot
2026-07-04 14:19:21 +00:00
committed by GitHub
co-authored by aecsocket
parent b26d048a63
commit 4a6fa9fc3d
103 changed files with 4546 additions and 1387 deletions
+3 -1
View File
@@ -1,10 +1,12 @@
use serde::{Deserialize, Serialize};
/// An error returned by the API
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, utoipa::ToSchema)]
pub struct ApiError<'a> {
#[schema(value_type = String)]
pub error: &'a str,
pub description: String,
#[schema(value_type = Option<serde_json::Value>)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub details: Option<serde_json::Value>,
}
+5 -3
View File
@@ -1,3 +1,5 @@
#![allow(clippy::large_stack_arrays)]
use crate::models::ids::{ThreadMessageId, VersionId};
use crate::models::v3::billing::PriceDuration;
use crate::models::{
@@ -13,7 +15,7 @@ use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, utoipa::ToSchema)]
pub struct LegacyNotification {
pub id: NotificationId,
pub user_id: UserId,
@@ -30,14 +32,14 @@ pub struct LegacyNotification {
pub actions: Vec<LegacyNotificationAction>,
}
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, utoipa::ToSchema)]
pub struct LegacyNotificationAction {
pub title: String,
/// The route to call when this notification action is called. Formatted HTTP Method, route
pub action_route: (String, String),
}
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, utoipa::ToSchema)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum LegacyNotificationBody {
TaxNotification {
+2 -2
View File
@@ -4,7 +4,7 @@ use ariadne::ids::UserId;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, utoipa::ToSchema)]
pub struct LegacyReport {
pub id: ReportId,
pub report_type: String,
@@ -17,7 +17,7 @@ pub struct LegacyReport {
pub thread_id: ThreadId,
}
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, utoipa::ToSchema)]
#[serde(rename_all = "kebab-case")]
pub enum LegacyItemType {
Project,
+2 -2
View File
@@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use crate::{routes::v2_reroute, search::ResultSearchProject};
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, utoipa::ToSchema)]
pub struct LegacySearchResults {
pub hits: Vec<LegacyResultSearchProject>,
pub offset: usize,
@@ -11,7 +11,7 @@ pub struct LegacySearchResults {
pub total_hits: usize,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone, utoipa::ToSchema)]
pub struct LegacyResultSearchProject {
pub project_id: String,
pub project_type: String,
+1 -1
View File
@@ -8,7 +8,7 @@ use crate::models::{
};
/// A member of a team
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, utoipa::ToSchema)]
pub struct LegacyTeamMember {
pub role: String,
// is_owner removed, and role hardcoded to Owner if true,
+6 -4
View File
@@ -7,7 +7,7 @@ use ariadne::ids::UserId;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, utoipa::ToSchema)]
pub struct LegacyThread {
pub id: ThreadId,
#[serde(rename = "type")]
@@ -18,7 +18,7 @@ pub struct LegacyThread {
pub members: Vec<User>,
}
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, utoipa::ToSchema)]
pub struct LegacyThreadMessage {
pub id: ThreadMessageId,
pub author_id: Option<UserId>,
@@ -26,7 +26,7 @@ pub struct LegacyThreadMessage {
pub created: DateTime<Utc>,
}
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, utoipa::ToSchema)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum LegacyMessageBody {
Text {
@@ -49,7 +49,9 @@ pub enum LegacyMessageBody {
},
}
#[derive(Serialize, Deserialize, Eq, PartialEq, Copy, Clone)]
#[derive(
Serialize, Deserialize, Eq, PartialEq, Copy, Clone, utoipa::ToSchema,
)]
#[serde(rename_all = "snake_case")]
pub enum LegacyThreadType {
Report,
+1 -1
View File
@@ -6,7 +6,7 @@ use ariadne::ids::UserId;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug)]
#[derive(Serialize, Deserialize, Clone, Debug, utoipa::ToSchema)]
pub struct LegacyUser {
pub id: UserId,
pub username: String,
+11 -1
View File
@@ -75,7 +75,17 @@ impl Price {
}
}
#[derive(Serialize, Deserialize, Hash, Eq, PartialEq, Debug, Copy, Clone)]
#[derive(
Serialize,
Deserialize,
Hash,
Eq,
PartialEq,
Debug,
Copy,
Clone,
utoipa::ToSchema,
)]
#[serde(rename_all = "kebab-case")]
pub enum PriceDuration {
FiveDays,
@@ -28,7 +28,7 @@ impl From<crate::database::models::DBModerationNote> for ModerationNote {
}
}
#[derive(Deserialize)]
#[derive(Deserialize, utoipa::ToSchema)]
pub struct PatchModerationNote {
pub notes: Option<String>,
pub user_rating: Option<i32>,
+1 -1
View File
@@ -706,7 +706,7 @@ impl From<DBNotification> for Notification {
}
}
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, utoipa::ToSchema)]
pub struct NotificationAction {
pub name: String,
/// The route to call when this notification action is called. Formatted HTTP Method, route
+4 -4
View File
@@ -10,14 +10,14 @@ use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
#[derive(Deserialize, Serialize)]
#[derive(Deserialize, Serialize, utoipa::ToSchema)]
pub struct OAuthRedirectUri {
pub id: OAuthRedirectUriId,
pub client_id: OAuthClientId,
pub uri: String,
}
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, utoipa::ToSchema)]
pub struct OAuthClientCreationResult {
#[serde(flatten)]
pub client: OAuthClient,
@@ -25,7 +25,7 @@ pub struct OAuthClientCreationResult {
pub client_secret: String,
}
#[derive(Deserialize, Serialize)]
#[derive(Deserialize, Serialize, utoipa::ToSchema)]
pub struct OAuthClient {
pub id: OAuthClientId,
pub name: String,
@@ -48,7 +48,7 @@ pub struct OAuthClient {
pub description: Option<String>,
}
#[derive(Deserialize, Serialize)]
#[derive(Deserialize, Serialize, utoipa::ToSchema)]
pub struct OAuthClientAuthorization {
pub id: OAuthClientAuthorizationId,
pub app_id: OAuthClientId,
+1 -1
View File
@@ -4,7 +4,7 @@ use crate::models::ids::{OrganizationId, TeamId};
use serde::{Deserialize, Serialize};
/// An organization of users who control a project
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, utoipa::ToSchema)]
pub struct Organization {
/// The id of the organization
pub id: OrganizationId,
+14 -3
View File
@@ -234,7 +234,7 @@ impl PayoutStatus {
}
}
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, utoipa::ToSchema)]
pub struct PayoutMethod {
pub id: String,
#[serde(rename = "type")]
@@ -252,7 +252,7 @@ pub struct PayoutMethod {
pub exchange_rate: Option<Decimal>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, utoipa::ToSchema)]
pub struct PayoutMethodFee {
#[serde(with = "rust_decimal::serde::float")]
pub percentage: Decimal,
@@ -274,6 +274,17 @@ impl PayoutMethodFee {
#[derive(Clone)]
pub struct PayoutDecimal(pub Decimal);
impl utoipa::PartialSchema for PayoutDecimal {
fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
utoipa::openapi::ObjectBuilder::new()
.schema_type(utoipa::openapi::schema::Type::Number)
.build()
.into()
}
}
impl utoipa::ToSchema for PayoutDecimal {}
impl Serialize for PayoutDecimal {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
@@ -293,7 +304,7 @@ impl<'de> Deserialize<'de> for PayoutDecimal {
}
}
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, utoipa::ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum PayoutInterval {
Standard {
+2
View File
@@ -8,6 +8,7 @@ use crate::models::exp;
use crate::models::ids::{
FileId, OrganizationId, ProjectId, TeamId, ThreadId, VersionId,
};
use crate::routes::{FileHash, HashAlgorithm};
use ariadne::ids::UserId;
use chrono::{DateTime, Utc};
use itertools::Itertools;
@@ -970,6 +971,7 @@ pub struct VersionFile {
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.
#[schema(value_type = std::collections::HashMap<HashAlgorithm, FileHash>)]
pub hashes: std::collections::HashMap<String, String>,
/// A direct link to the file for downloading it.
pub url: String,
+1 -1
View File
@@ -17,7 +17,7 @@ pub struct Report {
pub thread_id: ThreadId,
}
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, utoipa::ToSchema)]
#[serde(rename_all = "kebab-case")]
pub enum ItemType {
Project,
@@ -38,7 +38,7 @@ impl SharedInstance {
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct SharedInstanceVersion {
pub id: SharedInstanceVersionId,
pub shared_instance: SharedInstanceId,
+2 -2
View File
@@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
pub const DEFAULT_ROLE: &str = "Member";
/// A team of users who control a project
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, utoipa::ToSchema)]
pub struct Team {
/// The id of the team
pub id: TeamId,
@@ -157,7 +157,7 @@ impl OrganizationPermissions {
}
/// A member of a team
#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, utoipa::ToSchema)]
pub struct TeamMember {
/// The ID of the team this team member is a member of
pub team_id: TeamId,
+1 -1
View File
@@ -242,7 +242,7 @@ impl Role {
}
}
#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, utoipa::ToSchema)]
pub struct UserFriend {
// The user who accepted the friend request
pub id: UserId,