Add SCUM Source RCON transport
This commit is contained in:
@@ -90,6 +90,8 @@ import type {
|
||||
ServerLifecycleResponse,
|
||||
ServerConfigWriteApprovalRequest,
|
||||
ServerConfigWriteDispatchResponse,
|
||||
SourceRCONCommandRequest,
|
||||
SourceRCONCommandResponse,
|
||||
ServerInstanceListResponse,
|
||||
ServerDeletionRequest,
|
||||
ServerInstanceUpdateRequest,
|
||||
@@ -565,6 +567,10 @@ export class PlatformApiClient {
|
||||
return this.request<RemoteAdapterResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/remote-adapters`, { method: "POST", body: request });
|
||||
}
|
||||
|
||||
async sendSourceRCONCommand(serverInstanceId: string, request: SourceRCONCommandRequest): Promise<SourceRCONCommandResponse> {
|
||||
return this.request<SourceRCONCommandResponse>(`/server-instances/${encodeURIComponent(serverInstanceId)}/rcon/commands`, { method: "POST", body: request });
|
||||
}
|
||||
|
||||
async getServerConfig(id: string): Promise<ServerConfigResponse> {
|
||||
return this.request<ServerConfigResponse>(`/server-instances/${encodeURIComponent(id)}/config`);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { PlatformApiClient } from "./client";
|
||||
import type { SourceRCONCommandRequest } from "./types";
|
||||
|
||||
describe("PlatformApiClient Source RCON command dispatch", () => {
|
||||
afterEach(() => vi.unstubAllGlobals());
|
||||
|
||||
it("posts the typed server-scoped request and projects only safe queue state", async () => {
|
||||
const request: SourceRCONCommandRequest = {
|
||||
kind: "chat",
|
||||
chatType: 4,
|
||||
message: "Maintenance complete",
|
||||
targetSteamId: "76561198000000001",
|
||||
idempotencyKey: "web:source-rcon:chat:server-1:12"
|
||||
};
|
||||
const calls: Array<{ url: string; method: string; body?: unknown; authorization: string | null }> = [];
|
||||
vi.stubGlobal("fetch", vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||
calls.push({
|
||||
url: String(input),
|
||||
method: init?.method ?? "GET",
|
||||
body: init?.body ? JSON.parse(String(init.body)) : undefined,
|
||||
authorization: new Headers(init?.headers).get("Authorization")
|
||||
});
|
||||
return new Response(JSON.stringify({ jobId: "job-source-rcon", serverInstanceId: "server-1", status: "queued", message: "SCUM RCON command queued" }), {
|
||||
status: 202,
|
||||
headers: { "Content-Type": "application/json" }
|
||||
});
|
||||
}));
|
||||
|
||||
const response = await new PlatformApiClient("/api/v1", () => "operator-session").sendSourceRCONCommand("server/1", request);
|
||||
|
||||
expect(calls).toEqual([{
|
||||
url: "/api/v1/server-instances/server%2F1/rcon/commands",
|
||||
method: "POST",
|
||||
body: request,
|
||||
authorization: "Bearer operator-session"
|
||||
}]);
|
||||
expect(response).toEqual({ jobId: "job-source-rcon", serverInstanceId: "server-1", status: "queued", message: "SCUM RCON command queued" });
|
||||
for (const forbidden of ["command", "password", "host", "configRef", "response"]) {
|
||||
expect(Object.keys(response)).not.toContain(forbidden);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -485,6 +485,26 @@ export interface ServerLifecycleResponse {
|
||||
job: JobResponse;
|
||||
}
|
||||
|
||||
export type SourceRCONCommandKind = "chat" | "command";
|
||||
|
||||
export interface SourceRCONCommandRequest {
|
||||
kind: SourceRCONCommandKind;
|
||||
chatType?: number;
|
||||
message?: string;
|
||||
targetSteamId?: string;
|
||||
command?: string;
|
||||
idempotencyKey: string;
|
||||
}
|
||||
|
||||
// The Platform response intentionally excludes the command, local listener
|
||||
// details, credentials, and Source RCON response body.
|
||||
export interface SourceRCONCommandResponse {
|
||||
jobId: string;
|
||||
serverInstanceId: string;
|
||||
status: JobState;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface RunCapacityResponse {
|
||||
maxJobs: number;
|
||||
runningJobs: number;
|
||||
|
||||
Reference in New Issue
Block a user