Control panel started x2

This commit is contained in:
Michael Wain 2025-01-30 00:13:27 +03:00
parent 883457522d
commit d425583a7e
11 changed files with 197 additions and 31 deletions

2
.gitignore vendored
View File

@ -27,6 +27,8 @@ build/
### VS Code ### ### VS Code ###
.vscode/ .vscode/
last.log
### Mac OS ### ### Mac OS ###
.DS_Store .DS_Store
/cache/ /cache/

View File

@ -277,4 +277,8 @@ public class GameServer {
this.deleteSelf(playerId, this.players.get(playerId).getLocationId()); this.deleteSelf(playerId, this.players.get(playerId).getLocationId());
this.players.remove(playerId); this.players.remove(playerId);
} }
public int getPlayersCount() {
return this.players.size();
}
} }

View File

@ -16,6 +16,7 @@ import com.alterdekim.game.repository.BodyPartRepository;
import com.alterdekim.game.repository.SmileRepository; import com.alterdekim.game.repository.SmileRepository;
import com.alterdekim.game.service.*; import com.alterdekim.game.service.*;
import com.alterdekim.game.storage.StorageProperties; import com.alterdekim.game.storage.StorageProperties;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.javatuples.Pair; import org.javatuples.Pair;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -38,6 +39,9 @@ public class StartUpListener {
private static final String ADMIN_USERNAME = "admin"; private static final String ADMIN_USERNAME = "admin";
@Getter
private long startTime;
@Autowired @Autowired
private ServerConfig config; private ServerConfig config;
@ -139,6 +143,7 @@ public class StartUpListener {
@EventListener @EventListener
public void onApplicationEvent(ContextRefreshedEvent event) { public void onApplicationEvent(ContextRefreshedEvent event) {
this.startTime = System.currentTimeMillis();
// todo: compile other swf's // todo: compile other swf's
if( userService.findByUsername(ADMIN_USERNAME) != null ) { if( userService.findByUsername(ADMIN_USERNAME) != null ) {

View File

@ -0,0 +1,72 @@
package com.alterdekim.game.controller;
import com.alterdekim.game.component.GameServer;
import com.alterdekim.game.component.StartUpListener;
import com.alterdekim.game.service.UserService;
import com.alterdekim.game.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Date;
@Slf4j
@RestController
@RequestMapping("/api")
public class ApiController {
@Autowired
private Environment env;
@Autowired
private StartUpListener startUpListener;
@Autowired
private GameServer gameServer;
@Autowired
private UserService userService;
@GetMapping("/get_dashboard_logs")
public ResponseEntity<byte[]> getLogs() {
try {
Path logFile = Paths.get(env.getProperty("logging.file.name"));
RandomAccessFile file = new RandomAccessFile(logFile.toFile(), "r");
int n = 2048;
file.seek(logFile.toFile().length() - n);
byte[] b = new byte[n];
file.read(b, 0, n);
return ResponseEntity.ok(b);
} catch (IOException e) {
log.error("getLogs error: {}", e.getMessage());
return ResponseEntity.badRequest().build();
}
}
@GetMapping("/get_uptime")
public ResponseEntity<String> getUpTime() {
return ResponseEntity.ok(
DateUtils.prettyPrint(Duration.of(System.currentTimeMillis() - startUpListener.getStartTime(), ChronoUnit.MILLIS))
);
}
@GetMapping("/get_players_online")
public ResponseEntity<Integer> getPlayersOnline() {
return ResponseEntity.ok(gameServer.getPlayersCount());
}
@GetMapping("/get_users_count")
public ResponseEntity<Long> getUsersCount() {
return ResponseEntity.ok(userService.getUsersCount());
}
}

View File

@ -34,6 +34,7 @@ public class FileServerController {
case "css" -> MediaType.parseMediaType("text/css"); case "css" -> MediaType.parseMediaType("text/css");
case "_js" -> MediaType.parseMediaType("text/javascript"); case "_js" -> MediaType.parseMediaType("text/javascript");
case "img" -> MediaType.parseMediaType("image/jpeg"); case "img" -> MediaType.parseMediaType("image/jpeg");
case "svg" -> MediaType.parseMediaType("image/svg+xml");
default -> MediaType.TEXT_PLAIN; default -> MediaType.TEXT_PLAIN;
}).body(Files.readAllBytes(path)); }).body(Files.readAllBytes(path));
} catch (Exception e) { } catch (Exception e) {

View File

@ -14,10 +14,17 @@ import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import static com.alterdekim.game.controller.FileServerController.URL_PATH;
@Slf4j @Slf4j
@Controller @Controller
@ -43,7 +50,23 @@ public class StaticController {
} }
@GetMapping("/panel") @GetMapping("/panel")
public String panel() { public String panel(Model model, @RequestParam(value = "section", defaultValue = "Dashboard") PanelSection section) {
model.addAttribute("section", section.name());
model.addAttribute("script", "/" + URL_PATH + "/_js/" + section.name().toLowerCase() + ".js");
return "panel"; return "panel";
} }
public enum PanelSection {
Dashboard,
Locations,
Games,
Preloaders,
Users,
Banlist,
Cache,
Goods,
Items,
Actions,
Support
}
} }

