Initial
This commit is contained in:
@@ -52,6 +52,9 @@
|
||||
<span :class="['status-dot', status.configured ? 'success' : 'warning']"></span>
|
||||
{{ status.configured ? `Connected settings for ${status.baseUrl}` : 'vCenter settings incomplete' }}
|
||||
</span>
|
||||
<button v-if="traceCount" class="ghost-button compact" type="button" @click="$emit('open-trace')">
|
||||
<FileSearch :size="15" />View API trace
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -120,6 +123,9 @@
|
||||
<span>{{ diagnostics.totalFromVCenter }} VM(s) returned by vCenter</span>
|
||||
<span>{{ diagnostics.summaryMatched }} VM(s) matched before guest enrichment</span>
|
||||
<span>{{ diagnostics.scanned }} VM(s) scanned for guest FQDN/IP details</span>
|
||||
<button v-if="traceCount" class="ghost-button compact" type="button" @click="$emit('open-trace')">
|
||||
<FileSearch :size="15" />Trace
|
||||
</button>
|
||||
<small v-if="diagnostics.sampleNames?.length">Sample inventory names: {{ diagnostics.sampleNames.join(', ') }}</small>
|
||||
</div>
|
||||
<div class="preview-toolbar">
|
||||
@@ -174,7 +180,7 @@
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive, ref, watch } from 'vue';
|
||||
import { CheckSquare, DownloadCloud, Search } from '@lucide/vue';
|
||||
import { CheckSquare, DownloadCloud, FileSearch, Search } from '@lucide/vue';
|
||||
import BaseModal from '../ui/BaseModal.vue';
|
||||
|
||||
const props = defineProps({
|
||||
@@ -185,10 +191,11 @@ const props = defineProps({
|
||||
loading: Boolean,
|
||||
error: { type: String, default: '' },
|
||||
status: { type: Object, default: null },
|
||||
diagnostics: { type: Object, default: null }
|
||||
diagnostics: { type: Object, default: null },
|
||||
traceCount: { type: Number, default: 0 }
|
||||
});
|
||||
|
||||
const emit = defineEmits(['close', 'preview', 'import']);
|
||||
const emit = defineEmits(['close', 'preview', 'import', 'open-trace']);
|
||||
|
||||
const draft = reactive({
|
||||
filter: { field: 'hostname', operator: 'contains', value: 'ENG-ENT' },
|
||||
|
||||
85
client/src/components/hosts/VCenterTraceModal.vue
Normal file
85
client/src/components/hosts/VCenterTraceModal.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<BaseModal
|
||||
:open="open"
|
||||
title="vCenter API trace"
|
||||
eyebrow="REQUEST / RESPONSE"
|
||||
description="Redacted HTTP requests and responses captured during the last vCenter preview/import call."
|
||||
wide
|
||||
@close="$emit('close')"
|
||||
>
|
||||
<section class="vcenter-trace-modal">
|
||||
<div class="trace-toolbar">
|
||||
<div>
|
||||
<strong>{{ trace.length }} captured call{{ trace.length === 1 ? '' : 's' }}</strong>
|
||||
<span>Authorization and session identifiers are redacted.</span>
|
||||
</div>
|
||||
<button class="ghost-button compact" type="button" :disabled="!trace.length" @click="copyTrace">
|
||||
<Clipboard :size="15" />Copy JSON
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="!trace.length" class="wizard-empty">
|
||||
Run a vCenter preview to capture API request and response details.
|
||||
</div>
|
||||
|
||||
<article v-for="(entry, index) in trace" :key="index" class="trace-entry">
|
||||
<template v-if="entry.truncated">
|
||||
<div class="trace-entry-header warning">
|
||||
<AlertTriangle :size="16" />
|
||||
<strong>Trace truncated</strong>
|
||||
</div>
|
||||
<p>{{ entry.message }}</p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="trace-entry-header">
|
||||
<span class="trace-method">{{ entry.method || 'INFO' }}</span>
|
||||
<strong :class="statusClass(entry.status)">{{ entry.status || 'network' }}</strong>
|
||||
<small>{{ entry.durationMs ?? '-' }} ms</small>
|
||||
</div>
|
||||
<code class="trace-url">{{ entry.url }}</code>
|
||||
<div class="trace-grid">
|
||||
<div>
|
||||
<h4>Request headers</h4>
|
||||
<pre>{{ format(entry.requestHeaders) }}</pre>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Response summary</h4>
|
||||
<pre>{{ format(entry.responseSummary || { networkError: entry.networkError, statusText: entry.statusText }) }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Response body</h4>
|
||||
<pre class="trace-body">{{ entry.responseBody || entry.networkError || 'No body returned.' }}</pre>
|
||||
</div>
|
||||
</template>
|
||||
</article>
|
||||
</section>
|
||||
</BaseModal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { AlertTriangle, Clipboard } from '@lucide/vue';
|
||||
import BaseModal from '../ui/BaseModal.vue';
|
||||
|
||||
const props = defineProps({
|
||||
open: Boolean,
|
||||
trace: { type: Array, default: () => [] }
|
||||
});
|
||||
|
||||
defineEmits(['close']);
|
||||
|
||||
function format(value) {
|
||||
return JSON.stringify(value || {}, null, 2);
|
||||
}
|
||||
|
||||
function statusClass(status) {
|
||||
if (!status) return 'status-warn';
|
||||
if (status >= 200 && status < 300) return 'status-ok';
|
||||
if (status === 404) return 'status-warn';
|
||||
return 'status-error';
|
||||
}
|
||||
|
||||
async function copyTrace() {
|
||||
await navigator.clipboard?.writeText(JSON.stringify(props.trace, null, 2));
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user