Right, we're returning valid JSON again and are once again searchable from real Fedi servers! I'm not at all happy with either approach to generating the JSON here. Need some balance between the built in automation/jackson stuff and the custom/generated things we need to do...)
This commit is contained in:
parent
2d026711c1
commit
e4def78878
3 changed files with 99 additions and 8 deletions
|
|
@ -11,11 +11,13 @@ import org.bouncycastle.openssl.PEMWriter;
|
|||
import org.bouncycastle.util.io.pem.PemObject;
|
||||
import java.io.StringWriter;
|
||||
import java.io.IOException;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
|
||||
/**
|
||||
* POJO for our Bot...
|
||||
*/
|
||||
@Data
|
||||
@JsonSerialize(using = BotAPActorJsonSerializer.class)
|
||||
public class Bot {
|
||||
|
||||
@Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
package dev.activitypub.activitypubbot;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
|
||||
|
||||
/**
|
||||
* There is so much to be derived and generated when serialising a Bot
|
||||
* instanced for ActivityPub purposes it seems neater to just put all that in
|
||||
* one place than try to override each individual thing. FIXME: But I'm not
|
||||
* convinced this is the right approach, there's far too much manual specifying
|
||||
* and building of strings. So I need to look deeper into better options for
|
||||
* JSON generation. Part of the problem also is the need to mix in
|
||||
* configuration/property values to generate some fields. So there's a larger
|
||||
* design question to be resolved here.
|
||||
*
|
||||
* As per: https://www.baeldung.com/jackson-custom-serialization
|
||||
*/
|
||||
public class BotAPActorJsonSerializer extends StdSerializer<Bot> {
|
||||
|
||||
public BotAPActorJsonSerializer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public BotAPActorJsonSerializer(final Class<Bot> t) {
|
||||
super(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void serialize(final Bot bot, final JsonGenerator jgen, final SerializerProvider provider) throws IOException, JsonProcessingException {
|
||||
// FIXME: these are basically server settings and should come from properties?
|
||||
// or should they be in the database... as general settings, or as part of the user?
|
||||
// what are the implications of the domain, can it change and things still be valid?
|
||||
String domain = "springbot.seth.id.au";
|
||||
String scheme = "https";
|
||||
|
||||
jgen.writeStartObject();
|
||||
|
||||
// FIXME: got a whole bunch of string values here that should be refactored up or something
|
||||
jgen.writeArrayFieldStart("@context");
|
||||
jgen.writeString("https://www.w3.org/ns/activitystreams");
|
||||
jgen.writeString("https://w3id.org/security/v1");
|
||||
jgen.writeEndArray();
|
||||
|
||||
// FIXME: we at least need some sort of "URI generator"
|
||||
String id = scheme + "://" + domain + "/users/" + bot.getUsername();
|
||||
jgen.writeStringField("id", id );
|
||||
jgen.writeStringField("inbox", id + "/inbox" );
|
||||
jgen.writeStringField("outbox", id + "/outbox" );
|
||||
|
||||
jgen.writeStringField("preferredUsername", bot.getUsername());
|
||||
jgen.writeStringField("name", bot.getName());
|
||||
jgen.writeStringField("type", bot.getType());
|
||||
jgen.writeStringField("summary", bot.getSummary());
|
||||
|
||||
jgen.writeBooleanField("manuallyApproveFollowers", bot.isManuallyApproveFollowers());
|
||||
jgen.writeBooleanField("indexable", bot.isIndexable());
|
||||
|
||||
jgen.writeFieldName("publicKey");
|
||||
jgen.writeStartObject();
|
||||
jgen.writeStringField("id", id + "#main-key" );
|
||||
jgen.writeStringField("owner", id );
|
||||
jgen.writeStringField("publicKeyPem", bot.getPublicKeyPEMString());
|
||||
jgen.writeEndObject();
|
||||
jgen.writeEndObject();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -11,6 +11,10 @@ import org.springframework.http.HttpStatus;
|
|||
import org.springframework.http.ResponseEntity;
|
||||
import org.thymeleaf.context.Context;
|
||||
import org.thymeleaf.spring5.SpringTemplateEngine;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Here we handle any JSON/REST requests, which is how ActivityPub instances talk to each other.
|
||||
|
|
@ -25,7 +29,7 @@ public class RestHandler {
|
|||
* Really just an alias to /user/<username>
|
||||
*/
|
||||
@GetMapping(value = "/@{username}", produces = "application/activity+json") // content type based on Masto request
|
||||
public ResponseEntity<String> atactor(@PathVariable String username) {
|
||||
public Bot atactor(@PathVariable String username) {
|
||||
return this.actor( username );
|
||||
}
|
||||
|
||||
|
|
@ -33,33 +37,46 @@ public class RestHandler {
|
|||
* Get the bot/user 'actor' data response
|
||||
*/
|
||||
@GetMapping(value = "/users/{username}", produces = "application/activity+json") // content type based on Masto request
|
||||
public ResponseEntity<String> actor(@PathVariable String username) {
|
||||
public Bot actor(@PathVariable String username) {
|
||||
|
||||
Bot bot = botServ.getBotByUsername( username );
|
||||
if( bot == null ) {
|
||||
return new ResponseEntity<>("These are not the droids you are looking for.", HttpStatus.NOT_FOUND);
|
||||
//return new ResponseEntity<>("These are not the droids you are looking for.", HttpStatus.NOT_FOUND);
|
||||
}
|
||||
return ResponseEntity.ok(bot.toString());
|
||||
return bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Webfinger the user...
|
||||
*/
|
||||
@GetMapping(value = "/.well-known/webfinger", produces = "application/jrd+json") // content type based on Masto request
|
||||
public ResponseEntity<String> webfinger(@RequestParam("resource") String resource) {
|
||||
public String webfinger(@RequestParam("resource") String resource) throws JsonProcessingException {
|
||||
|
||||
// resource should be of the form: acct:<username>@<domain>
|
||||
// so this should be robustly checked, but for now just yoink out the username
|
||||
int colonPos = resource.indexOf(":");
|
||||
int atPos = resource.indexOf("@");
|
||||
if ( colonPos < 0 || atPos < 0 || atPos < colonPos ) {
|
||||
return new ResponseEntity<>("Incorrect query format",HttpStatus.BAD_REQUEST);
|
||||
//return new ResponseEntity<>("Incorrect query format",HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
String username = resource.substring(colonPos + 1, atPos);
|
||||
Bot bot = botServ.getBotByUsername( username );
|
||||
if( bot == null ) {
|
||||
return new ResponseEntity<>("These are not the droids you are looking for.", HttpStatus.NOT_FOUND);
|
||||
//return new ResponseEntity<>("These are not the droids you are looking for.", HttpStatus.NOT_FOUND);
|
||||
}
|
||||
return ResponseEntity.ok(bot.toString());
|
||||
|
||||
// TODO: So here's a whole other way of generating custom JSON as compared to the BotAPActorJSONSerializer approach
|
||||
Map<String, Object> response = Map.of(
|
||||
"subject", bot.getUsername() + "@springbot.seth.id.au",
|
||||
"links", List.of(
|
||||
Map.of(
|
||||
"rel", "self",
|
||||
"type", "application/activity+json",
|
||||
"href", "https://springbot.seth.id.au/users/" + bot.getUsername()
|
||||
)
|
||||
)
|
||||
);
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
return mapper.writeValueAsString( response );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue