Техническая документация

Документ для ИТ-специалистов: из чего состоит «Гараж», как его развернуть, настроить и связать с другими системами.

Назначение и состав

«Гараж» — веб-приложение для учёта корпоративного автопарка. Пользователи работают в веб-интерфейсе; данные из внешних сервисов загружают фоновые команды по расписанию; изменения данных публикуются в шину событий.

Часть Назначение
Веб-интерфейс учёт автомобилей, журналы событий, счета, отчёты в Excel
API чтение данных об автомобилях и полисах другими системами
Команды интеграций загрузка данных из внешних сервисов, уведомления и сводки
Шина событий публикация изменений в Apache Kafka

Технологии

Компонент Версия
Python 3.10
Django 4.2
Django REST Framework 3.15
СУБД PostgreSQL 14 (для разработки — SQLite)
Сервер приложений gunicorn за nginx
Шина событий Apache Kafka (SASL_SSL)
Отчёты XlsxWriter

Требования к серверу

  • Linux-сервер (Ubuntu 22.04 или новее), 2 ядра, 4 ГБ памяти, 20 ГБ диска — для парка до нескольких сотен автомобилей;
  • PostgreSQL 14;
  • nginx с сертификатом TLS;
  • исходящий доступ в интернет к подключаемым сервисам;
  • планировщик задач (cron или таймеры systemd).

Установка

apt update
apt install postgresql nginx python3.10 python3.10-venv -y

База данных и пользователь:

CREATE DATABASE garagedb;
CREATE USER garage WITH ENCRYPTED PASSWORD '<пароль>';
GRANT ALL PRIVILEGES ON DATABASE garagedb TO garage;

Приложение:

python3.10 -m venv venv
venv/bin/pip install -r requirements.txt
cp env.example.py env.py          # и заполнить, см. «Конфигурация»
venv/bin/python manage.py migrate
venv/bin/python manage.py collectstatic
venv/bin/python manage.py createsuperuser

Проверочный запуск:

venv/bin/gunicorn PGcars.wsgi --bind 0:8000

В рабочем режиме gunicorn запускается службой systemd за nginx; nginx отдаёт каталоги static/ и media/.

Конфигурация

Все настройки и секреты — в файле env.py в корне проекта. Он не хранится в репозитории и создаётся из шаблона env.example.py. При запуске система сверяет env.py с шаблоном и не стартует, если какой-то переменной не хватает, — в сообщении перечислены недостающие имена.

Группа Переменные
Общие DEBUG, ALLOWED_HOSTS, HOST, USE_TZ
Название PRODUCT_NAME, COMPANY_NAME, CONTRACT_OWNERS
База данных DB_ENGINE, DB_NAME, DB_HOST, DB_PORT, POSTGRES_USER, POSTGRES_PASSWORD
Почта EMAIL_HOST_USER, EMAIL_HOST_PASSWORD, ALERT_EMAILS
Мессенджер MAX_TOKEN, MAX_CHAT_ALERT, MAX_CHAT_NOTIFICATION
Топливные карты PPR_API_NEW — учётные данные кабинетов
Спутниковый мониторинг KGK_TOKENS — токены кабинетов
1С ONEC_URL, ONEC_LOGIN, ONEC_PASSWORD, ONEC_ALERT_EMAILS
Сервис-деск M4_CREATE_TASKS, M4_API_URL, M4_AUTH_URL, M4_USERNAME, M4_PASSWORD
Платные дороги CKAD_API_URL, CKAD_ACCOUNTS

Параметры подключения к Kafka — в отдельном файле kafka/config.py: адреса брокеров, учётные данные SASL, каталог сертификатов, тема.

Пользователи и права

Используется стандартная модель прав Django: пользователи, группы и права на каждую модель — просмотр, добавление, изменение, удаление. Права назначаются группам в веб-интерфейсе администратором.

Для счетов есть дополнительные роли, от которых зависит набор доступных полей: базовое редактирование, согласование директором по безопасности, финансовые поля.

Пароли хранятся в виде хешей (PBKDF2). Вход в веб-интерфейс — по сессии, защита форм от CSRF включена.

Интеграции

Загрузка данных выполняется командами manage.py, которые запускает планировщик. Ошибка в одном источнике не останавливает остальные и попадает в сводку.

Сервис Что загружается Периодичность
Оператор топливных карт карты, транзакции, штрафы; привязка карт к автомобилям раз в день
Спутниковый мониторинг трекеры, одометр, суточные пробеги раз в день; одометр — чаще
1С:Бухгалтерия (OData) основные средства и амортизация раз в месяц
Сервис-деск сотрудники; создание заявок по ДТП, ТО, полисам раз в день и по событиям
Оператор платных дорог проезды и постановления раз в день

Команды