View File

@ -2,10 +2,14 @@ package com.alterdekim.game.repository;
import com.alterdekim.game.entity.User; import com.alterdekim.game.entity.User;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@Repository @Repository
public interface UserRepository extends JpaRepository<User, Long> { public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username); User findByUsername(String username);
@Query(value = "SELECT COUNT(u) FROM User u")
Long findUsersCount();
} }

View File

@ -35,7 +35,8 @@ public class SpringSecurity {
authorize authorize
.requestMatchers("/").permitAll() .requestMatchers("/").permitAll()
.requestMatchers("/login").permitAll() .requestMatchers("/login").permitAll()
.requestMatchers("/panel").hasAnyAuthority("ROLE_USER") // todo: make several roles and separate them .requestMatchers("/panel").permitAll() // todo: make several roles and separate them
.requestMatchers("/api/**").permitAll() // todo: make several roles and separate them
.requestMatchers("/main").hasAnyAuthority("ROLE_USER") .requestMatchers("/main").hasAnyAuthority("ROLE_USER")
.requestMatchers("/"+ FileServerController.URL_PATH +"/**").permitAll() .requestMatchers("/"+ FileServerController.URL_PATH +"/**").permitAll()
.requestMatchers("/ConstructorACHandler.ashx").permitAll() .requestMatchers("/ConstructorACHandler.ashx").permitAll()

View File

@ -261,4 +261,8 @@ public class UserService {
public void removeUser(long userId) { public void removeUser(long userId) {
this.userRepository.deleteById(userId); this.userRepository.deleteById(userId);
} }
public Long getUsersCount() {
return this.userRepository.findUsersCount();
}
} }

View File

@ -0,0 +1,12 @@
package com.alterdekim.game.utils;
import java.time.Duration;
public class DateUtils {
public static String prettyPrint(Duration duration) {
return duration.toString()
.substring(2)
.replaceAll("(\\d[HMS])(?!$)", "$1 ")
.toLowerCase();
}
}

View File

@ -1,40 +1,78 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml"> <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head> <head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta charset="utf-8">
<meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark">
<link rel="stylesheet" href="/file/css/pico.min.css">
<script src="/file/_js/jquery-3.7.1.min.js"></script>
<title>WhimsyWorld</title> <title>WhimsyWorld</title>
<link rel="stylesheet" type="text/css" href="/file/css/mvp.css"/> <style>
aside > nav > ul > li {
font-size: 18px;
}
</style>
</head> </head>
<body> <body>
<header> <header>
<nav> <div class="container">
<h1><a href="/panel">WhimsyWorld</a></h1> <nav>
<ul> <ul>
<li>Dashboard</li> <li><strong>WhimsyWorld</strong></li>
<li><a href="#">Users</a></li> </ul>
<li> <ul>
<a href="#">Learn more</a> <li><a href="https://gitea.awain.net"><img src="/file/svg/github.svg"/></a></li>
<ul> <li><a href="/logout"><button class="secondary">Logout</button></a></li>
<li><a href="#">About us</a></li> </ul>
<li><a href="#">Contact us</a></li> </nav>
<li><a href="#">Get help</a></li> </div>
</ul>
</li>
</ul>
</nav>
<article>
<aside>
<center>System <b>warning</b> message</center>
</aside>
</article>
<br><br>
<h1>Where god <mark>thrive</mark> with <code>code</code>.</h1>
<p>We are world's <b>#1</b> idea building company 💡 coding all night long 👨🏻‍💻</p>
<br>
<p><a href="#"><i>Get Quote</i></a><a href="#"><b>Get Started &rarr;</b></a></p>
</header> </header>
<main> <main class="container grid" style="grid-template-columns: 1fr 6fr;">
<aside>
<nav>
<ul>
<li><a href="/panel">Dashboard</a></li>
<li><a href="/panel?section=Locations">Locations</a></li>
<li><a href="/panel?section=Games">Games</a></li>
<li><a href="/panel?section=Preloaders">Preloaders</a></li>
<li><a href="/panel?section=Users">Users</a></li>
<li><a href="/panel?section=Banlist">Banlist</a></li>
<li><a href="/panel?section=Cache">Cache</a></li>
<li><a href="/panel?section=Goods">Goods</a></li>
<li><a href="/panel?section=Items">Items list</a></li>
<li><a href="/panel?section=Actions">Actions</a></li>
<li><a href="/panel?section=Support">Support</a></li>
</ul>
</nav>
</aside>
<div class="container-fluid">
<th:block th:switch="${section}">
<th:block th:case="Preloaders">
<h2>Preloaders</h2>
</th:block>
<th:block th:case="*">
<h2>Dashboard</h2>
<div class="grid">
<div>
<h4>Uptime</h4>
<p id="uptime"></p>
</div>
<div>
<h4>Online</h4>
<p id="online"></p>
</div>
<div>
<h4>Users count</h4>
<p id="users_db"></p>
</div>
</div>
<h3>Logs</h3>
<article id="logs_div" aria-busy="true" style="overflow-y: scroll; max-height:750px;">
</article>
</th:block>
</th:block>
</div>
</main> </main>
<script th:src="${script}"></script>
</body> </body>
</html> </html>