From 7defa9c446ff38b187ee070aa66ad610f5eaf60d Mon Sep 17 00:00:00 2001 From: gsd Date: Sat, 11 Mar 2023 21:21:37 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BB=D0=BE=D0=B3=D0=B8=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=B8=20=D0=BF=D0=BE=D0=B4=D0=B3?= =?UTF-8?q?=D1=80=D1=83=D0=B7=D0=BA=D0=B0=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D1=85=20=D0=BD=D0=B0=20=D1=85=D0=BE=D0=B4=D1=83=20=D0=BF=D1=80?= =?UTF-8?q?=D0=B8=20=D0=B2=D1=8B=D0=B7=D0=BE=D0=B2=D0=B5=20=D1=8D=D0=BD?= =?UTF-8?q?=D0=B4=D0=BF=D0=BE=D0=B8=D0=BD=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ext/python-a2s-rcon-api/RCONPlayerModel.py | 2 +- ext/python-a2s-rcon-api/docker-compose.yaml | 2 +- ext/python-a2s-rcon-api/service.py | 8 +++-- .../annotations/impl/PermitionFlagAspect.java | 6 +++- .../annotations/impl/UpdatePlayersAspect.java | 31 +++++++++++++++++++ .../app/annotations/impl/WebAccessAspect.java | 11 +++++-- .../interfaces/BurstUpdatePlayers.java | 11 +++++++ .../java/app/controllers/StatsController.java | 2 ++ .../app/controllers/admin/BanController.java | 2 ++ .../app/controllers/admin/KickController.java | 2 ++ .../controllers/user/DetailController.java | 2 ++ .../controllers/user/ProfileController.java | 3 ++ .../a2s/external/ExternalValveClient.java | 18 +++++++---- src/main/java/app/updates/PlayersUpdater.java | 23 ++++++++++++++ src/main/resources/application.yaml | 3 +- 15 files changed, 111 insertions(+), 15 deletions(-) create mode 100644 src/main/java/app/annotations/impl/UpdatePlayersAspect.java create mode 100644 src/main/java/app/annotations/interfaces/BurstUpdatePlayers.java diff --git a/ext/python-a2s-rcon-api/RCONPlayerModel.py b/ext/python-a2s-rcon-api/RCONPlayerModel.py index 655d981..1bdae39 100644 --- a/ext/python-a2s-rcon-api/RCONPlayerModel.py +++ b/ext/python-a2s-rcon-api/RCONPlayerModel.py @@ -45,6 +45,6 @@ class RCONPlayer: self.steam2 = splited[len - 6] self.name = " ".join(splited[2:len-6])[1:-1] self.steam = GetSteam(self.steam2) - except ValueError as err: + except (ValueError, IndexError) as err: print(splited) raise err \ No newline at end of file diff --git a/ext/python-a2s-rcon-api/docker-compose.yaml b/ext/python-a2s-rcon-api/docker-compose.yaml index 6fc8379..bef689a 100644 --- a/ext/python-a2s-rcon-api/docker-compose.yaml +++ b/ext/python-a2s-rcon-api/docker-compose.yaml @@ -2,6 +2,6 @@ services: a2s_backend: build: ./ extra_hosts: - - "tf2.pblr-nyk.pro:192.168.3.1" + - "tf2.pblr-nyk.pro:192.168.3.3" ports: - 8085:8082 \ No newline at end of file diff --git a/ext/python-a2s-rcon-api/service.py b/ext/python-a2s-rcon-api/service.py index af86ff0..e12ee85 100644 --- a/ext/python-a2s-rcon-api/service.py +++ b/ext/python-a2s-rcon-api/service.py @@ -34,6 +34,10 @@ class RCON_Request(A2S_request): def fulled_data(self): return self.server and self.password and self.command +class EndpointFilter(logging.Filter): + def filter(self, record: logging.LogRecord) -> bool: + return record.getMessage().find("/api") == -1 + class SourceBackend: app = None @@ -43,7 +47,7 @@ class SourceBackend: def run(self, host = "0.0.0.0", port = 45353): import uvicorn - logging.getLogger("uvicorn.info").disabled = True + logging.getLogger("uvicorn.access").addFilter(EndpointFilter()) uvicorn.run(self.app, host = host, port = port) def setup_routes(self): @@ -119,7 +123,7 @@ class SourceBackend: continue try: players.append(RCONPlayer(line).__dict__) - except BotPlayer: + except (BotPlayer, ValueError, IndexError): pass except: traceback.print_exc() diff --git a/src/main/java/app/annotations/impl/PermitionFlagAspect.java b/src/main/java/app/annotations/impl/PermitionFlagAspect.java index fd4f893..0bbc976 100644 --- a/src/main/java/app/annotations/impl/PermitionFlagAspect.java +++ b/src/main/java/app/annotations/impl/PermitionFlagAspect.java @@ -11,6 +11,8 @@ import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; @@ -22,6 +24,8 @@ import java.util.List; public class PermitionFlagAspect { ProfileService profileService; + private final Logger logger = LoggerFactory.getLogger(PermitionFlagAspect.class); + @Autowired public PermitionFlagAspect(ProfileService profileService) { this.profileService = profileService; @@ -37,7 +41,7 @@ public class PermitionFlagAspect { @Before(value = "@annotation(app.annotations.interfaces.CheckPermitionFlag) && args(request,..)") public void before(JoinPoint joinPoint, HttpServletRequest request){ String flag = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(CheckPermitionFlag.class).flag(); - System.out.println("check permition flag"); + logger.info("check permition flag, requested: {}", flag); if(!(request instanceof HttpServletRequest)) { throw new RuntimeException("invalid request"); } diff --git a/src/main/java/app/annotations/impl/UpdatePlayersAspect.java b/src/main/java/app/annotations/impl/UpdatePlayersAspect.java new file mode 100644 index 0000000..c59d666 --- /dev/null +++ b/src/main/java/app/annotations/impl/UpdatePlayersAspect.java @@ -0,0 +1,31 @@ +package app.annotations.impl; + +import app.updates.PlayersUpdater; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; + +import java.time.Instant; + +@Aspect +@Configuration +public class UpdatePlayersAspect { + PlayersUpdater playersUpdater; + private long last_burst_update = 0; + private int burst_timeout = 5; + + @Autowired + public UpdatePlayersAspect(PlayersUpdater playersUpdater) { + this.playersUpdater = playersUpdater; + } + + @Before("@annotation(app.annotations.interfaces.BurstUpdatePlayers) && args(..)") + public void before() { + if (Instant.now().getEpochSecond() - last_burst_update < burst_timeout) { + return; + } + playersUpdater.burstUpdater(); + last_burst_update = Instant.now().getEpochSecond(); + } +} diff --git a/src/main/java/app/annotations/impl/WebAccessAspect.java b/src/main/java/app/annotations/impl/WebAccessAspect.java index 0608d2f..ca9d165 100644 --- a/src/main/java/app/annotations/impl/WebAccessAspect.java +++ b/src/main/java/app/annotations/impl/WebAccessAspect.java @@ -5,12 +5,15 @@ import app.annotations.exceptions.InvalidCookie; import app.annotations.exceptions.InvalidSecretKey; import app.annotations.exceptions.NeedCookie; import app.annotations.interfaces.CheckWebAccess; +import app.entities.a2s.external.ExternalValveClient; import app.utils.SaltedCookie; import jakarta.servlet.http.HttpServletRequest; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; @@ -19,6 +22,8 @@ import org.springframework.context.annotation.Configuration; public class WebAccessAspect { SaltedCookie saltedCookie; + private final Logger logger = LoggerFactory.getLogger(WebAccessAspect.class); + @Autowired public WebAccessAspect(SaltedCookie saltedCookie) { this.saltedCookie = saltedCookie; @@ -27,7 +32,7 @@ public class WebAccessAspect { @Before("@annotation(app.annotations.interfaces.CheckWebAccess) && args(request,..)") public void before(JoinPoint joinPoint, HttpServletRequest request){ AuthMethod auth_method = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(CheckWebAccess.class).auth_method(); - System.out.println("check web access"); + logger.info("check web access"); if(!(request instanceof HttpServletRequest)) { throw new RuntimeException("cannot read cookie from invalid request"); } @@ -62,7 +67,7 @@ public class WebAccessAspect { case COMBINED -> { if (!secret_key.isEmpty() && !steam64.isEmpty()) { if (saltedCookie.ValidateSecretKey(secret_key)) { - System.out.println("used secret key with steamid"); + logger.info("used secret key with steamid: {}", steam64); return; } else { throw new InvalidSecretKey(); @@ -73,7 +78,7 @@ public class WebAccessAspect { case SECRET_KEY -> { if (secret_key.isEmpty()) throw new InvalidSecretKey(); if (saltedCookie.ValidateSecretKey(secret_key)) { - System.out.println("used secret key without steamid"); + logger.info("used secret key without steamid"); return; } else { throw new InvalidSecretKey(); diff --git a/src/main/java/app/annotations/interfaces/BurstUpdatePlayers.java b/src/main/java/app/annotations/interfaces/BurstUpdatePlayers.java new file mode 100644 index 0000000..ddd8cc3 --- /dev/null +++ b/src/main/java/app/annotations/interfaces/BurstUpdatePlayers.java @@ -0,0 +1,11 @@ +package app.annotations.interfaces; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface BurstUpdatePlayers { +} diff --git a/src/main/java/app/controllers/StatsController.java b/src/main/java/app/controllers/StatsController.java index 332faa9..883aa60 100644 --- a/src/main/java/app/controllers/StatsController.java +++ b/src/main/java/app/controllers/StatsController.java @@ -1,5 +1,6 @@ package app.controllers; +import app.annotations.interfaces.BurstUpdatePlayers; import app.entities.Stats; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -19,6 +20,7 @@ public class StatsController { } @GetMapping + @BurstUpdatePlayers public ResponseEntity GetStats(){ return new ResponseEntity<>(stats, HttpStatus.OK); } diff --git a/src/main/java/app/controllers/admin/BanController.java b/src/main/java/app/controllers/admin/BanController.java index bded89f..a6b5e70 100644 --- a/src/main/java/app/controllers/admin/BanController.java +++ b/src/main/java/app/controllers/admin/BanController.java @@ -1,6 +1,7 @@ package app.controllers.admin; import app.annotations.enums.AuthMethod; +import app.annotations.interfaces.BurstUpdatePlayers; import app.annotations.interfaces.CheckPermitionFlag; import app.annotations.interfaces.CheckWebAccess; import app.entities.db.Ban; @@ -35,6 +36,7 @@ public class BanController { @PostMapping @CheckWebAccess @CheckPermitionFlag(flag = "d") + @BurstUpdatePlayers public ResponseEntity banPlayer( HttpServletRequest request, @CookieValue(value = "steam64") String admin_steam64, diff --git a/src/main/java/app/controllers/admin/KickController.java b/src/main/java/app/controllers/admin/KickController.java index db80e7c..98de5d0 100644 --- a/src/main/java/app/controllers/admin/KickController.java +++ b/src/main/java/app/controllers/admin/KickController.java @@ -1,5 +1,6 @@ package app.controllers.admin; +import app.annotations.interfaces.BurstUpdatePlayers; import app.annotations.interfaces.CheckPermitionFlag; import app.annotations.interfaces.CheckWebAccess; import app.entities.PlayerProfile; @@ -34,6 +35,7 @@ public class KickController { @PostMapping @CheckWebAccess @CheckPermitionFlag(flag = "c") + @BurstUpdatePlayers public ResponseEntity kickPlayer( HttpServletRequest request, @CookieValue(value = "steam64") String steam64, diff --git a/src/main/java/app/controllers/user/DetailController.java b/src/main/java/app/controllers/user/DetailController.java index 92d66fd..6ed23df 100644 --- a/src/main/java/app/controllers/user/DetailController.java +++ b/src/main/java/app/controllers/user/DetailController.java @@ -1,6 +1,7 @@ package app.controllers.user; import app.annotations.enums.AuthMethod; +import app.annotations.interfaces.BurstUpdatePlayers; import app.annotations.interfaces.CheckWebAccess; import app.entities.other.SteamID; import app.services.ProfileService; @@ -24,6 +25,7 @@ public class DetailController { @GetMapping @CheckWebAccess(auth_method = AuthMethod.SECRET_KEY) + @BurstUpdatePlayers public ResponseEntity GetUser(HttpServletRequest request, @RequestParam String steam64) { return new ResponseEntity(profileService.GetProfile(steam64), HttpStatus.OK); diff --git a/src/main/java/app/controllers/user/ProfileController.java b/src/main/java/app/controllers/user/ProfileController.java index f580e54..4c394ba 100644 --- a/src/main/java/app/controllers/user/ProfileController.java +++ b/src/main/java/app/controllers/user/ProfileController.java @@ -1,6 +1,7 @@ package app.controllers.user; import app.annotations.enums.AuthMethod; +import app.annotations.interfaces.BurstUpdatePlayers; import app.annotations.interfaces.CheckWebAccess; import app.entities.SocialAuth; import app.services.ProfileService; @@ -37,6 +38,7 @@ public class ProfileController { @GetMapping @CheckWebAccess + @BurstUpdatePlayers public ResponseEntity GetCurrentUser(HttpServletRequest request, @CookieValue(value = "steam64", defaultValue = "") String steam64, @RequestParam(value = "requests", defaultValue = "") String requests @@ -63,6 +65,7 @@ public class ProfileController { @PostMapping("/report") @CheckWebAccess + @BurstUpdatePlayers public ResponseEntity ReportUser(HttpServletRequest request, @CookieValue(value = "steam64", defaultValue = "") String steam64, @RequestParam(value = "steam64", defaultValue = "") String reported_steam64, diff --git a/src/main/java/app/entities/a2s/external/ExternalValveClient.java b/src/main/java/app/entities/a2s/external/ExternalValveClient.java index 25acbae..0b031bd 100644 --- a/src/main/java/app/entities/a2s/external/ExternalValveClient.java +++ b/src/main/java/app/entities/a2s/external/ExternalValveClient.java @@ -3,7 +3,10 @@ package app.entities.a2s.external; import app.entities.a2s.requests.A2SRequest; import app.entities.a2s.requests.RCONRequest; import app.entities.server.players.RCONPlayer; +import app.updates.BaseUpdater; import com.fasterxml.jackson.annotation.JsonIgnore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.web.client.RestTemplate; import java.util.*; @@ -16,35 +19,38 @@ public abstract class ExternalValveClient { @JsonIgnore public String gateway = System.getenv("A2S_BACKEND_URL"); + private final Logger logger = LoggerFactory.getLogger(ExternalValveClient.class); + public ExternalValveClient(){ restTemplate = new RestTemplate(); CheckApi(); } public void CheckApi(){ - System.out.printf("Check status: %s/api/ping\n", gateway); + logger.info("Check status: {}/api/ping", gateway); try { enabled = restTemplate.getForEntity("%s/api/ping".formatted(gateway), HashMap.class).getBody().containsKey("pong"); } catch (Exception err) { - System.out.print("A2S Backend not respond\n"); + logger.error("A2S Backend {} not respond", gateway); } } @JsonIgnore public String ExecuteRCON(RCONRequest request){ if(!enabled) { - System.out.printf("External client not enabled, cannot get rcon on %s\n", gateway); + logger.warn("External client not enabled, cannot get rcon on {}", gateway); return "not enabled"; } try { return restTemplate.postForEntity("%s/api/rcon".formatted(gateway), request, String.class).getBody(); } catch (Exception err) { + logger.error("Cannot execute rcon on {}, command: {}", request.getServer(), request.getCommand()); return "backend error"; } } @JsonIgnore public HashMap GetA2SInfo(A2SRequest request){ if(!enabled) { - System.out.printf("External client not enabled, cannot get a2s on %s\n", gateway); + logger.warn("External client not enabled, cannot get a2s on {}", gateway); return null; } try { @@ -56,13 +62,13 @@ public abstract class ExternalValveClient { public ArrayList GetRCONPlayers(RCONRequest request){ if(!enabled) { - System.out.printf("External client not enabled, cannot get rcon players on %s\n", gateway); + logger.warn("External client not enabled, cannot get rcon players on {}", gateway); return null; } try { return new ArrayList<>(Arrays.asList(restTemplate.postForEntity("%s/api/players".formatted(gateway), request, RCONPlayer[].class).getBody())); } catch (Exception err) { - System.out.printf("Cannot get rcon players from %s\n", request.getServer()); + logger.error("Cannot get rcon players from {}", request.getServer()); return new ArrayList<>(); } } diff --git a/src/main/java/app/updates/PlayersUpdater.java b/src/main/java/app/updates/PlayersUpdater.java index 2e383dc..2b18801 100644 --- a/src/main/java/app/updates/PlayersUpdater.java +++ b/src/main/java/app/updates/PlayersUpdater.java @@ -11,7 +11,12 @@ import org.springframework.stereotype.Component; import java.io.IOException; import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; @Component public class PlayersUpdater extends BaseUpdater{ @@ -40,4 +45,22 @@ public class PlayersUpdater extends BaseUpdater{ }); } } + + public void burstUpdater() { + ExecutorService executor = Executors.newCachedThreadPool(); + List tasks = new ArrayList<>(); + stats.getServers().forEach((server_name, server) -> { + tasks.add((Callable) () -> { + server.RefreshServerA2SData(); + return null; + }); + }); + + try { + executor.invokeAll(tasks); + executor.shutdown(); + } catch (InterruptedException err) { + logger.error("Cancel burst servers update"); + } + } } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 22d9476..8a2211a 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -54,4 +54,5 @@ backend: logging: level: - com.ibasco.agql.core.util.*: OFF \ No newline at end of file + com.ibasco.agql.core.util.*: OFF + app.updates.PlayersUpdater: ERROR \ No newline at end of file