Phase3
Some checks failed
CI / Node — lint, typecheck, test, build (push) Failing after 9s
CI / Go — node-agent tests (push) Failing after 9s
CI / Go — edge-gateway build (push) Successful in 13s

This commit is contained in:
smueller
2026-06-26 12:17:26 +02:00
parent c4077d4673
commit 9b061c3ee7
92 changed files with 4128 additions and 146 deletions

View File

@@ -0,0 +1,86 @@
import { getApiUrl } from "../env";
import { apiFetch } from "./auth";
export interface ConsoleTokenResponse {
token: string;
expiresAt: string;
}
export interface ConsoleWebSocketHandlers {
onOpen?: () => void;
onClose?: (event: CloseEvent) => void;
onError?: (event: Event) => void;
onLine?: (line: string, stream?: string) => void;
}
interface ConsoleLogMessage {
type?: string;
line?: string;
stream?: string;
message?: string;
}
function getConsoleWebSocketUrl(serverId: string, token: string): string {
const apiUrl = new URL(getApiUrl());
apiUrl.protocol = apiUrl.protocol === "https:" ? "wss:" : "ws:";
const basePath = apiUrl.pathname.replace(/\/$/, "");
apiUrl.pathname = `${basePath}/servers/${serverId}/console/ws`;
apiUrl.search = new URLSearchParams({ token }).toString();
return apiUrl.toString();
}
export async function getConsoleToken(serverId: string): Promise<ConsoleTokenResponse> {
return apiFetch<ConsoleTokenResponse>(`/servers/${serverId}/console/token`);
}
export function createConsoleWebSocket(
serverId: string,
token: string,
handlers: ConsoleWebSocketHandlers,
): WebSocket {
const socket = new WebSocket(getConsoleWebSocketUrl(serverId, token));
socket.addEventListener("open", () => {
handlers.onOpen?.();
});
socket.addEventListener("close", (event) => {
handlers.onClose?.(event);
});
socket.addEventListener("error", (event) => {
handlers.onError?.(event);
});
socket.addEventListener("message", (event) => {
const raw = typeof event.data === "string" ? event.data : "";
try {
const parsed = JSON.parse(raw) as ConsoleLogMessage;
if (parsed.type === "log" && parsed.line) {
handlers.onLine?.(parsed.line, parsed.stream);
return;
}
if (parsed.line) {
handlers.onLine?.(parsed.line, parsed.stream);
return;
}
if (parsed.message) {
handlers.onLine?.(parsed.message, parsed.stream);
return;
}
} catch {
// Plain text log line
}
if (raw) {
handlers.onLine?.(raw);
}
});
return socket;
}
export function sendConsoleCommand(socket: WebSocket, command: string): void {
socket.send(JSON.stringify({ command }));
}

View File

@@ -0,0 +1,61 @@
import { apiFetch } from "./auth";
export interface FileEntry {
name: string;
path: string;
isDir: boolean;
size: number;
modifiedAt?: string;
}
export interface ListFilesResponse {
path: string;
entries: FileEntry[];
}
export interface FileContentResponse {
path: string;
content: string;
truncated?: boolean;
version?: number;
}
export interface WriteFilePayload {
path: string;
content: string;
version?: number;
}
export async function listFiles(
serverId: string,
path = "",
): Promise<ListFilesResponse> {
const params = new URLSearchParams();
if (path) {
params.set("path", path);
}
const query = params.toString();
return apiFetch<ListFilesResponse>(
`/servers/${serverId}/files${query ? `?${query}` : ""}`,
);
}
export async function readFile(
serverId: string,
path: string,
): Promise<FileContentResponse> {
const params = new URLSearchParams({ path });
return apiFetch<FileContentResponse>(
`/servers/${serverId}/files/content?${params.toString()}`,
);
}
export async function writeFile(
serverId: string,
payload: WriteFilePayload,
): Promise<FileContentResponse> {
return apiFetch<FileContentResponse>(`/servers/${serverId}/files/content`, {
method: "PUT",
body: JSON.stringify(payload),
});
}

View File

@@ -0,0 +1,17 @@
import { apiFetch } from "./auth";
export interface OnlinePlayer {
name: string;
uuid?: string;
ping?: number;
}
export interface PlayerListResponse {
online: OnlinePlayer[];
maxPlayers: number;
whitelistCount: number;
}
export async function getPlayers(serverId: string): Promise<PlayerListResponse> {
return apiFetch<PlayerListResponse>(`/servers/${serverId}/players`);
}

View File

@@ -0,0 +1,36 @@
import { apiFetch } from "./auth";
export type GameMode = "survival" | "creative" | "adventure" | "spectator";
export type Difficulty = "peaceful" | "easy" | "normal" | "hard";
export interface ServerProperties {
motd: string;
gamemode: GameMode;
difficulty: Difficulty;
maxPlayers: number;
pvp: boolean;
whitelist: boolean;
hardcore?: boolean;
onlineMode?: boolean;
enableCommandBlock?: boolean;
viewDistance?: number;
simulationDistance?: number;
spawnProtection?: number;
allowFlight?: boolean;
}
export type UpdateServerPropertiesPayload = Partial<ServerProperties>;
export async function getProperties(serverId: string): Promise<ServerProperties> {
return apiFetch<ServerProperties>(`/servers/${serverId}/properties`);
}
export async function updateProperties(
serverId: string,
payload: UpdateServerPropertiesPayload,
): Promise<ServerProperties> {
return apiFetch<ServerProperties>(`/servers/${serverId}/properties`, {
method: "PATCH",
body: JSON.stringify(payload),
});
}