From 7ba5697e4f90d483cc69634429020fdd5718bf70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Z=C3=B6ller?= Date: Sun, 30 Nov 2025 10:51:09 +0100 Subject: [PATCH] More federation --- .../fitpub/repository/LikeRepository.java | 17 ++++ .../fitpub/service/InboxProcessor.java | 81 ++++++++++++++++++- 2 files changed, 95 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/operaton/fitpub/repository/LikeRepository.java b/src/main/java/org/operaton/fitpub/repository/LikeRepository.java index 64e609a..fb862f1 100644 --- a/src/main/java/org/operaton/fitpub/repository/LikeRepository.java +++ b/src/main/java/org/operaton/fitpub/repository/LikeRepository.java @@ -67,6 +67,15 @@ public interface LikeRepository extends JpaRepository { */ boolean existsByActivityIdAndUserId(UUID activityId, UUID userId); + /** + * Check if a remote actor has liked an activity. + * + * @param activityId the activity ID + * @param remoteActorUri the remote actor URI + * @return true if liked + */ + boolean existsByActivityIdAndRemoteActorUri(UUID activityId, String remoteActorUri); + /** * Delete a like by activity and user. * @@ -74,4 +83,12 @@ public interface LikeRepository extends JpaRepository { * @param userId the user ID */ void deleteByActivityIdAndUserId(UUID activityId, UUID userId); + + /** + * Delete a like by activity and remote actor. + * + * @param activityId the activity ID + * @param remoteActorUri the remote actor URI + */ + void deleteByActivityIdAndRemoteActorUri(UUID activityId, String remoteActorUri); } diff --git a/src/main/java/org/operaton/fitpub/service/InboxProcessor.java b/src/main/java/org/operaton/fitpub/service/InboxProcessor.java index dafb5b8..692169c 100644 --- a/src/main/java/org/operaton/fitpub/service/InboxProcessor.java +++ b/src/main/java/org/operaton/fitpub/service/InboxProcessor.java @@ -2,16 +2,21 @@ package org.operaton.fitpub.service; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.operaton.fitpub.model.entity.Activity; import org.operaton.fitpub.model.entity.Follow; +import org.operaton.fitpub.model.entity.Like; import org.operaton.fitpub.model.entity.RemoteActor; import org.operaton.fitpub.model.entity.User; +import org.operaton.fitpub.repository.ActivityRepository; import org.operaton.fitpub.repository.FollowRepository; +import org.operaton.fitpub.repository.LikeRepository; import org.operaton.fitpub.repository.UserRepository; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Map; +import java.util.UUID; /** * Processes incoming ActivityPub activities in the inbox. @@ -24,6 +29,8 @@ public class InboxProcessor { private final UserRepository userRepository; private final FollowRepository followRepository; private final FederationService federationService; + private final ActivityRepository activityRepository; + private final LikeRepository likeRepository; @Value("${fitpub.base-url}") private String baseUrl; @@ -114,10 +121,11 @@ public class InboxProcessor { } /** - * Process an Undo activity (e.g., unfollow). + * Process an Undo activity (e.g., unfollow, unlike). */ private void processUndo(String username, Map activity) { try { + String actor = (String) activity.get("actor"); Object object = activity.get("object"); if (object instanceof Map) { @SuppressWarnings("unchecked") @@ -131,6 +139,13 @@ public class InboxProcessor { followRepository.delete(follow); log.info("Processed Undo Follow: {}", activityId); } + } else if ("Like".equals(type)) { + String objectUri = (String) undoObject.get("object"); + UUID activityId = extractActivityIdFromUri(objectUri); + if (activityId != null) { + likeRepository.deleteByActivityIdAndRemoteActorUri(activityId, actor); + log.info("Processed Undo Like from {} for activity {}", actor, activityId); + } } } } catch (Exception e) { @@ -173,7 +188,67 @@ public class InboxProcessor { * Process a Like activity. */ private void processLike(String username, Map activity) { - // TODO: Implement Like activity processing - log.debug("Received Like activity for user {}", username); + try { + String actor = (String) activity.get("actor"); + String objectUri = (String) activity.get("object"); + + log.debug("Received Like from {} for object {}", actor, objectUri); + + // Extract activity ID from the object URI + // Expected format: https://fitpub.example/activities/{uuid} + UUID activityId = extractActivityIdFromUri(objectUri); + if (activityId == null) { + log.warn("Could not extract activity ID from object URI: {}", objectUri); + return; + } + + // Check if the activity exists + Activity localActivity = activityRepository.findById(activityId).orElse(null); + if (localActivity == null) { + log.warn("Activity not found: {}", activityId); + return; + } + + // Fetch remote actor information + RemoteActor remoteActor = federationService.fetchRemoteActor(actor); + + // Check if like already exists + if (likeRepository.existsByActivityIdAndRemoteActorUri(activityId, actor)) { + log.debug("Like already exists from {} for activity {}", actor, activityId); + return; + } + + // Create the like + Like like = Like.builder() + .activityId(activityId) + .userId(null) // Remote actor, not a local user + .remoteActorUri(actor) + .displayName(remoteActor.getDisplayName() != null ? remoteActor.getDisplayName() : remoteActor.getUsername()) + .avatarUrl(remoteActor.getAvatarUrl()) + .build(); + + likeRepository.save(like); + log.info("Processed Like from {} for activity {}", actor, activityId); + + } catch (Exception e) { + log.error("Error processing Like activity", e); + } + } + + /** + * Extract activity UUID from URI. + * Expects format: https://fitpub.example/activities/{uuid} + */ + private UUID extractActivityIdFromUri(String uri) { + try { + if (uri == null || !uri.startsWith(baseUrl + "/activities/")) { + return null; + } + String uuidStr = uri.substring((baseUrl + "/activities/").length()); + return UUID.fromString(uuidStr); + } catch (Exception e) { + log.warn("Failed to extract activity ID from URI: {}", uri, e); + return null; + } } }