1
0
Fork 0
forked from alemi/upub

feat: store like aid, also .any(&db) bikeshed

This commit is contained in:
əlemi 2024-05-28 01:47:40 +02:00
parent c97b35a6a7
commit 0c203528df
Signed by: alemi
GPG key ID: A4895B84D311642C
5 changed files with 95 additions and 72 deletions

View file

@ -58,3 +58,9 @@ impl Related<super::object::Entity> for Entity {
} }
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {}
impl Entity {
pub fn find_by_uid_oid(uid: i64, oid: i64) -> Select<Entity> {
Entity::find().filter(Column::Actor.eq(uid)).filter(Column::Object.eq(oid))
}
}

View file

@ -63,3 +63,22 @@ pub fn collection(id: &str, total_items: Option<u64>) -> crate::Result<JsonLD<se
.ld_context() .ld_context()
)) ))
} }
#[axum::async_trait]
pub trait AnyQuery {
async fn any(self, db: &sea_orm::DatabaseConnection) -> crate::Result<bool>;
}
#[axum::async_trait]
impl<T : sea_orm::EntityTrait> AnyQuery for sea_orm::Select<T> {
async fn any(self, db: &sea_orm::DatabaseConnection) -> crate::Result<bool> {
Ok(self.one(db).await?.is_some())
}
}
#[axum::async_trait]
impl<T : sea_orm::SelectorTrait + Send> AnyQuery for sea_orm::Selector<T> {
async fn any(self, db: &sea_orm::DatabaseConnection) -> crate::Result<bool> {
Ok(self.one(db).await?.is_some())
}
}

View file

