music on atproto
plyr.fm
1//! Application state and error types.
2
3use std::sync::Arc;
4
5use axum::{
6 http::StatusCode,
7 response::{IntoResponse, Response},
8 Json,
9};
10use tokio::sync::broadcast;
11use tracing::error;
12
13use crate::claude::ClaudeClient;
14use crate::db::LabelDb;
15use crate::labels::{Label, LabelError, LabelSigner};
16
17/// Shared application state.
18#[derive(Clone)]
19pub struct AppState {
20 pub audd_api_token: String,
21 pub audd_api_url: String,
22 pub db: Option<Arc<LabelDb>>,
23 pub signer: Option<Arc<LabelSigner>>,
24 pub label_tx: Option<broadcast::Sender<(i64, Label)>>,
25 /// Claude client for image moderation (if configured)
26 pub claude: Option<Arc<ClaudeClient>>,
27 /// Minimum AuDD score to flag as potential copyright violation
28 pub copyright_score_threshold: i32,
29}
30
31/// Application error type.
32#[derive(Debug, thiserror::Error)]
33pub enum AppError {
34 #[error("audd error: {0}")]
35 Audd(String),
36
37 #[error("claude error: {0}")]
38 Claude(String),
39
40 #[error("image moderation not configured")]
41 ImageModerationNotConfigured,
42
43 #[error("labeler not configured")]
44 LabelerNotConfigured,
45
46 #[error("bad request: {0}")]
47 BadRequest(String),
48
49 #[error("not found: {0}")]
50 NotFound(String),
51
52 #[error("label error: {0}")]
53 Label(#[from] LabelError),
54
55 #[error("database error: {0}")]
56 Database(#[from] sqlx::Error),
57
58 #[error("io error: {0}")]
59 Io(#[from] std::io::Error),
60}
61
62impl IntoResponse for AppError {
63 fn into_response(self) -> Response {
64 error!(error = %self, "request failed");
65 let (status, error_type) = match &self {
66 AppError::Audd(_) => (StatusCode::BAD_GATEWAY, "AuddError"),
67 AppError::Claude(_) => (StatusCode::BAD_GATEWAY, "ClaudeError"),
68 AppError::ImageModerationNotConfigured => {
69 (StatusCode::SERVICE_UNAVAILABLE, "ImageModerationNotConfigured")
70 }
71 AppError::LabelerNotConfigured => {
72 (StatusCode::SERVICE_UNAVAILABLE, "LabelerNotConfigured")
73 }
74 AppError::BadRequest(_) => (StatusCode::BAD_REQUEST, "BadRequest"),
75 AppError::NotFound(_) => (StatusCode::NOT_FOUND, "NotFound"),
76 AppError::Label(_) => (StatusCode::INTERNAL_SERVER_ERROR, "LabelError"),
77 AppError::Database(_) => (StatusCode::INTERNAL_SERVER_ERROR, "DatabaseError"),
78 AppError::Io(_) => (StatusCode::INTERNAL_SERVER_ERROR, "IoError"),
79 };
80 let body = serde_json::json!({
81 "error": error_type,
82 "message": self.to_string()
83 });
84 (status, Json(body)).into_response()
85 }
86}