WellNuo/PRD-COMPLETED-AUDIT.md
Sergei 8af7a11cd9 Fix WiFi credentials cache implementation in SecureStore
- Fix saveWiFiPassword to use encrypted passwords map instead of decrypted
- Fix getWiFiPassword to decrypt from encrypted storage
- Fix test expectations for migration and encryption functions
- Remove unused error variables to fix linting warnings
- All 27 tests now passing with proper encryption/decryption flow

The WiFi credentials cache feature was already implemented but had bugs
where encrypted and decrypted password maps were being mixed. This commit
ensures proper encryption is maintained throughout the storage lifecycle.
2026-01-31 15:55:24 -08:00

9.4 KiB
Raw Permalink Blame History

PRD — WellNuo Full Audit & Bug Fixes

Вопросы для уточнения

Вопрос 1: Формат серийного номера

Какой regex pattern должен валидировать serial number устройства? Сейчас проверяется только длина >= 8. Ответ: Использовать regex /^[A-Za-z0-9]{8,16}$/ — буквенно-цифровой, 8-16 символов.

Вопрос 2: Demo credentials configuration

Куда вынести hardcoded demo credentials (anandk)? В .env файл, SecureStore или отдельный config? Ответ: anandk — устаревший аккаунт. Нужно заменить на robster/rob2 (актуальный аккаунт для Legacy API). Вынести в .env файл как LEGACY_API_USER=robster и LEGACY_API_PASSWORD=rob2.

Вопрос 3: Максимальное количество beneficiaries

Сколько beneficiaries может быть у одного пользователя? Нужна ли пагинация для списка? Ответ: Максимум ~5 beneficiaries. Пагинация не нужна.

Цель

Исправить критические баги, улучшить безопасность и стабильность приложения WellNuo перед production release.

Контекст проекта

  • Тип: Expo / React Native приложение
  • Стек: expo 53, react-native 0.79, typescript, expo-router, livekit, stripe, BLE
  • API: WellNuo (wellnuo.smartlaunchhub.com) + Legacy (eluxnetworks.net)
  • БД: PostgreSQL через WellNuo API
  • Навигация: Expo Router + NavigationController.ts

Задачи

Phase 1: Критические исправления

  • @backend Заменить устаревшие credentials (anandk → robster) и вынести в .env

    • Файлы для замены:
      • services/api.ts:1508-1509 — основной API клиент
      • backend/src/services/mqtt.js:20-21 — MQTT сервис
      • WellNuoLite/app/(tabs)/chat.tsx:37-38 — текстовый чат
      • WellNuoLite/contexts/VoiceContext.tsx:27-28 — голосовой контекст
      • WellNuoLite/julia-agent/julia-ai/src/agent.py:31-32 — Python агент
      • wellnuo-debug/debug.html:728-733 — debug консоль
      • mqtt-test.js:15-16 — тестовый скрипт
    • Что сделать:
      1. Заменить anandk/anandk_8 на robster/rob2 везде
      2. Вынести в .env: LEGACY_API_USER=robster, LEGACY_API_PASSWORD=rob2
      3. Читать через process.env / Expo Constants
    • Готово когда: Все файлы используют robster, credentials в .env
  • @backend Fix displayName undefined в API response

    • Файл: services/api.ts:698-714
    • Что сделать: Добавить fallback в функцию getBeneficiariesFromResponse: displayName: item.customName || item.name || item.email || 'Unknown User'
    • Готово когда: BeneficiaryCard никогда не показывает undefined
  • @frontend BLE cleanup при logout

    • Файл: contexts/BLEContext.tsx
    • Переиспользует: services/ble/BLEManager.ts
    • Что сделать: В функции logout добавить вызов bleManager.disconnectAll() перед очисткой состояния
    • Готово когда: При logout все BLE соединения отключаются
  • @frontend Fix race condition с AbortController

    • Файл: app/(tabs)/index.tsx:207-248
    • Что сделать: В loadBeneficiaries создать AbortController, передать signal в API вызовы, отменить в useEffect cleanup
    • Готово когда: Быстрое переключение экранов не вызывает дублирующих запросов
  • @backend Обработка missing deploymentId

    • Файл: services/api.ts:1661-1665
    • Что сделать: Вместо return [] выбросить Error с кодом 'MISSING_DEPLOYMENT_ID' и message 'No deployment configured for user'
    • Готово когда: UI показывает понятное сообщение об ошибке

