feat(server): real agent credentials + agent.toml setup; per-game config honesty
Server page Host-agent panel now fetches GET /api/servers/agent- credentials and renders the real agent.toml (license UUID, nats_user, nats_password) instead of the broken LICENSE_ID=license_key env commands that would never connect. Password masked by default with a reveal toggle; copy-to-clipboard uses the real value. Setup commands point at --config /etc/corrosion/agent.toml. Configuration panel: World size / Current seed (Rust-only Facepunch concepts) gated behind isRust; Conan/Soulmask/Dune get an honest note pointing to the File Manager for their real config files instead of fake Rust fields. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useServerStore } from '@/stores/server'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useToastStore } from '@/stores/toast'
|
||||
import { useThemeGame } from '@/composables/useThemeGame'
|
||||
import { useGameProfile } from '@/config/gameProfiles'
|
||||
import type { DeploymentConfig, DeploymentStatus } from '@/types'
|
||||
import { useWebSocket } from '@/composables/useWebSocket'
|
||||
import { useApi } from '@/composables/useApi'
|
||||
import Panel from '@/components/ds/data/Panel.vue'
|
||||
import Button from '@/components/ds/core/Button.vue'
|
||||
import Badge from '@/components/ds/core/Badge.vue'
|
||||
@@ -19,7 +19,6 @@ import Switch from '@/components/ds/forms/Switch.vue'
|
||||
import Tabs from '@/components/ds/navigation/Tabs.vue'
|
||||
|
||||
const server = useServerStore()
|
||||
const auth = useAuthStore()
|
||||
const toast = useToastStore()
|
||||
const { activeGame } = useThemeGame()
|
||||
|
||||
@@ -66,6 +65,18 @@ const deployLoading = ref(false)
|
||||
const oxideStatus = ref<{ stage: string; progress: number; message: string; error?: string } | null>(null)
|
||||
const isInstallingOxide = ref(false)
|
||||
|
||||
// Agent credentials (fetched from /api/servers/agent-credentials on mount)
|
||||
interface AgentCreds {
|
||||
license_id: string
|
||||
nats_user: string
|
||||
nats_password: string
|
||||
nats_url: string
|
||||
}
|
||||
const agentCreds = ref<AgentCreds | null>(null)
|
||||
const showCreds = ref(false)
|
||||
// Ref for the TOML block copy button
|
||||
const tomlCopied = ref(false)
|
||||
|
||||
const deployForm = ref<DeploymentConfig>({
|
||||
server_name: 'My Rust Server',
|
||||
max_players: 100,
|
||||
@@ -97,25 +108,62 @@ const agentLastSeenLabel = computed(() => {
|
||||
return d.toLocaleDateString()
|
||||
})
|
||||
|
||||
const licenseKey = computed(() => auth.license?.license_key || 'YOUR-LICENSE-KEY')
|
||||
|
||||
const linuxCommands = computed(() => `# Download the agent
|
||||
curl -LO https://cdn.corrosionmgmt.com/host-agent/latest/corrosion-host-agent-linux-amd64
|
||||
chmod +x corrosion-host-agent-linux-amd64
|
||||
|
||||
# Start with your license key
|
||||
export LICENSE_ID="${licenseKey.value}"
|
||||
export NATS_URL="nats://nats.corrosionmgmt.com:4222"
|
||||
./corrosion-host-agent-linux-amd64`)
|
||||
# Write /etc/corrosion/agent.toml (see config block below), then run:
|
||||
sudo mkdir -p /etc/corrosion
|
||||
sudo ./corrosion-host-agent-linux-amd64 --config /etc/corrosion/agent.toml`)
|
||||
|
||||
const windowsCommands = computed(() => `# Requires PowerShell (not Command Prompt)
|
||||
# Download the agent
|
||||
Invoke-WebRequest -Uri "https://cdn.corrosionmgmt.com/host-agent/latest/corrosion-host-agent-windows-amd64.exe" -OutFile "corrosion-host-agent-windows-amd64.exe"
|
||||
|
||||
# Start with your license key
|
||||
$env:LICENSE_ID="${licenseKey.value}"
|
||||
$env:NATS_URL="nats://nats.corrosionmgmt.com:4222"
|
||||
.\\corrosion-host-agent-windows-amd64.exe`)
|
||||
# Write C:\\ProgramData\\Corrosion\\agent.toml (see config block below), then run:
|
||||
New-Item -ItemType Directory -Force -Path "C:\\ProgramData\\Corrosion"
|
||||
.\\corrosion-host-agent-windows-amd64.exe --config "C:\\ProgramData\\Corrosion\\agent.toml"`)
|
||||
|
||||
const agentTomlConfig = computed(() => {
|
||||
const c = agentCreds.value
|
||||
const licenseId = c?.license_id ?? 'YOUR-LICENSE-ID'
|
||||
const natsUrl = c?.nats_url ?? 'nats://nats.corrosionmgmt.com:4222'
|
||||
const natsUser = c?.nats_user ?? 'YOUR-LICENSE-ID'
|
||||
const natsPassword = c ? (showCreds.value ? c.nats_password : '••••••••') : 'YOUR-AGENT-TOKEN'
|
||||
return `[agent]
|
||||
license_id = "${licenseId}"
|
||||
nats_url = "${natsUrl}"
|
||||
nats_user = "${natsUser}"
|
||||
nats_password = "${natsPassword}"
|
||||
heartbeat_seconds = 60
|
||||
|
||||
[[instance]]
|
||||
id = "rust-main"
|
||||
game = "rust"
|
||||
root = "/opt/rustserver"
|
||||
label = "My Server"`
|
||||
})
|
||||
|
||||
// Returns the raw (unmasked) TOML for clipboard — always use actual password if available
|
||||
const agentTomlConfigRaw = computed(() => {
|
||||
const c = agentCreds.value
|
||||
const licenseId = c?.license_id ?? 'YOUR-LICENSE-ID'
|
||||
const natsUrl = c?.nats_url ?? 'nats://nats.corrosionmgmt.com:4222'
|
||||
const natsUser = c?.nats_user ?? 'YOUR-LICENSE-ID'
|
||||
const natsPassword = c?.nats_password ?? 'YOUR-AGENT-TOKEN'
|
||||
return `[agent]
|
||||
license_id = "${licenseId}"
|
||||
nats_url = "${natsUrl}"
|
||||
nats_user = "${natsUser}"
|
||||
nats_password = "${natsPassword}"
|
||||
heartbeat_seconds = 60
|
||||
|
||||
[[instance]]
|
||||
id = "rust-main"
|
||||
game = "rust"
|
||||
root = "/opt/rustserver"
|
||||
label = "My Server"`
|
||||
})
|
||||
|
||||
async function copySetupCommands() {
|
||||
try {
|
||||
@@ -133,6 +181,16 @@ async function copySetupCommands() {
|
||||
}
|
||||
}
|
||||
|
||||
async function copyTomlConfig() {
|
||||
try {
|
||||
await navigator.clipboard.writeText(agentTomlConfigRaw.value)
|
||||
tomlCopied.value = true
|
||||
setTimeout(() => { tomlCopied.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
|
||||
@@ -297,6 +355,14 @@ onMounted(async () => {
|
||||
await server.fetchServer()
|
||||
loadFormFromConfig()
|
||||
|
||||
// Fetch agent credentials for the TOML config block (leave null on error — honest fallback)
|
||||
try {
|
||||
const creds = await useApi().get<AgentCreds | null>('/servers/agent-credentials')
|
||||
agentCreds.value = creds
|
||||
} catch {
|
||||
agentCreds.value = null
|
||||
}
|
||||
|
||||
const ws = useWebSocket()
|
||||
ws.subscribe((msg) => {
|
||||
if (msg.type === 'event' && msg.event === 'deploy_status') {
|
||||
@@ -463,10 +529,9 @@ onMounted(async () => {
|
||||
<p class="sv__cmt"># Download the agent</p>
|
||||
<p>curl -LO https://cdn.corrosionmgmt.com/host-agent/latest/corrosion-host-agent-linux-amd64</p>
|
||||
<p>chmod +x corrosion-host-agent-linux-amd64</p>
|
||||
<p class="sv__cmt sv__mt"># Start with your license key</p>
|
||||
<p>export LICENSE_ID=<span class="sv__accent">"{{ licenseKey }}"</span></p>
|
||||
<p>export NATS_URL=<span class="sv__accent">"nats://nats.corrosionmgmt.com:4222"</span></p>
|
||||
<p>./corrosion-host-agent-linux-amd64</p>
|
||||
<p class="sv__cmt sv__mt"># Write /etc/corrosion/agent.toml (see config block below), then run:</p>
|
||||
<p>sudo mkdir -p /etc/corrosion</p>
|
||||
<p>sudo ./corrosion-host-agent-linux-amd64 --config <span class="sv__accent">/etc/corrosion/agent.toml</span></p>
|
||||
</div>
|
||||
|
||||
<!-- Windows commands -->
|
||||
@@ -474,11 +539,38 @@ onMounted(async () => {
|
||||
<p class="sv__cmt"># Requires PowerShell (not Command Prompt)</p>
|
||||
<p class="sv__cmt"># Download the agent</p>
|
||||
<p>Invoke-WebRequest -Uri <span class="sv__accent">"https://cdn.corrosionmgmt.com/host-agent/latest/corrosion-host-agent-windows-amd64.exe"</span> -OutFile <span class="sv__accent">"corrosion-host-agent-windows-amd64.exe"</span></p>
|
||||
<p class="sv__cmt sv__mt"># Start with your license key</p>
|
||||
<p>$env:LICENSE_ID=<span class="sv__accent">"{{ licenseKey }}"</span></p>
|
||||
<p>$env:NATS_URL=<span class="sv__accent">"nats://nats.corrosionmgmt.com:4222"</span></p>
|
||||
<p>.\corrosion-host-agent-windows-amd64.exe</p>
|
||||
<p class="sv__cmt sv__mt"># Write C:\ProgramData\Corrosion\agent.toml (see config block below), then run:</p>
|
||||
<p>New-Item -ItemType Directory -Force -Path <span class="sv__accent">"C:\ProgramData\Corrosion"</span></p>
|
||||
<p>.\corrosion-host-agent-windows-amd64.exe --config <span class="sv__accent">"C:\ProgramData\Corrosion\agent.toml"</span></p>
|
||||
</div>
|
||||
|
||||
<!-- Agent configuration (agent.toml) -->
|
||||
<div class="sv__section-head sv__mt">
|
||||
<Icon name="file-text" :size="14" />
|
||||
<span>Agent configuration (agent.toml)</span>
|
||||
</div>
|
||||
<div class="sv__setup-head">
|
||||
<div class="sv__toml-reveal">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
:icon="showCreds ? 'eye-off' : 'eye'"
|
||||
@click="showCreds = !showCreds"
|
||||
>{{ showCreds ? 'Hide credentials' : 'Reveal credentials' }}</Button>
|
||||
</div>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
:icon="tomlCopied ? 'check' : 'copy'"
|
||||
@click="copyTomlConfig"
|
||||
>{{ tomlCopied ? 'Copied' : 'Copy' }}</Button>
|
||||
</div>
|
||||
<div class="sv__codeblock">
|
||||
<pre class="sv__pre">{{ agentTomlConfig }}</pre>
|
||||
</div>
|
||||
<Alert v-if="!agentCreds" tone="warn" class="sv__mt">
|
||||
Could not load credentials from server. Copy this config and replace the placeholders with values from your Corrosion dashboard settings.
|
||||
</Alert>
|
||||
</Panel>
|
||||
|
||||
<!-- Deploy Server — Rust only (SteamCMD path). Other games use docker-compose or external tooling. -->
|
||||
@@ -778,15 +870,21 @@ onMounted(async () => {
|
||||
<div class="sv__field-label">Max players</div>
|
||||
<div class="sv__field-val sv__field-val--mono">{{ server.config?.max_players ?? '—' }}</div>
|
||||
</div>
|
||||
<div class="sv__field">
|
||||
<!-- Rust-only: world size and seed are Facepunch/procgen concepts -->
|
||||
<div v-if="isRust" class="sv__field">
|
||||
<div class="sv__field-label">World size</div>
|
||||
<div class="sv__field-val sv__field-val--mono">{{ server.config?.world_size ?? '—' }}</div>
|
||||
</div>
|
||||
<div class="sv__field">
|
||||
<div v-if="isRust" class="sv__field">
|
||||
<div class="sv__field-label">Current seed</div>
|
||||
<div class="sv__field-val sv__field-val--mono">{{ server.config?.current_seed ?? '—' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Non-Rust: game-specific settings live in config files on the host -->
|
||||
<Alert v-if="!editMode && !isRust" tone="neutral" class="sv__mt">
|
||||
Game-specific settings for {{ profile.label }} live in config files on the host — manage them in the
|
||||
<Button variant="ghost" size="sm" icon="folder" @click="$router.push('/files')">File Manager</Button>
|
||||
</Alert>
|
||||
|
||||
<!-- Edit mode -->
|
||||
<form v-else @submit.prevent="saveConfig" class="sv__form">
|
||||
@@ -803,7 +901,9 @@ onMounted(async () => {
|
||||
type="number"
|
||||
:mono="true"
|
||||
/>
|
||||
<!-- Rust-only: world size and seed are Facepunch/procgen concepts -->
|
||||
<Input
|
||||
v-if="isRust"
|
||||
:model-value="String(form.world_size)"
|
||||
@update:model-value="v => { form.world_size = Number(v) }"
|
||||
label="World size"
|
||||
@@ -811,6 +911,7 @@ onMounted(async () => {
|
||||
:mono="true"
|
||||
/>
|
||||
<Input
|
||||
v-if="isRust"
|
||||
:model-value="String(form.current_seed)"
|
||||
@update:model-value="v => { form.current_seed = Number(v) }"
|
||||
label="Current seed"
|
||||
@@ -818,6 +919,11 @@ onMounted(async () => {
|
||||
:mono="true"
|
||||
class="sv__col-span2"
|
||||
/>
|
||||
<!-- Non-Rust: redirect to file manager for game-specific config -->
|
||||
<Alert v-if="!isRust" tone="neutral" class="sv__col-span2">
|
||||
Game-specific settings for {{ profile.label }} live in config files on the host — manage them in the
|
||||
<Button variant="ghost" size="sm" icon="folder" @click="$router.push('/files')">File Manager</Button>
|
||||
</Alert>
|
||||
</div>
|
||||
</form>
|
||||
</Panel>
|
||||
@@ -931,6 +1037,12 @@ onMounted(async () => {
|
||||
/* Setup head */
|
||||
.sv__setup-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
|
||||
|
||||
/* TOML reveal row */
|
||||
.sv__toml-reveal { display: flex; align-items: center; }
|
||||
|
||||
/* Pre inside codeblock — preserve whitespace, no extra margin */
|
||||
.sv__pre { margin: 0; white-space: pre; }
|
||||
|
||||
/* Code block */
|
||||
.sv__codeblock {
|
||||
background: var(--surface-inset); border-radius: var(--radius-md);
|
||||
|
||||
Reference in New Issue
Block a user