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 let did = match crate::auth::validate_bearer_token(&state.db, &token).await {
44 Ok(user) => user.did,
45 Err(e) => return ApiError::from(e).into_response(),
46 };
47 let valid_reason_types = [
48 "com.atproto.moderation.defs#reasonSpam",
49 "com.atproto.moderation.defs#reasonViolation",
50 "com.atproto.moderation.defs#reasonMisleading",
51 "com.atproto.moderation.defs#reasonSexual",
52 "com.atproto.moderation.defs#reasonRude",
53 "com.atproto.moderation.defs#reasonOther",
54 "com.atproto.moderation.defs#reasonAppeal",
55 ];
56 if !valid_reason_types.contains(&input.reason_type.as_str()) {
57 return (
58 StatusCode::BAD_REQUEST,
59 Json(json!({"error": "InvalidRequest", "message": "Invalid reasonType"})),
60 )
61 .into_response();
62 }
63 let created_at = chrono::Utc::now();
64 let report_id = created_at.timestamp_millis();
65 let subject_json = json!(input.subject);
66 let insert = sqlx::query!(
67 "INSERT INTO reports (id, reason_type, reason, subject_json, reported_by_did, created_at) VALUES ($1, $2, $3, $4, $5, $6)",
68 report_id,
69 input.reason_type,
70 input.reason,
71 subject_json,
72 did,
73 created_at
74 )
75 .execute(&state.db)
76 .await;
77 if let Err(e) = insert {
78 error!("Failed to insert report: {:?}", e);
79 return (
80 StatusCode::INTERNAL_SERVER_ERROR,
81 Json(json!({"error": "InternalError"})),
82 )
83 .into_response();
84 }
85 (
86 StatusCode::OK,
87 Json(CreateReportOutput {
88 id: report_id,
89 reason_type: input.reason_type,
90 reason: input.reason,
91 subject: input.subject,
92 reported_by: did,
93 created_at: created_at.to_rfc3339(),
94 }),
95 )
96 .into_response()
97}