we are now writing and reading bot info to postgres! That was a slog... in the meantime we've changed from JSP to ThymeLeaf, which seems to be much nicer to work with
This commit is contained in:
parent
76af0c01d2
commit
06cb88f738
15 changed files with 310 additions and 54 deletions
|
|
@ -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.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 {
|
||||
|
|
|
|||
|
|
@ -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; }*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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; // "<p>A bot written using Java/Spring</p>",
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Bot> botlist = botRepo.findAll();
|
||||
model.addAttribute("bots", botlist);
|
||||
|
||||
return "viewbot";
|
||||
}
|
||||
|
||||
@GetMapping("/makebot")
|
||||
public String makebotget(Model model) {
|
||||
System.out.println("WebHandler::makebot");
|
||||
|
||||
Bot bot = new Bot();
|
||||
model.addAttribute("bot", bot);
|
||||
|
||||
return "makebot";
|
||||
}
|
||||
|
||||
@PostMapping("/makebot")
|
||||
public String makebotpost(@ModelAttribute("bot") Bot bot) {
|
||||
System.out.println(bot);
|
||||
|
||||
botRepo.save(bot);
|
||||
|
||||
return "makebot_submitted";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package dev.activitypub.activitypubbot;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface BotRepo extends JpaRepository<Bot, Long> {
|
||||
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ package dev.activitypub.activitypubbot;
|
|||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Here we handle any JSON/REST requests.
|
||||
|
|
@ -12,15 +11,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
@RequestMapping( headers = "accept=application/json" )
|
||||
public class RestHandler {
|
||||
|
||||
@Autowired
|
||||
public APProperties apProps;
|
||||
|
||||
// FIXME: just playing with working out app properties access here
|
||||
@GetMapping("/key")
|
||||
public String key() {
|
||||
return apProps.getKeyFilePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Really just an alias to /user/springbot
|
||||
* TODO: how to auto-map @<user> to /users/<user>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package dev.activitypub.activitypubbot;
|
|||
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* Here we handle all non-JSON/REST requests - i.e. the normal "web" view
|
||||
|
|
@ -9,6 +10,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||
@Controller
|
||||
public class WebHandler {
|
||||
|
||||
@Autowired
|
||||
private APProperties apProps;
|
||||
|
||||
/**
|
||||
* handle requests for our "actor" - this presents the web/html view of
|
||||
* the bot
|
||||
|
|
@ -32,7 +36,7 @@ public class WebHandler {
|
|||
System.out.println("WebHandler::root");
|
||||
return "index";
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO: presumably there is some way to map things like /<string> to capture string and attempt to resolve template
|
||||
@RequestMapping("/viewbot")
|
||||
public String viewbot() {
|
||||
|
|
@ -44,4 +48,5 @@ public class WebHandler {
|
|||
System.out.println("WebHandler::makebot");
|
||||
return "makebot";
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,22 @@
|
|||
spring.application.name=activitypubbot
|
||||
springbot.domain=springbot.seth.id.au
|
||||
springbot.scheme=https
|
||||
|
||||
spring.mvc.view.prefix: /WEB-INF/views/
|
||||
spring.mvc.view.suffix: .jsp
|
||||
|
||||
server.ssl.key-store-type=PKCS12
|
||||
server.ssl.key-store=classpath:keys/activitypubbot.p12
|
||||
server.ssl.key-store-password=password
|
||||
server.ssl.key-alias=activitypubbot
|
||||
server.ssl.enabled=true
|
||||
|
||||
spring.datasource.url=jdbc:postgresql://localhost:5432/springbot
|
||||
spring.datasource.username=springbot
|
||||
spring.datasource.password=spr1ngb0t
|
||||
# do not use the following property value in production
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
|
||||
spring.jpa.properties.jakarta.persistence.schema-generation.scripts.action=create
|
||||
spring.jpa.properties.jakarta.persistence.schema-generation.scripts.create-target=create.sql
|
||||
spring.jpa.properties.jakarta.persistence.schema-generation.scripts.create-source=metadata
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="ISO-8859-1">
|
||||
<title>Make Bot</title>
|
||||
<style type="text/css">
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 200px;
|
||||
margin: 5px;
|
||||
text-align: left;
|
||||
}
|
||||
input[type=text], input[type=password], select {
|
||||
display: inline-block;
|
||||
width: 200px;
|
||||
}
|
||||
input[type=radio] {
|
||||
margin-left: 45px;
|
||||
}
|
||||
input[type=checkbox] {
|
||||
margin-right: 190px;
|
||||
}
|
||||
button {
|
||||
padding: 5px;
|
||||
margin: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div align="center">
|
||||
<h1>Make A Bot!</h1>
|
||||
<form action="#" th:action="@{/makebot}" method="post" th:object="${bot}">
|
||||
<label>Username:</label>
|
||||
<input type="text" th:field="*{username}" /><br/>
|
||||
|
||||
<label>Display Name:</label>
|
||||
<input type="text" th:field="*{name}" /><br/>
|
||||
|
||||
<label>Description:</label>
|
||||
<textarea rows="5" cols="25" th:field="*{summary}"></textarea>
|
||||
<br/>
|
||||
|
||||
<button type="submit">Register</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="ISO-8859-1">
|
||||
<title>Made Bot</title>
|
||||
<style type="text/css">
|
||||
span {
|
||||
display: inline-block;
|
||||
width: 200px;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div align="center">
|
||||
<h2>Made Bot!</h2>
|
||||
<span>Username:</span><span th:text="${bot.username}"></span><br/>
|
||||
<span>Display Name:</span><span th:text="${bot.name}"></span><br/>
|
||||
<span>Description:</span><span th:text="${bot.summary}"></span><br/>
|
||||
<span>Published:</span><span th:text="${bot.published}"></span><br/>
|
||||
<span>Type:</span><span th:text="${bot.type}"></span><br/>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="ISO-8859-1">
|
||||
<title>View Bot</title>
|
||||
<style type="text/css">
|
||||
span {
|
||||
display: inline-block;
|
||||
width: 200px;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div align="center">
|
||||
<h2>Pick-a-Bot!</h2>
|
||||
<ul th:if="${bots.empty}">
|
||||
<li>No Bots!</li>
|
||||
</ul>
|
||||
<ul th:each="bot : ${bots}">
|
||||
<li>Bot: <span th:text="${bot.username}"> Bot </span></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="ISO-8859-1">
|
||||
<title>Make Bot - Spring Bot - an Activity Pub bot using the Spring Boot framework</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Make Yourself a Bot!</h1>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="ISO-8859-1">
|
||||
<title>View Bot - Spring Bot - an Activity Pub bot using the Spring Boot framework</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Pick a Bot!</h1>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue