This commit is contained in:
parent
d959169f0b
commit
8990a6308a
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024, Fascinated
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
8
pom.xml
8
pom.xml
@ -116,6 +116,14 @@
|
|||||||
<artifactId>jedis</artifactId>
|
<artifactId>jedis</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- SwaggerUI -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springdoc</groupId>
|
||||||
|
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||||
|
<version>2.0.2</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Unit Tests -->
|
<!-- Unit Tests -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package cc.fascinated;
|
package cc.fascinated;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||||
|
import io.swagger.v3.oas.annotations.info.Contact;
|
||||||
|
import io.swagger.v3.oas.annotations.info.Info;
|
||||||
|
import io.swagger.v3.oas.annotations.info.License;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
@ -12,7 +16,15 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@SpringBootApplication @Log4j2
|
@Log4j2
|
||||||
|
@SpringBootApplication
|
||||||
|
@OpenAPIDefinition(info = @Info(
|
||||||
|
title = "Minecraft Utilities API",
|
||||||
|
version = "1.0",
|
||||||
|
description = "Wrapper for the Minecraft APIs to make them easier to use.",
|
||||||
|
contact = @Contact(name = "Liam", email = "liam@fascinated.cc", url = "https://fascinated.cc"),
|
||||||
|
license = @License(name = "MIT License", url = "https://opensource.org/licenses/MIT")
|
||||||
|
))
|
||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
public static final Gson GSON = new Gson();
|
public static final Gson GSON = new Gson();
|
||||||
|
@ -19,6 +19,7 @@ public class HomeController {
|
|||||||
public String home(Model model) {
|
public String home(Model model) {
|
||||||
model.addAttribute("player_example_url", Config.INSTANCE.getWebPublicUrl() + "/player/" + exampleUuid);
|
model.addAttribute("player_example_url", Config.INSTANCE.getWebPublicUrl() + "/player/" + exampleUuid);
|
||||||
model.addAttribute("java_server_example_url", Config.INSTANCE.getWebPublicUrl() + "/server/java/play.hypixel.net");
|
model.addAttribute("java_server_example_url", Config.INSTANCE.getWebPublicUrl() + "/server/java/play.hypixel.net");
|
||||||
|
model.addAttribute("swagger_url", Config.INSTANCE.getWebPublicUrl() + "/swagger-ui.html");
|
||||||
return "index";
|
return "index";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import cc.fascinated.common.PlayerUtils;
|
|||||||
import cc.fascinated.model.player.Player;
|
import cc.fascinated.model.player.Player;
|
||||||
import cc.fascinated.model.player.Skin;
|
import cc.fascinated.model.player.Skin;
|
||||||
import cc.fascinated.service.PlayerService;
|
import cc.fascinated.service.PlayerService;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.CacheControl;
|
import org.springframework.http.CacheControl;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
@ -14,6 +15,7 @@ import org.springframework.web.bind.annotation.*;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@Tag(name = "Player Controller", description = "The Player Controller is used to get information about a player.")
|
||||||
@RequestMapping(value = "/player/")
|
@RequestMapping(value = "/player/")
|
||||||
public class PlayerController {
|
public class PlayerController {
|
||||||
|
|
||||||
|
@ -3,10 +3,8 @@ package cc.fascinated.controller;
|
|||||||
import cc.fascinated.common.ServerUtils;
|
import cc.fascinated.common.ServerUtils;
|
||||||
import cc.fascinated.common.Tuple;
|
import cc.fascinated.common.Tuple;
|
||||||
import cc.fascinated.model.cache.CachedMinecraftServer;
|
import cc.fascinated.model.cache.CachedMinecraftServer;
|
||||||
import cc.fascinated.model.server.JavaMinecraftServer;
|
|
||||||
import cc.fascinated.model.server.MinecraftServer;
|
|
||||||
import cc.fascinated.service.ServerService;
|
import cc.fascinated.service.ServerService;
|
||||||
import cc.fascinated.service.pinger.impl.JavaMinecraftServerPinger;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@ -14,6 +12,7 @@ import org.springframework.http.ResponseEntity;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
|
@Tag(name = "Server Controller", description = "The Server Controller is used to get information about a server.")
|
||||||
@RequestMapping(value = "/server/")
|
@RequestMapping(value = "/server/")
|
||||||
public class ServerController {
|
public class ServerController {
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package cc.fascinated.model.cache;
|
package cc.fascinated.model.cache;
|
||||||
|
|
||||||
import cc.fascinated.model.mojang.MojangProfile;
|
|
||||||
import cc.fascinated.model.player.Cape;
|
import cc.fascinated.model.player.Cape;
|
||||||
import cc.fascinated.model.player.Player;
|
import cc.fascinated.model.player.Player;
|
||||||
import cc.fascinated.model.player.Skin;
|
import cc.fascinated.model.player.Skin;
|
||||||
|
@ -3,7 +3,6 @@ package cc.fascinated.model.player;
|
|||||||
import cc.fascinated.common.Tuple;
|
import cc.fascinated.common.Tuple;
|
||||||
import cc.fascinated.common.UUIDUtils;
|
import cc.fascinated.common.UUIDUtils;
|
||||||
import cc.fascinated.model.mojang.MojangProfile;
|
import cc.fascinated.model.mojang.MojangProfile;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.springframework.data.annotation.Id;
|
import org.springframework.data.annotation.Id;
|
||||||
|
@ -11,7 +11,6 @@ import lombok.Getter;
|
|||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
package cc.fascinated.repository;
|
package cc.fascinated.repository;
|
||||||
|
|
||||||
import cc.fascinated.model.cache.CachedPlayer;
|
|
||||||
import cc.fascinated.model.cache.CachedPlayerName;
|
import cc.fascinated.model.cache.CachedPlayerName;
|
||||||
import org.springframework.data.repository.CrudRepository;
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A cache repository for player usernames.
|
* A cache repository for player usernames.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -44,6 +44,7 @@ public class PlayerService {
|
|||||||
*/
|
*/
|
||||||
public CachedPlayer getPlayer(String id) {
|
public CachedPlayer getPlayer(String id) {
|
||||||
id = id.toUpperCase(); // Convert the id to uppercase to prevent case sensitivity
|
id = id.toUpperCase(); // Convert the id to uppercase to prevent case sensitivity
|
||||||
|
log.info("Getting player: {}", id);
|
||||||
UUID uuid = PlayerUtils.getUuidFromString(id);
|
UUID uuid = PlayerUtils.getUuidFromString(id);
|
||||||
if (uuid == null) { // If the id is not a valid uuid, get the uuid from the username
|
if (uuid == null) { // If the id is not a valid uuid, get the uuid from the username
|
||||||
uuid = usernameToUuid(id);
|
uuid = usernameToUuid(id);
|
||||||
@ -51,11 +52,14 @@ public class PlayerService {
|
|||||||
|
|
||||||
Optional<CachedPlayer> cachedPlayer = playerCacheRepository.findById(uuid);
|
Optional<CachedPlayer> cachedPlayer = playerCacheRepository.findById(uuid);
|
||||||
if (cachedPlayer.isPresent()) { // Return the cached player if it exists
|
if (cachedPlayer.isPresent()) { // Return the cached player if it exists
|
||||||
|
log.info("Player {} is cached", id);
|
||||||
return cachedPlayer.get();
|
return cachedPlayer.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
log.info("Getting player profile from Mojang: {}", id);
|
||||||
MojangProfile mojangProfile = mojangAPIService.getProfile(uuid.toString()); // Get the player profile from Mojang
|
MojangProfile mojangProfile = mojangAPIService.getProfile(uuid.toString()); // Get the player profile from Mojang
|
||||||
|
log.info("Got player profile from Mojang: {}", id);
|
||||||
Tuple<Skin, Cape> skinAndCape = mojangProfile.getSkinAndCape();
|
Tuple<Skin, Cape> skinAndCape = mojangProfile.getSkinAndCape();
|
||||||
CachedPlayer player = new CachedPlayer(
|
CachedPlayer player = new CachedPlayer(
|
||||||
uuid, // Player UUID
|
uuid, // Player UUID
|
||||||
@ -80,6 +84,7 @@ public class PlayerService {
|
|||||||
* @return the uuid of the player
|
* @return the uuid of the player
|
||||||
*/
|
*/
|
||||||
private UUID usernameToUuid(String username) {
|
private UUID usernameToUuid(String username) {
|
||||||
|
log.info("Getting UUID from username: {}", username);
|
||||||
Optional<CachedPlayerName> cachedPlayerName = playerNameCacheRepository.findById(username);
|
Optional<CachedPlayerName> cachedPlayerName = playerNameCacheRepository.findById(username);
|
||||||
if (cachedPlayerName.isPresent()) {
|
if (cachedPlayerName.isPresent()) {
|
||||||
return cachedPlayerName.get().getUniqueId();
|
return cachedPlayerName.get().getUniqueId();
|
||||||
@ -87,10 +92,12 @@ public class PlayerService {
|
|||||||
try {
|
try {
|
||||||
MojangUsernameToUuid mojangUsernameToUuid = mojangAPIService.getUuidFromUsername(username);
|
MojangUsernameToUuid mojangUsernameToUuid = mojangAPIService.getUuidFromUsername(username);
|
||||||
if (mojangUsernameToUuid == null) {
|
if (mojangUsernameToUuid == null) {
|
||||||
|
log.info("Player with username '{}' not found", username);
|
||||||
throw new ResourceNotFoundException("Player with username '%s' not found".formatted(username));
|
throw new ResourceNotFoundException("Player with username '%s' not found".formatted(username));
|
||||||
}
|
}
|
||||||
UUID uuid = UUIDUtils.addDashes(mojangUsernameToUuid.getId());
|
UUID uuid = UUIDUtils.addDashes(mojangUsernameToUuid.getId());
|
||||||
playerNameCacheRepository.save(new CachedPlayerName(username, uuid));
|
playerNameCacheRepository.save(new CachedPlayerName(username, uuid));
|
||||||
|
log.info("Got UUID from username: {} -> {}", username, uuid);
|
||||||
return uuid;
|
return uuid;
|
||||||
} catch (RateLimitException exception) {
|
} catch (RateLimitException exception) {
|
||||||
throw new MojangAPIRateLimitException();
|
throw new MojangAPIRateLimitException();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package cc.fascinated.service;
|
package cc.fascinated.service;
|
||||||
|
|
||||||
import cc.fascinated.common.EnumUtils;
|
|
||||||
import cc.fascinated.common.DNSUtils;
|
import cc.fascinated.common.DNSUtils;
|
||||||
|
import cc.fascinated.common.EnumUtils;
|
||||||
import cc.fascinated.exception.impl.BadRequestException;
|
import cc.fascinated.exception.impl.BadRequestException;
|
||||||
import cc.fascinated.exception.impl.ResourceNotFoundException;
|
import cc.fascinated.exception.impl.ResourceNotFoundException;
|
||||||
import cc.fascinated.model.cache.CachedMinecraftServer;
|
import cc.fascinated.model.cache.CachedMinecraftServer;
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
server:
|
server:
|
||||||
address: 0.0.0.0
|
address: 0.0.0.0
|
||||||
port: 80
|
port: 80
|
||||||
|
servlet:
|
||||||
|
context-path: /
|
||||||
error:
|
error:
|
||||||
whitelabel:
|
whitelabel:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
@ -21,7 +21,8 @@
|
|||||||
|
|
||||||
<div class="flex flex-col mt-3">
|
<div class="flex flex-col mt-3">
|
||||||
<p>Player Data: <a class="text-blue-600" target=”_blank” th:href="${player_example_url}" th:text="${player_example_url}">???</a></p>
|
<p>Player Data: <a class="text-blue-600" target=”_blank” th:href="${player_example_url}" th:text="${player_example_url}">???</a></p>
|
||||||
<p>Java Server: <a class="text-blue-600" target=”_blank” th:href="${java_server_example_url}" th:text="${java_server_example_url}">???</a></p>
|
<p>Player Data: <a class="text-blue-600" target=”_blank” th:href="${java_server_example_url}" th:text="${java_server_example_url}">???</a></p>
|
||||||
|
<p>Swagger Docs: <a class="text-blue-600" target=”_blank” th:href="${swagger_url}" th:text="${swagger_url}">???</a></p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -1,6 +1,8 @@
|
|||||||
server:
|
server:
|
||||||
address: 0.0.0.0
|
address: 0.0.0.0
|
||||||
port: 80
|
port: 80
|
||||||
|
servlet:
|
||||||
|
context-path: /
|
||||||
error:
|
error:
|
||||||
whitelabel:
|
whitelabel:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
@ -21,7 +21,8 @@
|
|||||||
|
|
||||||
<div class="flex flex-col mt-3">
|
<div class="flex flex-col mt-3">
|
||||||
<p>Player Data: <a class="text-blue-600" target=”_blank” th:href="${player_example_url}" th:text="${player_example_url}">???</a></p>
|
<p>Player Data: <a class="text-blue-600" target=”_blank” th:href="${player_example_url}" th:text="${player_example_url}">???</a></p>
|
||||||
<p>Java Server: <a class="text-blue-600" target=”_blank” th:href="${java_server_example_url}" th:text="${java_server_example_url}">???</a></p>
|
<p>Player Data: <a class="text-blue-600" target=”_blank” th:href="${java_server_example_url}" th:text="${java_server_example_url}">???</a></p>
|
||||||
|
<p>Swagger Docs: <a class="text-blue-600" target=”_blank” th:href="${swagger_url}" th:text="${swagger_url}">???</a></p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
Loading…
Reference in New Issue
Block a user