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