diff --git a/Java/Spring/activitypubbot/build.gradle b/Java/Spring/activitypubbot/build.gradle index 3d5e023..1d68338 100644 --- a/Java/Spring/activitypubbot/build.gradle +++ b/Java/Spring/activitypubbot/build.gradle @@ -27,13 +27,22 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-rest' implementation 'org.springframework.boot:spring-boot-starter-groovy-templates' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-webflux' - implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.shell:spring-shell-starter' + implementation 'org.postgresql:postgresql' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'io.projectreactor:reactor-test' testImplementation 'org.springframework.shell:spring-shell-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + testCompileOnly 'org.projectlombok:lombok' + testAnnotationProcessor 'org.projectlombok:lombok' + } dependencyManagement { diff --git a/Java/Spring/activitypubbot/src/main/java/dev/activitypub/activitypubbot/APProperties.java b/Java/Spring/activitypubbot/src/main/java/dev/activitypub/activitypubbot/APProperties.java index 4951179..4e8dd41 100644 --- a/Java/Spring/activitypubbot/src/main/java/dev/activitypub/activitypubbot/APProperties.java +++ b/Java/Spring/activitypubbot/src/main/java/dev/activitypub/activitypubbot/APProperties.java @@ -2,36 +2,29 @@ package dev.activitypub.activitypubbot; import org.springframework.context.annotation.Configuration; import org.springframework.boot.context.properties.ConfigurationProperties; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; /** - * Object bound to "ap" prefix in {@link org.springframework.core.env.Environment}. - * - * @author Yvan Seth + * Object bound to "springbot" prefix in {@link org.springframework.core.env.Environment}. */ @Configuration -@ConfigurationProperties(prefix = "ap") +@ConfigurationProperties(prefix = "springbot") public class APProperties { /** - * Key File Path + * Scheme (i.e. https) */ - private String keyFilePath = "keyfile.pem"; + @Getter @Setter private String scheme; // = "https"; /** - * Server domain + * Server domain - i.e. https://<domain>/@username */ - private String serverDomain = "activitypub.bot"; + @Getter @Setter private String domain; // = "activitypub.bot"; - public String getKeyFilePath() { - return keyFilePath; - } - public void setKeyFilePath(String keyFilePath) { - this.keyFilePath = keyFilePath; - } - public String getServerDomain() { - return serverDomain; - } - public void setServerDomain(String serverDomain) { - this.serverDomain = serverDomain; - } + /*String getScheme() { return this.scheme; } + String getDomain() { return this.domain; } + void setScheme( String scheme ) { this.scheme = scheme; } + void setDomain( String domain ) { this.domain = domain; }*/ } diff --git a/Java/Spring/activitypubbot/src/main/java/dev/activitypub/activitypubbot/ActivityPubBotApplication.java b/Java/Spring/activitypubbot/src/main/java/dev/activitypub/activitypubbot/ActivityPubBotApplication.java index 7a9c1e0..f2ed07f 100644 --- a/Java/Spring/activitypubbot/src/main/java/dev/activitypub/activitypubbot/ActivityPubBotApplication.java +++ b/Java/Spring/activitypubbot/src/main/java/dev/activitypub/activitypubbot/ActivityPubBotApplication.java @@ -9,18 +9,21 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; @SpringBootApplication +@EnableJpaAuditing public class ActivityPubBotApplication extends SpringBootServletInitializer { //comment below if deploying outside web container --> - @Override + /*@Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { return builder.sources(ActivityPubBotApplication.class); - } + }*/ public static void main(String[] args) { SpringApplication.run(ActivityPubBotApplication.class); diff --git a/Java/Spring/activitypubbot/src/main/java/dev/activitypub/activitypubbot/Bot.java b/Java/Spring/activitypubbot/src/main/java/dev/activitypub/activitypubbot/Bot.java new file mode 100644 index 0000000..a65ee18 --- /dev/null +++ b/Java/Spring/activitypubbot/src/main/java/dev/activitypub/activitypubbot/Bot.java @@ -0,0 +1,114 @@ +package dev.activitypub.activitypubbot; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import java.sql.Date; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.Id; +import jakarta.persistence.Column; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import java.time.Instant; + +/** + * Our core Bot (aka user) data as stored persistently in the database. + * This is all the key non-derived data as required by the ActivityPub + * specification. NOTE: This is not a comprehensive implementation! + */ +@Entity +@EntityListeners(AuditingEntityListener.class) +public class Bot { + + // TODO: probably the wrong way to do this, and it isn't really compatible with 'Entity' anyway + //private APProperties props; + /*public Bot(APProperties props) { + this.props = props; + }*/ + // non-data auto-generated primary key, this should never be exposed + @Id + @GeneratedValue(strategy=GenerationType.AUTO) + private Long id; + + + /////////////////////////////////////////////////////////////////////////// + // The next values are user-supplied + + /** + * Username of the bot, i.e. the value after the @: @<username>@<domain> + * Note that in the Activity Pub spec this is encoded as 'preferredUsername', + * we shorten just to username here for clarity and brevity in the code. + * + * @param username the username value + * @return the username value + */ + @Column(nullable=false,unique=true) + @Getter @Setter private String username; // "preferredUsername: springbot", + + /** + * The "friendly" formatted name of the bot, can have spaces, etc. + * + * @param name a presentational name for the bot + * @return the presentational name for the bot + */ + @Column(nullable=true,unique=false) + @Getter @Setter private String name; // "Spring Bot", + + /** + * A bit of text to describe the bot/account, an "about". + * + * @param summary Text describing the bot/account + * @return the summary string + */ + @Column(nullable=true,unique=false) + @Getter @Setter private String summary; // "
A bot written using Java/Spring
", + + + /////////////////////////////////////////////////////////////////////////// + // The following values will be auto-generated on bot-creation + + @CreatedDate // TODO: is this really the right approach to auto-timestamp, not sure about all this "auditing" stuff, feels like a misuse + @Getter private Instant published; // "2025-01-24T00:00:00Z", + + // TODO how and where do we generate this beastie?!?! + @Column(nullable=true,unique=false) // FIXME: this isn't true, just easy for now + @Getter private String publicKeyPem; // "-----BEGIN PUBLIC KEY-----\\nMI [...] AB\\n-----END PUBLIC KEY-----" + + /////////////////////////////////////////////////////////////////////////// + // These values can just be constants for the timebeing + + @Column(nullable=false,unique=false) + @Getter private static final String type = "Person"; + + @Column(nullable=false,unique=false) + @Getter private static final boolean manuallyApproveFollowers = false; + + @Column(nullable=false,unique=false) + @Getter private static final boolean indexable = false; + + // TODO: should this class have the functions for generating derived values based on config vals? How does it get the config... + + /* these are all just derived from (preferred)Username - the scheme and domain-name should come from config + private String id; // "https://springbot.seth.id.au/users/springbot", + private String url; // "https://springbot.seth.id.au/@springbot", + private String inbox; // "https://springbot.seth.id.au/users/springbot/inbox", + private String publicKeyId; // "https://springbot.seth.id.au/users/springbot#main-key", + */ + public String getId() { + // TODO: is there some sort of 'uribuilder' - probably need our own, lots of uris to build + //return props.getScheme() + "://" + props.getDomain() + "/users/" + this.getUsername(); + return this.getUsername(); + } + + /* this is just a copy of "id" + private String publicKeyOwner; // "https://springap.seth.id.au/users/springbot", + */ + + @Override + public String toString() { + return "Bot: " + username; + } +} diff --git a/Java/Spring/activitypubbot/src/main/java/dev/activitypub/activitypubbot/BotController.java b/Java/Spring/activitypubbot/src/main/java/dev/activitypub/activitypubbot/BotController.java new file mode 100644 index 0000000..3b906d0 --- /dev/null +++ b/Java/Spring/activitypubbot/src/main/java/dev/activitypub/activitypubbot/BotController.java @@ -0,0 +1,45 @@ +package dev.activitypub.activitypubbot; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.ui.Model; +import java.util.List; + +@Controller +public class BotController { + + @Autowired + private BotRepo botRepo; + + @GetMapping("/viewbot") + public String listAll(Model model) { + System.out.println("WebHandler::viewbot"); + + List