this repo has no description
1use crate::api::ApiError;
2use crate::state::AppState;
3use axum::{
4 Json,
5 extract::State,
6 http::StatusCode,
7 response::{IntoResponse, Response},
8};
9use serde::{Deserialize, Serialize};
10use serde_json::{Value, json};
11use tracing::error;
12
13#[derive(Deserialize)]
14#[serde(rename_all = "camelCase")]
15pub struct CreateReportInput {
16 pub reason_type: String,
17 pub reason: Option<String>,
18 pub subject: Value,
19}
20
21#[derive(Serialize)]
22#[serde(rename_all = "camelCase")]
23pub struct CreateReportOutput {
24 pub id: i64,
25 pub reason_type: String,
26 pub reason: Option<String>,
27 pub subject: Value,
28 pub reported_by: String,
29 pub created_at: String,
30}
31
32pub async fn create_report(
33 State(state): State<AppState>,
34 headers: axum::http::HeaderMap,
35 Json(input): Json<CreateReportInput>,
36) -> Response {
37 let token = match crate::auth::extract_bearer_token_from_header(
38 headers.get("Authorization").and_then(|h| h.to_str().ok())
39 ) {
40 Some(t) => t,
41 None => return ApiError::AuthenticationRequired.into_response(),
42 };
43
44 let did = match crate::auth::validate_bearer_token(&state.db, &token).await {
45 Ok(user) => user.did,
46 Err(e) => return ApiError::from(e).into_response(),
47 };
48
49 let valid_reason_types = [
50 "com.atproto.moderation.defs#reasonSpam",
51 "com.atproto.moderation.defs#reasonViolation",
52 "com.atproto.moderation.defs#reasonMisleading",
53 "com.atproto.moderation.defs#reasonSexual",
54 "com.atproto.moderation.defs#reasonRude",
55 "com.atproto.moderation.defs#reasonOther",
56 "com.atproto.moderation.defs#reasonAppeal",
57 ];
58
59 if !valid_reason_types.contains(&input.reason_type.as_str()) {
60 return (
61 StatusCode::BAD_REQUEST,
62 Json(json!({"error": "InvalidRequest", "message": "Invalid reasonType"})),
63 )
64 .into_response();
65 }
66
67 let created_at = chrono::Utc::now();
68 let report_id = created_at.timestamp_millis();
69
70 let subject_json = json!(input.subject);
71 let insert = sqlx::query!(
72 "INSERT INTO reports (id, reason_type, reason, subject_json, reported_by_did, created_at) VALUES ($1, $2, $3, $4, $5, $6)",
73 report_id,
74 input.reason_type,
75 input.reason,
76 subject_json,
77 did,
78 created_at
79 )
80 .execute(&state.db)
81 .await;
82
83 if let Err(e) = insert {
84 error!("Failed to insert report: {:?}", e);
85 return (
86 StatusCode::INTERNAL_SERVER_ERROR,
87 Json(json!({"error": "InternalError"})),
88 )
89 .into_response();
90 }
91
92 (
93 StatusCode::OK,
94 Json(CreateReportOutput {
95 id: report_id,
96 reason_type: input.reason_type,
97 reason: input.reason,
98 subject: input.subject,
99 reported_by: did,
100 created_at: created_at.to_rfc3339(),
101 }),
102 )
103 .into_response()
104}