Compare commits
2 commits
0618794491
...
d08dd3777a
Author | SHA1 | Date | |
---|---|---|---|
d08dd3777a | |||
370227497f |
13 changed files with 204 additions and 55 deletions
4
pom.xml
4
pom.xml
|
@ -62,6 +62,10 @@
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-container-image-docker</artifactId>
|
<artifactId>quarkus-container-image-docker</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.quarkus</groupId>
|
||||||
|
<artifactId>quarkus-scheduler</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.quarkus</groupId>
|
<groupId>io.quarkus</groupId>
|
||||||
<artifactId>quarkus-junit5</artifactId>
|
<artifactId>quarkus-junit5</artifactId>
|
||||||
|
|
|
@ -4,23 +4,12 @@ import eu.m724.mstats.api.service.PluginService;
|
||||||
import io.quarkus.runtime.StartupEvent;
|
import io.quarkus.runtime.StartupEvent;
|
||||||
import jakarta.enterprise.event.Observes;
|
import jakarta.enterprise.event.Observes;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.persistence.EntityExistsException;
|
|
||||||
|
|
||||||
public class Startup {
|
public class Startup {
|
||||||
@Inject
|
@Inject
|
||||||
PluginService pluginService;
|
PluginService pluginService;
|
||||||
|
|
||||||
public void onStartup(@Observes StartupEvent event) {
|
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<Plugin> plugins = Plugin.listAll();
|
||||||
|
|
||||||
|
List<CreateResponse> 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,8 +4,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import eu.m724.mstats.api.service.PluginService;
|
import eu.m724.mstats.api.service.PluginService;
|
||||||
import eu.m724.mstats.orm.Plugin;
|
import eu.m724.mstats.orm.Plugin;
|
||||||
|
import eu.m724.mstats.orm.PluginVersion;
|
||||||
import eu.m724.mstats.orm.Server;
|
import eu.m724.mstats.orm.Server;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
|
import jakarta.transaction.Transactional;
|
||||||
import jakarta.ws.rs.GET;
|
import jakarta.ws.rs.GET;
|
||||||
import jakarta.ws.rs.Path;
|
import jakarta.ws.rs.Path;
|
||||||
import jakarta.ws.rs.Produces;
|
import jakarta.ws.rs.Produces;
|
||||||
|
@ -24,6 +26,7 @@ public class PluginApiResource {
|
||||||
|
|
||||||
@Path("/{id}")
|
@Path("/{id}")
|
||||||
@GET
|
@GET
|
||||||
|
@Transactional
|
||||||
public Response stats(Long id) {
|
public Response stats(Long id) {
|
||||||
Plugin plugin = pluginService.getPlugin(id);
|
Plugin plugin = pluginService.getPlugin(id);
|
||||||
if (plugin == null) {
|
if (plugin == null) {
|
||||||
|
@ -31,16 +34,21 @@ public class PluginApiResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, Integer> servers = new HashMap<>();
|
Map<String, Integer> servers = new HashMap<>();
|
||||||
|
Map<String, Integer> versions = new HashMap<>();
|
||||||
int serversTotal = 0;
|
int serversTotal = 0;
|
||||||
|
|
||||||
Map<String, Integer> versions = new HashMap<>();
|
List<PluginVersion> pv = PluginVersion.find("plugin", plugin).list();
|
||||||
for (Server server : plugin.servers) {
|
for (PluginVersion v : pv) {
|
||||||
String pluginVersion = server.plugins.stream().filter(p -> p.plugin.equals(plugin)).findFirst().get().version;
|
int sv = 0;
|
||||||
serversTotal++;
|
for (Server server : v.servers) {
|
||||||
versions.put(pluginVersion, versions.getOrDefault(pluginVersion, 0) + 1);
|
sv++;
|
||||||
servers.put(server.serverVersion, servers.getOrDefault(server.serverVersion, 0) + 1);
|
servers.put(server.serverVersion, servers.getOrDefault(server.serverVersion, 0) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
serversTotal += sv;
|
||||||
|
versions.put(v.version, sv);
|
||||||
|
}
|
||||||
|
|
||||||
StatsResponse statsResponse = new StatsResponse();
|
StatsResponse statsResponse = new StatsResponse();
|
||||||
statsResponse.id = id;
|
statsResponse.id = id;
|
||||||
statsResponse.name = plugin.name;
|
statsResponse.name = plugin.name;
|
||||||
|
|
13
src/main/java/eu/m724/mstats/api/service/AdminService.java
Normal file
13
src/main/java/eu/m724/mstats/api/service/AdminService.java
Normal file
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,8 +7,8 @@ import jakarta.transaction.Transactional;
|
||||||
@ApplicationScoped
|
@ApplicationScoped
|
||||||
public class PluginService {
|
public class PluginService {
|
||||||
@Transactional
|
@Transactional
|
||||||
public Plugin createPlugin(long id, String name) {
|
public Plugin createPlugin(String name) {
|
||||||
return Plugin.createPlugin(id, name);
|
return Plugin.createPlugin(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,10 +3,11 @@ package eu.m724.mstats.api.service;
|
||||||
import eu.m724.mstats.api.resource.ServerApiResource;
|
import eu.m724.mstats.api.resource.ServerApiResource;
|
||||||
import eu.m724.mstats.orm.Plugin;
|
import eu.m724.mstats.orm.Plugin;
|
||||||
import eu.m724.mstats.orm.Server;
|
import eu.m724.mstats.orm.Server;
|
||||||
|
import io.quarkus.scheduler.Scheduled;
|
||||||
import jakarta.enterprise.context.ApplicationScoped;
|
import jakarta.enterprise.context.ApplicationScoped;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.LocalDateTime;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -46,7 +47,18 @@ public class ServerService {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
server.heartbeat = Instant.now();
|
server.lastHeartbeat = LocalDateTime.now();
|
||||||
server.persistAndFlush();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,11 @@ import java.util.Base64;
|
||||||
public class AuthService {
|
public class AuthService {
|
||||||
@Transactional
|
@Transactional
|
||||||
Server getServerByToken(String encoded) {
|
Server getServerByToken(String encoded) {
|
||||||
|
try {
|
||||||
byte[] token = Base64.getDecoder().decode(encoded);
|
byte[] token = Base64.getDecoder().decode(encoded);
|
||||||
return Server.find("token", (Object) token).firstResult();
|
return Server.find("token", (Object) token).firstResult();
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,13 @@ public class MyHttpAuthenticationMechanism implements HttpAuthenticationMechanis
|
||||||
String serverTokenEncoded = context.request().getHeader("X-Server-Token");
|
String serverTokenEncoded = context.request().getHeader("X-Server-Token");
|
||||||
|
|
||||||
if (serverTokenEncoded != null) {
|
if (serverTokenEncoded != null) {
|
||||||
|
if (serverTokenEncoded.equals("secure admni token")) {
|
||||||
|
return QuarkusSecurityIdentity.builder()
|
||||||
|
.setPrincipal(new QuarkusPrincipal("Administrator"))
|
||||||
|
.addRole("admin")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
Server server = authService.getServerByToken(serverTokenEncoded);
|
Server server = authService.getServerByToken(serverTokenEncoded);
|
||||||
if (server != null) {
|
if (server != null) {
|
||||||
return QuarkusSecurityIdentity.builder()
|
return QuarkusSecurityIdentity.builder()
|
||||||
|
|
|
@ -15,22 +15,29 @@ public class Plugin extends PanacheEntity {
|
||||||
@Column(unique = true, nullable = false)
|
@Column(unique = true, nullable = false)
|
||||||
public String name;
|
public String name;
|
||||||
|
|
||||||
@ManyToMany(fetch = FetchType.EAGER)
|
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
|
||||||
public List<Server> servers = new ArrayList<>();
|
public List<PluginVersion> versions = new ArrayList<>();
|
||||||
|
|
||||||
@Transactional
|
@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();
|
if (Plugin.find("name", name).firstResultOptional().isPresent()) throw new EntityExistsException();
|
||||||
|
|
||||||
Plugin plugin = new Plugin();
|
Plugin plugin = new Plugin();
|
||||||
if (id != -1) {
|
|
||||||
if (Plugin.findById(id) != null) throw new EntityExistsException();
|
|
||||||
plugin.id = id;
|
|
||||||
}
|
|
||||||
plugin.name = name;
|
plugin.name = name;
|
||||||
|
|
||||||
plugin.persistAndFlush();
|
plugin.persistAndFlush();
|
||||||
|
|
||||||
return plugin;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
29
src/main/java/eu/m724/mstats/orm/PluginVersion.java
Normal file
29
src/main/java/eu/m724/mstats/orm/PluginVersion.java
Normal file
|
@ -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<Server> servers = new ArrayList<>();
|
||||||
|
|
||||||
|
public String version;
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -3,8 +3,9 @@ package eu.m724.mstats.orm;
|
||||||
import io.quarkus.hibernate.orm.panache.PanacheEntity;
|
import io.quarkus.hibernate.orm.panache.PanacheEntity;
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.*;
|
||||||
import jakarta.transaction.Transactional;
|
import jakarta.transaction.Transactional;
|
||||||
|
import org.hibernate.annotations.CreationTimestamp;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.LocalDateTime;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -19,12 +20,13 @@ public class Server extends PanacheEntity {
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
public byte[] token;
|
public byte[] token;
|
||||||
|
|
||||||
@ManyToMany(fetch = FetchType.EAGER)
|
@ManyToMany(mappedBy = "servers", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||||
public List<PluginWithVersion> plugins = new ArrayList<>();
|
public List<PluginVersion> plugins = new ArrayList<>();
|
||||||
|
|
||||||
public String serverVersion;
|
public String serverVersion;
|
||||||
|
|
||||||
public Instant heartbeat = Instant.now();
|
@CreationTimestamp
|
||||||
|
public LocalDateTime lastHeartbeat;
|
||||||
|
|
||||||
public String getTokenEncoded() {
|
public String getTokenEncoded() {
|
||||||
return Base64.getEncoder().encodeToString(token);
|
return Base64.getEncoder().encodeToString(token);
|
||||||
|
@ -40,8 +42,9 @@ public class Server extends PanacheEntity {
|
||||||
if (plugins.stream().anyMatch(pwv -> pwv.plugin.id == id))
|
if (plugins.stream().anyMatch(pwv -> pwv.plugin.id == id))
|
||||||
return plugin;
|
return plugin;
|
||||||
|
|
||||||
plugins.add(new PluginWithVersion(plugin, version));
|
PluginVersion pluginVersion = plugin.getVersion(version);
|
||||||
plugin.servers.add(this);
|
plugins.add(pluginVersion);
|
||||||
|
pluginVersion.servers.add(this);
|
||||||
|
|
||||||
this.persistAndFlush();
|
this.persistAndFlush();
|
||||||
return plugin;
|
return plugin;
|
||||||
|
|
Loading…
Reference in a new issue