- 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.
9.4 KiB
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— тестовый скрипт
- Что сделать:
- Заменить
anandk/anandk_8наrobster/rob2везде - Вынести в
.env:LEGACY_API_USER=robster,LEGACY_API_PASSWORD=rob2 - Читать через
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 вопроса выше.