initial commit

This commit is contained in:
SowinskiBraeden committed 2026-07-05 16:54:59 -07:00
commit ce8f719a05
106 files changed
+24584

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
export * from './roles.js';
export * from './types.js';
export * from './reforger-config.js';
+22
View File
@@ -0,0 +1,22 @@
/**
* Typed internal model of the parts of Reforger's server config.json the panel
* cares about. This is NOT the raw config file — generation/deployment of the
* real config.json is a later phase.
*/
export type ReforgerConfigMod = {
modId: string;
name?: string;
version?: string;
};
export type ReforgerServerConfig = {
serverName: string;
maxPlayers: number;
scenarioId: string;
aiLimit: number;
serverMaxViewDistance: number;
networkViewDistance: number;
crossPlatform: boolean;
disableThirdPerson: boolean;
mods: ReforgerConfigMod[];
};
+58
View File
@@ -0,0 +1,58 @@
export type Role = 'owner' | 'server_admin' | 'mission_lead' | 'viewer';
export const ROLES: readonly Role[] = ['owner', 'server_admin', 'mission_lead', 'viewer'];
/**
* Fine-grained capabilities. Backend middleware enforces these; the frontend
* only uses them to hide controls the API would reject anyway.
*/
export type Capability =
| 'server.view'
| 'server.power.start'
| 'server.power.stop'
| 'server.power.restart'
| 'mods.manage'
| 'config.edit'
| 'logs.sync'
| 'ops.health.view'
| 'users.manage'
| 'settings.view';
const VIEW_ONLY: Capability[] = ['server.view'];
export const ROLE_CAPABILITIES: Record<Role, readonly Capability[]> = {
owner: [
'server.view',
'server.power.start',
'server.power.stop',
'server.power.restart',
'mods.manage',
'config.edit',
'logs.sync',
'ops.health.view',
'users.manage',
'settings.view',
],
server_admin: [
'server.view',
'server.power.start',
'server.power.stop',
'server.power.restart',
'mods.manage',
'config.edit',
'ops.health.view',
],
mission_lead: ['server.view', 'server.power.restart'],
viewer: VIEW_ONLY,
};
export function roleHasCapability(role: Role, capability: Capability): boolean {
return ROLE_CAPABILITIES[role].includes(capability);
}
export const ROLE_LABELS: Record<Role, string> = {
owner: 'Owner',
server_admin: 'Server Admin',
mission_lead: 'Mission Lead',
viewer: 'Viewer',
};
+396
View File
@@ -0,0 +1,396 @@
import type { Capability, Role } from './roles.js';
import type { ReforgerServerConfig } from './reforger-config.js';
// ---------- API envelope ----------
export type ApiErrorCode =
| 'UNAUTHENTICATED'
| 'FORBIDDEN'
| 'NOT_FOUND'
| 'VALIDATION_ERROR'
| 'RATE_LIMITED'
| 'CONFLICT'
| 'UPSTREAM_UNAVAILABLE'
| 'NOT_CONFIGURED'
| 'INTERNAL_ERROR';
export type ApiErrorBody = {
error: {
code: ApiErrorCode;
message: string;
requestId?: string;
};
};
// ---------- Auth / users ----------
export type CurrentUser = {
id: string;
discordId: string;
username: string;
displayName: string | null;
avatarUrl: string | null;
role: Role;
capabilities: Capability[];
};
export type PanelUser = {
id: string;
discordId: string;
username: string;
displayName: string | null;
avatarUrl: string | null;
role: Role;
createdAt: string;
updatedAt: string;
};
// ---------- Servers ----------
export type ServerStatus = 'online' | 'offline' | 'starting' | 'stopping' | 'unknown';
export type ServerSummary = {
id: string;
slug: string;
name: string;
providerType: string;
status: ServerStatus;
maxPlayers: number | null;
onlinePlayerCount: number;
createdAt: string;
updatedAt: string;
};
export type ServerResources = {
status: ServerStatus;
cpuPercent: number;
cpuLimitPercent: number | null;
memoryBytes: number;
memoryLimitBytes: number | null;
diskBytes: number;
diskLimitBytes: number | null;
networkRxBytes: number;
networkTxBytes: number;
uptimeMs: number;
fetchedAt: string;
};
// ---------- Players ----------
export type OnlinePlayer = {
playerId: string;
displayName: string;
externalPlayerId: string | null;
connectedAt: string;
sessionDurationSeconds: number;
};
export type PlayersResponse = {
players: OnlinePlayer[];
onlineCount: number;
maxPlayers: number | null;
lastSyncedAt: string | null;
stale: boolean;
};
export type KnownPlayer = {
id: string;
displayName: string;
externalPlayerId: string | null;
firstSeenAt: string;
lastSeenAt: string;
totalSessions: number;
totalPlaytimeSeconds: number;
online: boolean;
};
// ---------- Activity ----------
export type ActivityItem = {
id: string;
kind: 'panel_action' | 'server_event';
action: string;
summary: string;
actor: { id: string; username: string; displayName: string | null } | null;
occurredAt: string;
};
export type PlayerPosition = {
x: number;
y: number;
z?: number | null;
};
export type KillfeedEvent = {
id: string;
occurredAt: string;
killerName: string;
victimName: string;
friendly: boolean;
killerTeam: string | null;
victimTeam: string | null;
killerPosition: PlayerPosition | null;
victimPosition: PlayerPosition | null;
distanceMeters: number | null;
weapon: string | null;
};
// ---------- Configuration ----------
/** The live config.json, downloaded from the server on request. */
export type ConfigurationResponse = {
config: ReforgerServerConfig;
fetchedAt: string;
};
export type MissionInfo = {
scenarioId: string;
/** Display name from the startup scenario listing, e.g. "Conflict - Everon". */
name: string;
/** 'official' or the source section header from the log. */
source: string;
};
export type MissionsResponse = {
missions: MissionInfo[];
/** Null when the current log contains no scenario listing. */
fetchedAt: string | null;
};
export type RawLogsResponse = {
path: string;
lines: string[];
truncated: boolean;
fetchedAt: string;
};
export type StartupVariable = {
name: string;
description: string;
envVariable: string;
value: string;
defaultValue: string;
isEditable: boolean;
};
export type StartupResponse = {
variables: StartupVariable[];
fetchedAt: string;
};
// ---------- Schedules ----------
export type ServerScheduleTask = {
id: string;
action: 'power' | 'command' | 'backup' | string;
payload: string;
timeOffsetSeconds: number;
continueOnFailure: boolean;
};
export type ServerScheduleSummary = {
id: string;
name: string;
isActive: boolean;
onlyWhenOnline: boolean;
minute: string;
hour: string;
dayOfMonth: string;
month: string;
dayOfWeek: string;
nextRunAt: string | null;
lastRunAt: string | null;
createdAt: string | null;
updatedAt: string | null;
tasks: ServerScheduleTask[];
};
export type RestartScheduleInput = {
name: string;
isActive: boolean;
minute: number;
hour: number;
dayOfWeek: '*' | '0' | '1' | '2' | '3' | '4' | '5' | '6';
onlyWhenOnline: boolean;
};
// ---------- Mod packs ----------
export type ModPackSummary = {
id: string;
name: string;
description: string | null;
status: string;
modCount: number;
latestVersion: number | null;
updatedAt: string;
};
// ---------- Resource history ----------
export type ResourceSample = {
/** Unix ms. */
t: number;
status: ServerStatus;
cpuPercent: number;
cpuLimitPercent: number | null;
memoryBytes: number;
memoryLimitBytes: number | null;
/** Bytes per second, derived from consecutive cumulative counters. */
networkRxRate: number;
networkTxRate: number;
};
export type ResourceHistoryResponse = {
samples: ResourceSample[];
intervalSeconds: number;
};
// ---------- Performance settings (subset of config.json) ----------
/**
* Server-performance fields of Reforger's config.json, per
* https://community.bistudio.com/wiki/Arma_Reforger:Server_Config.
* `null` means the key is absent from config.json (game default applies).
*/
export type PerformanceSettings = {
scenarioId: string | null; // game.scenarioId — the running mission
maxPlayers: number | null; // game.maxPlayers, 1128 (default 64)
serverMaxViewDistance: number | null; // game.gameProperties, 50010000 (default 1600)
networkViewDistance: number | null; // game.gameProperties, 5005000 (default 1500)
serverMinGrassDistance: number | null; // game.gameProperties, 0150 (default 0)
disableThirdPerson: boolean | null; // game.gameProperties (default false)
fastValidation: boolean | null; // game.gameProperties (default true)
battlEye: boolean | null; // game.gameProperties (default true)
aiLimit: number | null; // operating, -1 = unlimited (default -1)
playerSaveTime: number | null; // operating, seconds (default 120)
slotReservationTimeout: number | null; // operating, 5300 s (default 60)
lobbyPlayerSynchronise: boolean | null; // operating (default true)
};
export type PerformanceSettingsResponse = {
settings: PerformanceSettings;
fetchedAt: string;
};
/** PUT body: only the provided keys are touched; null removes the key. */
export type PerformanceSettingsPatch = Partial<PerformanceSettings>;
// ---------- Invites ----------
export type InviteSummary = {
id: string;
code: string;
role: Role;
createdBy: string | null;
expiresAt: string;
usedBy: string | null;
usedAt: string | null;
createdAt: string;
};
// ---------- Server mods (game.mods in config.json) ----------
export type ServerModsResponse = {
mods: { modId: string; name?: string; version?: string }[];
/** When the config.json this list came from was downloaded. */
fetchedAt: string;
};
export type UpdateModsResult = ServerModsResponse & {
added: number;
removed: number;
/** Reforger only picks up config changes on the next server restart. */
requiresRestart: true;
};
// ---------- Workshop ----------
export type WorkshopHealth = {
ok: boolean;
latencyMs: number | null;
checkedAt: string;
message: string | null;
};
export type WorkshopModPreview = {
id: string;
name: string;
author: string;
imageUrl: string | null;
size: string | null;
rating: string | null;
workshopUrl: string | null;
};
export type WorkshopSearchResponse = {
mods: WorkshopModPreview[];
meta: {
totalPages: number;
currentPage: number;
totalMods: number;
};
};
export type WorkshopScenario = {
name: string;
description: string | null;
scenarioId: string;
gamemode: string | null;
playerCount: number | null;
imageUrl: string | null;
};
export type WorkshopModDetail = WorkshopModPreview & {
version: string | null;
gameVersion: string | null;
subscribers: number | null;
downloads: number | null;
createdAtText: string | null;
lastModifiedText: string | null;
summary: string | null;
description: string | null;
license: string | null;
tags: string[];
dependencies: { name: string; id: string | null }[];
scenarios: WorkshopScenario[];
};
// ---------- Log ingestion ----------
export type ServerEventType =
| 'player_connected'
| 'player_disconnected'
| 'player_killed'
| 'server_started'
| 'server_stopped'
| 'server_restart_detected'
| 'log_sync_completed'
| 'log_sync_failed';
export type LogSyncResult = {
serverId: string;
logPath: string;
fetchedBytes: number;
processedLines: number;
createdEvents: number;
updatedSessions: number;
cursorReset: boolean;
startedAt: string;
finishedAt: string;
};
export type LogIngestionHealth = {
configured: boolean;
running: boolean;
logPath: string | null;
lastSuccessfulSyncAt: string | null;
lastErrorAt: string | null;
lastErrorMessage: string | null;
lastSync: {
processedLines: number;
createdEvents: number;
updatedSessions: number;
} | null;
stale: boolean;
};