diff --git a/.phpunit.result.cache b/.phpunit.result.cache index b8f23c1..18bef70 100644 --- a/.phpunit.result.cache +++ b/.phpunit.result.cache @@ -1 +1 @@ -{"version":2,"defects":{"App\\Containers\\VikonIntegration\\Tests\\Unit\\FilesystemTaskTest::test_atomic_swap_replaces_existing_entry":7,"App\\Containers\\VikonIntegration\\Tests\\Unit\\FilesystemTaskTest::test_atomic_swap_creates_new_entry_when_not_exists":7,"App\\Containers\\VikonIntegration\\Tests\\Unit\\FilesystemTaskTest::test_atomic_swap_returns_false_on_path_traversal":8,"App\\Containers\\VikonIntegration\\Tests\\Unit\\PollPartStatusTaskTest::test_returns_completed_when_status_is_completed":8,"App\\Containers\\VikonIntegration\\Tests\\Unit\\PollPartStatusTaskTest::test_returns_failed_when_status_is_failed":8,"App\\Containers\\VikonIntegration\\Tests\\Unit\\PollPartStatusTaskTest::test_returns_timeout_after_max_attempts":8,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_requests_generation_and_polls_status":7,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_throws_on_generation_failure":7,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_throws_on_poll_failure":7,"App\\Containers\\VikonIntegration\\Tests\\Unit\\FilesystemTaskTest::test_restore_after_fail_removes_new_and_restores_old":8,"App\\Containers\\VikonIntegration\\Tests\\Feature\\UpdatePartTest::test_update_part_validates_module_id":8,"App\\Containers\\VikonIntegration\\Tests\\Feature\\UpdatePartTest::test_update_part_validates_part":8,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_rejects_invalid_part":7,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_rejects_invalid_module_id":7},"times":{"App\\Containers\\VikonIntegration\\Tests\\Unit\\FilesystemTaskTest::test_atomic_swap_replaces_existing_entry":0.005,"App\\Containers\\VikonIntegration\\Tests\\Unit\\FilesystemTaskTest::test_atomic_swap_creates_new_entry_when_not_exists":0.001,"App\\Containers\\VikonIntegration\\Tests\\Unit\\FilesystemTaskTest::test_atomic_swap_returns_false_on_path_traversal":0.001,"App\\Containers\\VikonIntegration\\Tests\\Unit\\PollPartStatusTaskTest::test_returns_completed_when_status_is_completed":0.027,"App\\Containers\\VikonIntegration\\Tests\\Unit\\PollPartStatusTaskTest::test_returns_failed_when_status_is_failed":0.001,"App\\Containers\\VikonIntegration\\Tests\\Unit\\PollPartStatusTaskTest::test_returns_timeout_after_max_attempts":0.002,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_rejects_invalid_module_id":0.003,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_rejects_invalid_part":0.011,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_requests_generation_and_polls_status":0.003,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_throws_on_generation_failure":0.001,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_throws_on_poll_failure":0.001,"App\\Containers\\VikonIntegration\\Tests\\Unit\\FilesystemTaskTest::test_restore_after_fail_removes_new_and_restores_old":0.004,"App\\Containers\\VikonIntegration\\Tests\\Feature\\UpdatePartTest::test_update_part_requires_authentication":0.073,"App\\Containers\\VikonIntegration\\Tests\\Feature\\UpdatePartTest::test_update_part_validates_module_id":0.025,"App\\Containers\\VikonIntegration\\Tests\\Feature\\UpdatePartTest::test_update_part_validates_part":0.009,"App\\Containers\\VikonIntegration\\Tests\\Unit\\PollPartStatusTaskTest::test_returns_completed_when_status_is_1":0.026,"App\\Containers\\VikonIntegration\\Tests\\Unit\\PollPartStatusTaskTest::test_returns_failed_when_status_is_negative_1":0}} \ No newline at end of file +{"version":2,"defects":{"App\\Containers\\VikonIntegration\\Tests\\Unit\\FilesystemTaskTest::test_atomic_swap_replaces_existing_entry":7,"App\\Containers\\VikonIntegration\\Tests\\Unit\\FilesystemTaskTest::test_atomic_swap_creates_new_entry_when_not_exists":7,"App\\Containers\\VikonIntegration\\Tests\\Unit\\FilesystemTaskTest::test_atomic_swap_returns_false_on_path_traversal":8,"App\\Containers\\VikonIntegration\\Tests\\Unit\\PollPartStatusTaskTest::test_returns_completed_when_status_is_completed":8,"App\\Containers\\VikonIntegration\\Tests\\Unit\\PollPartStatusTaskTest::test_returns_failed_when_status_is_failed":8,"App\\Containers\\VikonIntegration\\Tests\\Unit\\PollPartStatusTaskTest::test_returns_timeout_after_max_attempts":8,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_requests_generation_and_polls_status":7,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_throws_on_generation_failure":7,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_throws_on_poll_failure":7,"App\\Containers\\VikonIntegration\\Tests\\Unit\\FilesystemTaskTest::test_restore_after_fail_removes_new_and_restores_old":8,"App\\Containers\\VikonIntegration\\Tests\\Feature\\UpdatePartTest::test_update_part_validates_module_id":8,"App\\Containers\\VikonIntegration\\Tests\\Feature\\UpdatePartTest::test_update_part_validates_part":8,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_rejects_invalid_part":7,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_rejects_invalid_module_id":7},"times":{"App\\Containers\\VikonIntegration\\Tests\\Unit\\FilesystemTaskTest::test_atomic_swap_replaces_existing_entry":0.018,"App\\Containers\\VikonIntegration\\Tests\\Unit\\FilesystemTaskTest::test_atomic_swap_creates_new_entry_when_not_exists":0.002,"App\\Containers\\VikonIntegration\\Tests\\Unit\\FilesystemTaskTest::test_atomic_swap_returns_false_on_path_traversal":0.002,"App\\Containers\\VikonIntegration\\Tests\\Unit\\PollPartStatusTaskTest::test_returns_completed_when_status_is_completed":0.027,"App\\Containers\\VikonIntegration\\Tests\\Unit\\PollPartStatusTaskTest::test_returns_failed_when_status_is_failed":0.001,"App\\Containers\\VikonIntegration\\Tests\\Unit\\PollPartStatusTaskTest::test_returns_timeout_after_max_attempts":0.002,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_rejects_invalid_module_id":0.003,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_rejects_invalid_part":0.011,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_requests_generation_and_polls_status":0.003,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_throws_on_generation_failure":0.001,"App\\Containers\\VikonIntegration\\Tests\\Unit\\UpdatePartActionTest::test_throws_on_poll_failure":0.001,"App\\Containers\\VikonIntegration\\Tests\\Unit\\FilesystemTaskTest::test_restore_after_fail_removes_new_and_restores_old":0.001,"App\\Containers\\VikonIntegration\\Tests\\Feature\\UpdatePartTest::test_update_part_requires_authentication":0.069,"App\\Containers\\VikonIntegration\\Tests\\Feature\\UpdatePartTest::test_update_part_validates_module_id":0.022,"App\\Containers\\VikonIntegration\\Tests\\Feature\\UpdatePartTest::test_update_part_validates_part":0.01,"App\\Containers\\VikonIntegration\\Tests\\Unit\\PollPartStatusTaskTest::test_returns_completed_when_status_is_1":0.023,"App\\Containers\\VikonIntegration\\Tests\\Unit\\PollPartStatusTaskTest::test_returns_failed_when_status_is_negative_1":0}} \ No newline at end of file diff --git a/resources/js/Pages/Dashboard/VikonUpdates/Index.vue b/resources/js/Pages/Dashboard/VikonUpdates/Index.vue index 9b611e0..1a46685 100644 --- a/resources/js/Pages/Dashboard/VikonUpdates/Index.vue +++ b/resources/js/Pages/Dashboard/VikonUpdates/Index.vue @@ -76,24 +76,25 @@

ID: {{ id }}

-

Обновление модуля запрещено сервером VIKON

-
- +
+
+ + {{ updatePhase }} +
- + {{ p.name }} @@ -102,31 +103,11 @@
-
- -
-
- -

Обновление...

-
-
-
-
-

{{ progress }}%

-
-
@@ -167,20 +148,19 @@ const isAuthenticated = ref(props.is_authenticated); const currentVersion = ref(props.current_version); const checkingVersion = ref(false); const checkingAccess = ref(false); -const updating = ref(false); -const progress = ref(0); +const updating = ref(null); +const updatePhase = ref(''); const updateError = ref(null); const versionInfo = ref({ current_version: props.current_version, has_update: false, latest_version: null }); const accessInfo = ref({ has_access: false, error: null }); const selectedParts = ref({}); if (props.parts) { for (const [id, moduleData] of Object.entries(props.parts)) { - if (!moduleData.disabled) { - selectedParts.value[id] = []; + if (!moduleData.disabled && moduleData.parts) { + selectedParts.value[id] = moduleData.parts.filter(p => p.access).map(p => p.id); } } } -const updatingPart = ref(null); const partStatus = ref({}); const partErrors = ref({}); @@ -246,35 +226,59 @@ async function checkVersion() { } async function updateModule(moduleId) { - if (!confirm('Обновить модуль?')) return; + const partsToUpdate = selectedParts.value[moduleId] || []; + const msg = partsToUpdate.length + ? `Обновить модуль и ${partsToUpdate.length} раздел(ов)?` + : 'Обновить модуль (только ядро и файлы)?'; + if (!confirm(msg)) return; - updating.value = true; - progress.value = 0; + updating.value = moduleId; + updatePhase.value = 'Обновление ядра модуля...'; updateError.value = null; - - const timer = setInterval(() => { - if (progress.value < 90) progress.value += 10; - }, 500); + // Clear previous part statuses + for (const p of partsToUpdate) { + delete partStatus.value[`${moduleId}-${p}`]; + delete partErrors.value[`${moduleId}-${p}`]; + } try { + // Phase 1: Core + FM sync const res = await axios.post(route('dashboard.vikon-updates.update-module'), { module_id: moduleId }); - progress.value = 100; - clearInterval(timer); - - if (res.data.success) { - updateError.value = null; - updating.value = false; - alert(res.data.message); - } else { + if (!res.data.success) { updateError.value = res.data.message; - updating.value = false; + updating.value = null; + return; } + + // Phase 2: Update selected parts + if (partsToUpdate.length) { + updatePhase.value = 'Обновление разделов...'; + for (const part of partsToUpdate) { + const key = `${moduleId}-${part}`; + partStatus.value[key] = 'loading'; + + try { + await axios.post(route('dashboard.vikon-updates.update-part'), { + module_id: moduleId, + part, + }); + partStatus.value[key] = 'success'; + } catch (e) { + partStatus.value[key] = 'error'; + partErrors.value[key] = e.response?.data?.message || e.message; + } + } + } + + updateError.value = null; + alert('Модуль обновлён.'); } catch (e) { - clearInterval(timer); - updating.value = false; const msg = e.response?.data?.message || e.message || 'Неизвестная ошибка'; const code = e.response?.status || ''; updateError.value = code ? `[${code}] ${msg}` : msg; + } finally { + updating.value = null; + updatePhase.value = ''; } } @@ -293,37 +297,6 @@ function toggleAllParts(moduleId) { } } -async function updateSelectedParts(moduleId) { - const partsToUpdate = selectedParts.value[moduleId] || []; - if (!partsToUpdate.length) return; - - updatingPart.value = moduleId; - // Clear previous statuses for this module - for (const part of partsToUpdate) { - delete partStatus.value[`${moduleId}-${part}`]; - delete partErrors.value[`${moduleId}-${part}`]; - } - - for (const part of partsToUpdate) { - const key = `${moduleId}-${part}`; - partStatus.value[key] = 'loading'; - partErrors.value[key] = null; - - try { - await axios.post(route('dashboard.vikon-updates.update-part'), { - module_id: moduleId, - part, - }); - partStatus.value[key] = 'success'; - } catch (e) { - partStatus.value[key] = 'error'; - partErrors.value[key] = e.response?.data?.message || e.message; - } - } - - updatingPart.value = null; -} - async function logout() { try { await axios.post(route('dashboard.vikon-updates.logout'));