spec-feature 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/bin/viewer.html +295 -88
  2. package/package.json +1 -1
  3. package/spec/core/tasks.md +3 -0
  4. package/spec/features/create-task/plan.md +36 -0
  5. package/spec/features/create-task/spec.md +33 -0
  6. package/spec/features/create-task/tasks.md +33 -0
  7. package/spec/features/create-task/verify-report.md +29 -0
  8. package/spec/features/image-zoom/plan.md +237 -0
  9. package/spec/features/image-zoom/spec.md +135 -0
  10. package/spec/features/image-zoom/tasks.md +105 -0
  11. package/spec/features/invite-user/plan.md +32 -0
  12. package/spec/features/invite-user/spec.md +31 -0
  13. package/spec/features/invite-user/tasks.md +31 -0
  14. package/spec/features/invite-user/verify-report.md +33 -0
  15. package/spec/features/list-projects/plan.md +40 -0
  16. package/spec/features/list-projects/spec.md +33 -0
  17. package/spec/features/list-projects/tasks.md +35 -0
  18. package/spec/features/list-tasks/plan.md +41 -0
  19. package/spec/features/list-tasks/spec.md +34 -0
  20. package/spec/features/list-tasks/tasks.md +41 -0
  21. package/spec/features/metrics/plan.md +41 -0
  22. package/spec/features/metrics/spec.md +37 -0
  23. package/spec/features/metrics/tasks.md +31 -0
  24. package/spec/features/project-edit/plan.md +36 -0
  25. package/spec/features/project-edit/spec.md +32 -0
  26. package/spec/features/project-edit/tasks.md +36 -0
  27. package/spec/features/review-order/plan.md +37 -0
  28. package/spec/features/review-order/spec.md +28 -0
  29. package/spec/features/review-order/tasks.md +26 -0
  30. package/spec/features/review-order/verify-report.md +19 -0
  31. package/spec/features/supabase-init/plan.md +89 -0
  32. package/spec/features/supabase-init/spec.md +73 -0
  33. package/spec/features/supabase-init/tasks.md +51 -0
  34. package/spec/features/task-details/plan.md +38 -0
  35. package/spec/features/task-details/spec.md +33 -0
  36. package/spec/features/task-details/tasks.md +33 -0
  37. package/spec/features/telegram-initData/plan.md +46 -0
  38. package/spec/features/telegram-initData/spec.md +40 -0
  39. package/spec/features/telegram-initData/tasks.md +52 -0
  40. package/spec/features/tg-user-save-to-supabase/plan.md +137 -0
  41. package/spec/features/tg-user-save-to-supabase/spec.md +54 -0
  42. package/spec/features/tg-user-save-to-supabase/tasks.md +58 -0
  43. package/spec/features/user-profile/plan.md +39 -0
  44. package/spec/features/user-profile/spec.md +36 -0
  45. package/spec/features/user-profile/tasks.md +38 -0
  46. package/spec/features/user-profile-edit/plan.md +36 -0
  47. package/spec/features/user-profile-edit/spec.md +33 -0
  48. package/spec/features/user-profile-edit/tasks.md +33 -0
  49. package/spec/features/user-profile-edit/verify-report.md +26 -0
