Внутренний сервис для организации очереди водителей, которые по очереди берут заказы на поездки. Решает простую задачу: кто из группы водителей едет следующим, кто уже отказался, а кто завершил поездку — без ручного учёта в чатах и таблицах.
Разработан для доверенной группы примерно из 10 человек.
- Две независимые очереди водителей — для дальних (межгородних) и коротких поездок, с автоматическим порядком каждая (
GET /queue); водитель может состоять в обеих - Создание заказа с обязательным указанием типа (дальний/короткий) и автоматическим предложением первому в очереди этого типа (
POST /orders) - Принятие или отказ от предложенного заказа (
POST /orders/{id}/respond) - Самоназначение на заказ вне очереди с указанием причины (
POST /orders/{id}/self-assign) — водитель сразу становится исполнителем и уходит в конец очереди, как при обычном принятии - Завершение поездки (
POST /orders/{id}/complete) - Отмена заказа (
POST /orders/{id}/cancel) — если заказ уже был принят, водитель возвращается в начало очереди - История всех заказов с фильтрами по статусу, типу и пользователю (
GET /orders) - Отображение активных предложений — кому и когда сейчас предложен заказ (
GET /orders/pending) - Журнал действий — отдельная от истории заказов хронология событий (создание, предложение, принятие, отказ, завершение, отмена, изменения очереди) с фильтрами (
GET /activity) - Статистика — общие счётчики по заказам и разбивка по водителям (получено/завершено/отменено), отдельная страница (
GET /statistics/summary) - Редактируемый прайс-лист (
GET/POST/PUT/DELETE /price) с поиском и логом изменений (GET /price/log) — добавлять/менять/удалять позиции может любой из группы - Роли — водитель/диспетчер/администратор, совмещение возможно (см. ARCHITECTURE.md, «Роли и права доступа»): водители принимают/самоназначаются на заказы, диспетчеры дополнительно назначают/переназначают заказы вне очереди (
POST /orders/{id}/assign), редактируют и заменяют уже созданные заказы (PATCH /orders/{id},POST /orders/{id}/replace) и отменяют любой заказ, администратору доступен весь функционал плюс управление пользователями/ролями/очередью - Страница «Пользователи» — список всех с ролями, видна любому залогиненному; страница «Админ» — добавление пользователей, управление ролями, порядок очереди (веб-аналог
scripts/add_user.py/scripts/reorder_queue.py), доступна только администраторам - Веб-интерфейс на русском языке: дэшборд с очередью и созданием заказов + страницы истории, прайса, пользователей, статистики и админки, все взаимосвязаны навигацией
- PWA — устанавливается на домашний экран телефона, кнопкой «Установить на экран» или вручную через браузер; страница «Прайс» ставится отдельным значком от сайта целиком; service worker кэширует только статику, данные всегда актуальны по сети (см. ARCHITECTURE.md, «PWA»)
- Уведомление в общую беседу VK о том, кому сейчас предложен или назначен заказ (опционально, см.
VK_GROUP_TOKEN/VK_PEER_IDв.env.exampleи ARCHITECTURE.md) - Вход через VK ID — серверная сессия вместо ручного выбора себя из списка, самопривязка VK-аккаунта к пользователю при первом входе (см. ARCHITECTURE.md, «Вход через VK ID»)
- Backend: Python 3.13, FastAPI, SQLModel, SQLite, Alembic
- Frontend: HTML, CSS, vanilla JavaScript (без фреймворков и шаблонизаторов)
- Инфраструктура: Ubuntu Server, Caddy, systemd (задеплоено на
zakaz.glorden.ru, см. DEPLOY.md)
- При создании заказа обязательно указывается тип — дальний или короткий. Дальше вся логика ниже работает только в рамках очереди этого типа: заказ дальней очереди никогда не затрагивает позиции в короткой, и наоборот. Один и тот же водитель может состоять в обеих очередях одновременно, с независимыми позициями в каждой.
- Кто-то создаёт заказ — он сразу автоматически предлагается первому в очереди нужного типа.
- Первый в очереди либо принимает заказ (сразу уходит в конец этой же очереди, чтобы новый заказ не пришёл ему повторно, пока он занят), либо отказывается (остаётся на своей позиции, заказ автоматически уходит следующему по кругу).
- Любой другой водитель этой же очереди может самоназначиться на ещё не назначенный заказ вне очереди — например, если он уже находится в городе подачи. Указание причины обязательно, она сохраняется в истории заказа и журнале действий; сам водитель точно так же уходит в конец очереди, как при обычном принятии.
- После завершения поездки водитель отмечает заказ как завершённый.
- Таймаутов на ответ нет — водители сами договариваются в личном чате, сколько ждать.
Подробности и обоснование этих решений — в ARCHITECTURE.md.
carpool-queue/
├── app/
│ ├── main.py # точка входа FastAPI, регистрация роутов
│ ├── config.py # настройки приложения
│ ├── database.py # подключение к БД (SQLite)
│ ├── notifications.py # уведомление в VK о предложенном заказе
│ ├── auth.py # текущий пользователь из сессии
│ ├── vk_oauth.py # механика VK ID OAuth (PKCE), см. ARCHITECTURE.md
│ └── models/
│ ├── user.py # модель User
│ ├── queue.py # модель QueuePosition, enum QueueType
│ ├── order.py # модели Order и OrderOffer
│ ├── price.py # модели PriceItem и PriceLogEntry
│ └── activity.py # модель ActivityLog (журнал действий)
├── alembic/ # миграции БД
│ └── versions/
├── static/
│ ├── index.html # дэшборд (обе очереди + создание заказа)
│ ├── history.html # история заказов
│ ├── activity.html # журнал действий
│ ├── price.html # прайс-лист (редактируемый)
│ ├── statistics.html # статистика
│ ├── users.html # список пользователей и их ролей (видна всем)
│ ├── admin.html # добавление пользователей, роли, порядок очереди (только is_admin)
│ ├── link-account.html # экран самопривязки VK-аккаунта при первом входе
│ ├── login-denied.html # отказ во входе (VK-аккаунт не сопоставлен)
│ ├── sw.js # service worker (PWA), отдаётся с /sw.js
│ ├── fav/ # favicon-иконки + PWA-манифесты
│ │ ├── site.webmanifest # манифест сайта целиком («Очередь»)
│ │ └── price.webmanifest # отдельный манифест страницы «Прайс»
│ ├── css/style.css
│ └── js/
│ ├── nav.js # общая навигация на всех страницах + регистрация service worker'а
│ ├── session.js # общий виджет "кто я" / вход через VK / выход
│ ├── install.js # кнопка "Установить на экран" (PWA)
│ ├── dashboard.js
│ ├── history.js
│ ├── activity.js
│ ├── price.js
│ ├── statistics.js
│ ├── users.js
│ ├── admin.js
│ └── link-account.js
├── scripts/
│ ├── seed.py # наполнение БД тестовыми данными
│ ├── add_user.py # добавление реального пользователя (с ролями) в очередь(и)
│ ├── reorder_queue.py # ручная перестановка порядка в одной из очередей
│ ├── grant_role.py # точечная выдача/снятие одной роли по username
│ └── backup_db.sh # ежедневный бэкап БД на проде (см. DEPLOY.md)
├── tests/ # автотесты (pytest)
├── requirements.txt
├── alembic.ini
├── .env.example
├── PROGRESS.md # журнал выполнения этапов
├── ARCHITECTURE.md # архитектурные решения и технический долг
├── DEPLOY.md # установка и запуск на VPS
├── CHANGELOG.md # журнал изменений (Keep a Changelog)
├── STYLEGUIDE.md # соглашения по стилю кода
└── README.md
cd C:\Users\Oleg\Desktop\carpool-queue
python -m venv .venv
.venv\Scripts\activate.bat
pip install -r requirements.txt
copy .env.example .env
alembic upgrade headПоследняя команда создаст файл carpool.db со всеми нужными таблицами.
cd C:\Users\Oleg\Desktop\carpool-queue
chcp 65001
.venv\Scripts\activate.bat
uvicorn app.main:app --reloadВеб-интерфейс (дэшборд): http://127.0.0.1:8000
Проверка работоспособности: http://127.0.0.1:8000/health должен вернуть:
{"status": "ok", "message": "Carpool queue service is running"}Запуск на Linux/macOS
source .venv/bin/activate
uvicorn app.main:app --reloadЧтобы наполнить очередь тестовыми пользователями:
python -m scripts.seedpython -m scripts.add_user "Имя Фамилия" usernameДобавляет пользователя с ролью «водитель» и ставит его в конец обеих очередей (дальней и короткой).
Только в одну из очередей:
python -m scripts.add_user "Имя Фамилия" username --queue-type=long
python -m scripts.add_user "Имя Фамилия" username --queue-type=shortДля диспетчера, который создаёт и назначает заказы, но сам не участвует в очереди водителей (роль «диспетчер», без очереди):
python -m scripts.add_user "Имя Фамилия" username --no-queueВсё это можно сделать и через веб-интерфейс — страница «Админ» (только
для is_admin) дублирует функциональность обоих скриптов ниже, включая
выбор ролей и очередей в форме. CLI остаётся как аварийный доступ по SSH.
Три роли — is_driver/is_dispatcher/is_admin на User, совмещение
возможно (см. ARCHITECTURE.md, «Роли и права доступа»). Точечно
выдать/снять одну роль (например, первому администратору после деплоя):
python -m scripts.grant_role username driver
python -m scripts.grant_role username dispatcher
python -m scripts.grant_role username admin
python -m scripts.grant_role username admin --removeДля редкой ручной правки порядка (не для повседневного использования —
обычная логика очереди отрабатывает это сама). Первый аргумент — тип
очереди (long или short), дальше список должен содержать ровно тех
же людей, что сейчас в этой очереди, в нужном порядке; вторая очередь
не затрагивается:
python -m scripts.reorder_queue long username1 username2 username3Точечные автотесты на самую хрупкую логику — арифметику позиций
в очереди (tests/test_queue_logic.py) и журнал действий
(tests/test_activity_log.py), на изолированной временной БД:
pytest tests/alembic revision --autogenerate -m "описание изменения"
alembic upgrade head| Метод | Путь | Описание |
|---|---|---|
GET |
/ |
веб-интерфейс (дэшборд) |
GET |
/health |
проверка работоспособности сервиса |
GET |
/users |
все пользователи с ролями (is_driver/is_dispatcher/is_admin), включая диспетчеров без очереди (требует сессию) |
GET |
/queue |
список пользователей в текущем порядке очереди; query-параметр queue_type (long/short) — без него отдаёт обе очереди сразу (требует сессию) |
POST |
/orders |
создать новый заказ (требует сессию — queue_type обязателен — long/short; route, comment — опциональны); создатель фиксируется как created_by |
POST |
/orders/{order_id}/respond |
принять/отклонить предложенный заказ (требует роль водителя; 403, если заказ предложен не вам) |
POST |
/orders/{order_id}/self-assign |
самоназначиться на pending-заказ вне очереди (требует роль водителя, обязательная reason) — сразу становится исполнителем и уходит в конец очереди |
POST |
/orders/{order_id}/assign |
диспетчер/админ напрямую назначает pending-заказ водителю или переназначает уже assigned (тело — driver_user_id); целевой должен быть водителем в очереди этого типа; прежний исполнитель (при переназначении) возвращается вторым в очередь, новый — в конец |
PATCH |
/orders/{order_id} |
диспетчер/админ правит route/comment уже созданного заказа «на месте» (из pending/assigned, иначе 400); queue_type не меняет — для этого /replace |
POST |
/orders/{order_id}/replace |
диспетчер/админ заменяет заказ новым: старый отменяется, новый создаётся с телом как у POST /orders (из pending/assigned старого, иначе 400) — используется для смены queue_type или правки уже предложенного/назначенного заказа; новый заказ хранит replaces_order_id на старый |
POST |
/orders/{order_id}/complete |
отметить заказ завершённым (требует роль водителя; заказ должен быть в статусе assigned и назначен вам, иначе 403) |
POST |
/orders/{order_id}/cancel |
отменить заказ (требует сессию; из pending/assigned); если был assigned — водитель возвращается в начало очереди. Диспетчер/админ может отменить любой заказ; остальным — pending может отменить только создатель, assigned — создатель или исполнитель, иначе 403 |
GET |
/orders |
история заказов; query-параметры status, user_id, queue_type, limit (по умолчанию 100, максимум 500); сортировка по created_at (новые сверху) (требует сессию) |
GET |
/orders/pending |
заказы в статусе pending вместе с данными активного предложения — кому сейчас предложен заказ и когда (требует сессию) |
GET |
/price |
весь прайс-лист (требует сессию) |
POST |
/price |
добавить позицию (требует сессию — category, name, price_text) |
PUT |
/price/{item_id} |
изменить позицию (требует сессию — поля, которые меняются) |
DELETE |
/price/{item_id} |
удалить позицию (требует сессию) |
GET |
/price/log |
лог изменений прайса, новые сверху; query-параметр limit (по умолчанию 100, максимум 500) (требует сессию) |
GET |
/activity |
журнал действий, новые сверху; query-параметры user_id, order_id, event_type, limit (по умолчанию 100, максимум 500) (требует сессию) |
GET |
/statistics/summary |
общая статистика (total/completed/active/cancelled) и разбивка по водителям, состоящим в очереди (received/completed/cancelled для каждого) (требует сессию) |
POST |
/admin/users |
создать пользователя с ролями и (опционально) сразу в конец указанных очередей (только is_admin) |
PATCH |
/admin/users/{user_id}/roles |
частично изменить роли пользователя (только is_admin) |
POST |
/admin/users/{user_id}/queue/{queue_type} |
добавить существующего водителя в конец очереди (только is_admin) |
DELETE |
/admin/users/{user_id}/queue/{queue_type} |
убрать водителя из очереди (только is_admin) |
POST |
/admin/queue/{queue_type}/reorder |
переставить порядок очереди целиком (тело — user_ids, тот же состав, что сейчас в очереди) (только is_admin) |
GET |
/auth/vk/login |
начинает вход через VK ID (редирект на VK) |
GET |
/auth/vk/callback |
обратный редирект от VK; логин, экран самопривязки или отказ |
GET |
/auth/vk/link-candidates |
пользователи без привязанного vk_id — для экрана самопривязки (только сразу после callback с новым vk_id) |
POST |
/auth/vk/link |
завершает самопривязку (user_id) — сохраняет vk_id, сразу логинит |
POST |
/auth/logout |
завершает сессию |
GET |
/me |
текущий пользователь из сессии вместе с ролями, либо {"authenticated": false} |
Ошибки возвращаются в стандартном формате FastAPI: {"detail": "..."} с соответствующим HTTP-статусом (404/400).
Интерактивная документация API доступна по адресу /docs (Swagger UI) при запущенном сервере — требует сессию, как и весь остальной сайт (см. «Технический долг — закрыт (Шаг 27)» в ARCHITECTURE.md).
Проект в активной разработке. Backend и веб-интерфейс полностью рабочие и покрывают основной сценарий использования. Актуальный список выполненных и предстоящих шагов — в PROGRESS.md.
- PROGRESS.md — журнал выполнения этапов
- ARCHITECTURE.md — архитектурные решения, логика очереди, технический долг, формат работы
- DEPLOY.md — установка и запуск на VPS (Ubuntu + Caddy + systemd + HTTPS)
- CHANGELOG.md — журнал изменений (формат Keep a Changelog)
- STYLEGUIDE.md — соглашения по стилю кода