diff --git a/pom.xml b/pom.xml
index 4796725..59d84f9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
xyz.etztech
ServerAPI
- 0.0.1
+ 0.0.3
jar
diff --git a/src/main/java/xyz/etztech/serverapi/ServerAPI.java b/src/main/java/xyz/etztech/serverapi/ServerAPI.java
index 4d871e2..0978d73 100644
--- a/src/main/java/xyz/etztech/serverapi/ServerAPI.java
+++ b/src/main/java/xyz/etztech/serverapi/ServerAPI.java
@@ -3,20 +3,20 @@ package xyz.etztech.serverapi;
import org.bukkit.BanEntry;
import org.bukkit.BanList;
-import org.bukkit.OfflinePlayer;
+import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
+import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import xyz.etztech.serverapi.commands.MainCommand;
+import xyz.etztech.serverapi.listeners.AsyncPlayerChatListener;
+import xyz.etztech.serverapi.token.TokenList;
import xyz.etztech.serverapi.tps.TPS;
import xyz.etztech.serverapi.web.IProvider;
import xyz.etztech.serverapi.web.Web;
import xyz.etztech.serverapi.web.api.*;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
import java.util.logging.Logger;
public class ServerAPI extends JavaPlugin implements IProvider {
@@ -26,6 +26,8 @@ public class ServerAPI extends JavaPlugin implements IProvider {
private Web web = new Web(this);
private final Logger log = Logger.getLogger( "Minecraft" );
+ private static final List chat = new ArrayList<>();
+
@Override
public void onEnable() {
instance = this;
@@ -34,6 +36,7 @@ public class ServerAPI extends JavaPlugin implements IProvider {
reloadConfig();
if (isEnabled()) {
+ new AsyncPlayerChatListener(this);
new MainCommand(this);
tps = new TPS();
@@ -52,7 +55,8 @@ public class ServerAPI extends JavaPlugin implements IProvider {
web.stop();
web.start(
getConfig().getInt("port", 8080),
- getConfig().getString("password", "")
+ new TokenList(getConfig().getConfigurationSection("auth")),
+ getConfig().getStringList("custom")
);
}
@@ -68,6 +72,10 @@ public class ServerAPI extends JavaPlugin implements IProvider {
return tps;
}
+ public static List getChat() {
+ return chat;
+ }
+
@Override
public TPSAPI TPS() {
return new TPSAPI(tps.getHistory());
@@ -111,8 +119,64 @@ public class ServerAPI extends JavaPlugin implements IProvider {
}
@Override
- public QueryAPI query() {
- return QueryAPI.fromMinecraft(getServer());
+ public List chat() {
+ return chat;
+ }
+
+ @Override
+ public void kick(BanAPI kick) {
+ Player player = Bukkit.getPlayerExact(kick.getTarget());
+ if (player != null) {
+ getServer().getScheduler().runTask(this, () -> {
+ player.kickPlayer("You have been kicked: " + kick.getReason());
+ });
+ }
+ }
+
+ @Override
+ public void ban(BanAPI ban) {
+ Date expires = null;
+ if (ban.getExpiration() != 0) {
+ expires = new Date(ban.getExpiration());
+ }
+ Bukkit.getBanList(BanList.Type.NAME).addBan(ban.getTarget(), ban.getReason(), expires, "ServerAPI");
+ Player player = Bukkit.getPlayerExact(ban.getTarget());
+ if (player != null) {
+ getServer().getScheduler().runTask(this, () -> {
+ player.kickPlayer("You have been banned: " + ban.getReason());
+ });
+ }
+ }
+
+ @Override
+ public void unban(BanAPI ban) {
+ Bukkit.getBanList(BanList.Type.NAME).pardon(ban.getTarget());
+ }
+
+ @Override
+ public void broadcast(BroadcastAPI broadcast) {
+ Bukkit.broadcastMessage(String.format("%s > %s", broadcast.getFrom(), broadcast.getMessage()));
+ }
+
+ @Override
+ public void custom(CustomAPI custom) {
+ getServer().getScheduler().runTask(this, () -> {
+ getServer().dispatchCommand(getServer().getConsoleSender(), custom.build());
+ });
+ }
+
+ @Override
+ public PingAPI ping() {
+ return PingAPI.fromMinecraft(getServer());
+ }
+
+ @Override
+ public List plugins() {
+ List plugins = new ArrayList<>();
+ for (Plugin plugin : getServer().getPluginManager().getPlugins()) {
+ plugins.add(PluginAPI.fromMinecraft(plugin));
+ }
+ return plugins;
}
}
diff --git a/src/main/java/xyz/etztech/serverapi/listeners/AsyncPlayerChatListener.java b/src/main/java/xyz/etztech/serverapi/listeners/AsyncPlayerChatListener.java
new file mode 100644
index 0000000..6206e2e
--- /dev/null
+++ b/src/main/java/xyz/etztech/serverapi/listeners/AsyncPlayerChatListener.java
@@ -0,0 +1,31 @@
+package xyz.etztech.serverapi.listeners;
+
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.AsyncPlayerChatEvent;
+import xyz.etztech.serverapi.ServerAPI;
+import xyz.etztech.serverapi.web.api.ChatAPI;
+
+import java.util.Date;
+
+public class AsyncPlayerChatListener implements Listener {
+ private final ServerAPI plugin;
+
+ public AsyncPlayerChatListener(ServerAPI plugin) {
+ this.plugin = plugin;
+ plugin.getServer().getPluginManager().registerEvents(this, plugin);
+ }
+
+ @EventHandler(priority= EventPriority.MONITOR, ignoreCancelled=true)
+ public void onChat(AsyncPlayerChatEvent event) {
+ int chatLimit = plugin.getConfig().getInt("chat", 100);
+ if (ServerAPI.getChat().size() >= chatLimit) {
+ ServerAPI.getChat().remove(0);
+ }
+ ServerAPI.getChat().add(new ChatAPI(
+ String.format(event.getFormat(), event.getPlayer().getDisplayName(), event.getMessage()),
+ new Date().getTime()
+ ));
+ }
+}
diff --git a/src/main/java/xyz/etztech/serverapi/token/Token.java b/src/main/java/xyz/etztech/serverapi/token/Token.java
new file mode 100644
index 0000000..ba1bf78
--- /dev/null
+++ b/src/main/java/xyz/etztech/serverapi/token/Token.java
@@ -0,0 +1,27 @@
+package xyz.etztech.serverapi.token;
+
+public class Token {
+ private final String token;
+ private final TokenScope scope;
+
+ public Token(String token, TokenScope scope) {
+ this.token = token;
+ this.scope = scope;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public TokenScope getScope() {
+ return scope;
+ }
+
+ public boolean canGET() {
+ return scope == TokenScope.GET || scope == TokenScope.ALL;
+ }
+
+ public boolean canPOST() {
+ return scope == TokenScope.POST || scope == TokenScope.ALL;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/xyz/etztech/serverapi/token/TokenList.java b/src/main/java/xyz/etztech/serverapi/token/TokenList.java
new file mode 100644
index 0000000..80119e4
--- /dev/null
+++ b/src/main/java/xyz/etztech/serverapi/token/TokenList.java
@@ -0,0 +1,49 @@
+package xyz.etztech.serverapi.token;
+
+import org.bukkit.configuration.ConfigurationSection;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TokenList {
+ private final boolean protectGET;
+ private final boolean protectPOST;
+ private final List tokens;
+
+ public TokenList(boolean protectGET, boolean protectPOST, List tokens) {
+ this.protectGET = protectGET;
+ this.protectPOST = protectPOST;
+ this.tokens = tokens;
+ }
+
+ public TokenList(ConfigurationSection auth) {
+ if (auth == null) {
+ this.protectGET = false;
+ this.protectPOST = false;
+ this.tokens = null;
+ return;
+ }
+ this.protectGET = auth.getBoolean("get", true);
+ this.protectPOST = auth.getBoolean("post", true);
+ this.tokens = new ArrayList<>();
+ ConfigurationSection tokenSection = auth.getConfigurationSection("tokens");
+ if (tokenSection == null) {
+ return;
+ }
+ for (String token : tokenSection.getKeys(false)) {
+ this.tokens.add(new Token(token, TokenScope.parseScope(tokenSection.getString(token, "none"))));
+ }
+ }
+
+ public boolean isProtectGET() {
+ return protectGET;
+ }
+
+ public boolean isProtectPOST() {
+ return protectPOST;
+ }
+
+ public List getTokens() {
+ return tokens;
+ }
+}
diff --git a/src/main/java/xyz/etztech/serverapi/token/TokenScope.java b/src/main/java/xyz/etztech/serverapi/token/TokenScope.java
new file mode 100644
index 0000000..17b6573
--- /dev/null
+++ b/src/main/java/xyz/etztech/serverapi/token/TokenScope.java
@@ -0,0 +1,24 @@
+package xyz.etztech.serverapi.token;
+
+public enum TokenScope {
+ NONE,
+ GET,
+ POST,
+ ALL;
+
+ public static TokenScope parseScope(String scope) {
+ if (scope == null) {
+ return NONE;
+ }
+ switch (scope.toLowerCase()) {
+ case "get":
+ return GET;
+ case "post":
+ return POST;
+ case "all":
+ return ALL;
+ default:
+ return NONE;
+ }
+ }
+}
diff --git a/src/main/java/xyz/etztech/serverapi/web/GraphQL.java b/src/main/java/xyz/etztech/serverapi/web/GraphQL.java
index 42f5ca7..81f62be 100644
--- a/src/main/java/xyz/etztech/serverapi/web/GraphQL.java
+++ b/src/main/java/xyz/etztech/serverapi/web/GraphQL.java
@@ -21,9 +21,14 @@ public class GraphQL implements QueryGraphql {
return provider.players().toArray(new PlayerAPI[0]);
}
- @GraphQLName("query")
- public QueryAPI getQuery() {
- return provider.query();
+ @GraphQLName("ping")
+ public PingAPI getPing() {
+ return provider.ping();
+ }
+
+ @GraphQLName("plugins")
+ public PluginAPI[] getPlugins() {
+ return provider.plugins().toArray(new PluginAPI[0]);
}
@GraphQLName("world")
@@ -40,4 +45,9 @@ public class GraphQL implements QueryGraphql {
public TPSAPI getTps() {
return provider.TPS();
}
+
+ @GraphQLName("chat")
+ public ChatAPI[] getChat() {
+ return provider.chat().toArray(new ChatAPI[0]);
+ }
}
diff --git a/src/main/java/xyz/etztech/serverapi/web/IProvider.java b/src/main/java/xyz/etztech/serverapi/web/IProvider.java
index cc353d1..17a4636 100644
--- a/src/main/java/xyz/etztech/serverapi/web/IProvider.java
+++ b/src/main/java/xyz/etztech/serverapi/web/IProvider.java
@@ -6,12 +6,24 @@ import java.util.List;
import java.util.Set;
public interface IProvider {
+
+ // GET
Set bans();
Set players();
- QueryAPI query();
+ PingAPI ping();
TPSAPI TPS();
List worlds();
WorldAPI world(String name);
+ List plugins();
+ List chat();
+ // POST
+ void kick(BanAPI kick);
+ void ban(BanAPI ban);
+ void unban(BanAPI ban);
+ void broadcast(BroadcastAPI broadcast);
+ void custom(CustomAPI custom);
+
+ // MISC
void log(String message);
}
diff --git a/src/main/java/xyz/etztech/serverapi/web/README.md b/src/main/java/xyz/etztech/serverapi/web/README.md
new file mode 100644
index 0000000..13fc2f2
--- /dev/null
+++ b/src/main/java/xyz/etztech/serverapi/web/README.md
@@ -0,0 +1,22 @@
+# Adding a new endpoint
+
+1. Create a new `API` class in [api](api).
+ * Make sure to correctly add the appropriate JSON and GraphQL annotations.
+ Look at other classes for examples.
+ * **NOTE:** If anything returned could be null, make sure to instead provide an appropriate zero-value property,
+ otherwise GraphQL will choke.
+ See [PluginAPI::new](api/PluginAPI.java) for an example.
+2. Add a method to return the needed data to [IProvider](IProvider.java).
+3. Add a new REST endpoint to the [Web::start](Web.java) method.
+4. Add a new GraphQL method to [GraphQL](GraphQL.java)
+ * Make sure to correctly add the appropriate GraphQL annotation.
+5. Modify both [ServerAPI](../ServerAPI.java)
+and [MockProvider](../../../../../../test/java/xyz/etztech/serverapi/MockProvider.java)
+to fulfill the [IProvider](IProvider.java) interface.
+
+# Testing
+
+[ServerRunner](../../../../../../test/java/xyz/etztech/serverapi/ServerRunner.java) should start up the API using
+[MockProvider](../../../../../../test/java/xyz/etztech/serverapi/MockProvider.java).
+
+If possible, a real test on a running Minecraft server would ideal!
\ No newline at end of file
diff --git a/src/main/java/xyz/etztech/serverapi/web/Web.java b/src/main/java/xyz/etztech/serverapi/web/Web.java
index a1d8cc4..395f4fd 100644
--- a/src/main/java/xyz/etztech/serverapi/web/Web.java
+++ b/src/main/java/xyz/etztech/serverapi/web/Web.java
@@ -4,6 +4,7 @@ import io.javalin.Javalin;
import io.javalin.core.event.EventListener;
import io.javalin.core.plugin.Plugin;
import io.javalin.core.util.RouteOverviewPlugin;
+import io.javalin.http.BadRequestResponse;
import io.javalin.http.Context;
import io.javalin.http.UnauthorizedResponse;
import io.javalin.plugin.graphql.GraphQLOptions;
@@ -12,18 +13,25 @@ import io.javalin.plugin.json.JavalinJson;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.helpers.NOPLogger;
import xyz.etztech.serverapi.ServerAPI;
+import xyz.etztech.serverapi.token.TokenList;
+import xyz.etztech.serverapi.web.api.BanAPI;
+import xyz.etztech.serverapi.web.api.BroadcastAPI;
+import xyz.etztech.serverapi.web.api.CustomAPI;
import xyz.etztech.serverapi.web.api.ErrorAPI;
+import java.util.List;
+
public class Web {
private final IProvider provider;
- private String password;
+ private TokenList tokens;
+ private List custom;
private Javalin app;
public Web(IProvider provider) {
this.provider = provider;
}
- public void start(int port, String password) {
+ public void start(int port, TokenList tokens, List custom) {
Javalin.log = NOPLogger.NOP_LOGGER;
app = Javalin.create(config -> {
config.registerPlugin(graphql());
@@ -31,17 +39,33 @@ public class Web {
config.enableCorsForAllOrigins();
}).events(this::events).exception(Exception.class, this::exception);
- this.password = password;
- if (!"".equals(password)) {
+ this.custom = custom;
+
+ this.tokens = tokens;
+ if (tokens != null) {
+ provider.log(String.format("Loaded %d tokens", tokens.getTokens().size()));
+ if (!tokens.isProtectPOST()) {
+ provider.log("WARNING: You have disabled POST protection, which can enable users to arbitrarily run actions against your server.");
+ }
app.before(this::access);
}
+ // REST Endpoints
+ // For GraphQL endpoints, see GraphQL.java
app.get("/bans", this::bans);
app.get("/players", this::players);
- app.get("/query", this::query);
+ app.get("/plugins", this::plugins);
+ app.get("/ping", this::ping);
app.get("/tps", this::tps);
app.get("/worlds", this::worlds);
app.get("/worlds/:name", this::world);
+ app.get("/chat", this::chat);
+
+ app.post("/kick", this::kick);
+ app.post("/ban", this::ban);
+ app.post("/unban", this::unban);
+ app.post("/broadcast", this::broadcast);
+ app.post("/custom", this::custom);
// What in the actual fuck... https://github.com/tipsy/javalin/issues/358
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
@@ -74,13 +98,22 @@ public class Web {
}
public void access(Context ctx) {
- String pw = ctx.header("X-ServerAPI-Password");
- if (pw == null) {
- pw = ctx.queryParam("password");
+ String token = ctx.header("X-ServerAPI-Token");
+ if (token == null) {
+ token = ctx.queryParam("token");
}
+ boolean isGET = "GET".equalsIgnoreCase(ctx.method());
+ boolean isPOST = "POST".equalsIgnoreCase(ctx.method());
- if (!password.equals(pw)) {
- throw new UnauthorizedResponse(JavalinJson.toJson(new ErrorAPI(401, "Unauthorized")));
+ if (isGET && !tokens.isProtectGET()) return;
+ if (isPOST && !tokens.isProtectPOST()) return;
+
+ String finalToken = token;
+ boolean pass = tokens.getTokens().stream().anyMatch(t -> t.getToken().equals(finalToken) &&
+ ((isGET && t.canGET()) || (isPOST && t.canPOST())));
+
+ if (!pass) {
+ throw new UnauthorizedResponse();
}
}
@@ -103,15 +136,52 @@ public class Web {
ctx.json(provider.world(ctx.pathParam("name")));
}
+ public void chat(Context ctx) {
+ ctx.json(provider.chat());
+ }
+
public void players(Context ctx) {
ctx.json(provider.players());
}
+ public void plugins(Context ctx) {
+ ctx.json(provider.plugins());
+ }
+
public void bans(Context ctx) {
ctx.json(provider.bans());
}
- public void query(Context ctx) {
- ctx.json(provider.query());
+ public void ping(Context ctx) {
+ ctx.json(provider.ping());
+ }
+
+ public void kick(Context ctx) {
+ provider.kick(JavalinJson.fromJson(ctx.body(), BanAPI.class));
+ ctx.status(200);
+ }
+
+ public void ban(Context ctx) {
+ provider.ban(JavalinJson.fromJson(ctx.body(), BanAPI.class));
+ ctx.status(200);
+ }
+
+ public void unban(Context ctx) {
+ provider.unban(JavalinJson.fromJson(ctx.body(), BanAPI.class));
+ ctx.status(200);
+ }
+
+ public void broadcast(Context ctx) {
+ provider.broadcast(JavalinJson.fromJson(ctx.body(), BroadcastAPI.class));
+ ctx.status(200);
+ }
+
+ public void custom(Context ctx) {
+ CustomAPI capi = JavalinJson.fromJson(ctx.body(), CustomAPI.class);
+ if (custom.stream().noneMatch(c -> c.equalsIgnoreCase(capi.getCommand()))) {
+ throw new BadRequestResponse();
+ }
+ provider.custom(capi);
+ ctx.status(200);
}
}
diff --git a/src/main/java/xyz/etztech/serverapi/web/api/BanAPI.java b/src/main/java/xyz/etztech/serverapi/web/api/BanAPI.java
index c1736c3..c70b2c9 100644
--- a/src/main/java/xyz/etztech/serverapi/web/api/BanAPI.java
+++ b/src/main/java/xyz/etztech/serverapi/web/api/BanAPI.java
@@ -1,12 +1,9 @@
package xyz.etztech.serverapi.web.api;
import com.expediagroup.graphql.annotations.GraphQLDescription;
-import com.expediagroup.graphql.annotations.GraphQLDirective;
import com.expediagroup.graphql.annotations.GraphQLName;
import org.bukkit.BanEntry;
-import java.util.Date;
-
@GraphQLName("Ban")
@GraphQLDescription("Ban GraphQL")
public class BanAPI {
@@ -16,6 +13,14 @@ public class BanAPI {
private final long created;
private final long expiration;
+ public BanAPI() {
+ this.target = "";
+ this.source = "";
+ this.reason = "";
+ this.created = 0;
+ this.expiration = 0;
+ }
+
public BanAPI(String target, String source, String reason, long created, long expiration) {
this.target = target;
this.source = source;
diff --git a/src/main/java/xyz/etztech/serverapi/web/api/BroadcastAPI.java b/src/main/java/xyz/etztech/serverapi/web/api/BroadcastAPI.java
new file mode 100644
index 0000000..8746d11
--- /dev/null
+++ b/src/main/java/xyz/etztech/serverapi/web/api/BroadcastAPI.java
@@ -0,0 +1,25 @@
+package xyz.etztech.serverapi.web.api;
+
+public class BroadcastAPI {
+ private final String from;
+ private final String message;
+
+ public BroadcastAPI() {
+ this.from = "";
+ this.message = "";
+ }
+
+ public BroadcastAPI(String from, String message) {
+ this.from = from;
+ this.message = message;
+ }
+
+ public String getFrom() {
+ return from;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+}
diff --git a/src/main/java/xyz/etztech/serverapi/web/api/ChatAPI.java b/src/main/java/xyz/etztech/serverapi/web/api/ChatAPI.java
new file mode 100644
index 0000000..9cb1623
--- /dev/null
+++ b/src/main/java/xyz/etztech/serverapi/web/api/ChatAPI.java
@@ -0,0 +1,26 @@
+package xyz.etztech.serverapi.web.api;
+
+import com.expediagroup.graphql.annotations.GraphQLDescription;
+import com.expediagroup.graphql.annotations.GraphQLName;
+
+@GraphQLName("Chat")
+@GraphQLDescription("Chat GraphQL")
+public class ChatAPI {
+ private final String message;
+ private final long timestamp;
+
+ public ChatAPI(String message, long timestamp) {
+ this.message = message;
+ this.timestamp = timestamp;
+ }
+
+ @GraphQLName("message")
+ public String getMessage() {
+ return message;
+ }
+
+ @GraphQLName("timestamp")
+ public long getTimestamp() {
+ return timestamp;
+ }
+}
diff --git a/src/main/java/xyz/etztech/serverapi/web/api/CustomAPI.java b/src/main/java/xyz/etztech/serverapi/web/api/CustomAPI.java
new file mode 100644
index 0000000..faeaa67
--- /dev/null
+++ b/src/main/java/xyz/etztech/serverapi/web/api/CustomAPI.java
@@ -0,0 +1,32 @@
+package xyz.etztech.serverapi.web.api;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class CustomAPI {
+ private final String command;
+ private final List args;
+
+ public CustomAPI() {
+ this.command = "";
+ this.args = new ArrayList<>();
+ }
+
+ public CustomAPI(String command, List args) {
+ this.command = command;
+ this.args = args;
+ }
+
+ public String getCommand() {
+ return command;
+ }
+
+ public List getArgs() {
+ return args;
+ }
+
+ public String build() {
+ return command + " " + String.join(" ", args);
+ }
+}
diff --git a/src/main/java/xyz/etztech/serverapi/web/api/QueryAPI.java b/src/main/java/xyz/etztech/serverapi/web/api/PingAPI.java
similarity index 83%
rename from src/main/java/xyz/etztech/serverapi/web/api/QueryAPI.java
rename to src/main/java/xyz/etztech/serverapi/web/api/PingAPI.java
index 348215e..4b0aeee 100644
--- a/src/main/java/xyz/etztech/serverapi/web/api/QueryAPI.java
+++ b/src/main/java/xyz/etztech/serverapi/web/api/PingAPI.java
@@ -5,9 +5,9 @@ import com.expediagroup.graphql.annotations.GraphQLName;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.bukkit.Server;
-@GraphQLName("Query")
-@GraphQLDescription("Query GraphQL")
-public class QueryAPI {
+@GraphQLName("Ping")
+@GraphQLDescription("Ping GraphQL")
+public class PingAPI {
private final String type;
private final String version;
private final String motd;
@@ -16,7 +16,7 @@ public class QueryAPI {
@JsonProperty("max_players")
private final int maxPlayers;
- public QueryAPI(String type, String version, String motd, int currentPlayers, int maxPlayers) {
+ public PingAPI(String type, String version, String motd, int currentPlayers, int maxPlayers) {
this.type = type;
this.version = version;
this.motd = motd;
@@ -49,8 +49,8 @@ public class QueryAPI {
return maxPlayers;
}
- public static QueryAPI fromMinecraft(Server server) {
- return new QueryAPI(
+ public static PingAPI fromMinecraft(Server server) {
+ return new PingAPI(
server.getName(),
server.getBukkitVersion().split("-")[0], // 1.x.x-R0.1-SNAPSHOT
server.getMotd(),
diff --git a/src/main/java/xyz/etztech/serverapi/web/api/PluginAPI.java b/src/main/java/xyz/etztech/serverapi/web/api/PluginAPI.java
new file mode 100644
index 0000000..f185700
--- /dev/null
+++ b/src/main/java/xyz/etztech/serverapi/web/api/PluginAPI.java
@@ -0,0 +1,54 @@
+package xyz.etztech.serverapi.web.api;
+
+import com.expediagroup.graphql.annotations.GraphQLDescription;
+import com.expediagroup.graphql.annotations.GraphQLName;
+import org.bukkit.plugin.Plugin;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@GraphQLName("Plugin")
+@GraphQLDescription("Plugin GraphQL")
+public class PluginAPI {
+ private final String name;
+ private final String version;
+ private final List authors;
+ private final String website;
+
+
+ public PluginAPI(String name, String version, List authors, String website) {
+ this.name = name;
+ this.version = version;
+ this.authors = authors != null ? authors : new ArrayList<>();
+ this.website = website != null ? website : "";
+ }
+
+ @GraphQLName("name")
+ public String getName() {
+ return name;
+ }
+
+ @GraphQLName("version")
+ public String getVersion() {
+ return version;
+ }
+
+ @GraphQLName("authors")
+ public List getAuthors() {
+ return authors;
+ }
+
+ @GraphQLName("website")
+ public String getWebsite() {
+ return website;
+ }
+
+ public static PluginAPI fromMinecraft(Plugin plugin) {
+ return new PluginAPI(
+ plugin.getName(),
+ plugin.getDescription().getVersion(),
+ plugin.getDescription().getAuthors(),
+ plugin.getDescription().getWebsite()
+ );
+ }
+}
diff --git a/src/main/java/xyz/etztech/serverapi/web/api/WorldAPI.java b/src/main/java/xyz/etztech/serverapi/web/api/WorldAPI.java
index 813315c..098d7d5 100644
--- a/src/main/java/xyz/etztech/serverapi/web/api/WorldAPI.java
+++ b/src/main/java/xyz/etztech/serverapi/web/api/WorldAPI.java
@@ -3,7 +3,6 @@ package xyz.etztech.serverapi.web.api;
import com.expediagroup.graphql.annotations.GraphQLDescription;
import com.expediagroup.graphql.annotations.GraphQLName;
import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.annotation.JsonNaming;
import org.bukkit.World;
@GraphQLName("World")
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 32a3443..9cd5c8c 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -1,5 +1,18 @@
# The port to run on
port: 8080
-# (Optional) password
-password: ''
\ No newline at end of file
+# Number of chat logs to retain
+chat: 100
+
+# Authentication
+auth:
+ # Protect GET routes. If false, GET routes are public.
+ get: true
+ # Protect POST routes. If false, POST routes are public.
+ post: true
+ tokens:
+ token: access
+
+# Custom commands (POST)
+custom:
+ - say
\ No newline at end of file
diff --git a/src/test/java/xyz/etztech/serverapi/MockProvider.java b/src/test/java/xyz/etztech/serverapi/MockProvider.java
index 5051acc..c733e85 100644
--- a/src/test/java/xyz/etztech/serverapi/MockProvider.java
+++ b/src/test/java/xyz/etztech/serverapi/MockProvider.java
@@ -46,6 +46,21 @@ public class MockProvider implements IProvider {
));
}
+ @Override
+ public void kick(BanAPI kick) {}
+
+ @Override
+ public void ban(BanAPI ban) {}
+
+ @Override
+ public void unban(BanAPI ban) {}
+
+ @Override
+ public void broadcast(BroadcastAPI broadcast) {}
+
+ @Override
+ public void custom(CustomAPI custom) {}
+
@Override
public Set players() {
return new HashSet<>(Arrays.asList(
@@ -56,8 +71,27 @@ public class MockProvider implements IProvider {
}
@Override
- public QueryAPI query() {
- return new QueryAPI("Mock", "0.0.1", "Hello, world!", 0, 100);
+ public PingAPI ping() {
+ return new PingAPI("Mock", "0.0.1", "Hello, world!", 0, 100);
+ }
+
+ @Override
+ public List plugins() {
+ return Arrays.asList(
+ new PluginAPI("ServerAPI", "0.0.1", Collections.singletonList("Etzelia"), "https://git.etztech.xyz"),
+ new PluginAPI("dynmap", "0.1.0", null, "https://www.spigotmc.org/resources/dynmap.274/"),
+ new PluginAPI("CoreProtect", "1.0.0", Collections.singletonList("Intelli"), null)
+ );
+ }
+
+ @Override
+ public List chat() {
+ long now = new Date().getTime();
+ return Arrays.asList(
+ new ChatAPI("message 1", now-2),
+ new ChatAPI("message 2", now-1),
+ new ChatAPI("message 3", now)
+ );
}
@Override
diff --git a/src/test/java/xyz/etztech/serverapi/ServerRunner.java b/src/test/java/xyz/etztech/serverapi/ServerRunner.java
index e34587f..821723b 100644
--- a/src/test/java/xyz/etztech/serverapi/ServerRunner.java
+++ b/src/test/java/xyz/etztech/serverapi/ServerRunner.java
@@ -1,12 +1,29 @@
package xyz.etztech.serverapi;
+import xyz.etztech.serverapi.token.Token;
+import xyz.etztech.serverapi.token.TokenList;
+import xyz.etztech.serverapi.token.TokenScope;
import xyz.etztech.serverapi.web.Web;
+import java.util.Arrays;
+import java.util.List;
+
public class ServerRunner {
+ static TokenList tokens = new TokenList(true, true, Arrays.asList(
+ new Token("testGET", TokenScope.GET),
+ new Token("testPOST", TokenScope.POST),
+ new Token("testAll", TokenScope.ALL),
+ new Token("testNone", TokenScope.NONE)
+ ));
+
+ static List custom = Arrays.asList(
+ "test",
+ "say"
+ );
+
public static void main(String[] args) {
Web web = new Web(new MockProvider());
-
- web.start(8080, "");
+ web.start(8080, tokens, custom);
}
}