fix(mobile): повторный enroll и удаление устройств в UI (0.9.10).

При повторной привязке Seaca переиспользуется запись device_uuid вместо INSERT.
Добавлены POST /devices/{id}/revoke и DELETE для полного удаления записи в настройках SAC.
This commit is contained in:
Andrey Lutsenko
2026-06-13 16:36:07 +10:00
parent e022f2f213
commit 3d36faa49d
10 changed files with 186 additions and 39 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
{
{
"name": "sac-ui",
"private": true,
"version": "0.7.4",
"version": "0.9.10",
"type": "module",
"scripts": {
"dev": "vite",
+6 -2
View File
@@ -1,4 +1,4 @@
const TOKEN_KEY = "sac_token";
const TOKEN_KEY = "sac_token";
const ROLE_KEY = "sac_role";
export function getToken(): string | null {
@@ -373,7 +373,11 @@ export function fetchMobileDevices(): Promise<MobileDevice[]> {
}
export function revokeMobileDevice(deviceId: number): Promise<MobileDevice> {
return apiFetch<MobileDevice>(`/api/v1/mobile/devices/${deviceId}`, { method: "DELETE" });
return apiFetch<MobileDevice>(`/api/v1/mobile/devices/${deviceId}/revoke`, { method: "POST" });
}
export function deleteMobileDevice(deviceId: number): Promise<void> {
return apiFetch<void>(`/api/v1/mobile/devices/${deviceId}`, { method: "DELETE" });
}
export function testMobileDevicePush(deviceId: number): Promise<{ message: string }> {
+2 -2
View File
@@ -1,4 +1,4 @@
/** Fallback до загрузки /health; при релизе держите в sync с backend/app/version.py */
/** Fallback до загрузки /health; при релизе держите в sync с backend/app/version.py */
export const APP_NAME = "Security Alert Center";
export const APP_VERSION = "0.9.9";
export const APP_VERSION = "0.9.10";
export const APP_VERSION_LABEL = `${APP_NAME} v.${APP_VERSION}`;
+28 -1
View File
@@ -1,4 +1,4 @@
<template>
<template>
<div class="settings-page">
<h1>Настройки</h1>
<p class="settings-intro">
@@ -190,6 +190,7 @@
<tr>
<th>Имя</th>
<th>Пользователь</th>
<th>Статус</th>
<th>FCM</th>
<th>Последняя активность</th>
<th>Действия</th>
@@ -199,6 +200,7 @@
<tr v-for="device in mobileDevices" :key="device.id">
<td>{{ device.display_name }}</td>
<td>{{ device.username }}</td>
<td>{{ device.is_active ? "активно" : "отключено" }}</td>
<td>{{ device.has_fcm_token ? "да" : "нет" }}</td>
<td>{{ device.last_seen_at || device.enrolled_at }}</td>
<td class="settings-mobile-actions">
@@ -219,6 +221,13 @@
>
Отключить
</button>
<button
type="button"
class="danger"
@click="deleteDevice(device.id)"
>
Удалить
</button>
</td>
</tr>
</tbody>
@@ -428,6 +437,7 @@ import {
fetchMobileSettings,
fetchUsers,
revokeMobileDevice,
deleteMobileDevice,
revokeMobileEnrollmentCode,
testMobileDevicePush,
updateMobileSettings,
@@ -781,6 +791,23 @@ async function revokeDevice(deviceId: number) {
}
}
async function deleteDevice(deviceId: number) {
const device = mobileDevices.value.find((item) => item.id === deviceId);
const label = device?.display_name || `#${deviceId}`;
if (!window.confirm(`Удалить запись устройства «${label}» из базы? Повторная привязка потребует новый код enroll.`)) {
return;
}
error.value = "";
success.value = "";
try {
await deleteMobileDevice(deviceId);
mobileDevices.value = await fetchMobileDevices();
success.value = "Запись устройства удалена.";
} catch (e) {
error.value = e instanceof Error ? e.message : "Ошибка";
}
}
async function testDevicePush(deviceId: number) {
error.value = "";
success.value = "";