Compare commits

...

2 commits

Author SHA1 Message Date
935dceacfc
fix!: instance counters as i64 2024-05-28 03:10:30 +02:00
e0273d5155
feat: fetch instance info when fetching other stuff 2024-05-28 03:10:10 +02:00
3 changed files with 71 additions and 5 deletions

View file

@ -108,8 +108,8 @@ impl MigrationTrait for Migration {
.col(ColumnDef::new(Instances::Version).string().null()) .col(ColumnDef::new(Instances::Version).string().null())
.col(ColumnDef::new(Instances::Icon).string().null()) .col(ColumnDef::new(Instances::Icon).string().null())
.col(ColumnDef::new(Instances::DownSince).date_time().null()) .col(ColumnDef::new(Instances::DownSince).date_time().null())
.col(ColumnDef::new(Instances::Users).integer().null()) .col(ColumnDef::new(Instances::Users).big_integer().null())
.col(ColumnDef::new(Instances::Posts).integer().null()) .col(ColumnDef::new(Instances::Posts).big_integer().null())
.col(ColumnDef::new(Instances::Published).date_time().not_null().default(Expr::current_timestamp())) .col(ColumnDef::new(Instances::Published).date_time().not_null().default(Expr::current_timestamp()))
.col(ColumnDef::new(Instances::Updated).date_time().not_null().default(Expr::current_timestamp())) .col(ColumnDef::new(Instances::Updated).date_time().not_null().default(Expr::current_timestamp()))
.to_owned() .to_owned()

View file

@ -1,3 +1,4 @@
use nodeinfo::NodeInfoOwned;
use sea_orm::{entity::prelude::*, QuerySelect, SelectColumns}; use sea_orm::{entity::prelude::*, QuerySelect, SelectColumns};
use crate::errors::UpubError; use crate::errors::UpubError;
@ -14,8 +15,8 @@ pub struct Model {
pub version: Option<String>, pub version: Option<String>,
pub icon: Option<String>, pub icon: Option<String>,
pub down_since: Option<ChronoDateTimeUtc>, pub down_since: Option<ChronoDateTimeUtc>,
pub users: Option<i32>, pub users: Option<i64>,
pub posts: Option<i32>, pub posts: Option<i64>,
pub published: ChronoDateTimeUtc, pub published: ChronoDateTimeUtc,
pub updated: ChronoDateTimeUtc, pub updated: ChronoDateTimeUtc,
} }
@ -57,4 +58,13 @@ impl Entity {
.await? .await?
.ok_or_else(UpubError::not_found) .ok_or_else(UpubError::not_found)
} }
pub async fn nodeinfo(domain: &str) -> crate::Result<NodeInfoOwned> {
Ok(
reqwest::get(format!("https://{domain}/nodeinfo/2.0.json"))
.await?
.json()
.await?
)
}
} }

View file

@ -3,7 +3,7 @@ use std::collections::BTreeMap;
use apb::{target::Addressed, Activity, Actor, ActorMut, Base, Collection, Object}; use apb::{target::Addressed, Activity, Actor, ActorMut, Base, Collection, Object};
use base64::Engine; use base64::Engine;
use reqwest::{header::{ACCEPT, CONTENT_TYPE, USER_AGENT}, Method, Response}; use reqwest::{header::{ACCEPT, CONTENT_TYPE, USER_AGENT}, Method, Response};
use sea_orm::EntityTrait; use sea_orm::{EntityTrait, IntoActiveModel, NotSet};
use crate::{errors::UpubError, model, VERSION}; use crate::{errors::UpubError, model, VERSION};
@ -13,6 +13,8 @@ use super::{httpsign::HttpSignature, normalizer::Normalizer, Context};
pub trait Fetcher { pub trait Fetcher {
async fn webfinger(&self, user: &str, host: &str) -> crate::Result<String>; async fn webfinger(&self, user: &str, host: &str) -> crate::Result<String>;
async fn fetch_domain(&self, domain: &str) -> crate::Result<model::instance::Model>;
async fn fetch_user(&self, id: &str) -> crate::Result<model::actor::Model>; async fn fetch_user(&self, id: &str) -> crate::Result<model::actor::Model>;
async fn pull_user(&self, id: &str) -> crate::Result<serde_json::Value>; async fn pull_user(&self, id: &str) -> crate::Result<serde_json::Value>;
@ -114,12 +116,62 @@ impl Fetcher for Context {
Err(UpubError::not_found()) Err(UpubError::not_found())
} }
async fn fetch_domain(&self, domain: &str) -> crate::Result<model::instance::Model> {
if let Some(x) = model::instance::Entity::find_by_domain(domain).one(self.db()).await? {
return Ok(x); // already in db, easy
}
let mut instance_model = model::instance::Model {
internal: 0,
domain: domain.to_string(),
name: None,
software: None,
down_since: None,
icon: None,
version: None,
users: None,
posts: None,
published: chrono::Utc::now(),
updated: chrono::Utc::now(),
};
if let Ok(res) = Self::request(
Method::GET, &format!("https://{domain}"), None, &format!("https://{}", self.domain()), self.pkey(), self.domain(),
).await {
if let Ok(actor) = res.json::<serde_json::Value>().await {
if let Some(name) = actor.name() {
instance_model.name = Some(name.to_string());
}
if let Some(icon) = actor.icon().id() {
instance_model.icon = Some(icon);
}
}
}
if let Ok(nodeinfo) = model::instance::Entity::nodeinfo(domain).await {
instance_model.software = Some(nodeinfo.software.name);
instance_model.version = nodeinfo.software.version;
instance_model.users = nodeinfo.usage.users.and_then(|x| x.total);
instance_model.posts = nodeinfo.usage.local_posts;
}
let mut active_model = instance_model.clone().into_active_model();
active_model.internal = NotSet;
model::instance::Entity::insert(active_model).exec(self.db()).await?;
let internal = model::instance::Entity::domain_to_internal(domain, self.db()).await?;
instance_model.internal = internal;
Ok(instance_model)
}
async fn fetch_user(&self, id: &str) -> crate::Result<model::actor::Model> { async fn fetch_user(&self, id: &str) -> crate::Result<model::actor::Model> {
if let Some(x) = model::actor::Entity::find_by_ap_id(id).one(self.db()).await? { if let Some(x) = model::actor::Entity::find_by_ap_id(id).one(self.db()).await? {
return Ok(x); // already in db, easy return Ok(x); // already in db, easy
} }
let _domain = self.fetch_domain(&Context::server(id)).await?;
let user_document = self.pull_user(id).await?; let user_document = self.pull_user(id).await?;
let user_model = model::actor::ActiveModel::new(&user_document)?; let user_model = model::actor::ActiveModel::new(&user_document)?;
@ -179,6 +231,8 @@ impl Fetcher for Context {
return Ok(x); // already in db, easy return Ok(x); // already in db, easy
} }
let _domain = self.fetch_domain(&Context::server(id)).await?;
let activity_document = self.pull_activity(id).await?; let activity_document = self.pull_activity(id).await?;
let activity_model = self.insert_activity(activity_document, Some(Context::server(id))).await?; let activity_model = self.insert_activity(activity_document, Some(Context::server(id))).await?;
@ -236,6 +290,8 @@ async fn fetch_object_inner(ctx: &Context, id: &str, depth: usize) -> crate::Res
return Ok(x); // already in db, easy return Ok(x); // already in db, easy
} }
let _domain = ctx.fetch_domain(&Context::server(id)).await?;
let object = ctx.pull_object(id).await?; let object = ctx.pull_object(id).await?;
if let Some(oid) = object.id() { if let Some(oid) = object.id() {