This commit is contained in:
2026-06-25 13:43:12 -05:00
parent dfc2c9b475
commit d8342a6475
9 changed files with 395 additions and 31 deletions

View File

@@ -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' },

View 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>