Files
cardy/development specification.md
T
2026-09-29 13:50:01 +03:00

65 lines
9.9 KiB
Markdown

# pnpm монорепо приложение для хранения скидочных карт (штрих/qr-код + тип кода, EAN-13, EAN-8, Code128, QR, PDF417, Aztec и т.д. + название + цвет + опционально лого + опционально скриншоты).
# серверное приложение (fastify+react+vite+ts):
* хранение пользователей в файле users.yaml (по email) и их групп (по названиям). для пользователя может быть храниться признак того, что это - администратор или суперадминистратор.
* пользователь является администратором только для своей группы. суперадминистратор - администратор всех групп.
* аутентификация пользователя для доступа к UI (не API) через заголовок Remote-Email
* для каждого пользователя может храниться несколько строковых ключей доступа. ключи хранятся в зашифрованном обратимым шифрованием AES-256-GCM виде, ключ расшифровки (32-байта в base64) в переменной окружении.
* административный (для суперадмина) CRUD UI для управления группами пользователей и выбора одной группы, как текущей. группу, к которой принадлежит хотя бы один суперадмин, невозможно удалить.
* административный UI CRUD пользователей текущей группы и приязанных к ним ключей доступа. ключи генерируются случайно (32 символа)
* хранилище списка карт в yaml файле в папке соответственной группы пользователей, картинки рядом с ним на диске в виде файлов. количество картинок на карту не ограничено. карты общие для всех пользователей одной группы. для каждой карты указывается числовой номер версии картинок.
* административный UI для CRUD всех хранящихся, включая удаленные, карт текущей группы пользователей, загрузки картинок к ним. изменение/добавление/удаление любой картинки увеличивает номер версии в карте. загруженные картинки хранятся как есть.
* UI для показа списка ключей текущего пользователя. Для одного выбранного ключа генерирутся QR код, содержащий адрес сервера и сам ключ.
* API для клиентского приложения:
* аутентификация пользователя по ключу (Authorization: Bearer)
* получение списка всех карт (без пагинации). удаленные карты получить невозможно
* получение картинок по выбранной карте
* редактирование, удаление и добавление карт с их лого. удалённые карты помечаются, как удаленные и не удаляются
* удаление и добавление картинок карт
* весь UI на английском
# клиентское мобильное приложение (react/vite/ts pwa + capacitor андроид):
* хранение ключа и пинкода для входа
* экран нового входа
* поле для ввода адреса серверного API
* показывает камеру, даёт отсканировать ключ из QR кода
* позволяет ввести ключ текстом в открытом виде.
обязательна аутентификация на сервере для подтверждения правильности ключа. экран можно вызвать даже если ключ уже хранится в приложении, в этом случае старый ключ удаляется. хранится всегда только один последний ключ
* экран цифрового кода (= "пинкода"). показывается для ввода пинкода (4 цифры) при смене ключа (пинкод сохраняется) и при каждом открытии приложения (пинкод сверяется с хранящимся). защиты от перебора нет.
* экран карт. список всех хранящихся локально карт (цветными названия с логотипами или, если их нет, с аббревиатурами на цветном фоне) с возможностью выбрать одну из них во весь экран. сортировка по давности использования
* экран (одной) карты: просмотр её названия, штрих/qr-кода и её хранящиеся локально картинки, если есть. яркость экрана на андроиде поднимается на максимум. открывание экрана считается использованием карты и сохраняет дату и время
* на экране карт кнопка "фильтр по названию" (по подстроке длиной от 1 символа)
* на экране карт кнопка "обновить". перекачивает заново список всех карт с сервера и обновляет сохраненный список (только если скачалось успешно). для изменившихся карт асинхронно по очереди (макс 5 запросов одновременно) скачиваются их картинки.
* экран редактирования/добавления новой карты: поле для ввода названия, выбора цвета (по умолчанию случайный), загрузки опционального лого, загрузки опциональных фото, ввода кода и выбора типа код карты текстом или распознаванием QR/штрих-кода и его типа с карты или из загруженной картинки. лого и картинки перед отправкой, пережимаются в webp до размера 1000px по наибольшей стороне, EXIF удаляется
* на экране карт кнопка "добавить", вызывает экран редактирования/добавления новой карты
* на экране карты кнопка "удалить" с подтверждением.
* на экране карты кнопка "редактировать", вызывает экран редактирования/добавления новой карты
* весь UI на английском
клиентское приложение должно работать без интернета за исключением функций аутентификации, скачивания, редактирования, удаления и добавления карт и картинок. функции, требующие наличия сети, показывают ошибки сообщениями на экране и отваливаются.
вопросы деплоя в этом задании не рассматриваются
# Дополнительная информация
Пользователи и группы
* Пользователь состоит ровно в одной группе
* Первый суперадмин создаётся из переменной окружения SUPERADMIN_EMAIL
* При удалении группы все пользователи, ключи и карты удаляются.
* Текущая группа пользователя читается из users.yaml и сохраняется в cookie. UI суперадмина позволяет записать в эту cookie другое значение.
* При ошибка аутентификации отвечать ошибкой 401.
* Новые ключи генерируются только админом или суперадмином
Сессии и ключи
* Шифрование ключей: AES-256-GCM с 32-байтным ключом в base64 в env подойдёт?
Данные
* Где генерируется ID карты? На сервере, при добавлении. Возвращается клиенту.
* Конкурентные правки одной карты с разных устройств: побеждает последняя запись? Да.
* Дата последнего использования карты хранится только локально на устройстве и сохраняется при «обновить» по ID карты? Да.
Клиент и деплой
* Как менять PIN без смены ключа? Или только через повторный вход? Да.
* PWA раздаётся с того же домена что и API.
* Нужны ли Dockerfile и docker-compose с томом под данные и пример конфига прокси с bypass для /api? Нет.