# πŸ” Анализ Π»ΠΎΠ³ΠΈΠΊΠΈ настроСк ESP - Π”Π΅Ρ‚Π°Π»ΡŒΠ½Π°Ρ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° ## βœ… ΠŸΡ€ΠΎΠ²Π΅Ρ€Π΅Π½Π½Ρ‹Π΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ ### 1. **Frontend (Client)** #### πŸ“„ [client/src/composables/config/useConfigESP.js](../client/src/composables/config/useConfigESP.js) **Бтатус:** βœ… Π›ΠΎΠ³ΠΈΠΊΠ° ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½Π° **ΠŸΡ€ΠΎΠ²Π΅Ρ€Π΅Π½Π½Ρ‹Π΅ аспСкты:** - βœ… Валидация ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² ΠΏΠ΅Ρ€Π΅Π΄ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΎΠΉ (строки 93-113) - βœ… Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° настроСк Ρ‡Π΅Ρ€Π΅Π· `cfg-esp-get` (строки 118-143) - βœ… Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° Ρ‡Π΅Ρ€Π΅Π· `cfg-esp` (строки 148-190) - βœ… ΠžΡ‚ΠΊΠ°Ρ‚ значСния ΠΏΡ€ΠΈ ошибкС (строки 176, 183) - βœ… ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ΠΎΡ‚Π²Π΅Ρ‚Π° `error` ΠΎΡ‚ сСрвСра (строки 174-177) - βœ… ΠžΡ‚ΡΠ»Π΅ΠΆΠΈΠ²Π°Π½ΠΈΠ΅ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ Ρ‡Π΅Ρ€Π΅Π· `hasChanges` (строки 79-88) **ΠšΠ»ΡŽΡ‡Π΅Π²Ρ‹Π΅ ΠΌΠΎΠΌΠ΅Π½Ρ‚Ρ‹:** ```javascript // Валидация const validation = validateParam(paramName, config.value[paramName]) if (!validation.valid) { error.value = validation.error return { success: false, error: validation.error } } // ΠžΡ‚ΠΊΠ°Ρ‚ ΠΏΡ€ΠΈ ошибкС if (response.do === 'error') { config.value[paramName] = originalConfig.value[paramName] throw new Error(response.message || 'Ошибка сохранСния') } ``` --- #### πŸ“„ [client/src/pages/ConfigPage.vue](../client/src/pages/ConfigPage.vue) **Бтатус:** βœ… Π›ΠΎΠ³ΠΈΠΊΠ° ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½Π° (исправлСна) **ΠŸΡ€ΠΎΠ²Π΅Ρ€Π΅Π½Π½Ρ‹Π΅ аспСкты:** - βœ… ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° ΠΏΡ€Π°Π² доступа: `user.taccess === 'technics'` ΠΈΠ»ΠΈ `'admins'` (строка 163) - βœ… Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° настроСк ΠΏΡ€ΠΈ ΠΌΠΎΠ½Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠΈ (строки 188-192) - βœ… Π’ΠΈΠ·ΡƒΠ°Π»ΡŒΠ½Π°Ρ индикация ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ - βœ… ΠŸΡ€Π΅Π΄ΡƒΠΏΡ€Π΅ΠΆΠ΄Π΅Π½ΠΈΠ΅ ΠΏΡ€ΠΈ Π²Ρ‹Ρ…ΠΎΠ΄Π΅ с нСсохранёнными измСнСниями (строки 178-186) **Π˜ΡΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΡ:** ```javascript // Π‘Π«Π›Πž (Π½Π΅ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎ): return role === 'technic' || role === 'admin' || user.group === 3 // Π‘Π’ΠΠ›Πž (ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎ): return user.taccess === 'technics' || user.taccess === 'admins' || user.group === 3 ``` --- #### πŸ“„ [client/src/composables/useWebSocket.js](../client/src/composables/useWebSocket.js) **Бтатус:** βœ… Π›ΠΎΠ³ΠΈΠΊΠ° ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½Π° (Π΄ΠΎΠ±Π°Π²Π»Π΅Π½Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ°) **ДобавлСнная ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° (строки 71-80):** ```javascript // ΠžΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅ΠΌ ΠΎΡ‚Π²Π΅Ρ‚Ρ‹ настроСк ESP ΠΈ ошибки Ρ‡Π΅Ρ€Π΅Π· promise if (data.do === 'cfg-esp-get' || data.do === 'cfg-esp' || data.do === 'error') { const promiseKeys = Object.keys(pendingPromises) if (promiseKeys.length > 0) { const promiseId = promiseKeys[promiseKeys.length - 1] pendingPromises[promiseId].resolve(data) delete pendingPromises[promiseId] return } } ``` **Π—Π°Ρ‡Π΅ΠΌ это Π½ΡƒΠΆΠ½ΠΎ:** - Π‘Π΅Π· этого ΠΊΠΎΠ΄Π° ΠΎΡ‚Π²Π΅Ρ‚Ρ‹ `cfg-esp-get`, `cfg-esp` ΠΈ `error` Π½Π΅ ΠΏΠΎΠΏΠ°Π΄Π°Π»ΠΈ Π±Ρ‹ Π² promise - Promise висСл Π±Ρ‹ Π² pending состоянии Π΄ΠΎ timeout - UI Π½Π΅ ΠΏΠΎΠ»ΡƒΡ‡Π°Π» Π±Ρ‹ ΠΎΡ‚Π²Π΅Ρ‚ ΠΎΡ‚ сСрвСра --- ### 2. **Backend (Server)** #### πŸ“„ [server/ws.js](../server/ws.js) **Бтатус:** βœ… Π›ΠΎΠ³ΠΈΠΊΠ° ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½Π° (Π΄ΠΎΠ±Π°Π²Π»Π΅Π½Π° ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° mock Ρ€Π΅ΠΆΠΈΠΌΠ°) **ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ `cfg-esp-get` (строки 952-972):** ```javascript case "cfg-esp-get": try { const espConfig = JSON.parse(await fs.readFile(path.join(__dirname, 'data/esp.ini'), 'utf8')); sendToClient(wsClient, { do: "cfg-esp-get", cfg: espConfig }); } catch (error) { sendToClient(wsClient, { do: "error", message: "НС ΡƒΠ΄Π°Π»ΠΎΡΡŒ Π·Π°Π³Ρ€ΡƒΠ·ΠΈΡ‚ΡŒ настройки ESP", type: "esp_config_read_error" }); } break; ``` **ΠžΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ `cfg-esp` (строки 974-1038):** **ΠšΠ»ΡŽΡ‡Π΅Π²Ρ‹Π΅ исправлСния:** ```javascript // Π‘Π«Π›Πž (Π½Π΅ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎ для Windows): if (result.success) { await fs.writeFile(configPath, JSON.stringify(espConfig, null, 2)); // ... } // Π‘Π’ΠΠ›Πž (ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎ): const isMockMode = result.error && result.error.includes('mock mode'); if (result.success || isMockMode) { await fs.writeFile(configPath, JSON.stringify(espConfig, null, 2)); // ... if (isMockMode) { console.log(`[WS CFG-ESP] ⚠️ MOCK Π Π•Π–Π˜Πœ: ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ сохранён Π±Π΅Π· ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΈ Π½Π° ESP`); } } ``` **Π—Π°Ρ‡Π΅ΠΌ это Π½ΡƒΠΆΠ½ΠΎ:** - Π’ Windows Ρ€Π΅ΠΆΠΈΠΌΠ΅ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ ESP Π½Π΅ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½Π° (mock Ρ€Π΅ΠΆΠΈΠΌ) - `game.safeWrite()` Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ `{ success: false, error: 'ESP disconnected (mock mode)' }` - Π‘Π΅Π· ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ Π½Π° `isMockMode` настройки НЕ ΡΠΎΡ…Ρ€Π°Π½ΡΠ»ΠΈΡΡŒ Π±Ρ‹ Π² Ρ„Π°ΠΉΠ» - Π’Π΅ΠΏΠ΅Ρ€ΡŒ Π² mock Ρ€Π΅ΠΆΠΈΠΌΠ΅ настройки ΡΠΎΡ…Ρ€Π°Π½ΡΡŽΡ‚ΡΡ, Π½ΠΎ с ΠΏΡ€Π΅Π΄ΡƒΠΏΡ€Π΅ΠΆΠ΄Π΅Π½ΠΈΠ΅ΠΌ --- ## πŸ”„ ΠŸΠΎΠ»Π½Ρ‹ΠΉ ΠΏΠΎΡ‚ΠΎΠΊ Π΄Π°Π½Π½Ρ‹Ρ… ### Π‘Ρ†Π΅Π½Π°Ρ€ΠΈΠΉ 1: Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° настроСк ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 1. ConfigPage.vue (onMounted) β”‚ β”‚ └─> loadConfig() β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 2. useConfigESP.js β”‚ β”‚ └─> socket.sendMessage({ do: 'cfg-esp-get' }) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ WebSocket β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 3. server/ws.js:952 β”‚ β”‚ └─> Π§ΠΈΡ‚Π°Π΅Ρ‚ data/esp.ini β”‚ β”‚ └─> ΠžΡ‚ΠΏΡ€Π°Π²Π»ΡΠ΅Ρ‚ {do:"cfg-esp-get", cfg:{...}} β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ WebSocket β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 4. useWebSocket.js:72 β”‚ β”‚ └─> ΠžΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ²Π°Π΅Ρ‚ data.do === 'cfg-esp-get' β”‚ β”‚ └─> Π Π΅Π·ΠΎΠ»Π²ΠΈΡ‚ promise β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 5. useConfigESP.js:128 β”‚ β”‚ └─> config.value = { ...response.cfg } β”‚ β”‚ └─> originalConfig.value = { ...response.cfg } β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 6. ConfigPage.vue β”‚ β”‚ └─> ΠžΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Π΅Ρ‚ настройки Π² UI β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ### Π‘Ρ†Π΅Π½Π°Ρ€ΠΈΠΉ 2: Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° (Π£Π‘ΠŸΠ•Π₯) ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 1. ConfigPage.vue β”‚ β”‚ └─> ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ измСняСт hz с 14000 Π½Π° 15000 β”‚ β”‚ └─> ΠšΠ»ΠΈΠΊΠ°Π΅Ρ‚ "ΠŸΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ" β”‚ β”‚ └─> saveParam('hz') β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 2. useConfigESP.js:148 β”‚ β”‚ └─> Валидация: 9999 ≀ 15000 ≀ 100000 βœ… β”‚ β”‚ └─> socket.sendMessage({ β”‚ β”‚ do: 'cfg-esp', β”‚ β”‚ cfg: 'hz', β”‚ β”‚ value: 15000 β”‚ β”‚ }) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ WebSocket β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 3. server/ws.js:974 β”‚ β”‚ └─> Π§ΠΈΡ‚Π°Π΅Ρ‚ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ esp.ini β”‚ β”‚ └─> БохраняСт previousValue = 14000 β”‚ β”‚ └─> ΠžΠ±Π½ΠΎΠ²Π»ΡΠ΅Ρ‚ espConfig.hz = 15000 β”‚ β”‚ └─> game.safeWrite("hz=15000;\n") β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ Serial Port β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 4. game.js:127 (safeWrite) β”‚ β”‚ └─> ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅Ρ‚ port.isOpen β”‚ β”‚ └─> Production: отправляСт Π½Π° ESP β”‚ β”‚ └─> Mock (Windows): return {success:false, error:...} β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 5. server/ws.js:1000 β”‚ β”‚ └─> const isMockMode = result.error?.includes('mock') β”‚ β”‚ └─> if (result.success || isMockMode) β”‚ β”‚ └─> БохраняСт Π² esp.ini β”‚ β”‚ └─> sendToClient({do:"cfg-esp", cfg:"hz", value:15000})β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ WebSocket β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 6. useWebSocket.js:72 β”‚ β”‚ └─> ΠžΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ²Π°Π΅Ρ‚ data.do === 'cfg-esp' β”‚ β”‚ └─> Π Π΅Π·ΠΎΠ»Π²ΠΈΡ‚ promise β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 7. useConfigESP.js:169 β”‚ β”‚ └─> originalConfig.value.hz = 15000 β”‚ β”‚ └─> return { success: true } β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 8. ConfigPage.vue β”‚ β”‚ └─> Кнопка "ΠŸΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ" исчСзаСт β”‚ β”‚ └─> ΠŸΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ΡΡ "βœ“ Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΎ" β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ### Π‘Ρ†Π΅Π½Π°Ρ€ΠΈΠΉ 3: Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° (ΠžΠ¨Π˜Π‘ΠšΠ ESP) ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 1-3. Аналогично ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠΌΡƒ ΡΡ†Π΅Π½Π°Ρ€ΠΈΡŽ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 4. game.js:127 (safeWrite) β”‚ β”‚ └─> ESP Π½Π΅ ΠΎΡ‚Π²Π΅Ρ‡Π°Π΅Ρ‚ / ошибка ΠΎΡ‚ΠΏΡ€Π°Π²ΠΊΠΈ β”‚ β”‚ └─> return { success: false, error: "timeout" } β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 5. server/ws.js:1018 β”‚ β”‚ └─> result.success === false β”‚ β”‚ └─> isMockMode === false (Ρ€Π΅Π°Π»ΡŒΠ½Π°Ρ ошибка) β”‚ β”‚ └─> espConfig.hz = previousValue (ΠΎΡ‚ΠΊΠ°Ρ‚ ΠΊ 14000) β”‚ β”‚ └─> sendToClient({ β”‚ β”‚ do: "error", β”‚ β”‚ message: "Ошибка настройки ESP: timeout", β”‚ β”‚ type: "esp_config_failed", β”‚ β”‚ cfg: "hz" β”‚ β”‚ }) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ WebSocket β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 6. useWebSocket.js:72 β”‚ β”‚ └─> ΠžΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ²Π°Π΅Ρ‚ data.do === 'error' β”‚ β”‚ └─> Π Π΅Π·ΠΎΠ»Π²ΠΈΡ‚ promise с error β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 7. useConfigESP.js:174 β”‚ β”‚ └─> response.do === 'error' β”‚ β”‚ └─> config.value.hz = originalConfig.value.hz (14000) β”‚ β”‚ └─> throw new Error(response.message) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 8. useConfigESP.js:181 (catch) β”‚ β”‚ └─> config.value.hz = originalConfig.value.hz (14000) β”‚ β”‚ └─> error.value = err.message β”‚ β”‚ └─> return { success: false, error: err.message } β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ 9. ConfigPage.vue β”‚ β”‚ └─> Π—Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ откатываСтся ΠΊ 14000 β”‚ β”‚ └─> ΠŸΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ΡΡ ошибка "❌ НС ΡƒΠ΄Π°Π»ΠΎΡΡŒ ΡΠΎΡ…Ρ€Π°Π½ΠΈΡ‚ΡŒ" β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## πŸ›‘οΈ ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ бСзопасности ### 1. **Валидация Π½Π° ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π΅** ```javascript validateParam(paramName, value) { const schema = ESP_CONFIG_SCHEMA[paramName] const numValue = Number(value) if (isNaN(numValue)) { return { valid: false, error: 'Π—Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ числом' } } if (numValue < schema.min || numValue > schema.max) { return { valid: false, error: `Π—Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ ΠΎΡ‚ ${schema.min} Π΄ΠΎ ${schema.max}` } } return { valid: true, error: null } } ``` ### 2. **ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° ΠΏΡ€Π°Π² доступа** ```javascript const hasAccess = computed(() => { return user.taccess === 'technics' || user.taccess === 'admins' || user.group === 3 }) ``` ### 3. **ΠžΡ‚ΠΊΠ°Ρ‚ ΠΏΡ€ΠΈ ΠΎΡˆΠΈΠ±ΠΊΠ°Ρ…** - **ΠšΠ»ΠΈΠ΅Π½Ρ‚:** ΠžΡ‚ΠΊΠ°Ρ‚ `config.value` ΠΊ `originalConfig.value` - **Π‘Π΅Ρ€Π²Π΅Ρ€:** ΠžΡ‚ΠΊΠ°Ρ‚ `espConfig` ΠΊ `previousValue` - **Π€Π°ΠΉΠ»:** Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΡ€ΠΈ успСхС --- ## ⚠️ НайдСнныС ΠΈ исправлСнныС ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ ### ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ° 1: ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° WebSocket ΠΎΡ‚Π²Π΅Ρ‚ΠΎΠ² **ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°:** ΠžΡ‚Π²Π΅Ρ‚Ρ‹ `cfg-esp-get`, `cfg-esp`, `error` Π½Π΅ ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π»ΠΈΡΡŒ Π² `useWebSocket.js` **РСшСниС:** Π”ΠΎΠ±Π°Π²Π»Π΅Π½Π° ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Π°Ρ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π² `onmessage` (строки 71-80) --- ### ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ° 2: Mock Ρ€Π΅ΠΆΠΈΠΌ Windows **ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°:** Π’ Windows (mock Ρ€Π΅ΠΆΠΈΠΌ) настройки НЕ ΡΠΎΡ…Ρ€Π°Π½ΡΠ»ΠΈΡΡŒ **РСшСниС:** Π”ΠΎΠ±Π°Π²Π»Π΅Π½Π° ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° `isMockMode` для Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ сохранСния Π±Π΅Π· ESP --- ### ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ° 3: ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° ΠΏΡ€Π°Π² доступа **ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°:** ΠΠ΅ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½Ρ‹Π΅ значСния Ρ€ΠΎΠ»Π΅ΠΉ (`technic` вмСсто `technics`) **РСшСниС:** Π˜ΡΠΏΡ€Π°Π²Π»Π΅Π½ΠΎ Π½Π° `user.taccess === 'technics'` ΠΈ `'admins'` --- ## βœ… Π˜Ρ‚ΠΎΠ³ΠΎΠ²Π°Ρ ΠΎΡ†Π΅Π½ΠΊΠ° ### Π§Ρ‚ΠΎ Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ ΠŸΠ ΠΠ’Π˜Π›Π¬ΠΠž: βœ… Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° настроСк ESP βœ… Π‘ΠΎΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² βœ… Валидация Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½ΠΎΠ² βœ… ΠžΡ‚ΠΊΠ°Ρ‚ ΠΏΡ€ΠΈ ΠΎΡˆΠΈΠ±ΠΊΠ°Ρ… βœ… ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° mock Ρ€Π΅ΠΆΠΈΠΌΠ° (Windows) βœ… ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° ΠΏΡ€Π°Π² доступа βœ… Π’ΠΈΠ·ΡƒΠ°Π»ΡŒΠ½Π°Ρ индикация ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ βœ… ΠŸΡ€Π΅Π΄ΡƒΠΏΡ€Π΅ΠΆΠ΄Π΅Π½ΠΈΠ΅ ΠΎ нСсохранённых измСнСниях βœ… WebSocket promise систСма βœ… Π”Π΅Ρ‚Π°Π»ΡŒΠ½ΠΎΠ΅ Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ --- ## πŸš€ Π Π΅ΠΊΠΎΠΌΠ΅Π½Π΄Π°Ρ†ΠΈΠΈ для тСстирования ### 1. **ВСст Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ настроСк** ``` 1. ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ /config 2. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°ΡŽΡ‚ΡΡ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠ΅ значСния ΠΈΠ· esp.ini 3. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ Π»ΠΎΠ³ΠΈ: "[WS CFG-ESP-GET] βœ… Настройки ESP ΠΎΡ‚ΠΏΡ€Π°Π²Π»Π΅Π½Ρ‹" ``` ### 2. **ВСст сохранСния (успСх)** ``` 1. Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ hz Π½Π° 15000 2. ΠΠ°ΠΆΠ°Ρ‚ΡŒ "ΠŸΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ" 3. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ ΠΊΠ½ΠΎΠΏΠΊΠ° исчСзла 4. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ esp.ini ΠΎΠ±Π½ΠΎΠ²Π»Ρ‘Π½ 5. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ Π»ΠΎΠ³ΠΈ: "[WS CFG-ESP] βœ… ΠŸΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ hz ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ сохранён" ``` ### 3. **ВСст Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ** ``` 1. Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ hz Π½Π° 999999 (Π²Π½Π΅ Π΄ΠΈΠ°ΠΏΠ°Π·ΠΎΠ½Π°) 2. ΠΠ°ΠΆΠ°Ρ‚ΡŒ "ΠŸΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ" 3. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ ΠΎΡˆΠΈΠ±ΠΊΡƒ: "Π—Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ ΠΎΡ‚ 9999 Π΄ΠΎ 100000" ``` ### 4. **ВСст ΠΏΡ€Π°Π² доступа** ``` 1. Π’ΠΎΠΉΡ‚ΠΈ ΠΊΠ°ΠΊ operator 2. ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ /config 3. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ сообщСниС "НСт доступа" ``` ### 5. **ВСст нСсохранённых ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ** ``` 1. Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ hz (НЕ Π½Π°ΠΆΠΈΠΌΠ°Ρ‚ΡŒ "ΠŸΡ€ΠΈΠΌΠ΅Π½ΠΈΡ‚ΡŒ") 2. ΠΠ°ΠΆΠ°Ρ‚ΡŒ "Назад" 3. ΠŸΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ ΠΏΠΎΠ΄Ρ‚Π²Π΅Ρ€ΠΆΠ΄Π΅Π½ΠΈΠ΅ "Π•ΡΡ‚ΡŒ нСсохранённыС измСнСния..." ``` --- *Анализ Π·Π°Π²Π΅Ρ€ΡˆΡ‘Π½: 2025-01-06* *ВсС критичСскиС ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹ Π½Π°ΠΉΠ΄Π΅Π½Ρ‹ ΠΈ исправлСны* βœ