lemmy/crates/api_common/src/send_activity.rs
Nutomic ad90cd77f9
Implement private communities (#5076)
* add private visibility

* filter private communities in post_view.rs

* also filter in comment_view

* community follower state

* remove unused method

* sql fmt

* add CommunityFollower.approved_by

* implement api endpoints

* api changes

* only admins can create private community for now

* add local api tests

* fix api tests

* follow remote private community

* use authorized fetch for content in private community

* federate community visibility

* dont mark content in private community as public

* expose ApprovalRequired in api

* also check content fetchable for outbox/featured

* address private community content to followers

* implement reject activity

* fix tests

* add files

* remove local api tests

* dont use delay

* is_new_instance

* single query for is_new_instance

* return subscribed type for pending follow

* working

* need to catch errors in waitUntil

* clippy

* fix query

* lint for unused async

* diesel.toml comment

* add comment

* avoid db reads

* rename approved_by to approver_id

* add helper

* form init

* list pending follows should return items for all communities

* clippy

* ci

* fix down migration

* fix api tests

* references

* rename

* run git diff

* ci

* fix schema check

* fix joins

* ci

* ci

* skip_serializing_none

* fix test

---------

Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
2024-11-07 05:49:05 -05:00

142 lines
3.9 KiB
Rust

use crate::{community::BanFromCommunity, context::LemmyContext, post::DeletePost};
use activitypub_federation::config::Data;
use futures::future::BoxFuture;
use lemmy_db_schema::{
newtypes::{CommunityId, DbUrl, PersonId},
source::{
comment::Comment,
community::Community,
person::Person,
post::Post,
private_message::PrivateMessage,
},
};
use lemmy_db_views::structs::PrivateMessageView;
use lemmy_utils::error::LemmyResult;
use std::sync::{LazyLock, OnceLock};
use tokio::{
sync::{
mpsc,
mpsc::{UnboundedReceiver, UnboundedSender, WeakUnboundedSender},
Mutex,
},
task::JoinHandle,
};
use url::Url;
type MatchOutgoingActivitiesBoxed =
Box<for<'a> fn(SendActivityData, &'a Data<LemmyContext>) -> BoxFuture<'a, LemmyResult<()>>>;
/// This static is necessary so that the api_common crates don't need to depend on lemmy_apub
pub static MATCH_OUTGOING_ACTIVITIES: OnceLock<MatchOutgoingActivitiesBoxed> = OnceLock::new();
#[derive(Debug)]
pub enum SendActivityData {
CreatePost(Post),
UpdatePost(Post),
DeletePost(Post, Person, DeletePost),
RemovePost {
post: Post,
moderator: Person,
reason: Option<String>,
removed: bool,
},
LockPost(Post, Person, bool),
FeaturePost(Post, Person, bool),
CreateComment(Comment),
UpdateComment(Comment),
DeleteComment(Comment, Person, Community),
RemoveComment {
comment: Comment,
moderator: Person,
community: Community,
reason: Option<String>,
},
LikePostOrComment {
object_id: DbUrl,
actor: Person,
community: Community,
score: i16,
},
FollowCommunity(Community, Person, bool),
AcceptFollower(CommunityId, PersonId),
RejectFollower(CommunityId, PersonId),
UpdateCommunity(Person, Community),
DeleteCommunity(Person, Community, bool),
RemoveCommunity {
moderator: Person,
community: Community,
reason: Option<String>,
removed: bool,
},
AddModToCommunity {
moderator: Person,
community_id: CommunityId,
target: PersonId,
added: bool,
},
BanFromCommunity {
moderator: Person,
community_id: CommunityId,
target: Person,
data: BanFromCommunity,
},
BanFromSite {
moderator: Person,
banned_user: Person,
reason: Option<String>,
remove_or_restore_data: Option<bool>,
ban: bool,
expires: Option<i64>,
},
CreatePrivateMessage(PrivateMessageView),
UpdatePrivateMessage(PrivateMessageView),
DeletePrivateMessage(Person, PrivateMessage, bool),
DeleteUser(Person, bool),
CreateReport {
object_id: Url,
actor: Person,
community: Community,
reason: String,
},
}
// TODO: instead of static, move this into LemmyContext. make sure that stopping the process with
// ctrl+c still works.
static ACTIVITY_CHANNEL: LazyLock<ActivityChannel> = LazyLock::new(|| {
let (sender, receiver) = mpsc::unbounded_channel();
let weak_sender = sender.downgrade();
ActivityChannel {
weak_sender,
receiver: Mutex::new(receiver),
keepalive_sender: Mutex::new(Some(sender)),
}
});
pub struct ActivityChannel {
weak_sender: WeakUnboundedSender<SendActivityData>,
receiver: Mutex<UnboundedReceiver<SendActivityData>>,
keepalive_sender: Mutex<Option<UnboundedSender<SendActivityData>>>,
}
impl ActivityChannel {
pub async fn retrieve_activity() -> Option<SendActivityData> {
let mut lock = ACTIVITY_CHANNEL.receiver.lock().await;
lock.recv().await
}
pub fn submit_activity(data: SendActivityData, _context: &Data<LemmyContext>) -> LemmyResult<()> {
// could do `ACTIVITY_CHANNEL.keepalive_sender.lock()` instead and get rid of weak_sender,
// not sure which way is more efficient
if let Some(sender) = ACTIVITY_CHANNEL.weak_sender.upgrade() {
sender.send(data)?;
}
Ok(())
}
pub async fn close(outgoing_activities_task: JoinHandle<()>) -> LemmyResult<()> {
ACTIVITY_CHANNEL.keepalive_sender.lock().await.take();
outgoing_activities_task.await?;
Ok(())
}
}