fix mojang status endpoint
This commit is contained in:
parent
ee5b1f12d8
commit
2b017f9ef7
@ -16,9 +16,9 @@ import java.util.Objects;
|
||||
@Log4j2(topic = "Main")
|
||||
@SpringBootApplication
|
||||
public class Main {
|
||||
public static final Gson GSON = new GsonBuilder()
|
||||
.setDateFormat("MM-dd-yyyy HH:mm:ss")
|
||||
.create();
|
||||
public static final Gson GSON = new GsonBuilder()
|
||||
.setDateFormat("MM-dd-yyyy HH:mm:ss")
|
||||
.create();
|
||||
public static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient();
|
||||
|
||||
@SneakyThrows
|
||||
|
@ -22,7 +22,7 @@ public final class AppConfig {
|
||||
private static boolean isRunningTest = true;
|
||||
static {
|
||||
try {
|
||||
Class.forName("org.junit.Test");
|
||||
Class.forName("org.junit.jupiter.engine.JupiterTestEngine");
|
||||
} catch (ClassNotFoundException e) {
|
||||
isRunningTest = false;
|
||||
}
|
||||
|
85
src/main/java/xyz/mcutils/backend/common/MojangServer.java
Normal file
85
src/main/java/xyz/mcutils/backend/common/MojangServer.java
Normal file
@ -0,0 +1,85 @@
|
||||
package xyz.mcutils.backend.common;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author Fascinated (fascinated7)
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@ToString
|
||||
public enum MojangServer {
|
||||
SESSION("Session Server", "https://sessionserver.mojang.com"),
|
||||
API("Mojang API", "https://api.mojang.com"),
|
||||
TEXTURES("Textures Server", "https://textures.minecraft.net"),
|
||||
ASSETS("Assets Server", "https://assets.mojang.com"),
|
||||
LIBRARIES("Libraries Server", "https://libraries.minecraft.net"),
|
||||
SERVICES("Minecraft Services", "https://api.minecraftservices.com");
|
||||
|
||||
private static final long STATUS_TIMEOUT = TimeUnit.SECONDS.toMillis(4);
|
||||
|
||||
/**
|
||||
* The name of this server.
|
||||
*/
|
||||
@NonNull private final String name;
|
||||
|
||||
/**
|
||||
* The endpoint of this service.
|
||||
*/
|
||||
@NonNull private final String endpoint;
|
||||
|
||||
/**
|
||||
* Ping this service and get the status of it.
|
||||
*
|
||||
* @return the service status
|
||||
*/
|
||||
@NonNull
|
||||
public Status getStatus() {
|
||||
try {
|
||||
InetAddress address = InetAddress.getByName(endpoint.substring(8));
|
||||
long before = System.currentTimeMillis();
|
||||
if (address.isReachable((int) STATUS_TIMEOUT)) {
|
||||
// The time it took to reach the host is 75% of
|
||||
// the timeout, consider it to be degraded.
|
||||
if ((System.currentTimeMillis() - before) > STATUS_TIMEOUT * 0.75D) {
|
||||
return Status.DEGRADED;
|
||||
}
|
||||
return Status.ONLINE;
|
||||
}
|
||||
} catch (UnknownHostException ex) {
|
||||
ex.printStackTrace();
|
||||
} catch (IOException ignored) {
|
||||
// We can safely ignore any errors, we're simply checking
|
||||
// if the host is reachable, if it's not, it's offline.
|
||||
}
|
||||
return Status.OFFLINE;
|
||||
}
|
||||
|
||||
/**
|
||||
* The status of a service.
|
||||
*/
|
||||
public enum Status {
|
||||
/**
|
||||
* The service is online and accessible.
|
||||
*/
|
||||
ONLINE,
|
||||
|
||||
/**
|
||||
* The service is online, but is experiencing degraded performance.
|
||||
*/
|
||||
DEGRADED,
|
||||
|
||||
/**
|
||||
* The service is offline and inaccessible.
|
||||
*/
|
||||
OFFLINE
|
||||
}
|
||||
}
|
@ -24,9 +24,11 @@ public class MojangController {
|
||||
|
||||
@ResponseBody
|
||||
@GetMapping(value = "/status")
|
||||
public ResponseEntity<CachedEndpointStatus> getStatus() {
|
||||
public ResponseEntity<?> getStatus() {
|
||||
CachedEndpointStatus status = mojangService.getMojangApiStatus();
|
||||
|
||||
|
||||
|
||||
return ResponseEntity.ok()
|
||||
.cacheControl(CacheControl.maxAge(1, TimeUnit.MINUTES).cachePublic())
|
||||
.body(status);
|
||||
|
@ -1,6 +1,7 @@
|
||||
package xyz.mcutils.backend.exception;
|
||||
|
||||
import io.micrometer.common.lang.NonNull;
|
||||
import io.sentry.Sentry;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
@ -39,6 +40,7 @@ public final class ExceptionControllerAdvice {
|
||||
}
|
||||
if (status == null) { // Fallback to 500
|
||||
status = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
Sentry.captureException(ex); // Capture the exception with Sentry
|
||||
}
|
||||
return new ResponseEntity<>(new ErrorResponse(status, message), status);
|
||||
}
|
||||
|
@ -8,10 +8,13 @@ import lombok.Setter;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.redis.core.RedisHash;
|
||||
import xyz.mcutils.backend.common.CachedResponse;
|
||||
import xyz.mcutils.backend.model.mojang.EndpointStatus;
|
||||
import xyz.mcutils.backend.common.MojangServer;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Setter @Getter @EqualsAndHashCode(callSuper = false)
|
||||
@RedisHash(value = "mojangEndpointStatus", timeToLive = 60L) // 1 minute (in seconds)
|
||||
@ -26,11 +29,21 @@ public class CachedEndpointStatus extends CachedResponse implements Serializable
|
||||
/**
|
||||
* The endpoint cache.
|
||||
*/
|
||||
private final List<EndpointStatus> endpoints;
|
||||
private final List<Map<String, Object>> endpoints;
|
||||
|
||||
public CachedEndpointStatus(@NonNull String id, List<EndpointStatus> endpoints) {
|
||||
public CachedEndpointStatus(@NonNull String id, Map<MojangServer, MojangServer.Status> mojangServers) {
|
||||
super(Cache.defaultCache());
|
||||
this.id = id;
|
||||
this.endpoints = endpoints;
|
||||
this.endpoints = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<MojangServer, MojangServer.Status> entry : mojangServers.entrySet()) {
|
||||
MojangServer server = entry.getKey();
|
||||
|
||||
Map<String, Object> serverStatus = new HashMap<>();
|
||||
serverStatus.put("name", server.getName());
|
||||
serverStatus.put("endpoint", server.getEndpoint());
|
||||
serverStatus.put("status", entry.getValue().name());
|
||||
endpoints.add(serverStatus);
|
||||
}
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package xyz.mcutils.backend.service;
|
||||
import com.maxmind.geoip2.DatabaseReader;
|
||||
import com.maxmind.geoip2.exception.GeoIp2Exception;
|
||||
import com.maxmind.geoip2.model.CityResponse;
|
||||
import io.sentry.Sentry;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.codehaus.plexus.archiver.tar.TarGZipUnArchiver;
|
||||
@ -68,6 +69,7 @@ public class MaxMindService {
|
||||
return database.city(InetAddress.getByName(ip));
|
||||
} catch (IOException | GeoIp2Exception e) {
|
||||
log.error("Failed to lookup the GeoIP information for '{}'", ip, e);
|
||||
Sentry.captureException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -13,16 +13,15 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import xyz.mcutils.backend.common.AppConfig;
|
||||
import xyz.mcutils.backend.common.ExpiringSet;
|
||||
import xyz.mcutils.backend.common.MojangServer;
|
||||
import xyz.mcutils.backend.common.WebRequest;
|
||||
import xyz.mcutils.backend.model.cache.CachedEndpointStatus;
|
||||
import xyz.mcutils.backend.model.mojang.EndpointStatus;
|
||||
import xyz.mcutils.backend.model.token.MojangProfileToken;
|
||||
import xyz.mcutils.backend.model.token.MojangUsernameToUuidToken;
|
||||
import xyz.mcutils.backend.repository.redis.EndpointStatusRepository;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
@ -49,18 +48,6 @@ public class MojangService {
|
||||
*/
|
||||
private static final long FETCH_BLOCKED_SERVERS_INTERVAL = TimeUnit.HOURS.toMillis(1L);
|
||||
|
||||
/**
|
||||
* Information about the Mojang API endpoints.
|
||||
*/
|
||||
private static final String MOJANG_ENDPOINT_STATUS_KEY = "mojang";
|
||||
private static final List<EndpointStatus> MOJANG_ENDPOINTS = List.of(
|
||||
new EndpointStatus("Minecraft Textures", "textures.minecraft.net"),
|
||||
new EndpointStatus("Minecraft Libraries", "libraries.minecraft.net"),
|
||||
new EndpointStatus("Minecraft Services", "api.minecraftservices.com"),
|
||||
new EndpointStatus("Mojang Assets", "assets.mojang.com"),
|
||||
new EndpointStatus("Mojang API", "api.mojang.com"),
|
||||
new EndpointStatus("Mojang Session Server", "sessionserver.mojang.com"));
|
||||
|
||||
@Autowired
|
||||
private EndpointStatusRepository mojangEndpointStatusRepository;
|
||||
|
||||
@ -107,6 +94,8 @@ public class MojangService {
|
||||
}
|
||||
bannedServerHashes = Collections.synchronizedList(hashes);
|
||||
log.info("Fetched {} banned server hashes", bannedServerHashes.size());
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to fetch blocked servers from Mojang", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,33 +173,24 @@ public class MojangService {
|
||||
*/
|
||||
public CachedEndpointStatus getMojangApiStatus() {
|
||||
log.info("Getting Mojang API status");
|
||||
Optional<CachedEndpointStatus> endpointStatus = mojangEndpointStatusRepository.findById(MOJANG_ENDPOINT_STATUS_KEY);
|
||||
Optional<CachedEndpointStatus> endpointStatus = mojangEndpointStatusRepository.findById("mojang-servers-status");
|
||||
if (endpointStatus.isPresent() && AppConfig.isProduction()) {
|
||||
log.info("Got cached Mojang API status");
|
||||
return endpointStatus.get();
|
||||
}
|
||||
|
||||
MOJANG_ENDPOINTS.parallelStream().forEach(endpoint -> {
|
||||
try {
|
||||
long start = System.currentTimeMillis();
|
||||
InetAddress address = InetAddress.getByName(endpoint.getHostname());
|
||||
if (address.isReachable((int) TimeUnit.SECONDS.toMillis(4))) { // Check if the endpoint is reachable
|
||||
endpoint.setStatus(EndpointStatus.Status.ONLINE);
|
||||
return;
|
||||
}
|
||||
// Check if the endpoint took too long to respond
|
||||
if (System.currentTimeMillis() - start > TimeUnit.SECONDS.toMillis(2)) {
|
||||
endpoint.setStatus(EndpointStatus.Status.DEGRADED);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
endpoint.setStatus(EndpointStatus.Status.OFFLINE);
|
||||
}
|
||||
Map<MojangServer, MojangServer.Status> mojangServers = new HashMap<>();
|
||||
Arrays.stream(MojangServer.values()).parallel().forEach(server -> {
|
||||
log.info("Pinging {}...", server.getEndpoint());
|
||||
MojangServer.Status status = server.getStatus(); // Retrieve the server status
|
||||
log.info("Retrieved status of {}: {}", server.getEndpoint(), status.name());
|
||||
mojangServers.put(server, status); // Cache the server status
|
||||
});
|
||||
|
||||
log.info("Fetched Mojang API status for {} endpoints", MOJANG_ENDPOINTS.size());
|
||||
log.info("Fetched Mojang API status for {} endpoints", mojangServers.size());
|
||||
CachedEndpointStatus status = new CachedEndpointStatus(
|
||||
MOJANG_ENDPOINT_STATUS_KEY,
|
||||
MOJANG_ENDPOINTS
|
||||
"mojang-servers-status",
|
||||
mojangServers
|
||||
);
|
||||
mojangEndpointStatusRepository.save(status);
|
||||
status.getCache().setCached(false);
|
||||
|
Loading…
x
Reference in New Issue
Block a user