use openssl::pkey::{PKey, Private}; use reqwest::header::USER_AGENT; use sea_orm::{DatabaseConnection, EntityTrait, IntoActiveModel}; use crate::{VERSION, model}; #[derive(Debug, thiserror::Error)] pub enum FetchError { #[error("could not dereference resource: {0}")] Network(#[from] reqwest::Error), #[error("error operating on database: {0}")] Database(#[from] sea_orm::DbErr), #[error("missing field when constructing object: {0}")] Field(#[from] model::FieldError), } pub struct Fetcher { db: DatabaseConnection, _key: PKey, // TODO store pre-parsed domain: String, // TODO merge directly with Context so we don't need to copy this } impl Fetcher { pub fn new(db: DatabaseConnection, domain: String, key: String) -> Self { Fetcher { db, domain, _key: PKey::private_key_from_pem(key.as_bytes()).unwrap() } } pub async fn user(&self, id: &str) -> Result { if let Some(x) = model::user::Entity::find_by_id(id).one(&self.db).await? { return Ok(x); // already in db, easy } // TODO sign http fetches, we got the app key and db to get user keys just in case tracing::info!("fetching {id}"); let user = reqwest::Client::new() .get(id) .header(USER_AGENT, format!("upub+{VERSION} ({})", self.domain)) // TODO put instance admin email .send() .await? .json::() .await?; let user_model = model::user::Model::new(&user)?; model::user::Entity::insert(user_model.clone().into_active_model()) .exec(&self.db).await?; Ok(user_model) } }