From 44411d8ecea4e8676dd2d4d0237c9cd4fd2ec981 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Wed, 24 Mar 2021 20:21:43 +0300 Subject: ActivityPub: add workaround for Nextcloud Service Remove when their fix will be merged - https://github.com/nextcloud/social/pull/1230 --- .../java/com/juick/www/api/activity/Profile.java | 115 ++++++++++++--------- .../com/juick/www/api/activity/model/Context.java | 1 + 2 files changed, 66 insertions(+), 50 deletions(-) (limited to 'src/main/java/com/juick/www/api') diff --git a/src/main/java/com/juick/www/api/activity/Profile.java b/src/main/java/com/juick/www/api/activity/Profile.java index 1a243c09..d416e591 100644 --- a/src/main/java/com/juick/www/api/activity/Profile.java +++ b/src/main/java/com/juick/www/api/activity/Profile.java @@ -101,7 +101,8 @@ public class Profile { @Inject private Remark remarkConverter; - @GetMapping(value = "/u/{userName}", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) + @GetMapping(value = "/u/{userName}", produces = { Context.LD_JSON_MEDIA_TYPE, + Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE, Context.FALLBACK_JSON_MEDIA_TYPE }) public Person getUser(@PathVariable String userName) { User user = userService.getUserByName(userName); if (!user.isAnonymous()) { @@ -128,7 +129,8 @@ public class Profile { throw new HttpNotFoundException(); } - @GetMapping(value = "/u/{userName}/blog/toc", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) + @GetMapping(value = "/u/{userName}/blog/toc", produces = { Context.LD_JSON_MEDIA_TYPE, + Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE }) public OrderedCollection getOutbox(@PathVariable String userName) { User user = userService.getUserByName(userName); if (!user.isAnonymous()) { @@ -142,16 +144,17 @@ public class Profile { throw new HttpNotFoundException(); } - @GetMapping(value = "/u/{userName}/blog", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) + @GetMapping(value = "/u/{userName}/blog", produces = { Context.LD_JSON_MEDIA_TYPE, + Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE }) public OrderedCollectionPage getOutboxPage(@Visitor User visitor, @PathVariable String userName, - @RequestParam(required = false, defaultValue = "0") int before) { + @RequestParam(required = false, defaultValue = "0") int before) { User user = userService.getUserByName(userName); if (!user.isAnonymous() && !user.isBanned()) { UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); String personUri = uri.path(String.format("/u/%s", userName)).toUriString(); List mids = messagesService.getUserBlog(user.getUid(), 0, before); - List notes = messagesService.getMessages(visitor, mids) - .stream().map(activityPubManager::makeNote).collect(Collectors.toList()); + List notes = messagesService.getMessages(visitor, mids).stream().map(activityPubManager::makeNote) + .collect(Collectors.toList()); OrderedCollectionPage page = new OrderedCollectionPage(); page.setPartOf(uri.replacePath(String.format("/u/%s/blog/toc", userName)).toUriString()); page.setFirst(uri.replacePath(String.format("/u/%s/blog", userName)).toUriString()); @@ -175,7 +178,8 @@ public class Profile { throw new HttpNotFoundException(); } - @GetMapping(value = "/u/{userName}/followers/toc", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) + @GetMapping(value = "/u/{userName}/followers/toc", produces = { Context.LD_JSON_MEDIA_TYPE, + Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE }) public OrderedCollection getFollowers(@PathVariable String userName) { User user = userService.getUserByName(userName); if (!user.isAnonymous()) { @@ -189,9 +193,10 @@ public class Profile { throw new HttpNotFoundException(); } - @GetMapping(value = "/u/{userName}/followers", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) + @GetMapping(value = "/u/{userName}/followers", produces = { Context.LD_JSON_MEDIA_TYPE, + Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE }) public OrderedCollectionPage getFollowersPage(@PathVariable String userName, - @RequestParam(required = false, defaultValue = "0") int page) { + @RequestParam(required = false, defaultValue = "0") int page) { User user = userService.getUserByName(userName); if (!user.isAnonymous()) { UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri); @@ -217,7 +222,8 @@ public class Profile { throw new HttpNotFoundException(); } - @GetMapping(value = "/u/{userName}/following/toc", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) + @GetMapping(value = "/u/{userName}/following/toc", produces = { Context.LD_JSON_MEDIA_TYPE, + Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE }) public OrderedCollection getFollowing(@PathVariable String userName) { User user = userService.getUserByName(userName); if (!user.isAnonymous()) { @@ -231,9 +237,10 @@ public class Profile { throw new HttpNotFoundException(); } - @GetMapping(value = "/u/{userName}/following", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) + @GetMapping(value = "/u/{userName}/following", produces = { Context.LD_JSON_MEDIA_TYPE, + Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE }) public OrderedCollectionPage getFollowingPage(@PathVariable String userName, - @RequestParam(required = false, defaultValue = "0") int page) { + @RequestParam(required = false, defaultValue = "0") int page) { User user = userService.getUserByName(userName); if (!user.isAnonymous()) { UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(baseUri); @@ -259,31 +266,28 @@ public class Profile { throw new HttpNotFoundException(); } - @GetMapping(value = "/n/{mid}-{rid}", produces = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) + @GetMapping(value = "/n/{mid}-{rid}", produces = { Context.LD_JSON_MEDIA_TYPE, + Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE }) public Context showNote(@PathVariable int mid, @PathVariable int rid) { if (rid > 0) { // reply - return Context.build(activityPubManager.makeNote( - messagesService.getReply(mid, rid))); + return Context.build(activityPubManager.makeNote(messagesService.getReply(mid, rid))); } - return Context.build(activityPubManager.makeNote( - messagesService.getMessage(mid).orElseThrow(IllegalStateException::new))); + return Context.build( + activityPubManager.makeNote(messagesService.getMessage(mid).orElseThrow(IllegalStateException::new))); } - @PostMapping(value = "/api/inbox", consumes = {Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE}) - public ResponseEntity processInbox( - @Visitor User visitor, - InputStream inboxData) throws Exception { + @PostMapping(value = "/api/inbox", consumes = { Context.LD_JSON_MEDIA_TYPE, + Context.ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE }) + public ResponseEntity processInbox(@Visitor User visitor, InputStream inboxData) throws Exception { String inbox = IOUtils.toString(inboxData, StandardCharsets.UTF_8); logger.info("Inbox: {}", inbox); Activity activity = jsonMapper.readValue(inbox, Activity.class); if ((StringUtils.isNotEmpty(visitor.getUri().toString()) - && visitor.getUri().equals(URI.create(activity.getActor()))) - || !visitor.isAnonymous()) { + && visitor.getUri().equals(URI.create(activity.getActor()))) || !visitor.isAnonymous()) { if (activity instanceof Follow) { Follow followRequest = (Follow) activity; - applicationEventPublisher.publishEvent( - new FollowEvent(this, followRequest)); + applicationEventPublisher.publishEvent(new FollowEvent(this, followRequest)); return new ResponseEntity<>(CommandResult.fromString("Follow request accepted"), HttpStatus.ACCEPTED); } @@ -292,11 +296,15 @@ public class Profile { String objectType = (String) object.get("type"); String objectObject = (String) object.get("object"); if (objectType.equals("Follow")) { - applicationEventPublisher.publishEvent(new UndoFollowEvent(this, activity.getActor(), objectObject)); - return new ResponseEntity<>(CommandResult.fromString("Undo follow request accepted"), HttpStatus.OK); + applicationEventPublisher + .publishEvent(new UndoFollowEvent(this, activity.getActor(), objectObject)); + return new ResponseEntity<>(CommandResult.fromString("Undo follow request accepted"), + HttpStatus.OK); } else if (objectType.equals("Like") || objectType.equals("Announce")) { - applicationEventPublisher.publishEvent(new UndoAnnounceEvent(this, activity.getActor(), objectObject)); - return new ResponseEntity<>(CommandResult.fromString("Undo like/announce request accepted"), HttpStatus.OK); + applicationEventPublisher + .publishEvent(new UndoAnnounceEvent(this, activity.getActor(), objectObject)); + return new ResponseEntity<>(CommandResult.fromString("Undo like/announce request accepted"), + HttpStatus.OK); } } if (activity instanceof Create) { @@ -305,7 +313,8 @@ public class Profile { if (note.get("type").equals("Note")) { URI noteId = URI.create((String) note.get("id")); if (messagesService.replyExists(noteId)) { - return new ResponseEntity<>(CommandResult.fromString("Reply already exists"), HttpStatus.OK); + return new ResponseEntity<>(CommandResult.fromString("Reply already exists"), + HttpStatus.OK); } else { String inReplyTo = (String) note.get("inReplyTo"); if (StringUtils.isNotBlank(inReplyTo)) { @@ -314,16 +323,17 @@ public class Profile { User user = new User(); user.setUri(URI.create(activity.getActor())); String markdown = remarkConverter.convertFragment((String) note.get("content")); - String commandBody = note.get("attachment") == null ? markdown : - ((List) note.get("attachment")).stream().map(attachmentObj -> { + String commandBody = note.get("attachment") == null ? markdown + : ((List) note.get("attachment")).stream().map(attachmentObj -> { Map attachment = (Map) attachmentObj; String attachmentUrl = attachment.get("url"); String attachmentName = attachment.get("name"); return PlainTextFormatter.markdownUrl(attachmentUrl, attachmentName); - }).reduce(markdown, (currentUrl, nextUrl) -> String.format("%s\n%s", currentUrl, nextUrl)); + }).reduce(markdown, (currentUrl, nextUrl) -> String.format("%s\n%s", + currentUrl, nextUrl)); - CommandResult result = commandsManager.processCommand( - user, String.format("#%s %s", postId, commandBody), + CommandResult result = commandsManager.processCommand(user, + String.format("#%s %s", postId, commandBody), URI.create(StringUtils.EMPTY)); logger.info(jsonMapper.writeValueAsString(result)); if (result.getNewMessage().isPresent()) { @@ -337,17 +347,19 @@ public class Profile { if (reply != null) { User user = new User(); user.setUri(URI.create(activity.getActor())); - String markdown = remarkConverter.convertFragment((String)note.get("content")); + String markdown = remarkConverter.convertFragment((String) note.get("content")); // combine note text with attachment urls - String commandBody = note.get("attachment") == null ? markdown : - ((List) note.get("attachment")).stream().map(attachmentObj -> { - Map attachment = (Map) attachmentObj; - String attachmentUrl = attachment.get("url"); - String attachmentName = attachment.get("name"); - return PlainTextFormatter.markdownUrl(attachmentUrl, attachmentName); - }).reduce(markdown, (currentUrl, nextUrl) -> String.format("%s\n%s", currentUrl, nextUrl)); - CommandResult result = commandsManager.processCommand( - user, + String commandBody = note.get("attachment") == null ? markdown + : ((List) note.get("attachment")).stream() + .map(attachmentObj -> { + Map attachment = (Map) attachmentObj; + String attachmentUrl = attachment.get("url"); + String attachmentName = attachment.get("name"); + return PlainTextFormatter.markdownUrl(attachmentUrl, + attachmentName); + }).reduce(markdown, (currentUrl, nextUrl) -> String + .format("%s\n%s", currentUrl, nextUrl)); + CommandResult result = commandsManager.processCommand(user, String.format("#%d/%d %s", reply.getMid(), reply.getRid(), commandBody), URI.create(StringUtils.EMPTY)); logger.info(jsonMapper.writeValueAsString(result)); @@ -369,13 +381,14 @@ public class Profile { // Delete gone user // TODO: check if it is really deleted and remove copy-paste if (activity.getActor().equals(activity.getObject())) { - return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"), HttpStatus.ACCEPTED); + return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"), + HttpStatus.ACCEPTED); } } Map tombstone = (Map) activity.getObject(); if (tombstone.get("type").equals("Tombstone")) { URI actor = URI.create(activity.getActor()); - URI reply = URI.create((String)tombstone.get("id")); + URI reply = URI.create((String) tombstone.get("id")); messagesService.deleteReply(actor, reply); return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"), HttpStatus.OK); } @@ -383,7 +396,7 @@ public class Profile { if (activity instanceof Like || activity instanceof Announce) { String messageUri = activity.getObject() instanceof String ? (String) activity.getObject() : activity.getObject() instanceof Context ? ((Context) activity.getObject()).getId() - : (String) ((Map)activity.getObject()).get("id"); + : (String) ((Map) activity.getObject()).get("id"); applicationEventPublisher.publishEvent(new AnnounceEvent(this, activity.getActor(), messageUri)); return new ResponseEntity<>(CommandResult.fromString("Like/announce request accepted"), HttpStatus.OK); } @@ -394,13 +407,15 @@ public class Profile { if (activity.getObject() instanceof String) { // Delete gone user if (activity.getActor().equals(activity.getObject())) { - return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"), HttpStatus.ACCEPTED); + return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"), + HttpStatus.ACCEPTED); } } } return new ResponseEntity<>(CommandResult.fromString("Can not authenticate"), HttpStatus.UNAUTHORIZED); } - @PostMapping(value = {"/u/", "/api/u/"}, produces = MediaType.APPLICATION_JSON_VALUE) + + @PostMapping(value = { "/u/", "/api/u/" }, produces = MediaType.APPLICATION_JSON_VALUE) public User fetchUser(@RequestParam URI uri) { return activityPubManager.personToUser(uri); } diff --git a/src/main/java/com/juick/www/api/activity/model/Context.java b/src/main/java/com/juick/www/api/activity/model/Context.java index 217d2dc1..f41aa69c 100644 --- a/src/main/java/com/juick/www/api/activity/model/Context.java +++ b/src/main/java/com/juick/www/api/activity/model/Context.java @@ -91,6 +91,7 @@ public abstract class Context { public final static String ACTIVITY_STREAMS_URI = "https://www.w3.org/ns/activitystreams"; public final static String SECURITY_URI = "https://w3id.org/security/v1"; public final static String LD_JSON_MEDIA_TYPE = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""; + public final static String FALLBACK_JSON_MEDIA_TYPE = "application/json; profile=\"https://www.w3.org/ns/activitystreams\""; public final static String ACTIVITY_MEDIA_TYPE = "application/activity+json"; public final static String ACTIVITYSTREAMS_PROFILE_MEDIA_TYPE = ACTIVITY_MEDIA_TYPE + "; profile=\"https://www.w3.org/ns/activitystreams\""; public final static String ACTIVITYSTREAMS_PUBLIC = "https://www.w3.org/ns/activitystreams#Public"; -- cgit v1.2.3