9.9 KiB
9.9 KiB
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? Нет.