This commit is contained in:
2026-06-25 18:02:16 -05:00
parent 402bf6782a
commit da16e5ff56
17 changed files with 1475 additions and 69 deletions

View File

@@ -597,6 +597,16 @@
</article>
</section>
<SystemJobsView
v-if="view === 'system-jobs'"
:jobs="systemJobs"
:actions="systemJobActions"
:loading="systemJobsLoading"
@refresh="refreshSystemJobs"
@run="runSystemJob"
@cancel="cancelSystemJob"
/>
<section v-if="view === 'admin'" class="admin-page">
<IntuneGraphConfig
:graph-connections="graphConnections"
@@ -984,6 +994,7 @@ import ErrorView from './views/ErrorView.vue';
import LoginView from './views/LoginView.vue';
import PsadtView from './views/PsadtView.vue';
import AssetsView from './views/AssetsView.vue';
import SystemJobsView from './views/SystemJobsView.vue';
self.MonacoEnvironment = {
getWorker() {
@@ -1205,6 +1216,9 @@ const assetFolders = ref([]);
const assets = ref([]);
const runplans = ref([]);
const jobs = ref([]);
const systemJobs = ref([]);
const systemJobActions = ref([]);
const systemJobsLoading = ref(false);
const variableCatalog = ref({ builtIns: [], psadt: [], custom: [] });
const psadtCatalog = ref({ module: {}, sources: [], supportMatrix: [], structure: [], invokeParameters: [], functions: [], admxPolicies: [], launchCommands: [], snippets: [] });
const psadtProfiles = ref([]);
@@ -1299,6 +1313,7 @@ const nav = [
{ id: 'credentials', label: 'Credential Vault', icon: KeyRound },
{ id: 'runplans', label: 'RunPlans', icon: Rocket },
{ id: 'logs', label: 'Logs', icon: Activity },
{ id: 'system-jobs', label: 'System Jobs', icon: Database },
{ id: 'users', label: 'Users & Groups', icon: UsersRound },
{ id: 'admin', label: 'Config', icon: Settings }
];
@@ -1745,6 +1760,11 @@ async function refreshAll() {
vcenterConnections.value = vcenterRows;
runplans.value = runplanRows;
jobs.value = jobRows;
if (user.value?.role === 'admin') await refreshSystemJobs(true);
else {
systemJobs.value = [];
systemJobActions.value = [];
}
if (selectedScript.value) {
const refreshedSelection = scriptRows.find((script) => script.id === selectedScript.value.id);
if (refreshedSelection) selectScript(refreshedSelection);
@@ -2532,7 +2552,7 @@ async function previewVCenterImport(payload) {
vcenterImportError.value = '';
vcenterTraceEntries.value = [{
info: true,
message: 'vCenter preview request is running.',
message: 'VMware host preview request is running.',
details: {
filter: payload.filter,
limit: payload.limit,
@@ -2550,7 +2570,7 @@ async function previewVCenterImport(payload) {
details: {
route: '/api/hosts/import/vcenter/preview',
elapsedMs: 8000,
note: 'If this remains here, the backend is still waiting on vCenter DNS, TCP, TLS, authentication, or VM inventory response.'
note: 'If this remains here, the backend is still waiting on VMware DNS, TCP, TLS, authentication, or inventory response.'
}
}
];
@@ -2564,7 +2584,7 @@ async function previewVCenterImport(payload) {
details: {
route: '/api/hosts/import/vcenter/preview',
elapsedMs: 18000,
note: 'The backend vCenter request timeout should return a traceable timeout shortly unless the API process is blocked before dispatch.'
note: 'The backend VMware request timeout should return a traceable timeout shortly unless the API process is blocked before dispatch.'
}
}
];
@@ -2581,11 +2601,14 @@ async function previewVCenterImport(payload) {
}];
vcenterPreviewRows.value = result.candidates || [];
vcenterTraceOpen.value = true;
notify(`Found ${result.count || 0} vCenter host candidate(s) from ${result.diagnostics?.totalFromVCenter ?? 'unknown'} VM(s)`);
const standalone = result.diagnostics?.standaloneHost;
notify(standalone
? `Prepared ${result.count || 0} standalone ESXi host candidate(s)`
: `Found ${result.count || 0} vCenter host candidate(s) from ${result.diagnostics?.totalFromVCenter ?? 'unknown'} VM(s)`);
} catch (err) {
vcenterTraceEntries.value = err.payload?.trace?.length ? err.payload.trace : [{
info: true,
message: 'Preview failed before a vCenter HTTP trace could be captured.',
message: 'Preview failed before a VMware API trace could be captured.',
details: { error: err.message, status: err.status || null }
}];
vcenterTraceOpen.value = true;
@@ -2602,7 +2625,7 @@ async function importVCenterHosts(payload) {
vcenterImportError.value = '';
vcenterTraceEntries.value = [{
info: true,
message: 'vCenter import request is running.',
message: 'VMware host import request is running.',
details: { vmIds: payload.vmIds, startedAt: new Date().toISOString() }
}];
vcenterTraceOpen.value = true;
@@ -2619,7 +2642,7 @@ async function importVCenterHosts(payload) {
} catch (err) {
vcenterTraceEntries.value = err.payload?.trace?.length ? err.payload.trace : [{
info: true,
message: 'Import failed before a vCenter HTTP trace could be captured.',
message: 'Import failed before a VMware API trace could be captured.',
details: { error: err.message, status: err.status || null }
}];
vcenterTraceOpen.value = true;
@@ -2717,6 +2740,49 @@ async function loadSystemLogs() {
systemLogs.value = await api.get('/api/logs/system?limit=300');
}
async function refreshSystemJobs(quiet = false) {
if (user.value?.role !== 'admin') return;
systemJobsLoading.value = true;
try {
const result = await api.get('/api/system-jobs');
systemJobs.value = result.jobs || [];
systemJobActions.value = result.actions || [];
} catch (err) {
if (!quiet) notify(err.message, 'error');
} finally {
systemJobsLoading.value = false;
}
}
async function runSystemJob(jobId) {
systemJobsLoading.value = true;
try {
const result = await api.post(`/api/system-jobs/${encodeURIComponent(jobId)}/run`, {});
notify(result.action?.message || 'System job completed');
await refreshSystemJobs(true);
} catch (err) {
notify(err.message, 'error');
await refreshSystemJobs(true);
} finally {
systemJobsLoading.value = false;
}
}
async function cancelSystemJob({ id, reason }) {
systemJobsLoading.value = true;
try {
const result = await api.post(`/api/system-jobs/${encodeURIComponent(id)}/cancel`, { reason });
notify(result.action?.message || 'Cancel requested');
jobs.value = await api.get('/api/jobs');
await refreshSystemJobs(true);
} catch (err) {
notify(err.message, 'error');
await refreshSystemJobs(true);
} finally {
systemJobsLoading.value = false;
}
}
async function saveSettings() {
const payload = {};
Object.entries(settingsDraft).forEach(([key, row]) => { payload[key] = row.value; });