@@ -0,0 +1,46 @@
1
+ # Implementation Plan
2
+
3
+ **Plan:** На стороне клиента нужно отправлять на сервер telegram initData. На стороне сервера нужно добавить логику валидации initData. Результат выводить в console.log. Используется npm пакет `@telegram-apps/init-data-node`.
4
+
5
+ ## Data sources / schemas
6
+
7
+ - InitData получается на клиенте из `window.Telegram.WebApp.initData` в виде строки (search parameters format: `query_id=...&user=...&auth_date=...&hash=...`).
8
+ - Bot Token для валидации берется из переменных окружения сервера (например, `TELEGRAM_BOT_TOKEN`).
9
+ - Данные передаются через HTTP запрос с телом запроса (POST) или query параметром (GET). Рекомендуется использовать POST для безопасности.
10
+ - На сервере initData принимается как строка и передается в функцию `validate` из пакета `@telegram-apps/init-data-node`.
11
+ - Не требуется создание специальных схем базы данных или миграций для этой функциональности.
12
+
13
+ ## Contracts and interfaces
14
+
15
+ - Клиентский composable (расширение `useTelegram` или новый метод): метод `sendInitDataToServer()` или `validateInitData()`, который получает initData и отправляет на сервер.
16
+ - Серверный API endpoint: `POST /api/telegram/validate-init-data` (или `/api/validate-init-data`) принимает body с полем `initData: string`.
17
+ - Response формат: `{ success: boolean, message?: string }` или простой статус код без body (результат логируется на сервере).
18
+ - Интерфейс для обработки ошибок: на клиенте используется try-catch, на сервере ошибки валидации перехватываются и логируются через `console.log`.
19
+ - Валидация на сервере использует функцию `validate(initData: string, secretToken: string)` из пакета, которая может выбросить исключения типов: `ERR_AUTH_DATE_INVALID`, `ERR_HASH_INVALID`, `ERR_SIGN_INVALID`, `ERR_EXPIRED`.
20
+
21
+ ## Architecture / Components
22
+
23
+ - Клиентская часть: расширить существующий composable `useTelegram` в `composables/useTelegram.ts`, добавив метод для получения и отправки initData, или создать отдельный composable `useTelegramInitData` для изоляции логики.
24
+ - HTTP клиент: использовать встроенный `$fetch` из Nuxt или `useFetch` для отправки запроса на серверный endpoint.
25
+ - Серверная часть: создать файл `server/api/telegram/validate-init-data.post.ts` (или `.get.ts` в зависимости от выбранного метода) в формате Nuxt 3 server route.
26
+ - Валидация: установить пакет `@telegram-apps/init-data-node` и импортировать функцию `validate`. Вызвать её с initData и Bot Token из переменных окружения.
27
+ - Логирование: использовать `console.log` для вывода результата валидации. Формат лога: `[Telegram InitData Validation] Success/Error: <message>`.
28
+ - Обработка ошибок: обернуть вызов `validate` в try-catch блок, логировать тип ошибки через `isErrorOfType` если требуется, или просто выводить сообщение ошибки.
29
+ - Инициализация: вызывать отправку initData автоматически после инициализации Telegram WebApp (в плагине `plugins/telegram.client.ts` или в composable при первом обращении).
30
+
31
+ ## Risks
32
+
33
+ - Если initData недоступен на клиенте (приложение запущено не в Telegram), запрос не должен отправляться или должен обрабатываться корректно на сервере. Митигация: проверка наличия `window.Telegram?.WebApp?.initData` перед отправкой.
34
+ - Bot Token может быть скомпрометирован, если логировать полный токен. Митигация: логировать только результат валидации, не логировать сам токен.
35
+ - Ошибки валидации могут быть связаны с истечением срока действия initData (1 день по умолчанию). Митигация: логировать тип ошибки для диагностики.
36
+ - Пакет `@telegram-apps/init-data-node` может требовать определенную версию Node.js. Митигация: проверить совместимость при установке и использовать последнюю стабильную версию пакета.
37
+
38
+ ## Assumptions
39
+
40
+ - Bot Token будет предоставлен через переменные окружения и уже настроен для использования с ботом.
41
+ - Nuxt 3 поддерживает создание server routes в директории `server/api/` с автоматической регистрацией endpoints.
42
+ - Пакет `@telegram-apps/init-data-node` установится без проблем и совместим с текущей версией Node.js.
43
+ - Существующий composable `useTelegram` можно расширить без нарушения обратной совместимости, или создание нового composable не вызовет конфликтов.
44
+ - Инициализация Telegram WebApp происходит до момента отправки initData, поэтому данные будут доступны.
45
+ - Для упрощения, endpoint может не возвращать детальную информацию об ошибке клиенту, только логировать на сервере (если не требуется иное поведение для UX).
46
+
@@ -0,0 +1,40 @@
1
+ # Telegram InitData Validation
2
+
3
+ **Specification:** На стороне клиента нужно отправлять на сервер telegram initData. На стороне сервера нужно добавить логику валидации initData. Результат выводить в console.log. Используется npm пакет `@telegram-apps/init-data-node`.
4
+
5
+ ## User Stories
6
+
7
+ - Как разработчик приложения, я хочу автоматически отправлять telegram initData на сервер при инициализации приложения, чтобы сервер мог проверить подлинность запроса от Telegram.
8
+ - Как администратор системы, я хочу видеть результаты валидации initData в логах сервера, чтобы контролировать безопасность и выявлять потенциальные проблемы аутентификации.
9
+ - Как пользователь Telegram Mini App, я хочу быть уверенным, что мое приложение защищено от несанкционированного доступа через валидацию данных инициализации Telegram.
10
+
11
+ ## Main scenarios and rules
12
+
13
+ - При инициализации приложения на клиенте необходимо получить `initData` из объекта `window.Telegram.WebApp.initData` (или через Telegram WebApp SDK, если доступен).
14
+ - Клиент отправляет `initData` на серверный API endpoint через HTTP запрос (POST или GET) сразу после инициализации Telegram WebApp или при загрузке приложения.
15
+ - Серверный endpoint принимает `initData` в виде строки (raw search parameters format).
16
+ - На сервере используется функция `validate` из пакета `@telegram-apps/init-data-node` для проверки подписи и валидности данных.
17
+ - В качестве секретного токена для валидации используется Bot Token от Telegram Bot Father (или его хешированная версия).
18
+ - Результат валидации (успех или ошибка) выводится в `console.log` на сервере с понятным сообщением.
19
+ - При ошибке валидации функция `validate` выбрасывает исключение с типом ошибки (`ERR_AUTH_DATE_INVALID`, `ERR_HASH_INVALID`, `ERR_SIGN_INVALID`, `ERR_EXPIRED`), которую нужно обработать и залогировать.
20
+ - По умолчанию функция `validate` проверяет срок действия initData (expiration), который установлен на 1 день (86400 секунд). Это поведение сохраняется без изменений.
21
+ - Если initData отсутствует на клиенте (приложение запущено вне Telegram), клиент должен корректно обработать эту ситуацию и не отправлять запрос на сервер или отправить с соответствующим флагом.
22
+
23
+ ## Non-functional requirements
24
+
25
+ - Серверная валидация должна выполняться синхронно и быстро (не более 50ms для стандартной валидации).
26
+ - Логирование результатов валидации должно быть информативным и не содержать чувствительных данных (например, не логировать полный Bot Token).
27
+ - Пакет `@telegram-apps/init-data-node` должен быть установлен только в зависимостях серверной части (devDependencies или dependencies, в зависимости от того, используется ли он в production).
28
+ - Код должен быть типизирован с использованием TypeScript, ошибки валидации должны быть корректно обработаны через try-catch блоки.
29
+ - API endpoint должен возвращать стандартный HTTP статус код (например, 200 при успехе, 400 при ошибке валидации).
30
+ - Решение должно следовать принципам KISS и не содержать избыточной логики или сложных абстракций.
31
+
32
+ ## Assumptions
33
+
34
+ - Bot Token будет храниться в переменных окружения сервера (например, `TELEGRAM_BOT_TOKEN` для Nuxt).
35
+ - Приложение работает в контексте Nuxt 3, серверные роуты создаются в директории `server/api/`.
36
+ - Пакет `@telegram-apps/init-data-node` совместим с Node.js версией, используемой в проекте.
37
+ - Telegram WebApp SDK уже загружен и доступен в приложении через существующий composable `useTelegram`.
38
+ - InitData доступен только на клиенте, поэтому его получение должно происходить в клиентском коде.
39
+ - Если требуется отключить проверку expiration, это будет явно указано в отдельном запросе (по умолчанию проверка включена).
40
+
@@ -0,0 +1,52 @@
1
+ # Orders
2
+
3
+ **Context:** На стороне клиента нужно отправлять на сервер telegram initData. На стороне сервера нужно добавить логику валидации initData. Результат выводить в console.log. Используется npm пакет `@telegram-apps/init-data-node`.
4
+
5
+ ## Main directions
6
+
7
+ ### Клиентская часть: получение и отправка initData
8
+
9
+ - [ ] Установить пакет `@telegram-apps/init-data-node` в зависимости проекта (через npm/pnpm/yarn).
10
+ - [ ] Расширить composable `useTelegram` или создать новый `useTelegramInitData`, добавив метод для получения `initData` из `window.Telegram.WebApp.initData`.
11
+ - [ ] Реализовать метод отправки initData на сервер через `$fetch` или `useFetch` на endpoint `/api/telegram/validate-init-data` (POST запрос с телом `{ initData: string }`).
12
+ - [ ] Добавить проверку наличия `initData` перед отправкой (если приложение запущено вне Telegram, не отправлять запрос).
13
+ - [ ] Интегрировать вызов метода отправки initData в плагин `plugins/telegram.client.ts` после успешной инициализации Telegram WebApp или вызвать при первом обращении к composable.
14
+
15
+ **Проверка:** Убедиться, что initData корректно получается на клиенте и отправляется на сервер при наличии Telegram WebApp. Проверить в DevTools Network tab, что запрос отправляется с корректными данными.
16
+
17
+ ### Серверная часть: валидация initData
18
+
19
+ - [ ] Создать серверный route `server/api/telegram/validate-init-data.post.ts` (или использовать GET метод, если требуется).
20
+ - [ ] В handler endpoint получить `initData` из body запроса (или query параметра, если используется GET).
21
+ - [ ] Получить Bot Token из переменных окружения (например, `process.env.TELEGRAM_BOT_TOKEN`).
22
+ - [ ] Импортировать функцию `validate` из пакета `@telegram-apps/init-data-node`.
23
+ - [ ] Обернуть вызов `validate(initData, botToken)` в try-catch блок.
24
+ - [ ] При успешной валидации вывести в `console.log` сообщение: `[Telegram InitData Validation] Success: InitData is valid`.
25
+ - [ ] При ошибке валидации перехватить исключение, определить тип ошибки (если нужно) и вывести в `console.log`: `[Telegram InitData Validation] Error: <error message>` или `[Telegram InitData Validation] Error: <error type> - <error message>`.
26
+ - [ ] Вернуть HTTP ответ клиенту (например, статус 200 при успехе, 400 при ошибке валидации, 500 при других ошибках).
27
+
28
+ **Проверка:** Проверить, что валидация работает корректно с валидным initData и корректно обрабатывает ошибки (неверная подпись, истекший срок и т.д.). Убедиться, что результаты выводятся в консоль сервера.
29
+
30
+ ### Конфигурация и окружение
31
+
32
+ - [ ] Добавить переменную окружения для Bot Token в документацию или .env.example файл (если используется).
33
+ - [ ] Убедиться, что Bot Token корректно читается из переменных окружения на сервере.
34
+
35
+ **Проверка:** Проверить, что приложение работает с корректным Bot Token и корректно обрабатывает отсутствие токена.
36
+
37
+ ## Supporting orders
38
+
39
+ - [ ] Документация: обновить README или добавить комментарии в код, описывающие процесс валидации initData и требования к переменным окружения.
40
+ - [ ] Observability: логирование через `console.log` уже реализовано в основных задачах. При необходимости можно расширить формат логов для лучшей диагностики.
41
+ - [ ] Code review and PR: подготовить изменения для review, убедиться что код соответствует стилю проекта, типизирован через TypeScript, не содержит чувствительных данных в логах.
42
+
43
+ ## Definition of Done
44
+
45
+ - [ ] Все задачи выполнены и протестированы.
46
+ - [ ] InitData корректно получается на клиенте и отправляется на сервер.
47
+ - [ ] Серверная валидация работает с пакетом `@telegram-apps/init-data-node` и выводит результаты в `console.log`.
48
+ - [ ] Ошибки валидации корректно обрабатываются и логируются.
49
+ - [ ] Код типизирован, проходит линтер без ошибок.
50
+ - [ ] Bot Token корректно читается из переменных окружения.
51
+ - [ ] `/spec/core/verify.md` выполнен после завершения всех задач для проверки списка задач.
52
+
@@ -0,0 +1,137 @@
1
+ # Implementation Plan
2
+
3
+ **Plan:** У нас есть серверная функция, которая принимает initData и валидирует его. Если валидация проходит успешно, то нужно парсить и сохранять пользователя в supabase в таблицу users используя server/utils/supabase.ts. Но нужно проверить, что если такой пользователь сохранен уже, то ничего не делать, не сохранять.
4
+
5
+ ## Data sources / schemas
6
+
7
+ ### Структура данных initData
8
+
9
+ - `initData` представляет собой строку в формате URL query parameters (например, `user=%7B%22id%22%3A123%2C%22first_name%22%3A%22John%22%7D&auth_date=1234567890&hash=...`).
10
+ - Параметр `user` содержит URL-encoded JSON-строку с данными пользователя Telegram.
11
+ - Объект пользователя в декодированном виде содержит поля: `id` (number), `first_name` (string), `last_name?` (string, опционально), `username?` (string, опционально), `language_code?` (string, опционально), `is_premium?` (boolean, опционально), `photo_url?` (string, опционально).
12
+
13
+ ### Схема таблицы users в Supabase
14
+
15
+ - Предполагается, что таблица `users` существует и имеет следующие колонки:
16
+ - `telegram_id` (bigint, primary key или unique) - уникальный идентификатор пользователя из Telegram
17
+ - `first_name` (text, nullable) - имя пользователя
18
+ - `last_name` (text, nullable) - фамилия пользователя
19
+ - `username` (text, nullable) - username пользователя в Telegram
20
+ - `language_code` (text, nullable) - код языка пользователя
21
+ - `is_premium` (boolean, nullable) - флаг наличия Telegram Premium
22
+ - `photo_url` (text, nullable) - URL фотографии профиля
23
+ - `created_at` (timestamp, default now()) - дата создания записи
24
+ - `updated_at` (timestamp, default now()) - дата последнего обновления
25
+
26
+ - Для проверки существования пользователя используется колонка `telegram_id` с уникальным индексом.
27
+
28
+ ### Миграции базы данных
29
+
30
+ - Не требуется создание новых миграций, так как таблица `users` предположительно уже существует.
31
+ - Если таблица не существует, необходимо создать миграцию для создания таблицы и необходимых индексов.
32
+
33
+ ## Contracts and interfaces
34
+
35
+ ### Изменения в существующем API endpoint
36
+
37
+ - **Endpoint**: `POST /api/telegram/validate-init-data`
38
+ - **Request**: без изменений - `{ initData: string }`
39
+ - **Response**: без изменений - `{ success: boolean }` или ошибка валидации
40
+ - **Побочные эффекты**: после успешной валидации выполняется попытка сохранения пользователя в Supabase (не влияет на ответ API)
41
+
42
+ ### Интерфейс функции парсинга initData
43
+
44
+ ```typescript
45
+ interface TelegramUser {
46
+ id: number
47
+ first_name: string
48
+ last_name?: string
49
+ username?: string
50
+ language_code?: string
51
+ is_premium?: boolean
52
+ photo_url?: string
53
+ }
54
+
55
+ function parseInitDataUser(initData: string): TelegramUser | null
56
+ ```
57
+
58
+ - Принимает строку `initData`, парсит query-параметры, извлекает параметр `user`, декодирует URL-encoding и парсит JSON.
59
+ - Возвращает объект пользователя или `null`, если парсинг не удался.
60
+
61
+ ### Интерфейс функции сохранения пользователя
62
+
63
+ ```typescript
64
+ async function saveUserToSupabase(user: TelegramUser): Promise<void>
65
+ ```
66
+
67
+ - Принимает объект пользователя Telegram.
68
+ - Получает клиент Supabase через `getSupabaseClient()`.
69
+ - Проверяет существование пользователя по `telegram_id`.
70
+ - Если пользователь не существует, выполняет вставку новой записи.
71
+ - Логирует все операции и ошибки.
72
+
73
+ ### Обработка ошибок
74
+
75
+ - Ошибки парсинга и сохранения логируются, но не возвращаются клиенту.
76
+ - Используется префикс `[Telegram User Save]` для всех логов, связанных с сохранением пользователя.
77
+
78
+ ## Architecture / Components
79
+
80
+ ### Компоненты реализации
81
+
82
+ 1. **Функция парсинга initData** (`parseInitDataUser`)
83
+ - Разбор query-параметров из строки `initData`
84
+ - Декодирование URL-encoded параметра `user`
85
+ - Парсинг JSON-строки в объект пользователя
86
+ - Обработка ошибок парсинга
87
+
88
+ 2. **Функция сохранения пользователя** (`saveUserToSupabase`)
89
+ - Получение клиента Supabase через `getSupabaseClient()`
90
+ - Проверка существования пользователя через `select().eq('telegram_id', user.id).single()` - значение поля `id` из объекта пользователя используется для сравнения с полем `telegram_id` в таблице `users`
91
+ - Обработка результата проверки (найден/не найден)
92
+ - Вставка новой записи через `insert()` если пользователь не найден, где поле `id` из объекта пользователя маппится в поле `telegram_id` в таблице `users`
93
+ - Логирование всех операций
94
+
95
+ 3. **Интеграция в существующий endpoint**
96
+ - Модификация `server/api/telegram/validate-init-data.post.ts`
97
+ - Добавление вызова `saveUserToSupabase` после успешной валидации
98
+ - Сохранение текущего поведения API (не нарушение контракта)
99
+
100
+ ### Зависимости
101
+
102
+ - `@supabase/supabase-js` - уже используется через `server/utils/supabase.ts`
103
+ - `@telegram-apps/init-data-node` - уже используется для валидации
104
+ - Встроенные модули Node.js: `URLSearchParams`, `decodeURIComponent`, `JSON.parse`
105
+
106
+ ### Обработка ошибок Supabase
107
+
108
+ - При проверке существования: ошибка с кодом `PGRST116` (или аналогичная) означает, что запись не найдена - это нормальная ситуация.
109
+ - Другие ошибки при проверке логируются как предупреждения, но не прерывают выполнение.
110
+ - Ошибки при вставке логируются, но не возвращаются клиенту.
111
+
112
+ ## Risks
113
+
114
+ 1. **Риск нарушения существующего контракта API**
115
+ - **Митигация**: Все операции сохранения выполняются асинхронно и не влияют на возвращаемый ответ API. Ошибки сохранения логируются, но не прерывают основной поток.
116
+
117
+ 2. **Риск race condition при одновременных запросах**
118
+ - **Описание**: Если два запроса с одинаковым пользователем приходят одновременно, оба могут не найти пользователя и попытаться вставить запись.
119
+ - **Митигация**: Использование уникального индекса на `telegram_id` в таблице `users` обеспечит, что вторая вставка завершится с ошибкой уникальности, которую можно обработать корректно.
120
+
121
+ 3. **Риск отсутствия таблицы users или неправильной схемы**
122
+ - **Митигация**: Проверка наличия таблицы перед реализацией, обработка ошибок Supabase с информативными сообщениями в логах.
123
+
124
+ 4. **Риск некорректного формата initData**
125
+ - **Митигация**: Парсинг выполняется в try-catch блоке, все ошибки логируются. Если парсинг не удался, функция сохранения просто не выполняется, но это не влияет на валидацию.
126
+
127
+ 5. **Риск снижения производительности endpoint**
128
+ - **Митигация**: Операции с базой данных выполняются асинхронно. Можно добавить таймаут для операций с Supabase, чтобы не блокировать ответ слишком долго.
129
+
130
+ ## Assumptions
131
+
132
+ - Таблица `users` существует в Supabase с необходимой структурой колонок и уникальным индексом на `telegram_id`.
133
+ - Клиент Supabase из `server/utils/supabase.ts` имеет права на чтение и запись в таблицу `users`.
134
+ - Формат `initData` соответствует стандартному формату Telegram WebApp initData с параметром `user` в виде URL-encoded JSON-строки.
135
+ - Пакет `@telegram-apps/init-data-node` не предоставляет готовой функции парсинга, поэтому парсинг выполняется вручную.
136
+ - Текущий API endpoint должен продолжать работать как раньше, сохранение пользователя - это внутренняя операция, не влияющая на публичный контракт API.
137
+
@@ -0,0 +1,54 @@
1
+ # Telegram User Save to Supabase
2
+
3
+ **Specification:** У нас есть серверная функция, которая принимает initData и валидирует его. Если валидация проходит успешно, то нужно парсить и сохранять пользователя в supabase в таблицу users используя server/utils/supabase.ts. Но нужно проверить, что если такой пользователь сохранен уже, то ничего не делать, не сохранять.
4
+
5
+ ## User Stories
6
+
7
+ - Как система, я хочу автоматически сохранять данные пользователя Telegram в базу данных после успешной валидации initData, чтобы обеспечить персистентность информации о пользователях и возможность их дальнейшего использования в приложении.
8
+ - Как администратор системы, я хочу, чтобы система не создавала дублирующие записи пользователей в базе данных, чтобы избежать избыточности данных и потенциальных проблем с целостностью данных.
9
+ - Как пользователь Telegram Mini App, я хочу, чтобы мои данные были корректно сохранены в системе после первой успешной валидации, чтобы система могла идентифицировать меня в последующих сессиях.
10
+
11
+ ## Main scenarios and rules
12
+
13
+ ### Основной сценарий успешного сохранения
14
+
15
+ 1. Серверная функция `POST /api/telegram/validate-init-data` получает `initData` в теле запроса.
16
+ 2. Выполняется валидация `initData` с помощью функции `validate` из пакета `@telegram-apps/init-data-node`.
17
+ 3. Если валидация успешна, выполняется парсинг `initData` для извлечения данных пользователя.
18
+ 4. Из парсированного `initData` извлекается объект пользователя (обычно находится в параметре `user` в формате JSON-строки).
19
+ 5. Получается клиент Supabase через функцию `getSupabaseClient()` из `server/utils/supabase.ts`.
20
+ 6. Выполняется проверка существования пользователя в таблице `users` по полю `telegram_id`, используя значение поля `id` из объекта пользователя из `initData`.
21
+ 7. Если пользователь не существует, выполняется вставка новой записи в таблицу `users` с данными из `initData`, где значение поля `id` из объекта пользователя сохраняется в поле `telegram_id`.
22
+ 8. Если пользователь уже существует, операция вставки не выполняется, функция завершается без ошибки.
23
+
24
+ ### Правила обработки ошибок
25
+
26
+ - Если валидация `initData` не прошла, процесс сохранения пользователя не запускается, возвращается ошибка валидации как обычно.
27
+ - Если парсинг `initData` не удался (отсутствует поле `user` или оно некорректно), логируется предупреждение, но процесс не прерывается с ошибкой, чтобы не нарушать текущую работу валидации.
28
+ - Если проверка существования пользователя в Supabase завершилась с ошибкой (кроме случая отсутствия записи), ошибка логируется, но не прерывает основной поток валидации.
29
+ - Если вставка нового пользователя завершилась с ошибкой, ошибка логируется, но не возвращается клиенту, чтобы не нарушать текущий контракт API валидации.
30
+
31
+ ### Правила определения существования пользователя
32
+
33
+ - Проверка выполняется по полю `telegram_id` в таблице `users` в Supabase.
34
+ - Значение для проверки берется из поля `id` объекта пользователя, извлеченного из `initData`.
35
+ - Используется метод `select` с фильтром `eq('telegram_id', user.id)` и `single()` для поиска пользователя в таблице `users`.
36
+ - Если запись найдена, считается, что пользователь уже существует, и операция сохранения не выполняется.
37
+ - Если запись не найдена (ошибка с кодом `PGRST116` или аналогичным), считается, что пользователь не существует и требуется вставка новой записи.
38
+
39
+ ## Non-functional requirements
40
+
41
+ - **Производительность**: Операции проверки существования и вставки пользователя должны выполняться асинхронно и не блокировать основной поток обработки запроса валидации более чем на 200-300ms.
42
+ - **Безопасность**: Данные пользователя из `initData` должны сохраняться только после успешной валидации подписи. Никакие данные не должны сохраняться, если валидация не прошла.
43
+ - **Надежность**: Операции с базой данных должны быть устойчивы к временным сбоям. Ошибки при сохранении пользователя не должны нарушать основной функционал валидации.
44
+ - **Логирование**: Все операции сохранения пользователя должны логироваться с соответствующими префиксами для удобства отладки и мониторинга.
45
+ - **Совместимость**: Реализация должна быть совместима с существующим API endpoint и не нарушать текущие контракты с клиентской частью.
46
+
47
+ ## Assumptions
48
+
49
+ - Таблица `users` уже существует в Supabase и имеет необходимые колонки для хранения данных пользователя Telegram, включая обязательное поле `telegram_id` для идентификации пользователя.
50
+ - Поле `telegram_id` в таблице `users` является уникальным и используется для проверки существования пользователя. Значение `telegram_id` берется из поля `id` объекта пользователя из `initData`.
51
+ - Объект пользователя в `initData` доступен в параметре `user` в формате JSON-строки, который может быть распарсен стандартными средствами JavaScript/TypeScript.
52
+ - Пакет `@telegram-apps/init-data-node` не предоставляет готовой функции парсинга `initData`, поэтому парсинг будет выполняться вручную через разбор query-параметров и декодирование JSON.
53
+ - Клиент Supabase настроен корректно и имеет необходимые права доступа для чтения и записи в таблицу `users`.
54
+
@@ -0,0 +1,58 @@
1
+ # Orders
2
+
3
+ **Context:** У нас есть серверная функция, которая принимает initData и валидирует его. Если валидация проходит успешно, то нужно парсить и сохранять пользователя в supabase в таблицу users используя server/utils/supabase.ts. Но нужно проверить, что если такой пользователь сохранен уже, то ничего не делать, не сохранять.
4
+
5
+ ## Main directions
6
+
7
+ ### Серверная часть: парсинг initData и извлечение данных пользователя
8
+
9
+ - [x] Создать утилитарную функцию `parseInitDataUser(initData: string)` для парсинга initData и извлечения объекта пользователя.
10
+ - [x] Реализовать разбор query-параметров из строки initData с использованием `URLSearchParams` или ручного парсинга.
11
+ - [x] Реализовать декодирование URL-encoded параметра `user` с использованием `decodeURIComponent`.
12
+ - [x] Реализовать парсинг JSON-строки в объект пользователя с обработкой ошибок парсинга.
13
+ - [x] Добавить TypeScript интерфейс `TelegramUser` для типизации объекта пользователя из initData.
14
+ - [x] Добавить логирование ошибок парсинга с префиксом `[Telegram User Save]`.
15
+
16
+ **Проверка:** Убедиться, что функция корректно парсит различные форматы initData, обрабатывает отсутствие параметра `user`, корректно декодирует URL-encoded строки и парсит JSON. Протестировать с валидными и невалидными входными данными.
17
+
18
+ ### Серверная часть: сохранение пользователя в Supabase
19
+
20
+ - [x] Создать функцию `saveUserToSupabase(user: TelegramUser)` для сохранения пользователя в таблицу `users`.
21
+ - [x] Реализовать получение клиента Supabase через `getSupabaseClient()` из `server/utils/supabase.ts`.
22
+ - [x] Реализовать проверку существования пользователя через запрос `select().eq('telegram_id', user.id).single()` к таблице `users`, где значение поля `id` из объекта пользователя сравнивается с полем `telegram_id` в таблице.
23
+ - [x] Реализовать обработку результата проверки: если пользователь найден, завершить функцию без действий.
24
+ - [x] Реализовать обработку ошибки `PGRST116` (или аналогичной) при проверке как нормальной ситуации (пользователь не найден).
25
+ - [x] Реализовать вставку новой записи через `insert()` с данными пользователя, если пользователь не найден.
26
+ - [x] Реализовать маппинг полей из объекта `TelegramUser` в колонки таблицы `users`, где поле `id` из объекта пользователя сохраняется в поле `telegram_id` таблицы, а остальные поля маппятся напрямую (first_name, last_name, username, language_code, is_premium, photo_url).
27
+ - [x] Добавить логирование всех операций (проверка существования, вставка) с префиксом `[Telegram User Save]`.
28
+ - [x] Добавить обработку ошибок вставки с логированием, но без прерывания выполнения.
29
+
30
+ **Проверка:** Убедиться, что функция корректно проверяет существование пользователя, не создает дублирующие записи, корректно сохраняет данные при первом сохранении. Протестировать сценарии: новый пользователь, существующий пользователь, ошибки Supabase.
31
+
32
+ ### Серверная часть: интеграция в endpoint валидации
33
+
34
+ - [x] Модифицировать файл `server/api/telegram/validate-init-data.post.ts` для добавления сохранения пользователя.
35
+ - [x] Добавить вызов `parseInitDataUser()` после успешной валидации initData.
36
+ - [x] Добавить проверку результата парсинга: если парсинг успешен, вызвать `saveUserToSupabase()`.
37
+ - [x] Обеспечить, что все операции сохранения выполняются асинхронно и не блокируют возврат ответа API.
38
+ - [x] Обеспечить, что ошибки парсинга и сохранения не влияют на успешный ответ валидации.
39
+ - [x] Добавить обработку ошибок с логированием, но без изменения формата ответа API.
40
+
41
+ **Проверка:** Убедиться, что endpoint продолжает возвращать `{ success: true }` при успешной валидации, независимо от результатов сохранения пользователя. Проверить, что валидация работает как раньше, сохранение пользователя происходит в фоне. Протестировать сценарии: успешная валидация с новым пользователем, успешная валидация с существующим пользователем, успешная валидация без данных пользователя в initData.
42
+
43
+ ## Supporting orders
44
+
45
+ - [x] Документация: добавить JSDoc комментарии к функциям `parseInitDataUser` и `saveUserToSupabase` с описанием параметров, возвращаемых значений и примеров использования.
46
+ - [x] Observability: убедиться, что все операции логируются с понятными префиксами для возможности мониторинга и отладки. Добавить логирование успешных операций сохранения пользователя.
47
+ - [x] Code review and PR: подготовить изменения для review, убедиться, что код соответствует стандартам проекта, не нарушает существующие контракты API.
48
+
49
+ ## Definition of Done
50
+
51
+ - [ ] Все задачи выполнены и протестированы.
52
+ - [ ] Функция парсинга initData корректно извлекает данные пользователя из различных форматов initData.
53
+ - [ ] Функция сохранения пользователя корректно проверяет существование и не создает дублирующие записи.
54
+ - [ ] Интеграция в endpoint не нарушает существующий контракт API.
55
+ - [ ] Все ошибки обрабатываются корректно с логированием, не прерывая основной поток валидации.
56
+ - [ ] Код покрыт комментариями и готов для code review.
57
+ - [ ] `/spec/core/verify.md` выполняется после завершения всех задач для верификации списка задач.
58
+
@@ -0,0 +1,39 @@
1
+ # Implementation Plan
2
+
3
+ **Plan:** Реализовать динамическую страницу `/colleagues/[id]` для публичного профиля коллеги, обеспечить передачу идентификатора из списка команды, подготовить источник данных и компоненты для отображения контактов, активных задач и истории, сохранив фирменные стили приложения.
4
+
5
+ ## Data sources / schemas
6
+
7
+ - Создать новый модуль `data/teamMembers.ts` (или расширить текущий `data/team.ts`, если допустимо) с полной моделью `TeamMemberProfile`, содержащей поля: `id`, `name`, `role`, `avatarUrl`, `bio`/`department`, `contacts` (telegram, phone, email), `orders` (массив с `id`, `title`, `status`, `dueDate`), `history` (массив завершённых задач). Определить перечисление статусов (`in-progress`, `completed`, `overdue`).
8
+ - Добавить вспомогательные функции/композабл `useTeamMemberProfile(id)` для получения данных по идентификатору, с мемоизацией и обработкой отсутствующих записей. Возвращать состояния: `profile`, `isLoading`, `error`.
9
+ - Обеспечить совместимость с существующим `useProjectTeam` — данные в списке коллег должны ссылаться на ту же `id`. При необходимости расширить `ProjectTeamMember` дополнительными полями или добавить маппинг `id -> profile`.
10
+
11
+ ## Contracts and interfaces
12
+
13
+ - Определить интерфейс `OrderItem` с полями `title`, `status`, `dueDate`, `description?`. Для истории добавить `completedAt`.
14
+ - Фильтры статусов реализовать как массив объектов `{ key, label }`, где `key` соответствует перечислению статусов. Контролировать активный фильтр через состояние компонента.
15
+ - Навигация: карточка `TeamMemberCard` получает обработчик `@click` и `aria-label` для открытия `/colleagues/{id}` (через `router.push`). На странице профиля реализовать кнопку «Назад», использующую `router.back()` и fallback на `/projects/{projectId}/team` (если проект передал `from` в query).
16
+ - Контактные элементы используют схемы ссылок: `href="https://t.me/${username}"`, `href="tel:${phone}"`, `href="mailto:${email}"`. Добавить атрибуты `rel="noopener noreferrer"` для внешних ссылок.
17
+
18
+ ## Architecture / Components
19
+
20
+ - Структура страницы: Nuxt dynamic route `pages/colleagues/[id].vue`. Использовать `useRoute` для получения `id`, `useHead` для метаданных и тематического оформления, `onMounted`/async setup для загрузки профиля.
21
+ - Верхний блок: `AppBar`/`header` с кнопкой назад, заголовком «Профиль коллеги», опциональной кнопкой «Написать» (если нужно). Применить существующие utility-классы (`bg-background-light`, `dark:bg-background-dark`, `font-display`).
22
+ - Основной контент: компонент `ProfileSummary` (локальный или общий) с аватаром (через background-image или `<img>`), именем, ролью, дополнительным текстом. Ниже — карточка «Контакты» (flex-столбец), секция «Назначенные задачи» с horizontal pills фильтров и списком карточек, секция «История задач» с пониженной непрозрачностью.
23
+ - Для задач реализовать вычисляемые списки по активному фильтру. Отображать индикаторы статуса (точка соответствующего цвета). При отсутствии данных показывать пустой state-компонент, используя существующие стили (`bg-white dark:bg-[#1C2431]`, текстовые цвета `text-gray-500`/`dark:text-[#9da6b9]`).
24
+ - Предусмотреть состояние загрузки (skeleton блоки или спиннер) и ошибку (карточка с сообщением и кнопкой возврата).
25
+
26
+ ## Risks
27
+
28
+ - Несоответствие идентификаторов между текущим списком команды и новым профилем может привести к ошибкам 404 — нужно синхронизировать источники данных.
29
+ - Увеличение размера мок-данных (контакты, история задач) может затруднить поддержку — стоит структурировать данные и вынести в отдельный файл.
30
+ - Возможны расхождения в палитре, если примерные цвета из макета не совпадают с палитрой проекта — необходимо проверять существующие переменные.
31
+ - При отсутствии серверного API логика загрузки сведена к mock-данным; потребуется последующая адаптация, которую нужно предусмотреть в архитектуре (добавить уровень абстракции через composable).
32
+
33
+ ## Assumptions
34
+
35
+ - Список коллег будет допилен, чтобы передавать query `from` с путём возврата; иначе кнопка «Назад» должна вести на список команды по умолчанию.
36
+ - Пользователь всегда имеет хотя бы один контактный канал; если нет — выводить placeholder «нет данных».
37
+ - Состояние загрузки можно эмулировать таймаутом или сразу отдавать данные — уточнить, нужен ли искусственный loader.
38
+ - Статусы задач ограничиваются тремя основными значениями; дополнительная детализация не требуется.
39
+ - Дизайн примера служит ориентиром по структуре, но все цвета и отступы берём из текущего проекта.
@@ -0,0 +1,36 @@
1
+ # Публичный профиль коллеги
2
+
3
+ **Specification:** Создать динамическую страницу публичного профиля участника команды, доступную из списка коллег, которая показывает основную информацию о человеке, контакты и актуальные задачи, оформленные в общих стилях приложения.
4
+
5
+ ## User Stories
6
+
7
+ - Как руководитель проекта я открываю профиль коллеги из списка команды, чтобы увидеть, чем он занимается и как с ним связаться.
8
+ - Как HR-специалист я просматриваю карточку сотрудника, чтобы быстро найти контактные данные и актуальную занятость.
9
+ - Как участник команды я хочу видеть историю выполненных задач коллеги, чтобы понимать контекст совместной работы.
10
+
11
+ ## Main scenarios and rules
12
+
13
+ 1. При клике по карточке участника в списке коллег открывается динамический маршрут с параметром `id`, где загружается профиль.
14
+ 2. Страница выводит аватар, имя, роль, краткое описание/команду и разделы: контакты, текущие задачи с индикаторами статуса и история выполненных задач.
15
+ 3. Данные профиля берутся из локального источника (mock) или API; при отсутствии записи отображается состояние «пользователь не найден» с кнопкой возврата.
16
+ 4. Контактные способы включают Telegram/мессенджер, телефон и email, каждый с подписью и возможностью скопировать/перейти (по клику открывается соответствующая схема `tel:`, `mailto:` или внешняя ссылка).
17
+ 5. Блок задач поддерживает фильтры по статусу (например, «В работе», «Завершено», «Просрочено») и отображает карточки с названием и датой; при отсутствии задач показывается дружелюбный пустой экран.
18
+ 6. История задач отображается в отдельной секции со слабым акцентом (пониженная непрозрачность); если данных нет — выводится пустое состояние.
19
+ 7. Страница использует общие темы (светлая/тёмная), шрифты Inter, палитру и компоненты кнопок, карточек, фильтров.
20
+ 8. Навигация предусматривает фиксированную верхнюю панель с кнопкой назад, ведущей на предыдущий экран или список коллег.
21
+
22
+ ## Non-functional requirements
23
+
24
+ - Адаптивность: корректное отображение на мобильных устройствах (основной сценарий), планшетах и десктопе.
25
+ - Производительность: загрузка данных профиля не должна блокировать интерфейс; показывать skeleton/loader при задержке.
26
+ - Доступность: семантические элементы, aria-метки для кнопок возврата и фильтров, достаточный контраст в обеих темах.
27
+ - Локализация: все тексты на русском языке с возможностью вынести в словари при будущей интернационализации.
28
+ - Повторное использование стилей: использовать существующие utility-классы Tailwind и компоненты для единообразия.
29
+
30
+ ## Assumptions
31
+
32
+ - Список коллег уже существует и передаёт идентификатор пользователя при переходе; если нет — нужно расширить текущую карточку участника.
33
+ - Контактные данные и информация о задачах будут добавлены в mock-данные (например, `data/teamMembers.ts`), которые в дальнейшем заменят API.
34
+ - История задач может быть ограничена несколькими последними записями; детальной расшифровки статусов не требуется.
35
+ - Нет необходимости в редактировании профиля в рамках этой итерации; страница только для просмотра.
36
+ - Нужно уточнить, какие статусы задач считать основными, если текущие статусы в проекте отличаются от примера.
@@ -0,0 +1,38 @@
1
+ # Orders
2
+
3
+ **Context:** Создать динамическую страницу публичного профиля коллеги с контактами и задачами, доступную из списка команды, соблюдая стили приложения.
4
+
5
+ ## Main directions
6
+
7
+ ### Данные и состояния
8
+
9
+ - [ ] Подготовить расширенный источник данных (`data/teamMembers.ts` или расширение `data/team.ts`) с полной моделью профиля, включая контакты, активные задачи и историю.
10
+ - [ ] Реализовать composable `useTeamMemberProfile` с API `profile`, `isLoading`, `error`, обеспечив поиск по `id`, мемоизацию и обработку отсутствующих данных.
11
+ - [ ] Синхронизировать идентификаторы между списком коллег (`useProjectTeam`/`TeamMemberCard`) и профилями, добавить передачу `id` при клике.
12
+
13
+ ### Маршруты и навигация
14
+
15
+ - [ ] Создать динамическую страницу `pages/colleagues/[id].vue`, настроить `useRoute`/`useRouter`, реализовать fallback-навигацию (query `from` → `router.back()` → `/projects/{projectId}/team`).
16
+ - [ ] Настроить `useHead` для установки заголовка, темы и meta-тегов страницы профиля.
17
+ - [ ] Обработать состояния загрузки, ошибки и пустых данных (профиль не найден, нет задач, нет истории).
18
+
19
+ ### UI и взаимодействия
20
+
21
+ - [ ] Сверстать шапку профиля с аватаром, именем, ролью, опциональным описанием, используя общие классы (`bg-background-light`, `dark:bg-background-dark`, `font-display`).
22
+ - [ ] Добавить карточку контактов с кликабельными ссылками (Telegram, телефон, email) и визуальными иконками.
23
+ - [ ] Реализовать секцию «Назначенные задачи» с фильтрами по статусу и карточками задач (название, статус-индикатор, дедлайн).
24
+ - [ ] Реализовать секцию «История задач» с карточками завершённых задач, пониженной контрастностью и пустым состоянием.
25
+ - [ ] Обеспечить адаптивность и доступность: проверка контрастности, aria-метки для кнопок, фокусные состояния.
26
+
27
+ ## Supporting orders
28
+
29
+ - [ ] Documentation: описать использование страницы и источника данных в README или соответствующем разделе.
30
+ - [ ] Observability: Not required — reason: в рамках клиентского Nuxt-приложения без бекэнда нечего настраивать.
31
+ - [ ] Code review and PR: подготовить PR с описанием изменений и скриншотами новой страницы.
32
+
33
+ ## Definition of Done
34
+
35
+ - [ ] All orders are completed and tested.
36
+ - [ ] Relevant unit/e2e/integration tests pass successfully.
37
+ - [ ] Documentation and operational instructions are updated.
38
+ - [ ] `/spec/core/verify.md` is executed after completing all orders to verify the order list.
@@ -0,0 +1,36 @@
1
+ # Implementation Plan
2
+
3
+ **Plan:** Deliver a profile editing page that loads data from the Pinia user store, allows form-based updates, and is reachable from the avatar in the main header.
4
+
5
+ ## Data sources / schemas
6
+
7
+ - Reuse the existing `useUserStore` Pinia store as the single source of user data; ensure getters provide up-to-date values and loading state.
8
+ - If updates require persistence, define a placeholder action in the store for now, returning mocked success until backend API is ready.
9
+ - Map editable fields (first name, last name, username, language code) to form inputs; treat `is_premium` and `photo_url` as read-only display data.
10
+
11
+ ## Contracts and interfaces
12
+
13
+ - UI contract: clicking the avatar triggers navigation to `/profile/edit`; ensure router configuration supports this path.
14
+ - Form submission interface: emit an update event that calls a store action (e.g., `updateUserProfile`) with validated payload.
15
+ - Validation contract: define required fields and constraints (length, allowed characters) consistent with other profile forms.
16
+ - Feedback interface: use shared notification/toast or inline status component to inform users about success or error states.
17
+
18
+ ## Architecture / Components
19
+
20
+ - Create a new page component at `pages/profile/edit.vue` (Nuxt route) with composition API and script setup.
21
+ - Compose the page from shared layout primitives (page container, card, form components) to align with design guidelines.
22
+ - Utilize composables or helpers for form state (e.g., `useForm` or local reactive object) and watchers to sync with store data.
23
+ - Update the header component to wrap the avatar in a link or click handler that navigates to the edit page without breaking existing dropdown behavior.
24
+ - Include responsive layout adjustments (stacked form on mobile, side-by-side summary on desktop if available).
25
+
26
+ ## Risks
27
+
28
+ - Store might be empty when the page loads; need fallback UI and fetch trigger.
29
+ - Saving without backend integration could mislead users; must clearly communicate temporary behavior.
30
+ - Modifying header avatar click could interfere with other interactions (menus, tooltips); test across breakpoints.
31
+
32
+ ## Assumptions
33
+
34
+ - Router already supports nested profile routes, and adding `/profile/edit` will not conflict with existing dynamic pages.
35
+ - Shared form components are available; if not, custom components must mimic existing styles.
36
+ - Localization resources can be extended without structural changes.