Compare commits
2 commits
1b08321d34
...
935dceacfc
Author | SHA1 | Date | |
---|---|---|---|
935dceacfc | |||
e0273d5155 |
3 changed files with 71 additions and 5 deletions
|
@ -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()
|
||||||
|
|
|
@ -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?
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
Loading…
Reference in a new issue