Команда Назначение
ppr daily карты, транзакции и штрафы по всем кабинетам, сводка ответственным
ppr bills счета на новые штрафы
kgk daily одометр, пробеги за 7 дней, проверки за вчера и сводка
kgk odometer --update-only только обновление одометра
kgk checks проверки за день по данным в базе, без рассылки
onec depreciation амортизация за последние закрытые месяцы
notifications напоминания о ТО, полисах и техосмотрах
outbox отправка накопленных событий в Kafka
cleanup_outbox удаление давно отправленных событий

Пример расписания:

0 7 * * *     manage.py ppr daily
30 7 * * 4    manage.py ppr bills --offset 4
0 6 * * *     manage.py kgk daily
0 8 * * *     manage.py notifications
*/5 * * * *   manage.py outbox
0 5 28 * *    manage.py onec depreciation

Особенности

  • Сопоставление автомобилей между системами идёт по госномеру, приведённому к единому виду.
  • Лимит запросов. Если у сервиса платное API, каждый запрос учитывается в журнале; при исчерпании лимита загрузка останавливается, а ручные запуски оставляют запас для планового.
  • Повторы. Клиенты повторяют запросы при обрыве соединения и таймауте; ответы с ошибкой не повторяются.
  • 1С читается через стандартный интерфейс OData, только на чтение; изменений в базе 1С система не делает.

API

API предназначен для чтения данных другими системами компании. Все методы — GET, ответы в JSON.

Адрес Ответ
/api/cars/ список госномеров автомобилей
/api/car/<госномер>/ данные автомобиля
/api/polis/<госномер>/ действующий полис ОСАГО
/api/sts/<госномер>/ файл СТС
/api/osago/<госномер>/ файл действующего полиса ОСАГО

Методы, возвращающие JSON, требуют авторизации — Basic или сессии веб-интерфейса. Файлы отдаются по прямой ссылке; доступ к ним ограничивается на уровне сети или nginx.

Пример:

curl -u user:password https://garage.example.com/api/car/A123BC77/
{
  "VINnumber": "XTA21901000000000",
  "model": "LADA GRANTA",
  "type_car": "Легковой комби (хэтчбек)",
  "car_category": "B/M1",
  "year": 2024,
  "color_name": "Белый",
  "sts_serial": "99 12",
  "sts_number": "345678",
  "sts_start_date": "15.03.2024",
  "leasing_agreement": "ЛД-2024/0153",
  "lising_org": "ООО «Вектор Лизинг»"
}

Шина событий

Изменения данных публикуются в Apache Kafka по схеме transactional outbox:

  1. При создании, изменении или удалении записи событие сохраняется в таблицу Outbox вместе с самим изменением.
  2. Команда outbox пачками отправляет события в Kafka.

Формат события:

{
  "model": "garage.Car",
  "action": "UPDATE",
  "data": { "id": 15, "grz": "A123BC77", "odometer": 84120 }
}

action принимает значения CREATE, UPDATE, DELETE; связи передаются идентификаторами.

Гарантии доставки:

  • события отправляются строго в порядке появления;
  • отправленным считается только непрерывное начало пачки, доставленное без ошибок;
  • при ошибке отправка останавливается и повторяется со следующего запуска.

Событие может прийти повторно, поэтому получатель должен быть идемпотентным. Сохранение записи без изменений события не создаёт.

Данные

Область Основные сущности
Автопарк автомобиль, модель, топливная карта, сотрудник, автосервис, организация, дивизион, регион, офис
Журналы полис, лизинг, транспортный налог, ТО, техосмотр, ДТП, штраф, транзакция, суточный пробег, смены водителя, организации и региона
Счета и расходы счёт, статья затрат, подразделение, юридическое лицо
Бухгалтерия основное средство, амортизация за месяц
Платные дороги проезд, постановление

Файлы документов (СТС, ПТС, полисы, акты ТО, договоры) хранятся на диске сервера в каталоге media/.

Резервное копирование

Копировать нужно три вещи: базу данных, каталог media/ и файлы конфигурации (env.py, kafka/config.py, сертификаты).

# дамп базы
pg_dump -U garage -Fc garagedb > garagedb_$(date +%F).dump

# восстановление
pg_restore -U garage -d garagedb --clean garagedb_2026-01-15.dump

Рекомендуется ежедневный дамп с хранением за месяц и копированием на другой сервер.

Обновление

git pull
venv/bin/pip install -r requirements.txt
venv/bin/python manage.py migrate
venv/bin/python manage.py collectstatic --noinput
systemctl restart garage

Перед обновлением сделайте дамп базы. Если в новой версии появились переменные конфигурации, добавьте их в env.py до обновления кода — иначе система не запустится.

Безопасность

  • доступ к веб-интерфейсу — только по HTTPS, после входа;
  • секреты внешних систем хранятся в env.py на сервере и не попадают в репозиторий;
  • значения из внешних систем в отчётах Excel не превращаются в формулы и ссылки;
  • интеграции с 1С работают только на чтение;
  • журнал запросов к платным API ведётся в базе.