Initial release
This commit is contained in:
commit
b5fa9c2756
18 changed files with 4397 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*.db
|
||||||
|
config.json
|
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"html.customData": ["./frontend/node_modules/mdui/html-data.zh-cn.json"],
|
||||||
|
"css.customData": ["./frontend/node_modules/mdui/css-data.zh-cn.json"]
|
||||||
|
}
|
2
README.md
Normal file
2
README.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# 我的世界监控台
|
||||||
|
用于查看我的世界服务器状态,并添加了重启功能
|
160
backend/.gitignore
vendored
Normal file
160
backend/.gitignore
vendored
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
#pdm.lock
|
||||||
|
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||||
|
# in version control.
|
||||||
|
# https://pdm.fming.dev/#use-with-ide
|
||||||
|
.pdm.toml
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
17
backend/README.md
Normal file
17
backend/README.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
## development
|
||||||
|
Create virtual environment and install dependencies
|
||||||
|
```
|
||||||
|
python -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
Debug the code
|
||||||
|
```
|
||||||
|
uvicorn main:app --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
## deployment
|
||||||
|
```
|
||||||
|
uvicorn main:app --host "127.0.0.1" --port 10010 --proxy-headers --forwarded-allow-ips "*"
|
||||||
|
```
|
19
backend/db.py
Normal file
19
backend/db.py
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import sqlite3
|
||||||
|
class MCDB:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.conn = sqlite3.connect('database.db')
|
||||||
|
self.cursor = self.conn.cursor()
|
||||||
|
self.cursor.execute(
|
||||||
|
"""CREATE TABLE IF NOT EXISTS mcstatus (
|
||||||
|
time TEXT NOT NULL,
|
||||||
|
latency REAL NOT NULL
|
||||||
|
)"""
|
||||||
|
)
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
|
def insert_time_and_latency(self, time, latency):
|
||||||
|
self.cursor.execute("""
|
||||||
|
INSERT INTO mcstatus (time, latency)
|
||||||
|
VALUES (?, ?)
|
||||||
|
""", (time, latency))
|
||||||
|
self.conn.commit()
|
109
backend/main.py
Normal file
109
backend/main.py
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
import json
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||||
|
from utils import getJavaServerStatus, schedule_job, get_last_hour_date_info, get_current_day_time_utc8, parse_date_to_h_m_s
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
from db import MCDB
|
||||||
|
from pydantic import BaseModel
|
||||||
|
import aiohttp
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
class Password(BaseModel):
|
||||||
|
code: str
|
||||||
|
|
||||||
|
mcdb = MCDB()
|
||||||
|
|
||||||
|
with open(Path(__file__).parent.parent / "config.json", encoding="utf-8") as f:
|
||||||
|
config = json.load(f)
|
||||||
|
|
||||||
|
HOST = config["mc_host"]
|
||||||
|
PASSWORD = config["password"]
|
||||||
|
RCON_SERVER = config["rcon_host"]
|
||||||
|
|
||||||
|
scheduler = AsyncIOScheduler()
|
||||||
|
# 每30s记录一次
|
||||||
|
scheduler.add_job(schedule_job, 'interval', [HOST, mcdb], seconds=30)
|
||||||
|
|
||||||
|
origins = [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
scheduler.start()
|
||||||
|
yield
|
||||||
|
mcdb.cursor.close()
|
||||||
|
mcdb.conn.close()
|
||||||
|
|
||||||
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
|
||||||
|
app.add_middleware(
|
||||||
|
CORSMiddleware,
|
||||||
|
allow_origins=origins,
|
||||||
|
allow_methods=["*"],
|
||||||
|
allow_headers=["*"],
|
||||||
|
)
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
async def read_root():
|
||||||
|
try:
|
||||||
|
status = await getJavaServerStatus(HOST)
|
||||||
|
except Exception:
|
||||||
|
return {
|
||||||
|
"time": get_current_day_time_utc8(),
|
||||||
|
"description": "请求错误",
|
||||||
|
"latency": 10000,
|
||||||
|
"max": "请求错误",
|
||||||
|
"online": "请求错误"
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"time": get_current_day_time_utc8(),
|
||||||
|
"description": status.description,
|
||||||
|
"latency": round(status.latency, 2),
|
||||||
|
"max": status.players.max,
|
||||||
|
"online": status.players.online
|
||||||
|
}
|
||||||
|
|
||||||
|
@app.get("/get_latency")
|
||||||
|
async def get_latency_info():
|
||||||
|
# 获取一个小时前的时间
|
||||||
|
one_hour_ago = get_last_hour_date_info()
|
||||||
|
mcdb.cursor.execute("SELECT time, latency FROM mcstatus WHERE time >= ?", (one_hour_ago, ))
|
||||||
|
rows = mcdb.cursor.fetchall()
|
||||||
|
|
||||||
|
data = [{"time": parse_date_to_h_m_s(row[0]), "latency": row[1]} for row in rows]
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/restart-server")
|
||||||
|
async def restart_mc_server(password: Password):
|
||||||
|
if PASSWORD != password.code:
|
||||||
|
return {
|
||||||
|
"status": "fail",
|
||||||
|
"msg": "密码错误"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with aiohttp.ClientSession() as asession:
|
||||||
|
async with asession.post(RCON_SERVER,
|
||||||
|
json={"code": password.code}) as resp1:
|
||||||
|
result1 = await resp1.json()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "成功",
|
||||||
|
"msg": "重启指令已发送,请耐心等待1分钟,或者查看图表观察服务器是否正常上线"
|
||||||
|
}
|
||||||
|
except Exception:
|
||||||
|
return {
|
||||||
|
"status": "失败",
|
||||||
|
"msg": "重启指令发送失败,请重试或者联系管理员"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import uvicorn
|
||||||
|
|
||||||
|
uvicorn.run(app, host="127.0.0.1", port=10000)
|
7
backend/requirements.txt
Normal file
7
backend/requirements.txt
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
uvicorn[standard]
|
||||||
|
fastapi
|
||||||
|
mcstatus
|
||||||
|
apscheduler
|
||||||
|
pytz
|
||||||
|
python-dateutil
|
||||||
|
aiohttp
|
36
backend/utils.py
Normal file
36
backend/utils.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
from mcstatus import JavaServer
|
||||||
|
from mcstatus.status_response import JavaStatusResponse
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from dateutil import parser
|
||||||
|
from pytz import timezone
|
||||||
|
|
||||||
|
from db import MCDB
|
||||||
|
|
||||||
|
async def getJavaServerStatus(host: str) -> JavaStatusResponse:
|
||||||
|
return await (await JavaServer.async_lookup(host)).async_status()
|
||||||
|
|
||||||
|
async def getJavaServerLatency(host: str) -> float:
|
||||||
|
status = await getJavaServerStatus(host)
|
||||||
|
return status.latency
|
||||||
|
|
||||||
|
async def schedule_job(host: str, mcdb: MCDB):
|
||||||
|
current_time = datetime.now(timezone("Asia/Shanghai"))
|
||||||
|
try:
|
||||||
|
latency = await getJavaServerLatency(host)
|
||||||
|
except Exception as e:
|
||||||
|
# 服务器连接失败,可能宕机了,把延迟设为10s秒
|
||||||
|
latency = 10000
|
||||||
|
print(e)
|
||||||
|
mcdb.insert_time_and_latency(current_time, latency)
|
||||||
|
|
||||||
|
def get_last_hour_date_info():
|
||||||
|
return (datetime.now(timezone("Asia/Shanghai")) - timedelta(hours=1))
|
||||||
|
|
||||||
|
def get_current_day_time_utc8():
|
||||||
|
return datetime.now(timezone("Asia/Shanghai")).strftime('%H:%M:%S')
|
||||||
|
|
||||||
|
|
||||||
|
def parse_date_to_h_m_s(timestamp_str) -> str:
|
||||||
|
timestamp = parser.parse(timestamp_str)
|
||||||
|
formatted_timestamp = timestamp.strftime("%H:%M:%S")
|
||||||
|
return formatted_timestamp
|
5
config.json.example
Normal file
5
config.json.example
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"mc_host": "xxxxx.xxx.xxx:25565",
|
||||||
|
"password": "xxxxxxxxxxxxxxx",
|
||||||
|
"rcon_host": "http://127.0.0.1:10001"
|
||||||
|
}
|
133
frontend/.gitignore
vendored
Normal file
133
frontend/.gitignore
vendored
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
# parcel
|
||||||
|
.parcel-cache
|
10
frontend/README.md
Normal file
10
frontend/README.md
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
## development
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
npx parcel src/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
## build
|
||||||
|
```
|
||||||
|
npx parcel build src/index.html
|
||||||
|
```
|
3710
frontend/package-lock.json
generated
Normal file
3710
frontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
11
frontend/package.json
Normal file
11
frontend/package.json
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"@parcel/transformer-webmanifest": "^2.12.0",
|
||||||
|
"parcel": "^2.12.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@mdui/icons": "^1.0.2",
|
||||||
|
"chart.js": "^4.4.2",
|
||||||
|
"mdui": "^2.0.6"
|
||||||
|
}
|
||||||
|
}
|
BIN
frontend/src/assets/mc.jpg
Normal file
BIN
frontend/src/assets/mc.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
26
frontend/src/index.html
Normal file
26
frontend/src/index.html
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="zh-CN" class="mdui-theme-auto">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, shrink-to-fit=no" />
|
||||||
|
<meta name="renderer" content="webkit" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||||
|
<link rel="stylesheet" href="main.css">
|
||||||
|
<script type="module" src="main.js"></script>
|
||||||
|
<title>MC监控台</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<main class="mdui-prose center">
|
||||||
|
<h1>MC状态监测-No-Flesh-Within-Chest(脆骨症)</h1>
|
||||||
|
<mdui-text-field autocomplete="on" readonly rows="5" id="server-status" helper="如果延迟等于10000,且下面图表是一条直线那大概率是服务器中断了"></mdui-text-field>
|
||||||
|
<canvas id="latency-chart"></canvas>
|
||||||
|
|
||||||
|
<mdui-button id="restart-server-btn">重启服务器</mdui-button>
|
||||||
|
<p>©BobMaster 2024</p>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
10
frontend/src/main.css
Normal file
10
frontend/src/main.css
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column wrap;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#restart-server-btn {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
136
frontend/src/main.js
Normal file
136
frontend/src/main.js
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
import 'mdui/mdui.css';
|
||||||
|
import 'mdui/components/layout.js';
|
||||||
|
import 'mdui/components/layout-item.js';
|
||||||
|
import 'mdui/components/layout-main.js';
|
||||||
|
import 'mdui/components/text-field.js';
|
||||||
|
import 'mdui/components/button.js';
|
||||||
|
import { prompt } from 'mdui/functions/prompt.js';
|
||||||
|
import { dialog } from 'mdui/functions/dialog.js';
|
||||||
|
import Chart from 'chart.js/auto';
|
||||||
|
import '@mdui/icons/copyright.js';
|
||||||
|
|
||||||
|
|
||||||
|
let ctx = document.getElementById("latency-chart").getContext('2d');
|
||||||
|
let chart = new Chart(
|
||||||
|
ctx,
|
||||||
|
{
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: [], // 存储时间信息
|
||||||
|
datasets: [{
|
||||||
|
label: '延迟信息',
|
||||||
|
data: [], // 存储延迟信息
|
||||||
|
backgroundColor: 'rgba(0, 123, 255, 0.5)',
|
||||||
|
borderColor: 'rgba(0, 123, 255, 1)',
|
||||||
|
borderWidth: 1
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
plugins: {
|
||||||
|
title: {
|
||||||
|
display: true,
|
||||||
|
text: '延迟曲线图'
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
display: true,
|
||||||
|
text: '单位: ms'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const restartBtn = document.getElementById("restart-server-btn")
|
||||||
|
|
||||||
|
window.onload = function () {
|
||||||
|
fetch('http://127.0.0.1:10000/')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
let compose_string = "";
|
||||||
|
compose_string += "当前时间: " + data.time;
|
||||||
|
compose_string += "\n服务器描述: " + data.description;
|
||||||
|
compose_string += "\n延迟: " + data.latency + "ms";
|
||||||
|
compose_string += "\n最大玩家容量: " + data.max;
|
||||||
|
compose_string += "\n当前在线人数: " + data.online;
|
||||||
|
document.getElementById("server-status").value = compose_string;
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
fetch('http://127.0.0.1:10000/get_latency')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
for (let i = 0; i < data.length; i = i + 2) {
|
||||||
|
chart.data.labels.push(data[i].time);
|
||||||
|
chart.data.datasets[0].data.push(data[i].latency);
|
||||||
|
}
|
||||||
|
chart.update();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
setInterval(function () {
|
||||||
|
|
||||||
|
fetch('http://127.0.0.1:10000')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
compose_string = "";
|
||||||
|
compose_string += "当前时间: " + data.time;
|
||||||
|
compose_string += "\n服务器描述: " + data.description;
|
||||||
|
compose_string += "\n延迟: " + data.latency + "ms";
|
||||||
|
compose_string += "\n最大玩家容量: " + data.max;
|
||||||
|
compose_string += "\n当前在线人数: " + data.online;
|
||||||
|
document.getElementById("server-status").value = compose_string;
|
||||||
|
|
||||||
|
// 添加新的数据到图表并更新
|
||||||
|
chart.data.labels.push(data.time);
|
||||||
|
chart.data.datasets[0].data.push(data.latency);
|
||||||
|
if (chart.data.labels.length > 30) {
|
||||||
|
chart.data.labels.shift();
|
||||||
|
chart.data.datasets[0].data.shift();
|
||||||
|
}
|
||||||
|
chart.update();
|
||||||
|
});
|
||||||
|
}, 30 * 1000); // 每30s执行一次
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
restartBtn.addEventListener("click", () => {
|
||||||
|
prompt({
|
||||||
|
headline: "请输入重置密码",
|
||||||
|
confirmText: "OK",
|
||||||
|
cancelText: "Cancel",
|
||||||
|
closeOnEsc: true,
|
||||||
|
onConfirm: (value) => {
|
||||||
|
restartBtn.disabled = true;
|
||||||
|
restartBtn.loading = true;
|
||||||
|
fetch("http://127.0.0.1:10000/restart-server", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify({"code": value})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
dialog({
|
||||||
|
headline: "执行结果",
|
||||||
|
description: data.status,
|
||||||
|
body: data.msg,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
"text": "了解",
|
||||||
|
onClick: () => {
|
||||||
|
restartBtn.disabled = false;
|
||||||
|
restartBtn.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in a new issue