use std::sync::Arc;
use axum::{extract::FromRef, http::StatusCode, routing::get, Router};
use tokio::io;
use crate::{
config::AppConfig,
constants::routes::*,
controllers,
sources::{self, file::FileSource, http::HttpSource, manager::SourceManagerHandle, SourceList},
};
#[derive(Debug, Clone, FromRef)]
struct AppState {
config: Arc<AppConfig>,
source_handles: Vec<SourceManagerHandle>,
}
pub async fn create_app(config: Arc<AppConfig>) -> Result<Router, Error> {
let mut app_state = AppState {
config: config.clone(),
source_handles: vec![],
};
if let Some(path) = config.local_list_path() {
let handle =
sources::manager::spawn(FileSource::new(path.to_owned(), config.source_lifetime()))
.await?;
app_state.source_handles.push(handle);
}
if let Some(path) = config.sources_path() {
let file = tokio::fs::read(path).await?;
let sources: SourceList = serde_json::from_slice(&file)?;
for url in sources.sources() {
let handle =
sources::manager::spawn(HttpSource::new(url.to_owned(), config.source_lifetime()))
.await?;
app_state.source_handles.push(handle);
}
}
Ok(Router::new()
.route(HEALTH_CHECK_ROUTE, get(controllers::health_check))
.route(FETCH_ALL_ROUTE, get(controllers::list::fetch_all))
.route(
FETCH_ALL_CSV_ROUTE,
get(controllers::list::fetch_all_as_mastodon_csv),
)
.route(SEARCH_LIST_ROUTE, get(controllers::list::search_domain))
.fallback(|| async { StatusCode::NOT_FOUND })
.with_state(app_state))
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("failed to read from local source")]
File(#[from] io::Error),
#[error("failed to deserialize local source")]
Deserialize(#[from] serde_json::Error),
#[error("failed to spawn actor")]
Manager(#[from] sources::manager::Error),
}