Put Federation on async executor
This commit is contained in:
parent
fde80672f2
commit
0e32aab244
2 changed files with 31 additions and 5 deletions
|
|
@ -201,13 +201,27 @@ public class CommentController {
|
||||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
|
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Soft delete
|
// Soft delete locally
|
||||||
comment.setDeleted(true);
|
comment.setDeleted(true);
|
||||||
commentRepository.save(comment);
|
commentRepository.save(comment);
|
||||||
|
|
||||||
log.info("User {} deleted comment {}", user.getUsername(), commentId);
|
log.info("User {} deleted comment {}", user.getUsername(), commentId);
|
||||||
|
|
||||||
// TODO: Send ActivityPub Delete activity to followers if activity is public
|
// Federate the deletion to remote followers so they tombstone their cached
|
||||||
|
// copy of the Note. The visibility check mirrors createComment(): the
|
||||||
|
// original Create was only sent if the parent activity was PUBLIC or
|
||||||
|
// FOLLOWERS, so the Delete needs to follow the same audience rule. The
|
||||||
|
// commentUri must match exactly what was used in the original Create
|
||||||
|
// activity, otherwise remote servers won't be able to match the tombstone
|
||||||
|
// to the cached note.
|
||||||
|
Activity activity = activityRepository.findById(activityId).orElse(null);
|
||||||
|
if (activity != null
|
||||||
|
&& (activity.getVisibility() == Activity.Visibility.PUBLIC
|
||||||
|
|| activity.getVisibility() == Activity.Visibility.FOLLOWERS)) {
|
||||||
|
String commentUri = baseUrl + "/activities/" + activityId + "/comments/" + commentId;
|
||||||
|
federationService.sendDeleteActivity(commentUri, user);
|
||||||
|
log.info("Sent Delete federation for comment {} on activity {}", commentId, activityId);
|
||||||
|
}
|
||||||
|
|
||||||
return ResponseEntity.noContent().build();
|
return ResponseEntity.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -311,10 +311,16 @@ public class FederationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all follower inbox URLs for a local user.
|
* Get the inbox URLs of all <em>remote</em> followers of a local user.
|
||||||
|
*
|
||||||
|
* <p>Local followers are deliberately skipped: they live on the same server and
|
||||||
|
* see new activities via the local timeline queries, so there is nothing to
|
||||||
|
* federate to them. Without this filter, every call would attempt to
|
||||||
|
* {@code fetchRemoteActor(null)} for each local follower row, log a stack trace
|
||||||
|
* at ERROR level, and then drop the resulting null from the inbox list.
|
||||||
*
|
*
|
||||||
* @param userId the local user's ID
|
* @param userId the local user's ID
|
||||||
* @return list of inbox URLs
|
* @return list of remote follower inbox URLs (deduplicated, shared inbox preferred)
|
||||||
*/
|
*/
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public List<String> getFollowerInboxes(UUID userId) {
|
public List<String> getFollowerInboxes(UUID userId) {
|
||||||
|
|
@ -325,6 +331,7 @@ public class FederationService {
|
||||||
List<Follow> followers = followRepository.findAcceptedFollowersByActorUri(actorUri);
|
List<Follow> followers = followRepository.findAcceptedFollowersByActorUri(actorUri);
|
||||||
|
|
||||||
return followers.stream()
|
return followers.stream()
|
||||||
|
.filter(follow -> follow.getRemoteActorUri() != null) // skip local followers (no federation needed)
|
||||||
.map(follow -> {
|
.map(follow -> {
|
||||||
try {
|
try {
|
||||||
RemoteActor actor = remoteActorRepository.findByActorUri(follow.getRemoteActorUri())
|
RemoteActor actor = remoteActorRepository.findByActorUri(follow.getRemoteActorUri())
|
||||||
|
|
@ -529,9 +536,14 @@ public class FederationService {
|
||||||
/**
|
/**
|
||||||
* Send a Delete activity to notify followers that an object has been deleted.
|
* Send a Delete activity to notify followers that an object has been deleted.
|
||||||
*
|
*
|
||||||
* @param objectUri the URI of the deleted object (e.g., activity URI)
|
* <p>Runs on the {@code taskExecutor} pool. Used for both activity deletes and
|
||||||
|
* comment (Note) deletes — the user-facing HTTP response shouldn't wait on the
|
||||||
|
* federation fanout to remote follower inboxes.
|
||||||
|
*
|
||||||
|
* @param objectUri the URI of the deleted object (e.g., activity URI or comment Note URI)
|
||||||
* @param sender the user who deleted the object
|
* @param sender the user who deleted the object
|
||||||
*/
|
*/
|
||||||
|
@Async("taskExecutor")
|
||||||
public void sendDeleteActivity(String objectUri, User sender) {
|
public void sendDeleteActivity(String objectUri, User sender) {
|
||||||
try {
|
try {
|
||||||
String deleteId = baseUrl + "/activities/delete/" + UUID.randomUUID();
|
String deleteId = baseUrl + "/activities/delete/" + UUID.randomUUID();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue