Маркетплейс аренды
Ijara
Маркетплейс аренды для Ташкента, где владельцы размещают вещи — инструменты, электронику, оборудование для мероприятий, транспорт — а арендаторы выбирают, бронируют и оплачивают онлайн. Создан на Django REST Framework и фронтенде Nuxt 3 + Pinia.
Обзор
В Ташкенте есть много вещей, которые люди покупают однажды, а потом неформально одалживают соседям. Ijara формализует это: владельцы размещают инструменты, электронику, оборудование для мероприятий и транспорт; арендаторы выбирают по дате, бронируют и платят онлайн. Я построил полный стек — от Django API, обрабатывающего логику бронирования, до фронтенда Nuxt 3, где происходят транзакции.
Что я сделал
Спроектировал и построил полный Django REST API — пять модулей приложений, охватывающих аккаунты, объявления, бронирования, платежи и поиск, все со схемой OpenAPI для чётких контрактов.
Создал машину состояний бронирования и предотвращение двойного бронирования — ограничение диапазона Postgres, которое делает физически невозможным бронирование одного и того же предмета на пересекающиеся даты.
Построил фронтенд Nuxt 3 — маршрутизация страниц, аутентификация с JWT токенами в памяти плюс безопасные refresh cookies, получение данных, валидация форм и поток оплаты Stripe.
Добавил ИИ-ассистент на основе OpenAI, предлагающий заголовок, описание и категорию по загруженным фотографиям — снижая барьер для владельцев при размещении первого объявления.
Архитектура
Django API и фронтенд Nuxt работают в отдельных контейнерах, проверяемых на работоспособность для запуска в правильном порядке. Сервер Nuxt действует как прокси для всех API-вызовов, поэтому браузер обращается только к одному источнику. JWT токены живут в памяти (безопасно, но очищаются при обновлении), а refresh-токены живут в httpOnly cookie — шаблон аутентификации, балансирующий безопасность с хорошим пользовательским опытом.
Под капотом
Предотвращение двойного бронирования на уровне базы данных с использованием ограничения перекрытия диапазонов Postgres — поэтому даже если два запроса поступают одновременно, база данных отклоняет второй. Отменённые или завершённые бронирования выходят за рамки ограничения, поэтому то же временное окно может быть забронировано повторно.
Машина состояний бронирования с полным журналом аудита: каждое изменение состояния проходит через единственную функцию, проверяющую права ролей и добавляющую запись перехода — так что есть полная история того, кто что и когда изменил.
Полнотекстовый поиск с тригрэмным запасным вариантом: объявления индексируются для быстрого поиска по ключевым словам, а проверка тригрэмного сходства ловит опечатки и частичные совпадения — так что арендаторы находят то, что ищут, даже при неправильном написании.
Чему я научился
Применение бизнес-правил на уровне базы данных — не только в коде приложения — единственный способ оставаться в безопасности, когда несколько сервисов или запросов одновременно обращаются к одним данным.
Функция ИИ-предложения объявления оказала непропорционально большое влияние на опыт создания объявления относительно затраченных усилий — иногда функции с наибольшим рычагом — самые маленькие.
Технологии
Фронтенд
- Nuxt 3
- Vue 3
- TypeScript
- Pinia
- TanStack Query
- vee-validate
- Zod
- @stripe/stripe-js
Бэкенд
- Django 5.2
- Django REST Framework 3.16
- SimpleJWT
- drf-spectacular
- Stripe Python SDK v10
- OpenAI Python SDK v1
Данные
- PostgreSQL 16
- btree_gist
- pg_trgm
- SearchVectorField
- GinIndex
Инфраструктура и тестирование
- Docker Compose
- uv
- pytest-django
- factory-boy
- Playwright
- Ruff