feat: improve manager recovery and expose server pid in dashboard

This commit is contained in:
2026-07-21 20:45:40 +02:00
parent 268734275a
commit 8354d94cbc
6 changed files with 72 additions and 9 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ export interface Wrapper {
export interface Server { export interface Server {
id: string; name: string; status: string; wrapperId: string; port: number; id: string; name: string; status: string; wrapperId: string; port: number;
queryPort: number; players: number; maxPlayers: number; created: number; pid?: number; queryPort: number; players: number; maxPlayers: number; created: number;
startedAt?: number; emptySince?: number; alive?: boolean; started?: number; startedAt?: number; emptySince?: number; alive?: boolean; started?: number;
} }
+22 -3
View File
@@ -5,8 +5,27 @@ import { PageState } from "../components/PageState";
import { StatusBadge } from "../components/StatusBadge"; import { StatusBadge } from "../components/StatusBadge";
const date = (value?: number) => value ? new Date(value).toLocaleString("de-DE") : ""; const date = (value?: number) => value ? new Date(value).toLocaleString("de-DE") : "";
export function ServerDetail() { export function ServerDetail() {
const { id = "" } = useParams(); const [item, setItem] = useState<Server>(); const [error, setError] = useState<Error>(); const { id = "" } = useParams();
useEffect(() => { api.server(id).then(setItem).catch(setError); }, [id]); if (!item) return <PageState error={error} />; const [item, setItem] = useState<Server>();
return <><section className="title"><div><p><Link to="/servers"> Server</Link></p><h1>{item.name}</h1><StatusBadge status={item.status} /></div></section><section className="details"><div><small>ID</small><span>{item.id}</span></div><div><small>Wrapper</small><span>{item.wrapperId}</span></div><div><small>Spieler / Kapazität</small><span>{item.players} / {item.maxPlayers}</span></div><div><small>Port / Query Port</small><span>{item.port} / {item.queryPort}</span></div><div><small>Erstellt</small><span>{date(item.created)}</span></div><div><small>Gestartet</small><span>{date(item.startedAt)}</span></div><div><small>Leer seit</small><span>{date(item.emptySince)}</span></div></section></>; const [error, setError] = useState<Error>();
useEffect(() => { api.server(id).then(setItem).catch(setError); }, [id]);
if (!item) return <PageState error={error} />;
return <>
<section className="title"><div><p><Link to="/servers"> Server</Link></p><h1>{item.name}</h1><StatusBadge status={item.status} /></div></section>
<section className="details">
<div><small>ID</small><span>{item.id}</span></div>
<div><small>Wrapper</small><span>{item.wrapperId}</span></div>
<div><small>PID</small><span>{item.pid ?? ""}</span></div>
<div><small>Spieler / Kapazität</small><span>{item.players} / {item.maxPlayers}</span></div>
<div><small>Port / Query Port</small><span>{item.port} / {item.queryPort}</span></div>
<div><small>Erstellt</small><span>{date(item.created)}</span></div>
<div><small>Gestartet</small><span>{date(item.startedAt)}</span></div>
<div><small>Leer seit</small><span>{date(item.emptySince)}</span></div>
</section>
</>;
} }
+38 -2
View File
@@ -164,6 +164,40 @@ export class ServerLifecycle {
case "RECOVERING":
if(
Date.now() -
server.lastSeen
>
LIFECYCLE_CRASH_TIMEOUT
){
server.status =
"CRASHED";
this.serverRegistry.update(
server
);
logger.error(
"Server-Recovery fehlgeschlagen:",
server.name
);
}
break;
case "RUNNING": case "RUNNING":
@@ -318,7 +352,8 @@ export class ServerLifecycle {
server => server =>
server.status === "RUNNING" || server.status === "RUNNING" ||
server.status === "STARTING" server.status === "STARTING" ||
server.status === "RECOVERING"
); );
@@ -582,7 +617,8 @@ export class ServerLifecycle {
s => s =>
s.status === "RUNNING" || s.status === "RUNNING" ||
s.status === "STARTING" s.status === "STARTING" ||
s.status === "RECOVERING"
) )
.length; .length;
+8 -3
View File
@@ -16,6 +16,7 @@ export interface ServerInfo {
status: status:
| "STARTING" | "STARTING"
| "RECOVERING"
| "RUNNING" | "RUNNING"
| "STOPPING" | "STOPPING"
| "CRASHED"; | "CRASHED";
@@ -90,7 +91,7 @@ export class ServerRegistry {
port: serverData.port, port: serverData.port,
queryPort: serverData.queryPort, queryPort: serverData.queryPort,
created: serverData.created, created: serverData.created,
status: "STARTING", status: "RECOVERING",
players: 0, players: 0,
maxPlayers: 0, maxPlayers: 0,
lastSeen: Date.now() lastSeen: Date.now()
@@ -347,7 +348,10 @@ export class ServerRegistry {
} }
else if(server.status === "RUNNING"){ else if(
server.status === "RUNNING" ||
server.status === "RECOVERING"
){
server.status = server.status =
@@ -424,7 +428,8 @@ export class ServerRegistry {
} }
else if( else if(
server.status === "RUNNING" server.status === "RUNNING" ||
server.status === "RECOVERING"
){ ){
server.status = server.status =
+2
View File
@@ -102,6 +102,7 @@ export class WebApi {
name: server.name, name: server.name,
status: server.status, status: server.status,
wrapperId: server.wrapperId, wrapperId: server.wrapperId,
pid: server.pid,
port: server.port, port: server.port,
queryPort: server.queryPort, queryPort: server.queryPort,
players: server.players, players: server.players,
@@ -124,6 +125,7 @@ export class WebApi {
name: server.name, name: server.name,
status: server.status, status: server.status,
wrapperId: server.wrapperId, wrapperId: server.wrapperId,
pid: server.pid,
port: server.port, port: server.port,
queryPort: server.queryPort, queryPort: server.queryPort,
players: server.players, players: server.players,
+1
View File
@@ -46,6 +46,7 @@ export interface ServerSummary {
name: string; name: string;
status: string; status: string;
wrapperId: string; wrapperId: string;
pid?: number;
port: number; port: number;
queryPort: number; queryPort: number;
players: number; players: number;