Phase 2: Безопасность

  • @frontend WiFi password в SecureStore

    • Файл: app/(tabs)/beneficiaries/[id]/setup-wifi.tsx
    • Переиспользует: services/storage.ts
    • Что сделать: Заменить AsyncStorage.setItem на storage.setItem для WiFi credentials, добавить ключ wifi_${beneficiaryId}
    • Готово когда: WiFi пароли сохраняются в зашифрованном виде
  • @backend Проверить equipmentStatus mapping

    • Файл: services/api.ts:113, services/NavigationController.ts:89-95
    • Что сделать: Убедиться что API возвращает точно 'demo', не 'demo_mode'. Добавить debug логи в BeneficiaryDetailController
    • Готово когда: Demo beneficiary корректно определяется в навигации

Phase 3: UX улучшения

  • @frontend Fix avatar caching после upload

    • Файл: app/(tabs)/profile/index.tsx
    • Переиспользует: services/api.ts метод getMe()
    • Что сделать: После успешного upload avatar вызвать api.getMe() и обновить state, не использовать локальный imageUri
    • Готово когда: Avatar обновляется сразу после upload
  • @frontend Retry button в error state

    • Файл: app/(tabs)/index.tsx:317-327
    • Переиспользует: components/ui/Button.tsx
    • Что сделать: В error блоке добавить <Button onPress={loadBeneficiaries}>Retry</Button> под текстом ошибки
    • Готово когда: При ошибке загрузки есть кнопка повтора
  • @frontend Улучшить serial validation

    • Файл: app/(auth)/activate.tsx:33-48
    • Что сделать: Добавить regex validation перед API вызовом, показывать ошибку "Invalid serial format" в real-time
    • Готово когда: Некорректный формат serial показывает ошибку до отправки
  • @frontend Role-based UI для Edit кнопки

    • Файл: app/(tabs)/index.tsx:133-135
    • Что сделать: Обернуть Edit кнопку в условие {beneficiary.role === 'custodian' && <TouchableOpacity>...}
    • Готово когда: Caretaker не видит кнопку Edit у beneficiary
  • @frontend Debouncing для refresh button

    • Файл: app/(tabs)/index.tsx:250-254
    • Что сделать: Добавить state isRefreshing, disable кнопку на 1 секунду после нажатия
    • Готово когда: Нельзя spam нажимать refresh

Phase 4: Очистка кода

  • @backend Удалить mock data из getBeneficiaries

    • Файл: services/api.ts:562-595
    • Что сделать: Удалить функцию getBeneficiaries полностью, оставить только getAllBeneficiaries
    • Готово когда: Функция не существует в коде
  • @backend Константы для magic numbers

    • Файл: services/api.ts:608-609
    • Что сделать: Создать const ONLINE_THRESHOLD_MS = 30 * 60 * 1000 в начале файла, использовать в коде
    • Готово когда: Нет magic numbers в логике online/offline
  • @backend Удалить console.logs

    • Файл: services/api.ts:1814-1895
    • Что сделать: Удалить все console.log в функции attachDeviceToBeneficiary
    • Готово когда: Нет console.log в production коде
  • @frontend Null safety в navigation

    • Файл: app/(tabs)/index.tsx:259
    • Что сделать: Добавить guard if (!beneficiary?.id) return; перед router.push
    • Готово когда: Нет crash при нажатии на beneficiary без ID
  • @frontend BLE scanning cleanup

    • Файл: services/ble/BLEManager.ts:64-80
    • Переиспользует: useFocusEffect из React Navigation
    • Что сделать: Добавить stopScan() в cleanup функцию всех экранов с BLE scanning
    • Готово когда: BLE scanning останавливается при уходе с экрана

Критерии готовности

  • Нет hardcoded credentials в коде
  • BLE соединения отключаются при logout
  • WiFi пароли зашифрованы
  • Нет race conditions при быстром переключении
  • Console.logs удалены
  • Avatar caching исправлен
  • Role-based доступ работает корректно

Статус

15 задач распределены между @backend (6) и @frontend (9). Готов к запуску после ответа на 3 вопроса выше.