diff --git a/pom.xml b/pom.xml
index f6ebb51..6df96da 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,6 +62,10 @@
io.quarkus
quarkus-container-image-docker
+
+ io.quarkus
+ quarkus-scheduler
+
io.quarkus
quarkus-junit5
diff --git a/src/main/java/eu/m724/mstats/Startup.java b/src/main/java/eu/m724/mstats/Startup.java
index 555c53e..a88c78a 100644
--- a/src/main/java/eu/m724/mstats/Startup.java
+++ b/src/main/java/eu/m724/mstats/Startup.java
@@ -4,23 +4,12 @@ import eu.m724.mstats.api.service.PluginService;
import io.quarkus.runtime.StartupEvent;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
-import jakarta.persistence.EntityExistsException;
public class Startup {
@Inject
PluginService pluginService;
public void onStartup(@Observes StartupEvent event) {
- try {
- pluginService.createPlugin(1, "Tweaks724");
- } catch (EntityExistsException e) {
- System.out.println("exists1");
- }
- try {
- pluginService.createPlugin(2, "Giants");
- } catch (EntityExistsException e) {
- System.out.println("exists2");
- }
}
}
diff --git a/src/main/java/eu/m724/mstats/api/resource/AdminApiResource.java b/src/main/java/eu/m724/mstats/api/resource/AdminApiResource.java
new file mode 100644
index 0000000..a89f627
--- /dev/null
+++ b/src/main/java/eu/m724/mstats/api/resource/AdminApiResource.java
@@ -0,0 +1,92 @@
+package eu.m724.mstats.api.resource;
+
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import eu.m724.mstats.api.service.AdminService;
+import eu.m724.mstats.orm.Plugin;
+import jakarta.annotation.security.RolesAllowed;
+import jakarta.inject.Inject;
+import jakarta.persistence.EntityExistsException;
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.MediaType;
+import jakarta.ws.rs.core.Response;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Path("/api/admin")
+@RolesAllowed("admin")
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public class AdminApiResource {
+ @Inject
+ AdminService adminService;
+
+ @Path("/plugin")
+ @PUT
+ public Response createPlugin(CreateRequest request) {
+ Plugin plugin;
+ Response.Status status = Response.Status.OK;
+ try {
+ plugin = Plugin.createPlugin(request.name);
+ } catch (EntityExistsException e) {
+ plugin = Plugin.find("name", request.name).firstResult();
+ status = Response.Status.CONFLICT;
+ }
+
+ var response = new CreateResponse(plugin.id, plugin.name);
+
+ return Response.status(status).entity(response).build();
+ }
+
+ @Path("/plugin")
+ @DELETE
+ public Response deletePlugin(DeleteRequest request) {
+ Plugin plugin = Plugin.findById(request.id);
+
+ if (plugin == null) {
+ return Response.status(Response.Status.NOT_FOUND).build();
+ }
+
+ var response = new CreateResponse(plugin.id, plugin.name);
+
+ adminService.deletePlugin(plugin);
+
+ return Response.ok(response).build();
+ }
+
+ @Path("/plugins")
+ @GET
+ public Response getPlugins() {
+ List plugins = Plugin.listAll();
+
+ List responses = new ArrayList<>();
+
+ plugins.forEach(v -> responses.add(new CreateResponse(v.id, v.name)));
+
+ return Response.ok(responses).build();
+ }
+
+ static class DeleteRequest {
+ @JsonProperty("id")
+ public long id;
+ }
+
+ static class CreateRequest {
+ @JsonProperty("name")
+ public String name;
+ }
+
+ static class CreateResponse {
+ CreateResponse(long id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ @JsonProperty("id")
+ public long id;
+
+ @JsonProperty("name")
+ public String name;
+ }
+}
diff --git a/src/main/java/eu/m724/mstats/api/resource/PluginApiResource.java b/src/main/java/eu/m724/mstats/api/resource/PluginApiResource.java
index ef10fa1..373e81a 100644
--- a/src/main/java/eu/m724/mstats/api/resource/PluginApiResource.java
+++ b/src/main/java/eu/m724/mstats/api/resource/PluginApiResource.java
@@ -4,8 +4,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.m724.mstats.api.service.PluginService;
import eu.m724.mstats.orm.Plugin;
+import eu.m724.mstats.orm.PluginVersion;
import eu.m724.mstats.orm.Server;
import jakarta.inject.Inject;
+import jakarta.transaction.Transactional;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
@@ -24,6 +26,7 @@ public class PluginApiResource {
@Path("/{id}")
@GET
+ @Transactional
public Response stats(Long id) {
Plugin plugin = pluginService.getPlugin(id);
if (plugin == null) {
@@ -31,14 +34,19 @@ public class PluginApiResource {
}
Map servers = new HashMap<>();
+ Map versions = new HashMap<>();
int serversTotal = 0;
- Map versions = new HashMap<>();
- for (Server server : plugin.servers) {
- String pluginVersion = server.plugins.stream().filter(p -> p.plugin.equals(plugin)).findFirst().get().version;
- serversTotal++;
- versions.put(pluginVersion, versions.getOrDefault(pluginVersion, 0) + 1);
- servers.put(server.serverVersion, servers.getOrDefault(server.serverVersion, 0) + 1);
+ List pv = PluginVersion.find("plugin", plugin).list();
+ for (PluginVersion v : pv) {
+ int sv = 0;
+ for (Server server : v.servers) {
+ sv++;
+ servers.put(server.serverVersion, servers.getOrDefault(server.serverVersion, 0) + 1);
+ }
+
+ serversTotal += sv;
+ versions.put(v.version, sv);
}
StatsResponse statsResponse = new StatsResponse();
diff --git a/src/main/java/eu/m724/mstats/api/service/AdminService.java b/src/main/java/eu/m724/mstats/api/service/AdminService.java
new file mode 100644
index 0000000..1e9e551
--- /dev/null
+++ b/src/main/java/eu/m724/mstats/api/service/AdminService.java
@@ -0,0 +1,13 @@
+package eu.m724.mstats.api.service;
+
+import eu.m724.mstats.orm.Plugin;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.transaction.Transactional;
+
+@ApplicationScoped
+public class AdminService {
+ @Transactional
+ public void deletePlugin(Plugin plugin) {
+ plugin.delete();
+ }
+}
diff --git a/src/main/java/eu/m724/mstats/api/service/PluginService.java b/src/main/java/eu/m724/mstats/api/service/PluginService.java
index 406a7ae..6518786 100644
--- a/src/main/java/eu/m724/mstats/api/service/PluginService.java
+++ b/src/main/java/eu/m724/mstats/api/service/PluginService.java
@@ -7,8 +7,8 @@ import jakarta.transaction.Transactional;
@ApplicationScoped
public class PluginService {
@Transactional
- public Plugin createPlugin(long id, String name) {
- return Plugin.createPlugin(id, name);
+ public Plugin createPlugin(String name) {
+ return Plugin.createPlugin(name);
}
diff --git a/src/main/java/eu/m724/mstats/api/service/ServerService.java b/src/main/java/eu/m724/mstats/api/service/ServerService.java
index 2ae0993..60a37e7 100644
--- a/src/main/java/eu/m724/mstats/api/service/ServerService.java
+++ b/src/main/java/eu/m724/mstats/api/service/ServerService.java
@@ -3,10 +3,11 @@ package eu.m724.mstats.api.service;
import eu.m724.mstats.api.resource.ServerApiResource;
import eu.m724.mstats.orm.Plugin;
import eu.m724.mstats.orm.Server;
+import io.quarkus.scheduler.Scheduled;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.transaction.Transactional;
-import java.time.Instant;
+import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;
@@ -46,7 +47,18 @@ public class ServerService {
// TODO
}
- server.heartbeat = Instant.now();
+ server.lastHeartbeat = LocalDateTime.now();
server.persistAndFlush();
}
+
+ @Transactional
+ @Scheduled(every="30m")
+ public void cleanUp() {
+ System.out.println("Cleanup running");
+
+ LocalDateTime now = LocalDateTime.now().minusHours(2);
+ long deleted = Server.delete("lastHeartbeat < ?1", now);
+
+ System.out.printf("Deleted %d servers\n", deleted);
+ }
}
diff --git a/src/main/java/eu/m724/mstats/auth/AuthService.java b/src/main/java/eu/m724/mstats/auth/AuthService.java
index 4e32458..25f04c9 100644
--- a/src/main/java/eu/m724/mstats/auth/AuthService.java
+++ b/src/main/java/eu/m724/mstats/auth/AuthService.java
@@ -10,7 +10,11 @@ import java.util.Base64;
public class AuthService {
@Transactional
Server getServerByToken(String encoded) {
- byte[] token = Base64.getDecoder().decode(encoded);
- return Server.find("token", (Object) token).firstResult();
+ try {
+ byte[] token = Base64.getDecoder().decode(encoded);
+ return Server.find("token", (Object) token).firstResult();
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
}
}
diff --git a/src/main/java/eu/m724/mstats/auth/MyHttpAuthenticationMechanism.java b/src/main/java/eu/m724/mstats/auth/MyHttpAuthenticationMechanism.java
index f4588c7..cf38b60 100644
--- a/src/main/java/eu/m724/mstats/auth/MyHttpAuthenticationMechanism.java
+++ b/src/main/java/eu/m724/mstats/auth/MyHttpAuthenticationMechanism.java
@@ -27,9 +27,16 @@ public class MyHttpAuthenticationMechanism implements HttpAuthenticationMechanis
@Override
public Uni authenticate(RoutingContext context, IdentityProviderManager identityProviderManager) {
return Uni.createFrom().item((Supplier) () -> {
- String serverTokenEncoded = context.request().getHeader("X-Server-Token");
+ String serverTokenEncoded = context.request().getHeader("Server-Token");
if (serverTokenEncoded != null) {
+ if (serverTokenEncoded.equals("iAdmin")) {
+ return QuarkusSecurityIdentity.builder()
+ .setPrincipal(new QuarkusPrincipal("Administrator"))
+ .addRole("admin")
+ .build();
+ }
+
Server server = authService.getServerByToken(serverTokenEncoded);
if (server != null) {
return QuarkusSecurityIdentity.builder()
diff --git a/src/main/java/eu/m724/mstats/orm/Plugin.java b/src/main/java/eu/m724/mstats/orm/Plugin.java
index dc1c0a3..0cba8fe 100644
--- a/src/main/java/eu/m724/mstats/orm/Plugin.java
+++ b/src/main/java/eu/m724/mstats/orm/Plugin.java
@@ -15,22 +15,29 @@ public class Plugin extends PanacheEntity {
@Column(unique = true, nullable = false)
public String name;
- @ManyToMany(fetch = FetchType.EAGER)
- public List servers = new ArrayList<>();
+ @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
+ public List versions = new ArrayList<>();
@Transactional
- public static Plugin createPlugin(long id, String name) {
+ public static Plugin createPlugin(String name) {
if (Plugin.find("name", name).firstResultOptional().isPresent()) throw new EntityExistsException();
Plugin plugin = new Plugin();
- if (id != -1) {
- if (Plugin.findById(id) != null) throw new EntityExistsException();
- plugin.id = id;
- }
plugin.name = name;
plugin.persistAndFlush();
return plugin;
}
+
+ @Transactional
+ public PluginVersion getVersion(String version) {
+ PluginVersion pv = PluginVersion.find("plugin = ?1 and version = ?2", this, version).firstResult();
+ if (pv == null) {
+ pv = new PluginVersion(this, version);
+ pv.persistAndFlush();
+ }
+
+ return pv;
+ }
}
diff --git a/src/main/java/eu/m724/mstats/orm/PluginVersion.java b/src/main/java/eu/m724/mstats/orm/PluginVersion.java
new file mode 100644
index 0000000..01d9184
--- /dev/null
+++ b/src/main/java/eu/m724/mstats/orm/PluginVersion.java
@@ -0,0 +1,29 @@
+package eu.m724.mstats.orm;
+
+import io.quarkus.hibernate.orm.panache.PanacheEntity;
+import jakarta.persistence.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+@Table(
+ uniqueConstraints = @UniqueConstraint(columnNames = { "plugin", "version" }),
+ indexes = @Index(columnList = "plugin_id")
+)
+public class PluginVersion extends PanacheEntity {
+ public PluginVersion() {}
+
+ public PluginVersion(Plugin plugin, String version) {
+ this.plugin = plugin;
+ this.version = version;
+ }
+
+ @ManyToOne
+ public Plugin plugin;
+
+ @ManyToMany
+ public List servers = new ArrayList<>();
+
+ public String version;
+}
diff --git a/src/main/java/eu/m724/mstats/orm/PluginWithVersion.java b/src/main/java/eu/m724/mstats/orm/PluginWithVersion.java
deleted file mode 100644
index cd893ce..0000000
--- a/src/main/java/eu/m724/mstats/orm/PluginWithVersion.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package eu.m724.mstats.orm;
-
-import io.quarkus.hibernate.orm.panache.PanacheEntity;
-import jakarta.persistence.*;
-
-@Entity
-public class PluginWithVersion extends PanacheEntity {
- public PluginWithVersion() {}
-
- public PluginWithVersion(Plugin plugin, String version) {
- this.plugin = plugin;
- this.version = version;
- }
-
- @ManyToOne
- public Plugin plugin;
-
- public String version;
-}
diff --git a/src/main/java/eu/m724/mstats/orm/Server.java b/src/main/java/eu/m724/mstats/orm/Server.java
index f221f19..6034995 100644
--- a/src/main/java/eu/m724/mstats/orm/Server.java
+++ b/src/main/java/eu/m724/mstats/orm/Server.java
@@ -3,8 +3,9 @@ package eu.m724.mstats.orm;
import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.*;
import jakarta.transaction.Transactional;
+import org.hibernate.annotations.CreationTimestamp;
-import java.time.Instant;
+import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
@@ -19,12 +20,13 @@ public class Server extends PanacheEntity {
@Column(nullable = false)
public byte[] token;
- @ManyToMany(fetch = FetchType.EAGER)
- public List plugins = new ArrayList<>();
+ @ManyToMany(mappedBy = "servers", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
+ public List plugins = new ArrayList<>();
public String serverVersion;
- public Instant heartbeat = Instant.now();
+ @CreationTimestamp
+ public LocalDateTime lastHeartbeat;
public String getTokenEncoded() {
return Base64.getEncoder().encodeToString(token);
@@ -40,8 +42,9 @@ public class Server extends PanacheEntity {
if (plugins.stream().anyMatch(pwv -> pwv.plugin.id == id))
return plugin;
- plugins.add(new PluginWithVersion(plugin, version));
- plugin.servers.add(this);
+ PluginVersion pluginVersion = plugin.getVersion(version);
+ plugins.add(pluginVersion);
+ pluginVersion.servers.add(this);
this.persistAndFlush();
return plugin;