├── .github
└── workflows
│ └── maven.yml
├── .gitignore
├── README.md
├── aggregates
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── social
│ │ └── pantheon
│ │ └── aggregates
│ │ ├── Actor.java
│ │ └── AggregatesServer.java
│ └── resources
│ └── bootstrap.properties
├── api
├── activitypub
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── social
│ │ │ └── pantheon
│ │ │ └── api
│ │ │ └── activitypub
│ │ │ ├── ActivityPubServer.java
│ │ │ ├── controllers
│ │ │ ├── ActorController.java
│ │ │ └── InboxController.java
│ │ │ ├── filters
│ │ │ └── SignatureVerificationFilter.java
│ │ │ └── model
│ │ │ └── Actor.java
│ │ └── resources
│ │ └── bootstrap.properties
├── graphql
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── social
│ │ │ └── pantheon
│ │ │ └── graphql
│ │ │ ├── GraphQLServer.java
│ │ │ ├── controllers
│ │ │ └── ActorsController.java
│ │ │ └── model
│ │ │ ├── Actor.java
│ │ │ └── Relationship.java
│ │ └── resources
│ │ └── bootstrap.properties
├── nodeinfo
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── social
│ │ │ └── pantheon
│ │ │ └── api
│ │ │ └── nodeinfo
│ │ │ └── NodeInfoServer.java
│ │ └── resources
│ │ └── bootstrap.properties
├── pom.xml
└── webfinger
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ └── social
│ │ └── pantheon
│ │ └── api
│ │ └── webfinger
│ │ ├── WebFingerServer.java
│ │ ├── controllers
│ │ └── WebFingerController.java
│ │ ├── converters
│ │ └── ActorToJRD.java
│ │ └── model
│ │ ├── JsonResourceDescriptor.java
│ │ └── LinkRelation.java
│ └── resources
│ └── bootstrap.properties
├── cloud
├── axon
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── social
│ │ │ └── pantheon
│ │ │ └── cloud
│ │ │ └── axon
│ │ │ └── AxonServer.java
│ │ └── resources
│ │ └── application.properties
├── config
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── social
│ │ │ └── pantheon
│ │ │ └── cloud
│ │ │ └── config
│ │ │ └── ConfigServer.java
│ │ └── resources
│ │ ├── application.properties
│ │ └── bootstrap.yaml
├── discovery
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── social
│ │ │ └── pantheon
│ │ │ └── cloud
│ │ │ └── discovery
│ │ │ └── DiscoveryServer.java
│ │ └── resources
│ │ └── application.properties
├── gateway
│ ├── pom.xml
│ └── src
│ │ └── main
│ │ ├── java
│ │ └── social
│ │ │ └── pantheon
│ │ │ └── cloud
│ │ │ └── gateway
│ │ │ └── GatewayServer.java
│ │ └── resources
│ │ └── bootstrap.properties
├── pom.xml
└── status
│ ├── pom.xml
│ └── src
│ └── main
│ ├── java
│ └── social
│ │ └── pantheon
│ │ └── cloud
│ │ └── monitor
│ │ └── StatusServer.java
│ └── resources
│ └── bootstrap.properties
├── commons
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── social
│ │ └── pantheon
│ │ ├── CommonsAutoConfiguration.java
│ │ ├── jsonld
│ │ ├── Context.java
│ │ └── JsonLDSerializer.java
│ │ ├── model
│ │ ├── commands
│ │ │ ├── CreateActorCommand.java
│ │ │ ├── FollowActorCommand.java
│ │ │ └── UnfollowActorCommand.java
│ │ ├── dto
│ │ │ ├── ActorDTO.java
│ │ │ └── RelationshipDTO.java
│ │ ├── events
│ │ │ ├── ActorCreatedEvent.java
│ │ │ ├── ActorFollowedEvent.java
│ │ │ └── ActorUnfollowedEvent.java
│ │ ├── queries
│ │ │ ├── FetchExternalPublicKeyById.java
│ │ │ ├── GetActorById.java
│ │ │ ├── GetFollowersForActor.java
│ │ │ ├── GetFollowingForActor.java
│ │ │ ├── GetPublicKeyById.java
│ │ │ ├── GetSignatureForInput.java
│ │ │ └── VerifySignatureForInput.java
│ │ └── value
│ │ │ ├── ActorId.java
│ │ │ └── PublicKey.java
│ │ └── services
│ │ └── QueryService.java
│ └── resources
│ └── META-INF
│ └── spring.factories
├── docker-compose.yml
├── pom.xml
└── views
├── actorgraph
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── social
│ │ └── pantheon
│ │ └── views
│ │ └── actorgraph
│ │ ├── ActorGraphServer.java
│ │ ├── Neo4jConfiguration.java
│ │ ├── Neo4jTokenStore.java
│ │ ├── serializers
│ │ ├── ActorSerializer.java
│ │ └── NodeSerializer.java
│ │ └── services
│ │ └── ActorService.java
│ └── resources
│ └── bootstrap.properties
├── keyholder
├── pom.xml
└── src
│ └── main
│ ├── java
│ └── social
│ │ └── pantheon
│ │ └── views
│ │ └── keyholder
│ │ ├── KeyHolderServer.java
│ │ ├── VaultConfiguration.java
│ │ └── services
│ │ ├── KeyService.java
│ │ └── SignatureService.java
│ └── resources
│ └── bootstrap.properties
└── pom.xml
/.github/workflows/maven.yml:
--------------------------------------------------------------------------------
1 | name: Java CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v1
12 | - name: Set up JDK 11
13 | uses: actions/setup-java@v1
14 | with:
15 | java-version: 11
16 | - name: Build with Maven
17 | run: mvn package --file pom.xml
18 | - name: Deploy
19 | run: mvn deploy --file pom.xml -Dregistry=https://maven.pkg.github.com/TGNThump
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **.idea/**
2 | **.iml
3 | configuration/**
4 | **/target/**
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pantheon
2 | A federated platform for building communities.
--------------------------------------------------------------------------------
/aggregates/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | pantheon
7 | social.pantheon
8 | 0.0.6-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | aggregates
13 |
14 |
15 |
16 | social.pantheon
17 | commons
18 |
19 |
20 | org.axonframework
21 | axon-spring-boot-starter
22 |
23 |
24 | org.springframework.boot
25 | spring-boot-starter-web
26 |
27 |
28 |
29 |
30 |
31 |
32 | org.springframework.boot
33 | spring-boot-maven-plugin
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/aggregates/src/main/java/social/pantheon/aggregates/Actor.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.aggregates;
2 |
3 | import lombok.NoArgsConstructor;
4 | import lombok.extern.log4j.Log4j2;
5 | import org.axonframework.commandhandling.CommandHandler;
6 | import org.axonframework.eventsourcing.EventSourcingHandler;
7 | import org.axonframework.modelling.command.AggregateIdentifier;
8 | import org.axonframework.spring.stereotype.Aggregate;
9 | import social.pantheon.model.commands.CreateActorCommand;
10 | import social.pantheon.model.commands.FollowActorCommand;
11 | import social.pantheon.model.commands.UnfollowActorCommand;
12 | import social.pantheon.model.events.ActorCreatedEvent;
13 | import social.pantheon.model.events.ActorFollowedEvent;
14 | import social.pantheon.model.events.ActorUnfollowedEvent;
15 | import social.pantheon.model.value.ActorId;
16 |
17 | import javax.validation.Valid;
18 | import java.util.ArrayList;
19 | import java.util.List;
20 |
21 | import static org.axonframework.modelling.command.AggregateLifecycle.apply;
22 |
23 | @Log4j2
24 | @Aggregate
25 | @NoArgsConstructor
26 | public class Actor {
27 |
28 | @AggregateIdentifier
29 | ActorId id;
30 |
31 | List blockedActors;
32 | List following;
33 |
34 | @CommandHandler
35 | public Actor(@Valid CreateActorCommand command) {
36 | apply(new ActorCreatedEvent(command.getId(), command.getKeyId()));
37 | }
38 |
39 | @EventSourcingHandler
40 | public void on(ActorCreatedEvent event){
41 | id = event.getId();
42 | blockedActors = new ArrayList<>();
43 | following = new ArrayList<>();
44 | }
45 |
46 | @CommandHandler
47 | public void handle(FollowActorCommand command){
48 | if (following.contains(command.getTarget())) return;
49 | apply(new ActorFollowedEvent(command.getSource(), command.getTarget()));
50 | }
51 |
52 | @EventSourcingHandler
53 | public void on(ActorFollowedEvent event){
54 | following.add(event.getTarget());
55 | }
56 |
57 | @CommandHandler
58 | public void handle(UnfollowActorCommand command){
59 | if (!following.contains(command.getTarget())) return;
60 | apply(new ActorUnfollowedEvent(command.getSource(), command.getTarget()));
61 | }
62 |
63 | @EventSourcingHandler
64 | public void on(ActorUnfollowedEvent event){
65 | following.remove(event.getTarget());
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/aggregates/src/main/java/social/pantheon/aggregates/AggregatesServer.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.aggregates;
2 |
3 | import lombok.extern.log4j.Log4j2;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
7 |
8 | @Log4j2
9 | @SpringBootApplication
10 | @EnableDiscoveryClient
11 | public class AggregatesServer {
12 |
13 | public static void main(String[] args){
14 | SpringApplication.run(AggregatesServer.class, args);
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/aggregates/src/main/resources/bootstrap.properties:
--------------------------------------------------------------------------------
1 | spring.application.name=aggregates
--------------------------------------------------------------------------------
/api/activitypub/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | api
7 | social.pantheon
8 | 0.0.6-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | social.pantheon.api
13 | activitypub
14 |
15 |
16 |
17 |
18 | org.springframework.boot
19 | spring-boot-maven-plugin
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/api/activitypub/src/main/java/social/pantheon/api/activitypub/ActivityPubServer.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.api.activitypub;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class ActivityPubServer {
8 |
9 | public static void main(String[] args){
10 | SpringApplication.run(ActivityPubServer.class, args);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/api/activitypub/src/main/java/social/pantheon/api/activitypub/controllers/ActorController.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.api.activitypub.controllers;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import org.springframework.web.bind.annotation.GetMapping;
5 | import org.springframework.web.bind.annotation.PathVariable;
6 | import org.springframework.web.bind.annotation.RestController;
7 | import reactor.core.publisher.Mono;
8 | import social.pantheon.api.activitypub.model.Actor;
9 | import social.pantheon.model.dto.ActorDTO;
10 | import social.pantheon.model.queries.GetActorById;
11 | import social.pantheon.model.queries.GetPublicKeyById;
12 | import social.pantheon.model.value.ActorId;
13 | import social.pantheon.model.value.PublicKey;
14 | import social.pantheon.services.QueryService;
15 |
16 | @RestController
17 | @RequiredArgsConstructor
18 | public class ActorController {
19 |
20 | private final QueryService queryService;
21 | private final ActorId.Provider actorIdProvider;
22 |
23 | @GetMapping("/@{username}")
24 | public Mono getActor(@PathVariable String username){
25 | ActorId actorId = actorIdProvider.get(username);
26 |
27 | Mono actorMono = queryService.mono(new GetActorById(actorId), ActorDTO.class);
28 | Mono keyMono = queryService.mono(new GetPublicKeyById(actorId.getLocalUrl() + "#main-key"), PublicKey.class);
29 |
30 | return Mono.zip(actorMono, keyMono, (actorDTO, publicKey) -> {
31 | Actor actor = new Actor();
32 | actor.setId("https://" + actorDTO.getId().getLocalUrl());
33 | actor.setSharedInbox("https://" + actorDTO.getId().getDomain() + "/inbox");
34 | actor.setPreferredUsername(actorDTO.getId().getUsername());
35 | actor.setPublicKey(publicKey);
36 | return actor;
37 | });
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/api/activitypub/src/main/java/social/pantheon/api/activitypub/controllers/InboxController.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.api.activitypub.controllers;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import lombok.extern.log4j.Log4j2;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.web.bind.annotation.RestController;
7 | import org.springframework.web.reactive.function.server.RouterFunction;
8 | import org.springframework.web.reactive.function.server.RouterFunctions;
9 | import org.springframework.web.reactive.function.server.ServerRequest;
10 | import org.springframework.web.reactive.function.server.ServerResponse;
11 | import reactor.core.publisher.Mono;
12 | import social.pantheon.api.activitypub.filters.SignatureVerificationFilter;
13 |
14 | import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
15 | import static org.springframework.web.reactive.function.server.ServerResponse.ok;
16 |
17 | @Log4j2
18 | @RestController
19 | @RequiredArgsConstructor
20 | public class InboxController {
21 |
22 | private final SignatureVerificationFilter signatureVerificationFilter;
23 |
24 | @Bean
25 | public RouterFunction route(SignatureVerificationFilter filter) {
26 | return RouterFunctions.route(POST("/@{username}/inbox").or(POST("/inbox")), this::processActivity).filter(signatureVerificationFilter);
27 | }
28 |
29 | public Mono processActivity(ServerRequest request){
30 | log.debug(request.bodyToMono(String.class));
31 | return ok().build();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/api/activitypub/src/main/java/social/pantheon/api/activitypub/filters/SignatureVerificationFilter.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.api.activitypub.filters;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import org.springframework.http.HttpHeaders;
5 | import org.springframework.http.HttpStatus;
6 | import org.springframework.stereotype.Component;
7 | import org.springframework.web.reactive.function.server.HandlerFilterFunction;
8 | import org.springframework.web.reactive.function.server.HandlerFunction;
9 | import org.springframework.web.reactive.function.server.ServerRequest;
10 | import org.springframework.web.reactive.function.server.ServerResponse;
11 | import org.springframework.web.server.ResponseStatusException;
12 | import reactor.core.publisher.Mono;
13 | import social.pantheon.model.queries.VerifySignatureForInput;
14 | import social.pantheon.services.QueryService;
15 |
16 | import java.nio.ByteBuffer;
17 | import java.security.MessageDigest;
18 | import java.security.NoSuchAlgorithmException;
19 | import java.time.Instant;
20 | import java.time.format.DateTimeParseException;
21 | import java.util.Arrays;
22 | import java.util.Base64;
23 | import java.util.HashMap;
24 | import java.util.Map;
25 | import java.util.regex.Matcher;
26 | import java.util.regex.Pattern;
27 | import java.util.stream.Collectors;
28 |
29 | @Component
30 | @RequiredArgsConstructor
31 | public class SignatureVerificationFilter implements HandlerFilterFunction {
32 |
33 | private static final Pattern pattern = Pattern.compile("([a-z]+)=\"([^\"]+)\"", Pattern.CASE_INSENSITIVE);
34 | private static final MessageDigest digester = getDigester();
35 | private static final String REQUEST_TARGET = "(request-target)";
36 |
37 | private static MessageDigest getDigester(){
38 | try {
39 | return MessageDigest.getInstance("SHA-256");
40 | } catch (NoSuchAlgorithmException e) {
41 | e.printStackTrace();
42 | }
43 | return null;
44 | }
45 |
46 | private final QueryService queryService;
47 |
48 | @Override
49 | public Mono filter(ServerRequest request, HandlerFunction next) {
50 | try {
51 | HttpHeaders headers = request.headers().asHttpHeaders();
52 |
53 | if (!headers.containsKey("signature")) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Request not signed.");
54 | if (!headers.containsKey("date")) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Signed request date not present.");
55 | if (!checkTimeWindow(headers)) throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Signed request date outside acceptable time window.");
56 |
57 | String rawSignature = headers.getFirst("signature");
58 | Map params = new HashMap<>();
59 |
60 | for (String part : rawSignature.split(",")){
61 | Matcher matcher = pattern.matcher(part);
62 | if (!matcher.matches()) continue;
63 | params.put(matcher.group(1), matcher.group(2));
64 | }
65 |
66 | if (!params.containsKey("keyId") || !params.containsKey("signature"))
67 | throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Signature header does not contain required properties keyId and signature.");
68 |
69 | String keyId = params.get("keyId");
70 | String signature = params.get("signature");
71 | String signedHeaders = params.containsKey("headers") ? params.get("headers") : "date";
72 |
73 | String message = Arrays.asList(signedHeaders.toLowerCase().split(" ")).stream().map(signedHeader -> {
74 | if (signedHeader.equals(REQUEST_TARGET)){
75 | return REQUEST_TARGET + ": " + request.methodName().toLowerCase() + " " + request.path();
76 | } else if (signedHeader.equals("digest")){
77 | digester.update(request.bodyToMono(ByteBuffer.class).block());
78 | return "digest: " + Base64.getEncoder().encodeToString(digester.digest());
79 | } else {
80 | return signedHeader + ": " + String.join(",", headers.get(signedHeader));
81 | }
82 | }).collect(Collectors.joining("\n"));
83 |
84 | Mono verify = queryService.mono(new VerifySignatureForInput(keyId, message, signature), Boolean.class);
85 |
86 | return verify.flatMap(verified -> {
87 | if (verified) return next.handle(request);
88 | else throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid request signature.");
89 | });
90 | } catch (Throwable e){
91 | return Mono.error(e);
92 | }
93 | }
94 |
95 | private boolean checkTimeWindow(HttpHeaders headers) {
96 | try{
97 | Instant timeSent = Instant.parse(headers.getFirst("date"));
98 | return timeSent.isAfter(Instant.now().minusSeconds(30)) && timeSent.isBefore(Instant.now().plusSeconds(30));
99 | } catch (DateTimeParseException | NullPointerException ex){ return false; }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/api/activitypub/src/main/java/social/pantheon/api/activitypub/model/Actor.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.api.activitypub.model;
2 |
3 | import lombok.Data;
4 | import social.pantheon.jsonld.Context;
5 | import social.pantheon.model.value.PublicKey;
6 |
7 | @Data
8 | @Context("https://www.w3.org/ns/activitystreams")
9 | public class Actor {
10 | private String id;
11 | private String type = "Person";
12 | private String preferredUsername;
13 | private String summary;
14 | private String sharedInbox;
15 |
16 | private PublicKey publicKey;
17 |
18 | public String getInbox(){
19 | return getId() + "/inbox";
20 | }
21 |
22 | public String getOutbox(){
23 | return getId() + "/outbox";
24 | }
25 |
26 | public String getFollowing(){
27 | return getId() + "/following";
28 | }
29 |
30 | public String getFollowers(){
31 | return getId() + "/followers";
32 | }
33 |
34 | public String getLiked(){
35 | return getId() + "/liked";
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/api/activitypub/src/main/resources/bootstrap.properties:
--------------------------------------------------------------------------------
1 | spring.application.name=activitypub
--------------------------------------------------------------------------------
/api/graphql/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | api
7 | social.pantheon
8 | 0.0.6-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | social.pantheon.api
13 | graphql
14 |
15 |
16 |
17 | io.leangen.graphql
18 | graphql-spqr-spring-boot-starter
19 |
20 |
21 |
22 |
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-maven-plugin
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/api/graphql/src/main/java/social/pantheon/graphql/GraphQLServer.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.graphql;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class GraphQLServer {
8 |
9 | public static void main(String[] args){
10 | SpringApplication.run(GraphQLServer.class, args);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/api/graphql/src/main/java/social/pantheon/graphql/controllers/ActorsController.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.graphql.controllers;
2 |
3 | import io.leangen.graphql.annotations.GraphQLMutation;
4 | import io.leangen.graphql.annotations.GraphQLQuery;
5 | import io.leangen.graphql.spqr.spring.annotations.GraphQLApi;
6 | import lombok.RequiredArgsConstructor;
7 | import org.axonframework.commandhandling.gateway.CommandGateway;
8 | import org.springframework.beans.factory.ObjectProvider;
9 | import org.springframework.stereotype.Controller;
10 | import reactor.core.publisher.Mono;
11 | import social.pantheon.model.commands.CreateActorCommand;
12 | import social.pantheon.model.commands.FollowActorCommand;
13 | import social.pantheon.model.commands.UnfollowActorCommand;
14 | import social.pantheon.model.dto.ActorDTO;
15 | import social.pantheon.model.queries.GetActorById;
16 | import social.pantheon.model.value.ActorId;
17 | import social.pantheon.graphql.model.Actor;
18 | import social.pantheon.services.QueryService;
19 |
20 | import java.util.concurrent.CompletableFuture;
21 |
22 | @GraphQLApi
23 | @Controller
24 | @RequiredArgsConstructor
25 | public class ActorsController {
26 |
27 | private final QueryService queryService;
28 | private final CommandGateway commandGateway;
29 | private final ObjectProvider actorProvider;
30 |
31 | @GraphQLQuery
32 | public Mono getActor(ActorId id){
33 | return queryService.mono(new GetActorById(id), ActorDTO.class, actorProvider);
34 | }
35 |
36 | @GraphQLMutation
37 | public boolean create(CreateActorCommand cmd){
38 | commandGateway.sendAndWait(cmd);
39 | return true;
40 | }
41 |
42 | @GraphQLMutation
43 | public boolean follow(FollowActorCommand cmd){
44 | commandGateway.sendAndWait(cmd);
45 | return true;
46 | }
47 |
48 | @GraphQLMutation
49 | public boolean unfollow(UnfollowActorCommand cmd){
50 | commandGateway.sendAndWait(cmd);
51 | return true;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/api/graphql/src/main/java/social/pantheon/graphql/model/Actor.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.graphql.model;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import lombok.experimental.Delegate;
5 | import org.springframework.beans.factory.ObjectProvider;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.beans.factory.config.BeanDefinition;
8 | import org.springframework.context.annotation.Scope;
9 | import org.springframework.stereotype.Component;
10 | import reactor.core.publisher.Flux;
11 | import social.pantheon.model.dto.ActorDTO;
12 | import social.pantheon.model.dto.RelationshipDTO;
13 | import social.pantheon.model.queries.GetFollowersForActor;
14 | import social.pantheon.model.queries.GetFollowingForActor;
15 | import social.pantheon.services.QueryService;
16 |
17 | import java.util.List;
18 | import java.util.concurrent.CompletableFuture;
19 |
20 | @Component
21 | @Scope(BeanDefinition.SCOPE_PROTOTYPE)
22 | @RequiredArgsConstructor
23 | public class Actor{
24 | @Delegate
25 | private final ActorDTO actorDTO;
26 |
27 | @Autowired
28 | private QueryService queryService;
29 |
30 | private @Autowired ObjectProvider relationshipProvider;
31 |
32 | public Flux getFollowers(){
33 | return queryService.flux(new GetFollowersForActor(getId()), RelationshipDTO.class, relationshipProvider);
34 | }
35 |
36 | public Flux getFollowing(){
37 | return queryService.flux(new GetFollowingForActor(getId()), RelationshipDTO.class, relationshipProvider);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/api/graphql/src/main/java/social/pantheon/graphql/model/Relationship.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.graphql.model;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import org.springframework.beans.factory.ObjectProvider;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.beans.factory.config.BeanDefinition;
7 | import org.springframework.context.annotation.Scope;
8 | import org.springframework.stereotype.Component;
9 | import social.pantheon.model.dto.RelationshipDTO;
10 |
11 | import java.time.Instant;
12 |
13 | @Component
14 | @Scope(BeanDefinition.SCOPE_PROTOTYPE)
15 | @RequiredArgsConstructor
16 | public class Relationship {
17 |
18 | private final RelationshipDTO relationship;
19 |
20 | private @Autowired ObjectProvider actorProvider;
21 |
22 | public Actor getSource(){
23 | return actorProvider.getObject(relationship.getSource());
24 | }
25 |
26 | public Actor getTarget(){
27 | return actorProvider.getObject(relationship.getTarget());
28 | }
29 |
30 | public Instant getCreatedAt(){
31 | return relationship.getCreatedAt();
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/api/graphql/src/main/resources/bootstrap.properties:
--------------------------------------------------------------------------------
1 | spring.application.name=graphql
--------------------------------------------------------------------------------
/api/nodeinfo/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | api
7 | social.pantheon
8 | 0.0.6-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | social.pantheon.api
13 | nodeinfo
14 |
15 |
16 |
17 |
18 | org.springframework.boot
19 | spring-boot-maven-plugin
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/api/nodeinfo/src/main/java/social/pantheon/api/nodeinfo/NodeInfoServer.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.api.nodeinfo;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class NodeInfoServer {
8 |
9 | public static void main(String[] args){
10 | SpringApplication.run(NodeInfoServer.class, args);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/api/nodeinfo/src/main/resources/bootstrap.properties:
--------------------------------------------------------------------------------
1 | spring.application.name=nodeinfo
--------------------------------------------------------------------------------
/api/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | pantheon
7 | social.pantheon
8 | 0.0.6-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | social.pantheon
13 | api
14 | pom
15 |
16 |
17 | graphql
18 | activitypub
19 | webfinger
20 | nodeinfo
21 |
22 |
23 |
24 |
25 | social.pantheon
26 | commons
27 |
28 |
29 | org.axonframework
30 | axon-spring-boot-starter
31 |
32 |
33 | org.springframework.boot
34 | spring-boot-starter-webflux
35 |
36 |
37 |
--------------------------------------------------------------------------------
/api/webfinger/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | api
7 | social.pantheon
8 | 0.0.6-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | social.pantheon.api
13 | webfinger
14 |
15 |
16 |
17 |
18 | org.springframework.boot
19 | spring-boot-maven-plugin
20 |
21 |
22 | org.apache.maven.plugins
23 | maven-compiler-plugin
24 |
25 | 9
26 | 9
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/api/webfinger/src/main/java/social/pantheon/api/webfinger/WebFingerServer.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.api.webfinger;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class WebFingerServer {
8 |
9 | public static void main(String[] args){
10 | SpringApplication.run(WebFingerServer.class, args);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/api/webfinger/src/main/java/social/pantheon/api/webfinger/controllers/WebFingerController.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.api.webfinger.controllers;
2 |
3 | import lombok.RequiredArgsConstructor;
4 | import org.springframework.beans.factory.annotation.Value;
5 | import org.springframework.web.bind.annotation.GetMapping;
6 | import org.springframework.web.bind.annotation.RequestParam;
7 | import org.springframework.web.bind.annotation.RestController;
8 | import reactor.core.publisher.Mono;
9 | import social.pantheon.api.webfinger.converters.ActorToJRD;
10 | import social.pantheon.api.webfinger.model.JsonResourceDescriptor;
11 | import social.pantheon.model.dto.ActorDTO;
12 | import social.pantheon.model.queries.GetActorById;
13 | import social.pantheon.model.value.ActorId;
14 | import social.pantheon.services.QueryService;
15 |
16 | import java.util.List;
17 | import java.util.concurrent.CompletableFuture;
18 |
19 | @RestController
20 | @RequiredArgsConstructor
21 | public class WebFingerController {
22 |
23 | @Value("${pantheon.domain}")
24 | String domain;
25 |
26 | private final QueryService queryService;
27 | private final ActorToJRD actorToJRD;
28 |
29 | @GetMapping(".well-known/webfinger")
30 | public Mono getResource(@RequestParam String resource, @RequestParam(required = false) List rel){
31 | if (resource.startsWith("acct:")) resource = resource.substring("acct:".length());
32 | ActorId id = ActorId.of(resource);
33 |
34 | if (!id.getDomain().equals(domain)) return Mono.empty();
35 |
36 | return queryService.mono(new GetActorById(id), ActorDTO.class).map(actorToJRD);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/api/webfinger/src/main/java/social/pantheon/api/webfinger/converters/ActorToJRD.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.api.webfinger.converters;
2 |
3 | import org.springframework.stereotype.Component;
4 | import social.pantheon.api.webfinger.model.JsonResourceDescriptor;
5 | import social.pantheon.api.webfinger.model.LinkRelation;
6 | import social.pantheon.model.dto.ActorDTO;
7 |
8 | import java.util.List;
9 | import java.util.function.Function;
10 |
11 | @Component
12 | public class ActorToJRD implements Function {
13 |
14 | @Override
15 | public JsonResourceDescriptor apply(ActorDTO actor) {
16 | JsonResourceDescriptor jrd = new JsonResourceDescriptor("acct:" + actor.getId());
17 |
18 | jrd.setAliases(List.of(
19 | "https://" + actor.getId().getLocalUrl()
20 | ));
21 |
22 | jrd.setLinks(List.of(
23 | new LinkRelation("http://webfinger.net/rel/profile-page", "text/html", "https://" + actor.getId().getLocalUrl()),
24 | new LinkRelation("self", "application/activity+json", "https://" + actor.getId().getLocalUrl())
25 | ));
26 |
27 | return jrd;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/api/webfinger/src/main/java/social/pantheon/api/webfinger/model/JsonResourceDescriptor.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.api.webfinger.model;
2 |
3 | import com.google.common.collect.Lists;
4 | import lombok.AllArgsConstructor;
5 | import lombok.Data;
6 | import lombok.RequiredArgsConstructor;
7 |
8 | import java.util.HashMap;
9 | import java.util.List;
10 | import java.util.Map;
11 |
12 | @Data
13 | @AllArgsConstructor
14 | @RequiredArgsConstructor
15 | public class JsonResourceDescriptor {
16 |
17 | private final String subject;
18 | private List aliases = Lists.newArrayList();
19 | private Map properties = new HashMap<>();
20 | private List links = Lists.newArrayList();
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/api/webfinger/src/main/java/social/pantheon/api/webfinger/model/LinkRelation.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.api.webfinger.model;
2 |
3 | import lombok.AllArgsConstructor;
4 | import lombok.Data;
5 | import lombok.RequiredArgsConstructor;
6 |
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | @Data
11 | @AllArgsConstructor
12 | @RequiredArgsConstructor
13 | public class LinkRelation {
14 |
15 | private final String rel;
16 | private final String type;
17 | private final String href;
18 | private Map titles = new HashMap<>();
19 | private Map properties = new HashMap<>();
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/api/webfinger/src/main/resources/bootstrap.properties:
--------------------------------------------------------------------------------
1 | spring.application.name=webfinger
--------------------------------------------------------------------------------
/cloud/axon/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | cloud
7 | social.pantheon
8 | 0.0.6-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | social.pantheon.cloud
13 | axon
14 | ${axon.server.version}
15 |
16 |
17 |
18 | com.github.AxonIQ.axon-server-se
19 | axonserver
20 | axonserver-se-${axon.server.version}
21 |
22 |
23 |
24 |
25 |
26 | jitpack.io
27 | https://jitpack.io
28 |
29 |
30 |
31 |
32 |
33 |
34 | org.springframework.boot
35 | spring-boot-maven-plugin
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/cloud/axon/src/main/java/social/pantheon/cloud/axon/AxonServer.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.cloud.axon;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.scheduling.annotation.EnableAsync;
6 | import org.springframework.scheduling.annotation.EnableScheduling;
7 |
8 | @SpringBootApplication(scanBasePackageClasses = io.axoniq.axonserver.AxonServer.class)
9 | @EnableAsync
10 | @EnableScheduling
11 | public class AxonServer extends io.axoniq.axonserver.AxonServer {
12 |
13 | public static void main(String[] args){
14 | SpringApplication.run(AxonServer.class, args);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/cloud/axon/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.application.name=axon
2 | server.port=8024
--------------------------------------------------------------------------------
/cloud/config/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | social.pantheon
9 | cloud
10 | 0.0.6-SNAPSHOT
11 |
12 |
13 | social.pantheon.cloud
14 | config
15 |
16 |
17 |
18 | org.springframework.cloud
19 | spring-cloud-config-server
20 |
21 |
22 | org.springframework.cloud
23 | spring-cloud-starter-netflix-eureka-client
24 |
25 |
26 |
27 |
28 |
29 |
30 | org.springframework.boot
31 | spring-boot-maven-plugin
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/cloud/config/src/main/java/social/pantheon/cloud/config/ConfigServer.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.cloud.config;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.cloud.config.server.EnableConfigServer;
6 |
7 | @SpringBootApplication
8 | @EnableConfigServer
9 | public class ConfigServer {
10 |
11 | public static void main(String[] args){
12 | SpringApplication.run(ConfigServer.class, args);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/cloud/config/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port: 8888
--------------------------------------------------------------------------------
/cloud/config/src/main/resources/bootstrap.yaml:
--------------------------------------------------------------------------------
1 | spring:
2 | application:
3 | name: config
4 | profiles:
5 | active: composite
6 |
7 | cloud:
8 | config:
9 | server:
10 | bootstrap: true
11 | composite:
12 | -
13 | type: native
14 | searchLocations: file:./configuration
--------------------------------------------------------------------------------
/cloud/discovery/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | social.pantheon
9 | cloud
10 | 0.0.6-SNAPSHOT
11 |
12 |
13 | social.pantheon.cloud
14 | discovery
15 |
16 |
17 |
18 | org.springframework.cloud
19 | spring-cloud-starter-config
20 |
21 |
22 | org.springframework.cloud
23 | spring-cloud-starter-netflix-eureka-server
24 |
25 |
26 | de.codecentric
27 | spring-boot-admin-starter-client
28 |
29 |
30 | javax.xml.bind
31 | jaxb-api
32 |
33 |
34 | com.sun.xml.bind
35 | jaxb-core
36 |
37 |
38 | com.sun.xml.bind
39 | jaxb-impl
40 |
41 |
42 | javax.activation
43 | activation
44 |
45 |
46 |
47 |
48 |
49 |
50 | org.springframework.boot
51 | spring-boot-maven-plugin
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/cloud/discovery/src/main/java/social/pantheon/cloud/discovery/DiscoveryServer.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.cloud.discovery;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
6 |
7 | @SpringBootApplication
8 | @EnableEurekaServer
9 | public class DiscoveryServer {
10 |
11 | public static void main(String[] args) {
12 | SpringApplication.run(DiscoveryServer.class, args);
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/cloud/discovery/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | server.port=8761
2 |
3 | spring.application.name=discovery
4 | eureka.client.register-with-eureka=false
5 | eureka.client.fetch-registry=false
6 |
7 | logging.level.com.netflix.eureka=OFF
8 | logging.level.com.netflix.discovery=OFF
9 |
10 | spring.boot.admin.client.url=http://host.docker.internal:8762
11 | spring.boot.admin.client.instance.name=DISCOVERY
12 | spring.boot.admin.client.instance.service-base-url=http://host.docker.internal:8761
--------------------------------------------------------------------------------
/cloud/gateway/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | cloud
7 | social.pantheon
8 | 0.0.6-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | social.pantheon.cloud
13 | gateway
14 |
15 |
16 |
17 | org.springframework.cloud
18 | spring-cloud-starter-gateway
19 |
20 |
21 |
22 |
23 |
24 |
25 | org.springframework.boot
26 | spring-boot-maven-plugin
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/cloud/gateway/src/main/java/social/pantheon/cloud/gateway/GatewayServer.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.cloud.gateway;
2 |
3 | import brave.sampler.Sampler;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 | import org.springframework.cloud.gateway.route.RouteLocator;
7 | import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
8 | import org.springframework.context.annotation.Bean;
9 | import org.springframework.http.HttpMethod;
10 |
11 | import static java.util.regex.Pattern.quote;
12 |
13 | @SpringBootApplication
14 | public class GatewayServer {
15 |
16 | private static final String AP_CONTENT_TYPE = "^.*(" + quote("application/activity+json") + "|" + quote("application/ld+json") + ").*$";
17 |
18 | public static void main(String[] args) {
19 | SpringApplication.run(GatewayServer.class, args);
20 | }
21 |
22 | @Bean
23 | public Sampler getSampler(){
24 | return Sampler.ALWAYS_SAMPLE;
25 | }
26 |
27 | @Bean
28 | public RouteLocator routeLocator(RouteLocatorBuilder builder){
29 | return builder.routes()
30 | .route(p -> p.path("/.well-known/webfinger").uri("lb://webfinger"))
31 | .route(p -> p.path("/.well-known/nodeinfo").uri("lb://nodeinfo"))
32 | .route(p -> p.path("/graphql").uri("lb://graphql"))
33 | .route(p -> p.header("content-type", AP_CONTENT_TYPE).or().header("accept", AP_CONTENT_TYPE).uri("lb://activitypub"))
34 | .build();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/cloud/gateway/src/main/resources/bootstrap.properties:
--------------------------------------------------------------------------------
1 | spring.application.name=gateway
2 | server.port=80
3 | management.server.port=8080
--------------------------------------------------------------------------------
/cloud/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | pantheon
7 | social.pantheon
8 | 0.0.6-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | cloud
13 | pom
14 |
15 |
16 | axon
17 | config
18 | status
19 | discovery
20 | gateway
21 |
22 |
23 |
--------------------------------------------------------------------------------
/cloud/status/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | social.pantheon
9 | cloud
10 | 0.0.6-SNAPSHOT
11 |
12 |
13 | social.pantheon.cloud
14 | status
15 |
16 |
17 |
18 | de.codecentric
19 | spring-boot-admin-starter-server
20 |
21 |
22 |
23 |
24 |
25 |
26 | org.springframework.boot
27 | spring-boot-maven-plugin
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/cloud/status/src/main/java/social/pantheon/cloud/monitor/StatusServer.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.cloud.monitor;
2 |
3 | import de.codecentric.boot.admin.server.config.EnableAdminServer;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
6 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
7 | import org.springframework.context.annotation.Configuration;
8 |
9 | @Configuration
10 | @EnableAutoConfiguration
11 | @EnableDiscoveryClient
12 | @EnableAdminServer
13 | public class StatusServer {
14 |
15 | public static void main(String[] args) {
16 | SpringApplication.run(StatusServer.class, args);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/cloud/status/src/main/resources/bootstrap.properties:
--------------------------------------------------------------------------------
1 | spring.application.name=status
2 | server.port=8762
3 | spring.boot.admin.ui.brand=Pantheon Console
4 | spring.boot.admin.ui.title=Pantheon Console
--------------------------------------------------------------------------------
/commons/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | pantheon
7 | social.pantheon
8 | 0.0.6-SNAPSHOT
9 |
10 | 4.0.0
11 |
12 | commons
13 |
14 |
15 |
16 |
17 | org.apache.maven.plugins
18 | maven-compiler-plugin
19 |
20 | 11
21 | 11
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | org.axonframework
30 | axon-modelling
31 |
32 |
33 | javax.validation
34 | validation-api
35 |
36 |
37 | commons-codec
38 | commons-codec
39 |
40 |
41 | io.projectreactor
42 | reactor-core
43 |
44 |
45 | org.springframework.cloud
46 | spring-cloud-starter-zipkin
47 |
48 |
49 |
--------------------------------------------------------------------------------
/commons/src/main/java/social/pantheon/CommonsAutoConfiguration.java:
--------------------------------------------------------------------------------
1 | package social.pantheon;
2 |
3 | import brave.sampler.Sampler;
4 | import org.axonframework.queryhandling.QueryGateway;
5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 | import social.pantheon.model.value.ActorId;
9 | import social.pantheon.services.QueryService;
10 |
11 | @Configuration
12 | public class CommonsAutoConfiguration {
13 |
14 | @Bean
15 | @ConditionalOnMissingBean
16 | public ActorId.Provider actorIdProvider(){
17 | return new ActorId.Provider();
18 | }
19 |
20 | @Bean
21 | @ConditionalOnMissingBean
22 | public QueryService queryService(QueryGateway queryGateway){
23 | return new QueryService(queryGateway);
24 | }
25 |
26 | @Bean
27 | @ConditionalOnMissingBean
28 | public Sampler getSampler(){
29 | return Sampler.ALWAYS_SAMPLE;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/commons/src/main/java/social/pantheon/jsonld/Context.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.jsonld;
2 |
3 | import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
4 | import com.fasterxml.jackson.databind.annotation.JsonSerialize;
5 |
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 |
9 | @JacksonAnnotationsInside
10 | @Retention(RetentionPolicy.RUNTIME)
11 | @JsonSerialize(using = JsonLDSerializer.class)
12 | public @interface Context {
13 | String value();
14 | }
15 |
--------------------------------------------------------------------------------
/commons/src/main/java/social/pantheon/jsonld/JsonLDSerializer.java:
--------------------------------------------------------------------------------
1 | package social.pantheon.jsonld;
2 |
3 | import com.fasterxml.jackson.core.JsonGenerator;
4 | import com.fasterxml.jackson.databind.BeanDescription;
5 | import com.fasterxml.jackson.databind.JavaType;
6 | import com.fasterxml.jackson.databind.JsonSerializer;
7 | import com.fasterxml.jackson.databind.SerializerProvider;
8 | import com.fasterxml.jackson.databind.ser.BeanSerializerFactory;
9 | import com.fasterxml.jackson.databind.ser.std.StdSerializer;
10 | import lombok.extern.log4j.Log4j2;
11 |
12 | import java.io.IOException;
13 |
14 | @Log4j2
15 | public class JsonLDSerializer extends StdSerializer {
16 |
17 | protected JsonLDSerializer(){ this(null); }
18 | protected JsonLDSerializer(Class t){ super(t); }
19 |
20 | @Override
21 | public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
22 | jsonGenerator.writeStartObject();
23 | jsonGenerator.writeStringField("@context", o.getClass().getAnnotation(Context.class).value());
24 |
25 | serializeFields(o, jsonGenerator, serializerProvider);
26 |
27 | jsonGenerator.writeEndObject();
28 | }
29 |
30 | private void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider)
31 | throws IOException {
32 | JavaType javaType = provider.constructType(bean.getClass());
33 | BeanDescription beanDesc = provider.getConfig().introspect(javaType);
34 | JsonSerializer