summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrtkay123 <dev@kanjala.com>2025-07-20 10:51:13 +0200
committerrtkay123 <dev@kanjala.com>2025-07-20 10:51:13 +0200
commit9623a23e5134416e1e8d4c7bfd3edb64c8397501 (patch)
tree8a9aadcf2d3c11a66f89e600375ef6917a87ae18
parent9219896676085e3a7dc0bba5c81cd97cd61099c1 (diff)
downloadsellershut-9623a23e5134416e1e8d4c7bfd3edb64c8397501.tar.bz2
sellershut-9623a23e5134416e1e8d4c7bfd3edb64c8397501.zip
feat: remove follower table
-rw-r--r--crates/sellershut/migrations/20250713161354_account.sql9
-rw-r--r--crates/sellershut/migrations/20250719223814_activity.sql9
-rw-r--r--crates/sellershut/src/server/activities/follow.rs50
3 files changed, 36 insertions, 32 deletions
diff --git a/crates/sellershut/migrations/20250713161354_account.sql b/crates/sellershut/migrations/20250713161354_account.sql
index 1b967b8..b62feb9 100644
--- a/crates/sellershut/migrations/20250713161354_account.sql
+++ b/crates/sellershut/migrations/20250713161354_account.sql
@@ -16,15 +16,6 @@ create table account (
public_key text not null
);
-create table following (
- id uuid primary key,
- follower text references account(ap_id) on delete cascade,
- followee text references account(ap_id) on delete cascade,
- created_at timestamptz not null default now(),
- constraint unique_following unique (follower, followee)
-);
-create index "following_pagination" on "following" ("created_at" asc);
-
create unique index unique_username_local
on account (username)
where local = true;
diff --git a/crates/sellershut/migrations/20250719223814_activity.sql b/crates/sellershut/migrations/20250719223814_activity.sql
index 107a21b..b5105c1 100644
--- a/crates/sellershut/migrations/20250719223814_activity.sql
+++ b/crates/sellershut/migrations/20250719223814_activity.sql
@@ -1,5 +1,5 @@
create table activity (
- ap_id text references account(ap_id),
+ actor text references account(ap_id),
activity jsonb not null,
activity_id text generated always as (
activity->>'id'
@@ -7,6 +7,7 @@ create table activity (
constraint unique_activity_id unique (activity_id),
created_at timestamptz not null default now()
);
-
-create index idx_activity_owner_id on activity(ap_id);
-create index idx_activity_created on activity(created_at);
+-- create index activity_data_index on activity using gin (activity);
+create index idx_activity_type on activity using gin ((activity->'type'));
+create index idx_activity_actor on activity using gin ((activity->'actor')); create index idx_activity_object on activity using gin ((activity->'object'));
+create index idx_activity_created on activity(created_at asc);
diff --git a/crates/sellershut/src/server/activities/follow.rs b/crates/sellershut/src/server/activities/follow.rs
index 48d9caa..07d2793 100644
--- a/crates/sellershut/src/server/activities/follow.rs
+++ b/crates/sellershut/src/server/activities/follow.rs
@@ -6,9 +6,8 @@ use activitypub_federation::{
};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
-use tracing::trace;
+use tracing::{debug, trace, warn};
use url::Url;
-use uuid::Uuid;
use crate::{
entity::user::User,
@@ -70,38 +69,51 @@ impl Activity for Follow {
#[doc = " Should perform validation and possibly write action to the database. In case the activity"]
#[doc = " has a nested `object` field, must call `object.from_json` handler."]
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
- let id = Uuid::now_v7();
-
let mut transaction = data.services.postgres.begin().await?;
- trace!("adding a follower");
- sqlx::query!(
- "insert into following (id, follower, followee) values ($1, $2, $3)",
- id,
- self.actor.inner().as_str(),
- self.object.inner().as_str(),
- )
- .execute(&mut *transaction)
- .await?;
-
+ let count = sqlx::query_scalar!("select count (*) from activity where activity->'type' ? $1 and actor = $2 and activity->'object' ? $3",
+ self.kind.to_string(),
+ self.actor.inner().as_str(),
+ self.object.inner().as_str(),
+ ).fetch_one(&data.services.postgres).await?;
+ trace!(count = count, "executed query");
+
+ if let Some(count) = count
+ && count > 1
+ {
+ warn!("found duplicate follows, cleaning up");
+ sqlx::query!("delete from activity where activity->'type' ? $1 and actor = $2 and activity->'object' ? $3",
+ self.kind.to_string(),
+ self.actor.inner().as_str(),
+ self.object.inner().as_str(),
+ ).execute(&mut *transaction).await?;
+
+ // clean up
+ } else if let Some(count) = count
+ && count == 1
+ {
+ debug!("this user already follows the actor");
+ return Ok(());
+ }
let follower = self.actor.dereference(data).await?;
+
let id = generate_object_id(data.domain(), data.environment)?;
let local_user = self.object.dereference(data).await?;
let accept = Accept::new(self.object.clone(), self.clone(), id.clone());
- local_user
- .send(accept, vec![follower.shared_inbox_or_inbox()], false, data)
- .await?;
-
sqlx::query!(
- "insert into activity (ap_id, activity) values ($1, $2)",
+ "insert into activity (actor, activity) values ($1, $2)",
self.actor.inner().as_str(),
sqlx::types::Json(&self) as _
)
.execute(&mut *transaction)
.await?;
+ local_user
+ .send(accept, vec![follower.shared_inbox_or_inbox()], false, data)
+ .await?;
+
transaction.commit().await?;
Ok(())