WellNuo/PRD.md
Sergei f0d39af6dc Add security audit report and PRD for custom names
AUDIT_REPORT.md:
- Full security audit (90 findings reviewed)
- 6 critical tasks for immediate fix
- 45 recommendations for later
- Complete RLS implementation plan (1-2 weeks)
- Doppler for secrets management
- Winston + Sentry for logging

PRD.md:
- Personalized beneficiary names feature
- custom_name in user_access table
- Backend + Frontend tasks

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

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-22 18:52:01 -08:00

151 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# PRD — Персонализированные имена beneficiaries
## Цель
Позволить каждому пользователю иметь своё персональное имя для каждого beneficiary. Custodian редактирует оригинальное имя (видно всем по умолчанию), остальные роли — своё `custom_name`.
## Контекст
Сейчас имя beneficiary хранится в `beneficiaries.name` и одинаково для всех пользователей. Нужно добавить возможность персонализации: каждый accessor (кроме custodian) может задать своё имя через `user_access.custom_name`.
## User Flow
### Flow 1: Custodian редактирует имя (оригинал)
| # | Актор | Действие | Система | Результат |
|---|-------|----------|---------|-----------|
| 1 | Custodian | Открывает список beneficiaries | GET `/me/beneficiaries` | Показывает `name` из `beneficiaries` таблицы |
| 2 | Custodian | Нажимает на beneficiary | GET `/me/beneficiaries/:id` | Открывает детали |
| 3 | Custodian | Нажимает "Edit" | — | Открывает Edit модал |
| 4 | Custodian | Меняет имя, нажимает "Save" | PATCH `/me/beneficiaries/:id` | Обновляет `beneficiaries.name` |
| 5 | System | — | Сохраняет в БД | Имя обновлено для ВСЕХ |
### Flow 2: Guardian/Caretaker редактирует имя (персональное)
| # | Актор | Действие | Система | Результат |
|---|-------|----------|---------|-----------|
| 1 | Caretaker | Открывает список beneficiaries | GET `/me/beneficiaries` | Показывает `custom_name` || `name` |
| 2 | Caretaker | Нажимает на beneficiary | GET `/me/beneficiaries/:id` | Открывает детали |
| 3 | Caretaker | Нажимает "Edit" | — | Открывает Edit модал |
| 4 | Caretaker | Меняет имя, нажимает "Save" | PATCH `/me/beneficiaries/:id` | Обновляет `user_access.custom_name` |
| 5 | System | — | Сохраняет в БД | Имя видно только ЭТОМУ пользователю |
### Flow 3: Отображение (все роли)
| # | Актор | Действие | Система | Результат |
|---|-------|----------|---------|-----------|
| 1 | User | Открывает Dashboard/список | GET `/me/beneficiaries` | — |
| 2 | System | — | Для каждого: `custom_name \|\| name` | Возвращает `displayName` |
| 3 | User | Видит список | — | Каждый beneficiary показан с персональным именем |
---
## Задачи
### Backend
- [x] **Migration: добавить custom_name в user_access**
- Путь: `backend/migrations/009_add_custom_name.sql`
- SQL: `ALTER TABLE user_access ADD COLUMN custom_name VARCHAR(200);`
- Индекс не нужен (поле не для поиска)
- [x] **API: изменить GET /me/beneficiaries (список)**
- Файл: `backend/src/routes/beneficiaries.js`
- В SELECT добавить `custom_name` из `user_access`
- В ответе добавить поле `displayName`: `custom_name || name`
- Также вернуть `originalName` (из `beneficiaries.name`) для UI
- [x] **API: изменить GET /me/beneficiaries/:id (детали)**
- Файл: `backend/src/routes/beneficiaries.js`
- Добавить `custom_name` из `user_access` в SELECT
- В ответе: `displayName`, `originalName`, `customName`
- [x] **API: изменить PATCH /me/beneficiaries/:id (обновление)**
- Файл: `backend/src/routes/beneficiaries.js`
- Логика:
- Если `role === 'custodian'` → обновить `beneficiaries.name`
- Иначе → обновить `user_access.custom_name`
- Добавить параметр `customName` в body
- [x] **Деплой миграции на сервер**
- SSH: `root@91.98.205.156`
- Путь: `/var/www/wellnuo-api/`
- Команда: `node run-migration.js`
- PM2: `pm2 restart wellnuo-api`
### Frontend
- [x] **Types: обновить Beneficiary interface**
- Файл: `types/index.ts` или где определён тип
- Добавить: `displayName?: string`, `originalName?: string`, `customName?: string`
- [x] **API service: обновить типы ответов**
- Файл: `services/api.ts`
- Обновить интерфейсы для beneficiary endpoints
- [x] **UI: список beneficiaries — показывать displayName**
- Файл: `app/(tabs)/index.tsx` или где рендерится список
- Заменить `beneficiary.name` на `beneficiary.displayName || beneficiary.name`
- [x] **UI: header в BeneficiaryDetail — показывать displayName**
- Файл: `app/(tabs)/beneficiaries/[id]/index.tsx`
- Строка 378: `{beneficiary.name}``{beneficiary.displayName || beneficiary.name}`
- [x] **UI: Edit модал — разная логика для ролей**
- Файл: `app/(tabs)/beneficiaries/[id]/index.tsx`
- Для custodian:
- Label: "Name"
- Редактирует `name` (оригинал)
- Для guardian/caretaker:
- Label: "Your name for [originalName]"
- Placeholder: originalName
- Редактирует `customName`
- При сохранении отправлять правильное поле
- [x] **UI: MockDashboard — показывать displayName**
- Файл: `components/MockDashboard.tsx`
- Передавать `displayName` вместо `name`
---
## Вне scope (не делаем)
- Синхронизация имён с голосовым AI (Ultravox) — будет отдельной задачей
- Интеграция с WellNuo Lite — пока не трогаем
- Миграция существующих данных — `custom_name` изначально NULL, fallback работает
---
## Чеклист верификации
### Функциональность
- [x] Custodian может редактировать оригинальное имя (`beneficiaries.name`)
- [x] Guardian/Caretaker могут редактировать своё персональное имя (`user_access.custom_name`)
- [x] Список beneficiaries показывает `displayName` (custom_name || name)
- [x] Header на детальной странице показывает `displayName`
- [x] Edit модал показывает разные labels для разных ролей
- [x] При первом открытии (custom_name = NULL) показывается оригинальное имя
### Backend
- [x] Миграция применена без ошибок
- [x] GET `/me/beneficiaries` возвращает `displayName`, `originalName`
- [x] GET `/me/beneficiaries/:id` возвращает `displayName`, `originalName`, `customName`
- [x] PATCH `/me/beneficiaries/:id` правильно определяет что обновлять по роли
### Код
- [x] Нет TypeScript ошибок (`npx tsc --noEmit`)
- [x] Backend работает без ошибок в логах PM2
- [x] Нет console.log в продакшн коде (кроме отладочных с `[DEBUG]`)
### UI/UX
- [x] Имена отображаются корректно во всех местах
- [x] Edit модал понятен для обоих типов редактирования
- [x] Нет визуальных багов
### Edge Cases
- [x] custom_name = NULL → показывается originalName
- [x] Пустая строка custom_name = "" → считается как NULL
- [x] Длинные имена не ломают UI
---
**Минимальный проходной балл: 8/10**