Initial
This commit is contained in:
@@ -216,6 +216,7 @@
|
||||
:credentials="credentials"
|
||||
:job="scriptTestJob"
|
||||
:running="scriptTestRunning"
|
||||
:error="scriptTestError"
|
||||
@close="closeScriptTestRun"
|
||||
@execute="executeScriptTestRun"
|
||||
/>
|
||||
@@ -952,6 +953,7 @@ const scriptTestModalOpen = ref(false);
|
||||
const scriptTestTarget = ref(null);
|
||||
const scriptTestJob = ref(null);
|
||||
const scriptTestRunning = ref(false);
|
||||
const scriptTestError = ref('');
|
||||
const userModalOpen = ref(false);
|
||||
const groupModalOpen = ref(false);
|
||||
const hostModalOpen = ref(false);
|
||||
@@ -1113,6 +1115,7 @@ const selectedScript = ref(null);
|
||||
const editorMount = ref(null);
|
||||
let editor;
|
||||
let scriptTestPollTimer = null;
|
||||
let scriptTestPollFailures = 0;
|
||||
|
||||
const loginForm = reactive({ email: 'admin@posh.local', password: 'change-me-now' });
|
||||
const scriptDraft = reactive({ id: null, folderId: null, name: '', description: '', content: '', visibility: 'personal', groupId: null });
|
||||
@@ -1370,10 +1373,11 @@ const filteredRunPlans = computed(() => filterRows(runplans.value, runPlanFilter
|
||||
const runPlanPageCount = computed(() => pageCount(filteredRunPlans.value, runPlanFilters));
|
||||
const pagedRunPlans = computed(() => pageRows(filteredRunPlans.value, runPlanFilters));
|
||||
|
||||
watch(view, async () => {
|
||||
watch(view, async (nextView, previousView) => {
|
||||
contextMenu.open = false;
|
||||
if (view.value === 'scripts') await ensureEditorReady();
|
||||
if (view.value === 'profile') hydrateProfileForm();
|
||||
if (previousView === 'scripts' && nextView !== 'scripts') disposeEditor();
|
||||
if (nextView === 'scripts') await ensureEditorReady();
|
||||
if (nextView === 'profile') hydrateProfileForm();
|
||||
});
|
||||
|
||||
watch(() => scriptDraft.content, (value) => {
|
||||
@@ -1452,7 +1456,7 @@ async function probeEntraLogin() {
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('click', closeContext);
|
||||
stopScriptTestPolling();
|
||||
editor?.dispose();
|
||||
disposeEditor();
|
||||
});
|
||||
|
||||
function filterRows(rows, filters, fields) {
|
||||
@@ -1520,32 +1524,49 @@ function toggleNav() {
|
||||
}
|
||||
|
||||
function setEditorMount(el) {
|
||||
if (el) {
|
||||
editorMount.value = el;
|
||||
if (view.value === 'scripts') void ensureEditorReady();
|
||||
if (!el) {
|
||||
editorMount.value = null;
|
||||
disposeEditor();
|
||||
return;
|
||||
}
|
||||
if (editorMount.value && editorMount.value !== el) {
|
||||
disposeEditor();
|
||||
}
|
||||
editorMount.value = el;
|
||||
if (view.value === 'scripts') void ensureEditorReady();
|
||||
}
|
||||
|
||||
async function safeRefresh(label, promise, fallback, failures, quiet = false) {
|
||||
try {
|
||||
return await promise;
|
||||
} catch (err) {
|
||||
console.warn(`POSHManager refresh failed for ${label}`, err);
|
||||
if (!quiet) failures.push(`${label}: ${err.message || 'request failed'}`);
|
||||
return typeof fallback === 'function' ? fallback() : fallback;
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshAll() {
|
||||
// Bootstrap all API-backed view state together so navigation does not trigger partial reload races.
|
||||
const refreshFailures = [];
|
||||
const [meResult, boot, userRows, groupRows, credentialRows, hostRows, folderRows, scriptRows, assetFolderRows, assetRows, variableRows, psadtCatalogRows, psadtProfileRows, psadtIntuneRows, graphRows, runplanRows, jobRows] = await Promise.all([
|
||||
api.get('/api/auth/me'),
|
||||
api.get('/api/bootstrap'),
|
||||
api.get('/api/users').catch(() => []),
|
||||
api.get('/api/groups'),
|
||||
api.get('/api/credentials'),
|
||||
api.get('/api/hosts'),
|
||||
api.get('/api/folders'),
|
||||
api.get('/api/scripts'),
|
||||
api.get('/api/assets/folders'),
|
||||
api.get('/api/assets'),
|
||||
api.get('/api/variables/catalog'),
|
||||
api.get('/api/psadt/catalog'),
|
||||
api.get('/api/psadt/profiles'),
|
||||
api.get('/api/psadt/intune/deployments'),
|
||||
api.get('/api/graph/connections'),
|
||||
api.get('/api/runplans'),
|
||||
api.get('/api/jobs')
|
||||
safeRefresh('bootstrap', api.get('/api/bootstrap'), () => ({ summary: summary.value, settings: settings.value }), refreshFailures),
|
||||
safeRefresh('users', api.get('/api/users'), () => users.value, refreshFailures, true),
|
||||
safeRefresh('groups', api.get('/api/groups'), () => groups.value, refreshFailures),
|
||||
safeRefresh('credentials', api.get('/api/credentials'), () => credentials.value, refreshFailures),
|
||||
safeRefresh('hosts', api.get('/api/hosts'), () => hosts.value, refreshFailures),
|
||||
safeRefresh('folders', api.get('/api/folders'), () => folders.value, refreshFailures),
|
||||
safeRefresh('scripts', api.get('/api/scripts'), () => scripts.value, refreshFailures),
|
||||
safeRefresh('asset folders', api.get('/api/assets/folders'), () => assetFolders.value, refreshFailures),
|
||||
safeRefresh('assets', api.get('/api/assets'), () => assets.value, refreshFailures),
|
||||
safeRefresh('variables', api.get('/api/variables/catalog'), () => variableCatalog.value, refreshFailures),
|
||||
safeRefresh('PSADT catalog', api.get('/api/psadt/catalog'), () => psadtCatalog.value, refreshFailures),
|
||||
safeRefresh('PSADT profiles', api.get('/api/psadt/profiles'), () => psadtProfiles.value, refreshFailures),
|
||||
safeRefresh('Intune deployments', api.get('/api/psadt/intune/deployments'), () => psadtIntuneDeployments.value, refreshFailures),
|
||||
safeRefresh('Graph connections', api.get('/api/graph/connections'), () => graphConnections.value, refreshFailures),
|
||||
safeRefresh('RunPlans', api.get('/api/runplans'), () => runplans.value, refreshFailures),
|
||||
safeRefresh('jobs', api.get('/api/jobs'), () => jobs.value, refreshFailures)
|
||||
]);
|
||||
syncUser(meResult.user);
|
||||
summary.value = boot.summary;
|
||||
@@ -1567,7 +1588,13 @@ async function refreshAll() {
|
||||
graphConnections.value = graphRows;
|
||||
runplans.value = runplanRows;
|
||||
jobs.value = jobRows;
|
||||
if (!selectedScript.value && scriptRows.length) selectScript(scriptRows[0]);
|
||||
if (selectedScript.value) {
|
||||
const refreshedSelection = scriptRows.find((script) => script.id === selectedScript.value.id);
|
||||
if (refreshedSelection) selectScript(refreshedSelection);
|
||||
} else if (scriptRows.length) {
|
||||
selectScript(scriptRows[0]);
|
||||
}
|
||||
if (refreshFailures.length) notify(`Some data did not refresh: ${refreshFailures.slice(0, 2).join('; ')}`);
|
||||
}
|
||||
|
||||
async function openHelpCenter() {
|
||||
@@ -1740,6 +1767,8 @@ function openScriptTestRun(script) {
|
||||
scriptTestTarget.value = script;
|
||||
scriptTestJob.value = null;
|
||||
scriptTestRunning.value = false;
|
||||
scriptTestError.value = '';
|
||||
scriptTestPollFailures = 0;
|
||||
scriptTestModalOpen.value = true;
|
||||
}
|
||||
|
||||
@@ -1748,6 +1777,8 @@ function closeScriptTestRun() {
|
||||
scriptTestTarget.value = null;
|
||||
scriptTestJob.value = null;
|
||||
scriptTestRunning.value = false;
|
||||
scriptTestError.value = '';
|
||||
scriptTestPollFailures = 0;
|
||||
stopScriptTestPolling();
|
||||
}
|
||||
|
||||
@@ -1755,6 +1786,8 @@ async function executeScriptTestRun(payload) {
|
||||
if (!scriptTestTarget.value) return;
|
||||
scriptTestRunning.value = true;
|
||||
scriptTestJob.value = null;
|
||||
scriptTestError.value = '';
|
||||
scriptTestPollFailures = 0;
|
||||
stopScriptTestPolling();
|
||||
try {
|
||||
const job = await api.post(`/api/scripts/${scriptTestTarget.value.id}/test-run`, payload);
|
||||
@@ -1763,6 +1796,13 @@ async function executeScriptTestRun(payload) {
|
||||
notify('Script test run started');
|
||||
} catch (err) {
|
||||
scriptTestRunning.value = false;
|
||||
scriptTestError.value = err.message;
|
||||
scriptTestJob.value = {
|
||||
id: 'not-started',
|
||||
scriptName: scriptTestTarget.value?.name,
|
||||
status: 'failed',
|
||||
logs: [{ stream: 'stderr', line: err.message, createdAt: new Date().toISOString() }]
|
||||
};
|
||||
notify(err.message);
|
||||
}
|
||||
}
|
||||
@@ -1772,14 +1812,20 @@ function startScriptTestPolling(jobId) {
|
||||
scriptTestPollTimer = window.setInterval(async () => {
|
||||
try {
|
||||
const job = await api.get(`/api/jobs/${jobId}`);
|
||||
scriptTestPollFailures = 0;
|
||||
scriptTestError.value = '';
|
||||
scriptTestJob.value = job;
|
||||
if (!['queued', 'running'].includes(job.status)) {
|
||||
scriptTestRunning.value = false;
|
||||
stopScriptTestPolling();
|
||||
}
|
||||
} catch {
|
||||
scriptTestRunning.value = false;
|
||||
stopScriptTestPolling();
|
||||
} catch (err) {
|
||||
scriptTestPollFailures += 1;
|
||||
scriptTestError.value = `Could not refresh test output (${scriptTestPollFailures}/3): ${err.message}`;
|
||||
if (scriptTestPollFailures >= 3) {
|
||||
scriptTestRunning.value = false;
|
||||
stopScriptTestPolling();
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
@@ -2406,10 +2452,22 @@ async function ensureEditorReady(attempt = 0) {
|
||||
return;
|
||||
}
|
||||
if (!editor) mountEditor();
|
||||
editor?.layout();
|
||||
if (editor) {
|
||||
editor.setValue(scriptDraft.content || '');
|
||||
editor.layout({ width: Math.floor(box.width), height: Math.floor(box.height) });
|
||||
}
|
||||
}
|
||||
|
||||
function disposeEditor() {
|
||||
if (editor) {
|
||||
scriptDraft.content = editor.getValue();
|
||||
editor.dispose();
|
||||
editor = null;
|
||||
}
|
||||
}
|
||||
|
||||
function mountEditor() {
|
||||
if (!editorMount.value) return;
|
||||
configurePowerShellMonaco(monaco, () => ({
|
||||
variableCatalog: variableCatalog.value,
|
||||
psadtCatalog: psadtCatalog.value,
|
||||
|
||||
@@ -69,7 +69,8 @@ const props = defineProps({
|
||||
hosts: { type: Array, default: () => [] },
|
||||
credentials: { type: Array, default: () => [] },
|
||||
job: { type: Object, default: null },
|
||||
running: Boolean
|
||||
running: Boolean,
|
||||
error: { type: String, default: '' }
|
||||
});
|
||||
|
||||
const emit = defineEmits(['close', 'execute']);
|
||||
@@ -95,6 +96,7 @@ const terminalRows = computed(() => {
|
||||
`Status: ${props.job.status || 'queued'}`,
|
||||
''
|
||||
];
|
||||
if (props.error) lines.push(`[${new Date().toISOString()}] [ERROR] ${props.error}`);
|
||||
for (const log of props.job.logs || []) {
|
||||
lines.push(`[${log.createdAt || ''}] [${String(log.stream || 'system').toUpperCase()}] [${log.hostName || selectedHost.value?.name || 'host'}] ${log.line}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user