From b94717d51bb9a9f30fe7a53a4687056980bff70e Mon Sep 17 00:00:00 2001 From: Vantz Stockwell Date: Sat, 21 Feb 2026 14:48:05 -0500 Subject: [PATCH] feat: Add frontend support for one-click Rust server deployment Adds DeploymentConfig/DeploymentStatus types, deployment state management in the server store, tabbed Linux/Windows quick setup commands, and a Deploy Rust Server card with progress tracker and configuration form. Co-Authored-By: Claude Opus 4.6 --- frontend/src/stores/server.ts | 33 ++- frontend/src/types/index.ts | 18 ++ frontend/src/views/admin/ServerView.vue | 279 ++++++++++++++++++++++-- 3 files changed, 313 insertions(+), 17 deletions(-) diff --git a/frontend/src/stores/server.ts b/frontend/src/stores/server.ts index 8db28b8..fec656f 100644 --- a/frontend/src/stores/server.ts +++ b/frontend/src/stores/server.ts @@ -1,6 +1,6 @@ import { defineStore } from 'pinia' import { ref } from 'vue' -import type { ServerConnection, ServerConfig, ServerStats } from '@/types' +import type { ServerConnection, ServerConfig, ServerStats, DeploymentConfig, DeploymentStatus } from '@/types' import { useApi } from '@/composables/useApi' export const useServerStore = defineStore('server', () => { @@ -8,6 +8,8 @@ export const useServerStore = defineStore('server', () => { const config = ref(null) const stats = ref(null) const isLoading = ref(false) + const deploymentStatus = ref(null) + const isDeploying = ref(false) const api = useApi() @@ -50,6 +52,30 @@ export const useServerStore = defineStore('server', () => { return api.post('/servers/restart') } + async function deployServer(config: DeploymentConfig) { + isDeploying.value = true + deploymentStatus.value = null + try { + await api.post('/servers/deploy', config) + } catch (e) { + console.error('Failed to start deployment:', e) + isDeploying.value = false + throw e + } + } + + function updateDeploymentStatus(status: DeploymentStatus) { + deploymentStatus.value = status + if (status.stage === 'online' || status.stage === 'failed') { + isDeploying.value = false + } + } + + function clearDeploymentStatus() { + deploymentStatus.value = null + isDeploying.value = false + } + function updateStats(newStats: ServerStats) { stats.value = newStats } @@ -59,12 +85,17 @@ export const useServerStore = defineStore('server', () => { config, stats, isLoading, + deploymentStatus, + isDeploying, fetchServer, updateConfig, sendCommand, startServer, stopServer, restartServer, + deployServer, + updateDeploymentStatus, + clearDeploymentStatus, updateStats, } }) diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 7db23e4..1fcb049 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -423,3 +423,21 @@ export interface StoreTransaction { payer_email: string | null created_at: string } + +// Deployment types +export interface DeploymentConfig { + server_name: string + max_players: number + world_size: number + seed: number + server_port: number + rcon_port: number + rcon_password: string +} + +export interface DeploymentStatus { + stage: 'downloading_steamcmd' | 'installing_steamcmd' | 'downloading_rust' | 'configuring' | 'starting' | 'online' | 'failed' + progress: number + message: string + error?: string +} diff --git a/frontend/src/views/admin/ServerView.vue b/frontend/src/views/admin/ServerView.vue index e48984b..d251587 100644 --- a/frontend/src/views/admin/ServerView.vue +++ b/frontend/src/views/admin/ServerView.vue @@ -14,7 +14,14 @@ import { Download, Terminal, Monitor, + Rocket, + AlertTriangle, + Check, + ClipboardCopy, + ChevronRight, } from 'lucide-vue-next' +import type { DeploymentConfig, DeploymentStatus } from '@/types' +import { useWebSocket } from '@/composables/useWebSocket' const server = useServerStore() const auth = useAuthStore() @@ -23,6 +30,20 @@ const editMode = ref(false) const saving = ref(false) const actionLoading = ref(null) const copied = ref(false) +const setupTab = ref<'linux' | 'windows'>('linux') +const windowsCopied = ref(false) +const showDeployForm = ref(false) +const deployLoading = ref(false) + +const deployForm = ref({ + server_name: 'My Rust Server', + max_players: 100, + world_size: 4000, + seed: Math.floor(Math.random() * 2147483647), + server_port: 28015, + rcon_port: 28016, + rcon_password: '', +}) const isAgentConnected = computed(() => server.connection?.connection_type === 'bare_metal' && @@ -58,16 +79,72 @@ export NATS_TOKEN="" export GAME_SERVER_PATH="/path/to/RustDedicated" ./corrosion-companion-linux-amd64`) -async function copyCommands() { +const windowsCommands = computed(() => `# Requires PowerShell (not Command Prompt) +# Download the agent +Invoke-WebRequest -Uri "https://cdn.corrosionmgmt.com/companion/latest/corrosion-companion-windows-amd64.exe" -OutFile "corrosion-companion-windows-amd64.exe" + +# Start with your license key +$env:LICENSE_ID="${licenseKey.value}" +$env:NATS_URL="nats://nats.corrosionmgmt.com:4222" +$env:NATS_TOKEN="" +$env:GAME_SERVER_PATH="C:\\RustServer\\server\\RustDedicated.exe" +.\\corrosion-companion-windows-amd64.exe`) + +async function copySetupCommands() { try { - await navigator.clipboard.writeText(linuxCommands.value) - copied.value = true - setTimeout(() => { copied.value = false }, 2000) + const text = setupTab.value === 'linux' ? linuxCommands.value : windowsCommands.value + await navigator.clipboard.writeText(text) + if (setupTab.value === 'linux') { + copied.value = true + setTimeout(() => { copied.value = false }, 2000) + } else { + windowsCopied.value = true + setTimeout(() => { windowsCopied.value = false }, 2000) + } } catch { // Clipboard API unavailable } } +async function startDeploy() { + if (!deployForm.value.rcon_password || deployForm.value.rcon_password.length < 6) return + deployLoading.value = true + try { + await server.deployServer(deployForm.value) + showDeployForm.value = false + } catch { + // Error handled in store + } finally { + deployLoading.value = false + } +} + +const deployStages = [ + { key: 'downloading_steamcmd', label: 'Download SteamCMD' }, + { key: 'installing_steamcmd', label: 'Install SteamCMD' }, + { key: 'downloading_rust', label: 'Download Rust Server' }, + { key: 'configuring', label: 'Configure' }, + { key: 'starting', label: 'Start Server' }, + { key: 'online', label: 'Online' }, +] as const + +function getStageState(stageKey: string): 'pending' | 'active' | 'complete' | 'failed' { + const status = server.deploymentStatus + if (!status) return 'pending' + if (status.stage === 'failed') { + const idx = deployStages.findIndex(s => s.key === stageKey) + const failIdx = deployStages.findIndex(s => s.key === status.stage) + if (idx < failIdx) return 'complete' + if (idx === failIdx) return 'failed' + return 'pending' + } + const currentIdx = deployStages.findIndex(s => s.key === status.stage) + const thisIdx = deployStages.findIndex(s => s.key === stageKey) + if (thisIdx < currentIdx) return 'complete' + if (thisIdx === currentIdx) return status.stage === 'online' ? 'complete' : 'active' + return 'pending' +} + const form = ref({ server_name: '', max_players: 0, @@ -115,6 +192,13 @@ async function serverAction(action: 'start' | 'stop' | 'restart') { onMounted(async () => { await server.fetchServer() loadFormFromConfig() + + const ws = useWebSocket() + ws.on('event', (data: any) => { + if (data.event === 'deploy_status') { + server.updateDeploymentStatus(data.data as DeploymentStatus) + } + }) }) @@ -262,24 +346,47 @@ onMounted(async () => { - +
-

Quick Setup (Linux)

+

Quick Setup

+
+
+ +
+ + +
+
-
-
+ + +
+ +

PowerShell Required — Command Prompt is not supported

+
+ + +

# Download the agent

curl -LO https://cdn.corrosionmgmt.com/companion/latest/corrosion-companion-linux-amd64

chmod +x corrosion-companion-linux-amd64

@@ -290,6 +397,146 @@ onMounted(async () => {

export GAME_SERVER_PATH="/path/to/RustDedicated"

./corrosion-companion-linux-amd64

+ + +
+

# Requires PowerShell (not Command Prompt)

+

# Download the agent

+

Invoke-WebRequest -Uri "https://cdn.corrosionmgmt.com/companion/latest/corrosion-companion-windows-amd64.exe" -OutFile "corrosion-companion-windows-amd64.exe"

+

# Start with your license key

+

$env:LICENSE_ID="{{ licenseKey }}"

+

$env:NATS_URL="nats://nats.corrosionmgmt.com:4222"

+

$env:NATS_TOKEN="<your-nats-token>"

+

$env:GAME_SERVER_PATH="C:\RustServer\server\RustDedicated.exe"

+

.\corrosion-companion-windows-amd64.exe

+
+
+
+ + +
+
+ +

Deploy Rust Server

+
+ + +
+
+
+ +
+ + + + +
+ + {{ stage.label }} +
+
+ + +
+

{{ server.deploymentStatus.message }}

+
+ + +
+

{{ server.deploymentStatus.error }}

+
+ + + +
+ + +
+
+

Automatically install SteamCMD, download Rust Dedicated Server, configure, and start — all with one click.

+ +
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+