WellNuo/PRD.md
Sergei 1dd7eb8289 Remove hardcoded credentials and use environment variables
- Remove hardcoded database credentials from all scripts
- Remove hardcoded Legacy API tokens from backend scripts
- Remove hardcoded MQTT credentials from mqtt-test.js
- Update backend/.env.example with DB_HOST, DB_USER, DB_PASSWORD, DB_NAME
- Update backend/.env.example with LEGACY_API_TOKEN and MQTT credentials
- Add dotenv config to all scripts requiring credentials
- Create comprehensive documentation:
  - scripts/README.md - Root scripts usage
  - backend/scripts/README.md - Backend scripts documentation
  - MQTT_TESTING.md - MQTT testing guide
  - SECURITY_CREDENTIALS_CLEANUP.md - Security changes summary

All scripts now read credentials from backend/.env instead of hardcoded values.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-29 12:13:32 -08:00

9.4 KiB
Raw 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 вопроса выше.