From b600cf6e86a68d1eef2e633139681a265103efd7 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Fri, 22 May 2026 09:03:38 -0400 Subject: [PATCH] =?UTF-8?q?fc5:=20migration=20Pinia=20store=20=E2=80=94=20?= =?UTF-8?q?trigger=20+=20FormData=20upload=20+=202s=20poll=20+=20history?= =?UTF-8?q?=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 (1M context) --- frontend/src/stores/migration.js | 83 ++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 frontend/src/stores/migration.js diff --git a/frontend/src/stores/migration.js b/frontend/src/stores/migration.js new file mode 100644 index 0000000..5bdebd1 --- /dev/null +++ b/frontend/src/stores/migration.js @@ -0,0 +1,83 @@ +import { defineStore } from 'pinia' +import { ref, computed } from 'vue' +import { useApi } from '../composables/useApi.js' + +const POLL_MS = 2000 + +export const useMigrationStore = defineStore('migration', () => { + const api = useApi() + + const activeRun = ref(null) + const recentRuns = ref([]) + const error = ref(null) + const loading = ref(false) + + let pollHandle = null + + async function trigger(kind, params, file = null) { + loading.value = true + error.value = null + try { + let body + if (file) { + // Multipart upload for ingest kinds. + const form = new FormData() + form.append('export_file', file) + if (params && params.dry_run) form.append('dry_run', 'true') + if (params && params.force) form.append('force', 'true') + body = await api.post(`/api/migrate/${kind}`, { body: form }) + } else { + body = await api.post(`/api/migrate/${kind}`, { body: params || {} }) + } + await loadRun(body.run_id) + pollActive(body.run_id) + return body + } catch (e) { + error.value = e + throw e + } finally { + loading.value = false + } + } + + async function loadRun(runId) { + activeRun.value = await api.get(`/api/migrate/runs/${runId}`) + } + + async function loadRecent() { + recentRuns.value = await api.get('/api/migrate/runs', { params: { limit: 10 } }) + } + + function pollActive(runId) { + stopPolling() + pollHandle = setInterval(async () => { + try { + const run = await api.get(`/api/migrate/runs/${runId}`) + activeRun.value = run + if (run.status === 'ok' || run.status === 'error') { + stopPolling() + await loadRecent() + } + } catch (e) { + error.value = e + stopPolling() + } + }, POLL_MS) + } + + function stopPolling() { + if (pollHandle) { + clearInterval(pollHandle) + pollHandle = null + } + } + + const isRunning = computed( + () => activeRun.value && activeRun.value.status === 'running', + ) + + return { + activeRun, recentRuns, error, loading, isRunning, + trigger, loadRun, loadRecent, pollActive, stopPolling, + } +})