@ -6,7 +6,7 @@ use sea_orm::{ActiveValue::NotSet, ColumnTrait, DatabaseConnection, EntityTrait,
use crate::{config::Config, errors::UpubError, model, server::fetcher::Fetcher}; use crate::{config::Config, errors::UpubError, model, server::fetcher::Fetcher};
use uriproxy::UriClass; use uriproxy::UriClass;
use super::dispatcher::Dispatcher; use super::{builders::AnyQuery, dispatcher::Dispatcher};
#[derive(Clone)] #[derive(Clone)]
@ -181,43 +181,34 @@ impl Context {
} }
pub async fn is_local_internal_object(&self, internal: i64) -> crate::Result<bool> { pub async fn is_local_internal_object(&self, internal: i64) -> crate::Result<bool> {
Ok(
model::object::Entity::find() model::object::Entity::find()
.filter(model::object::Column::Internal.eq(internal)) .filter(model::object::Column::Internal.eq(internal))
.select_only() .select_only()
.select_column(model::object::Column::Internal) .select_column(model::object::Column::Internal)
.into_tuple::<i64>() .into_tuple::<i64>()
.one(self.db()) .any(self.db())
.await? .await
.is_some()
)
} }
pub async fn is_local_internal_activity(&self, internal: i64) -> crate::Result<bool> { pub async fn is_local_internal_activity(&self, internal: i64) -> crate::Result<bool> {
Ok(
model::activity::Entity::find() model::activity::Entity::find()
.filter(model::activity::Column::Internal.eq(internal)) .filter(model::activity::Column::Internal.eq(internal))
.select_only() .select_only()
.select_column(model::activity::Column::Internal) .select_column(model::activity::Column::Internal)
.into_tuple::<i64>() .into_tuple::<i64>()
.one(self.db()) .any(self.db())
.await? .await
.is_some()
)
} }
#[allow(unused)] #[allow(unused)]
pub async fn is_local_internal_actor(&self, internal: i64) -> crate::Result<bool> { pub async fn is_local_internal_actor(&self, internal: i64) -> crate::Result<bool> {
Ok(
model::actor::Entity::find() model::actor::Entity::find()
.filter(model::actor::Column::Internal.eq(internal)) .filter(model::actor::Column::Internal.eq(internal))
.select_only() .select_only()
.select_column(model::actor::Column::Internal) .select_column(model::actor::Column::Internal)
.into_tuple::<i64>() .into_tuple::<i64>()
.one(self.db()) .any(self.db())
.await? .await
.is_some()
)
} }
pub async fn expand_addressing(&self, targets: Vec<String>) -> crate::Result<Vec<String>> { pub async fn expand_addressing(&self, targets: Vec<String>) -> crate::Result<Vec<String>> {

View file

@ -2,7 +2,7 @@ use apb::{target::Addressed, Activity, Base, Object};
use reqwest::StatusCode; use reqwest::StatusCode;
use sea_orm::{sea_query::Expr, ActiveValue::{Set, NotSet}, ColumnTrait, Condition, EntityTrait, QueryFilter, QuerySelect, SelectColumns}; use sea_orm::{sea_query::Expr, ActiveValue::{Set, NotSet}, ColumnTrait, Condition, EntityTrait, QueryFilter, QuerySelect, SelectColumns};
use crate::{errors::{LoggableError, UpubError}, model, server::normalizer::Normalizer}; use crate::{errors::{LoggableError, UpubError}, model, server::{builders::AnyQuery, normalizer::Normalizer}};
use super::{fetcher::Fetcher, Context}; use super::{fetcher::Fetcher, Context};
@ -31,21 +31,27 @@ impl apb::server::Inbox for Context {
let internal_uid = model::actor::Entity::ap_to_internal(&uid, self.db()).await?; let internal_uid = model::actor::Entity::ap_to_internal(&uid, self.db()).await?;
let object_uri = activity.object().id().ok_or(UpubError::bad_request())?; let object_uri = activity.object().id().ok_or(UpubError::bad_request())?;
let obj = self.fetch_object(&object_uri).await?; let obj = self.fetch_object(&object_uri).await?;
if model::like::Entity::find_by_uid_oid(internal_uid, obj.internal)
.any(self.db())
.await?
{
return Err(UpubError::not_modified());
}
let activity_model = self.insert_activity(activity, Some(server)).await?;
let like = model::like::ActiveModel { let like = model::like::ActiveModel {
internal: NotSet, internal: NotSet,
actor: Set(internal_uid), actor: Set(internal_uid),
object: Set(obj.internal), object: Set(obj.internal),
published: Set(activity.published().unwrap_or(chrono::Utc::now())), activity: Set(activity_model.internal),
published: Set(activity_model.published),
}; };
match model::like::Entity::insert(like).exec(self.db()).await { model::like::Entity::insert(like).exec(self.db()).await?;
Err(sea_orm::DbErr::RecordNotInserted) => Err(UpubError::not_modified()), model::object::Entity::update_many()
Err(sea_orm::DbErr::Exec(_)) => Err(UpubError::not_modified()), // bad fix for sqlite .col_expr(model::object::Column::Likes, Expr::col(model::object::Column::Likes).add(1))
Err(e) => { .filter(model::object::Column::Internal.eq(obj.internal))
tracing::error!("unexpected error procesing like from {uid} to {}: {e}", obj.id); .exec(self.db())
Err(UpubError::internal_server_error()) .await?;
}
Ok(_) => {
let activity_model = self.insert_activity(activity, Some(server)).await?;
let mut expanded_addressing = self.expand_addressing(activity_model.addressed()).await?; let mut expanded_addressing = self.expand_addressing(activity_model.addressed()).await?;
if expanded_addressing.is_empty() { // WHY MASTODON!!!!!!! if expanded_addressing.is_empty() { // WHY MASTODON!!!!!!!
expanded_addressing.push( expanded_addressing.push(
@ -59,15 +65,8 @@ impl apb::server::Inbox for Context {
); );
} }
self.address_to(Some(activity_model.internal), None, &expanded_addressing).await?; self.address_to(Some(activity_model.internal), None, &expanded_addressing).await?;
model::object::Entity::update_many()
.col_expr(model::object::Column::Likes, Expr::col(model::object::Column::Likes).add(1))
.filter(model::object::Column::Internal.eq(obj.internal))
.exec(self.db())
.await?;
tracing::info!("{} liked {}", uid, obj.id); tracing::info!("{} liked {}", uid, obj.id);
Ok(()) Ok(())
},
}
} }
async fn follow(&self, _: String, activity: serde_json::Value) -> crate::Result<()> { async fn follow(&self, _: String, activity: serde_json::Value) -> crate::Result<()> {

View file

@ -4,7 +4,7 @@ use sea_orm::{sea_query::Expr, ActiveValue::{Set, NotSet, Unchanged}, ColumnTrai
use crate::{errors::UpubError, model}; use crate::{errors::UpubError, model};
use super::{fetcher::Fetcher, normalizer::Normalizer, Context}; use super::{builders::AnyQuery, fetcher::Fetcher, normalizer::Normalizer, Context};
#[axum::async_trait] #[axum::async_trait]
@ -114,25 +114,33 @@ impl apb::server::Outbox for Context {
let activity_targets = activity.addressed(); let activity_targets = activity.addressed();
let oid = activity.object().id().ok_or_else(UpubError::bad_request)?; let oid = activity.object().id().ok_or_else(UpubError::bad_request)?;
let obj_model = self.fetch_object(&oid).await?; let obj_model = self.fetch_object(&oid).await?;
let activity_model = model::activity::ActiveModel::new(
&activity
.set_id(Some(&aid))
.set_actor(Node::link(uid.clone()))
.set_published(Some(chrono::Utc::now()))
)?;
let internal_uid = model::actor::Entity::ap_to_internal(&uid, self.db()).await?; let internal_uid = model::actor::Entity::ap_to_internal(&uid, self.db()).await?;
if model::like::Entity::find_by_uid_oid(internal_uid, obj_model.internal)
.any(self.db())
.await?
{
return Err(UpubError::not_modified());
}
let activity_model = self.insert_activity(
activity
.set_id(Some(&aid))
.set_actor(Node::link(uid.clone()))
.set_published(Some(chrono::Utc::now())),
Some(self.domain().to_string()),
).await?;
let like_model = model::like::ActiveModel { let like_model = model::like::ActiveModel {
internal: NotSet, internal: NotSet,
actor: Set(internal_uid), actor: Set(internal_uid),
object: Set(obj_model.internal), object: Set(obj_model.internal),
activity: Set(activity_model.internal),
published: Set(chrono::Utc::now()), published: Set(chrono::Utc::now()),
}; };
model::like::Entity::insert(like_model).exec(self.db()).await?; model::like::Entity::insert(like_model).exec(self.db()).await?;
model::activity::Entity::insert(activity_model)
.exec(self.db()).await?;
model::object::Entity::update_many() model::object::Entity::update_many()
.col_expr(model::object::Column::Likes, Expr::col(model::object::Column::Likes).add(1)) .col_expr(model::object::Column::Likes, Expr::col(model::object::Column::Likes).add(1))
.filter(model::object::Column::Internal.eq(obj_model.internal)) .filter(model::object::Column::Internal.eq(obj_model.internal))