Маркетплейс аренды

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