feat(scan): add guided Linux agent setup in administration
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { InstanceAgent } from "~/composables/useAdmin"
|
||||
import AgentSetupWizard from "~/components/AgentSetupWizard.vue"
|
||||
|
||||
const auth = useAuthStore()
|
||||
const router = useRouter()
|
||||
@@ -8,15 +9,16 @@ const admin = useAdmin()
|
||||
|
||||
const loading = ref(true)
|
||||
const saving = ref(false)
|
||||
const creating = ref(false)
|
||||
const agents = ref<InstanceAgent[]>([])
|
||||
const selectedAgentId = ref<string | null>(null)
|
||||
const generatedToken = ref("")
|
||||
|
||||
const newAgent = reactive({
|
||||
name: "",
|
||||
description: "",
|
||||
})
|
||||
const setupOpen = ref(false)
|
||||
const setupTarget = ref<InstanceAgent | null>(null)
|
||||
const setupKey = ref(0)
|
||||
const openSetup = (agent: InstanceAgent | null = null) => {
|
||||
setupTarget.value = agent
|
||||
setupKey.value++
|
||||
setupOpen.value = true
|
||||
}
|
||||
|
||||
const editState = reactive({
|
||||
name: "",
|
||||
@@ -103,38 +105,9 @@ const loadAgents = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const createAgent = async () => {
|
||||
if (!newAgent.name.trim()) return
|
||||
|
||||
creating.value = true
|
||||
generatedToken.value = ""
|
||||
|
||||
try {
|
||||
const response = await admin.createInstanceAgent({
|
||||
name: newAgent.name.trim(),
|
||||
description: newAgent.description.trim() || null,
|
||||
})
|
||||
|
||||
generatedToken.value = response.token
|
||||
newAgent.name = ""
|
||||
newAgent.description = ""
|
||||
selectedAgentId.value = response.agent.id
|
||||
await loadAgents()
|
||||
|
||||
toast.add({
|
||||
title: "Agent angelegt",
|
||||
description: "Der Token wird nur jetzt vollständig angezeigt.",
|
||||
color: "success",
|
||||
})
|
||||
} catch (err: any) {
|
||||
toast.add({
|
||||
title: "Agent konnte nicht angelegt werden",
|
||||
description: err?.data?.error || err?.message || "Unbekannter Fehler",
|
||||
color: "error",
|
||||
})
|
||||
} finally {
|
||||
creating.value = false
|
||||
}
|
||||
const setupUpdated = async (id: string) => {
|
||||
selectedAgentId.value = id
|
||||
await loadAgents()
|
||||
}
|
||||
|
||||
const saveAgent = async () => {
|
||||
@@ -192,6 +165,7 @@ onMounted(async () => {
|
||||
<template>
|
||||
<UDashboardNavbar title="Administration: Scanner">
|
||||
<template #right>
|
||||
<UButton v-if="!setupOpen" icon="i-heroicons-plus" @click="openSetup()">Agent einrichten</UButton>
|
||||
<UButton
|
||||
icon="i-heroicons-arrow-path"
|
||||
color="neutral"
|
||||
@@ -205,47 +179,17 @@ onMounted(async () => {
|
||||
</UDashboardNavbar>
|
||||
|
||||
<UDashboardPanelContent>
|
||||
<div class="grid min-h-0 gap-6 xl:grid-cols-[360px_minmax(0,1fr)]">
|
||||
<AgentSetupWizard v-if="setupOpen" :key="setupKey" :initial-agent="setupTarget" @updated="setupUpdated" @close="setupOpen = false" />
|
||||
<div v-show="!setupOpen" class="grid min-h-0 gap-6 xl:grid-cols-[360px_minmax(0,1fr)]">
|
||||
<div class="space-y-4">
|
||||
<UCard :ui="{ root: 'rounded-lg' }">
|
||||
<template #header>
|
||||
<div class="flex items-center gap-2">
|
||||
<UIcon name="i-heroicons-plus-circle" class="size-5 text-primary" />
|
||||
<h2 class="text-base font-semibold text-highlighted">Agent anlegen</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<form class="space-y-3" @submit.prevent="createAgent">
|
||||
<UFormField label="Name">
|
||||
<UInput v-model="newAgent.name" placeholder="MacBook Büro" />
|
||||
</UFormField>
|
||||
<UFormField label="Beschreibung">
|
||||
<UTextarea v-model="newAgent.description" :rows="2" placeholder="Arbeitsplatz oder Standort" />
|
||||
</UFormField>
|
||||
<UButton
|
||||
type="submit"
|
||||
icon="i-heroicons-plus"
|
||||
:loading="creating"
|
||||
:disabled="!newAgent.name.trim()"
|
||||
>
|
||||
Agent anlegen
|
||||
</UButton>
|
||||
</form>
|
||||
<div class="space-y-4">
|
||||
<h2 class="text-base font-semibold text-highlighted">Neuen Scanner-Rechner verbinden</h2>
|
||||
<p class="text-sm text-muted">Der Assistent erstellt die passende Linux-Installation und begleitet dich bis zum ersten Scan.</p>
|
||||
<UButton icon="i-heroicons-sparkles" @click="openSetup()">Einrichtung starten</UButton>
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<UAlert
|
||||
v-if="generatedToken"
|
||||
color="warning"
|
||||
variant="soft"
|
||||
icon="i-heroicons-key"
|
||||
title="Agent-Token"
|
||||
description="Diesen Token jetzt in die .env des lokalen Agenten übernehmen. Später wird er nicht erneut angezeigt."
|
||||
>
|
||||
<template #description>
|
||||
<code class="mt-2 block break-all rounded bg-muted px-2 py-1 text-xs">{{ generatedToken }}</code>
|
||||
</template>
|
||||
</UAlert>
|
||||
|
||||
<UCard :ui="{ root: 'rounded-lg' }">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between gap-3">
|
||||
@@ -290,7 +234,8 @@ onMounted(async () => {
|
||||
<h2 class="text-base font-semibold text-highlighted">{{ selectedAgent.name }}</h2>
|
||||
<p class="text-xs text-muted">Token-Präfix {{ selectedAgent.tokenPrefix }} · zuletzt gesehen {{ formatDate(selectedAgent.lastSeenAt) }}</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<UButton color="neutral" variant="outline" size="sm" @click="openSetup(selectedAgent)">Einrichtung öffnen</UButton>
|
||||
<UBadge :color="isOnline(selectedAgent) ? 'success' : 'neutral'" variant="soft">
|
||||
{{ isOnline(selectedAgent) ? "Online" : "Offline" }}
|
||||
</UBadge>
|
||||
|
||||
Reference in New Issue
Block a user