18 KiB
RideBus DE API
Система управления информацией об общественном транспорте города Полоцка с REST API, веб-панелью администратора и поддержкой offline-синхронизации.
🚀 Возможности
MVP (Версия 1.0 - статические данные)
- ✅ REST API для маршрутов и остановок
- ✅ Геопространственные запросы (PostGIS)
- ✅ Расчет ETA на основе расписания
- ✅ Offline-синхронизация для мобильных приложений
- ✅ Веб-панель администратора
- ✅ Кеширование через Redis
- ✅ Аутентификация по API ключам
- ✅ Rate limiting
- ✅ Серверные push-уведомления об алертах через Firebase Cloud Messaging
- ✅ Импорт отмен из Telegram через отдельный аккаунт, проверка в админке и срок действия до полуночи (настройка)
Технологический стек
Backend:
- Node.js + Express
- PostgreSQL + PostGIS
- Redis
- Docker
Frontend (Admin Panel):
- React + Vite
- Tailwind CSS
- React Leaflet (карты)
- Axios
📋 Требования
- Docker и Docker Compose
- Node.js 22+ (для разработки без Docker и Firebase Admin SDK)
- Статический IP или динамический DNS
- ~2GB свободного места на диске
🛠 Установка и запуск
1. Клонирование и настройка
# Создайте .env файл из примера
cp .env.example .env
# Настройте переменные окружения
nano .env
Важные переменные в .env:
# Обязательно измените в продакшене!
DB_PASSWORD=your_secure_password_here
API_KEY_SALT=generate_random_salt_with_openssl
ADMIN_PASSWORD=your_secure_admin_password_here
# Для разработки/деплоя через встроенный proxy оставьте как есть
CORS_ORIGIN=http://localhost:3001
API_URL=/api/v1
# Push отключён по умолчанию
FCM_ENABLED=false
# Для пассажирского режима backend проверяет Firebase ID token.
# Укажите проект из google-services.json мобильного приложения и путь к service account JSON.
FIREBASE_PROJECT_ID=your-firebase-project-id
GOOGLE_APPLICATION_CREDENTIALS=/absolute/private/path/firebase-service-account.json
Для входа по passkey на публичном адресе укажите в .env точный домен панели
и используйте HTTPS: WEBAUTHN_RP_ID=admin.example.by и
WEBAUTHN_ORIGIN=https://admin.example.by. В WEBAUTHN_ORIGIN не применяются
wildcard-маски; при нескольких разрешённых адресах перечислите их через запятую.
Генерация безопасного salt:
openssl rand -base64 32
2. Запуск с Docker (рекомендуется)
# Сборка и запуск всех сервисов
docker-compose up -d
# Проверка статуса
docker-compose ps
# Просмотр логов
docker-compose logs -f
Сервисы будут доступны:
- API: http://localhost:3000
- Admin Panel: http://localhost:3001
- PostgreSQL: localhost:5432
- Redis: localhost:6379
Мастер-аккаунт создается автоматически при старте backend:
- login:
admin - password: значение
ADMIN_PASSWORDиз.env(если не задано, fallbackadmin123)
3. Проверка работоспособности
# Проверка API
curl http://localhost:3000/health
# Проверка базы данных
docker exec -it polotsk-transit-db psql -U transit_admin -d polotsk_transit -c "SELECT COUNT(*) FROM routes;"
4. Разработка без Docker
Backend:
cd backend
npm install
cp ../.env.example .env
# Убедитесь, что PostgreSQL и Redis запущены локально
# Настройте DATABASE_URL и REDIS_URL в .env
npm run dev
Frontend:
cd frontend
npm install
cp .env.example .env
npm run dev
📱 Использование API
Аутентификация
Все запросы требуют API ключ в заголовке:
curl -H "X-API-Key: dev_key_12345678" http://localhost:3000/api/v1/routes
Основные эндпоинты
Маршруты:
GET /api/v1/routes # Все маршруты
GET /api/v1/routes/:id # Маршрут с остановками
POST /api/v1/routes # Создать маршрут
PUT /api/v1/routes/:id # Обновить маршрут
DELETE /api/v1/routes/:id # Удалить маршрут
Остановки:
GET /api/v1/stops # Все остановки
GET /api/v1/stops/:id # Конкретная остановка
GET /api/v1/stops/nearby?lat=55.4869&lon=28.7856&radius=500 # Ближайшие
POST /api/v1/stops # Создать остановку
PUT /api/v1/stops/:id # Обновить остановку
DELETE /api/v1/stops/:id # Удалить остановку
ETA (расчет времени прибытия):
POST /api/v1/eta/calculate
{
"stopId": 1,
"routeId": 1
}
GET /api/v1/eta/stop/:stopId # ETA для всех маршрутов на остановке
Синхронизация (для offline-режима):
GET /api/v1/sync # Полная синхронизация
GET /api/v1/sync?lastSync=2025-11-19T10:00:00Z # Инкрементальная
GET /api/v1/sync/status # Статус системы
Алерты:
GET /api/v1/alerts/active # Активные алерты для клиентов
POST /api/v1/alerts # Создать алерт и, если он активен, отправить FCM
Настройка Firebase, контракт сообщения, идемпотентность и способы локальной проверки описаны в документации FCM-уведомлений.
Примеры запросов
Создание маршрута:
curl -X POST http://localhost:3000/api/v1/routes \
-H "X-API-Key: dev_key_12345678" \
-H "Content-Type: application/json" \
-d '{
"route_number": "1",
"name": "Центр - Вокзал",
"type": "bus",
"color": "#0066CC",
"description": "Основной маршрут через центр города"
}'
Создание остановки:
curl -X POST http://localhost:3000/api/v1/stops \
-H "X-API-Key: dev_key_12345678" \
-H "Content-Type: application/json" \
-d '{
"name": "Площадь Свободы",
"latitude": 55.4869,
"longitude": 28.7856,
"address": "ул. Ленина, 1"
}'
Поиск ближайших остановок:
curl -H "X-API-Key: dev_key_12345678" \
"http://localhost:3000/api/v1/stops/nearby?lat=55.4869&lon=28.7856&radius=500"
🔐 Безопасность домашнего сервера
Обязательные меры безопасности
- Cloudflare Tunnel (рекомендуется):
# Установка cloudflared
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o cloudflared
sudo mv cloudflared /usr/local/bin/
sudo chmod +x /usr/local/bin/cloudflared
# Аутентификация
cloudflared tunnel login
# Создание туннеля
cloudflared tunnel create polotsk-transit
# Запуск
cloudflared tunnel run polotsk-transit
- Настройка SSL в Nginx:
# Получение сертификата Let's Encrypt
sudo certbot --nginx -d api.yourdomain.com -d admin.yourdomain.com
# Сертификаты автоматически обновятся
- Fail2ban:
sudo apt install fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
- Мониторинг:
# Установка Prometheus + Grafana (опционально)
# Или простой мониторинг через Docker stats
docker stats
Создание API ключей
API ключи можно создать через базу данных:
INSERT INTO api_keys (key, app_name, rate_limit)
VALUES ('your_secure_key_here', 'iOS App', 10000);
Или через скрипт:
const crypto = require('crypto');
const apiKey = crypto.randomBytes(32).toString('hex');
console.log(apiKey);
📊 Админ-панель
Откройте http://localhost:3001 в браузере.
Функции:
- Управление маршрутами
- Создание и редактирование остановок на карте
- Просмотр статистики
- Управление расписаниями (в разработке)
🗄 Структура базы данных
routes # Маршруты
stops # Остановки (с PostGIS геометрией)
route_stops # Связь маршрутов и остановок
schedules # Расписания
vehicles # Транспортные средства
historical_delays # Статистика задержек
alerts # Уведомления
api_keys # API ключи
sync_log # Лог изменений для синхронизации
🔄 Обновления
# Остановка сервисов
docker-compose down
# Обновление кода
git pull
# Перезапуск
docker-compose up -d --build
🐛 Troubleshooting
База данных не стартует:
docker-compose logs postgres
docker-compose down -v # Удалит volumes, ОСТОРОЖНО!
docker-compose up -d
API не отвечает:
docker-compose logs backend
docker-compose restart backend
Frontend не собирается:
cd frontend
npm install
npm run build
PostGIS ошибки:
docker exec -it polotsk-transit-db psql -U transit_admin -d polotsk_transit
SELECT PostGIS_Version();
📝 Следующие шаги (Roadmap)
Версия 2.0 - Краудсорсинг GPS
- Endpoint для получения GPS от пользователей
- Агрегация и фильтрация GPS данных
- WebSocket для реалтайм-обновлений
- Функция "Я еду в маршруте" в приложениях
Версия 3.0 - Продвинутые функции
- IoT GPS-трекеры интеграция
- Машинное обучение для предсказания задержек
- Push-уведомления о новых активных алертах
- Аналитика и отчеты
🤝 Контакты и поддержка
Создано для города Полоцка 🇧🇾
📄 Лицензия
MIT License - используйте свободно!
Устройства RideBus Board
В админке появился раздел Устройства (только для администратора). Он работает с обновлённым проектом ridebus-board: одноразовый код из четырёх цифр на экране привязывает отдельный браузер телевизора. В форме выбираются площадка и маршрут, тема/палитры, профиль читаемости, число строк, интервалы, информационные блоки и рекламный предпросмотр. Город задаётся переключателем транспортной сети в админке; устройства и каталоги изолированы по выбранному городу.
Перед запуском примените только новую миграцию backend/migrations/016_board_devices.sql к существующей базе данных (обычным способом миграций при развёртывании). Не перезапускайте общий npm run migrate на уже созданной базе: существующий скрипт повторяет все старые SQL-файлы. Таблицы board_pairings и board_devices содержат одноразовые сессии и зарегистрированные устройства. Миграция 016 повторяемая.
В сервере Board включите BOARD_REMOTE_SETTINGS=true, укажите обновлённый API в RIDEBUS_API_BASE_URL и адрес этой админки в необязательном BOARD_ADMIN_URL. Серверный RideBus API-ключ остаётся на Board и не выдаётся телевизору. Используйте HTTPS с Secure cookie.
Новый узкий API монтируется до общей API-key авторизации:
POST /api/v1/board-devices/pairings— создать сессию, TTL 5 минут, до 10 запросов за 10 минут с одного IP.GET /api/v1/board-devices/selfиPOST /self/heartbeat— только индивидуальныйX-Board-Token.GET /api/v1/board-devices/adminи/admin/routes?stopId=…— администратор + контекст города.POST /admin/claim— код, название, настройки; до 5 попыток за 5 минут с одного IP.PUT /admin/:id— настройки с ожидаемойrevision; конфликт даёт 409.POST /admin/:id/revoke— отозвать устройство в выбранном городе.
Короткий код не является токеном доступа. Секрет — 32 случайных байта, в базе только хеш; занятие кода и создание устройства атомарны в одной транзакции. Повторный запрос устройства после потерянного HTTP-ответа узнаёт уже созданную привязку. В списках и конфигурации секреты не возвращаются.
Табло опрашивает настройки раз в 15 секунд, отправляет сигнал работы раз в 30 и подтверждает применённую версию. «В сети» означает связь за последние 90 секунд. Офлайн-устройство продолжает работать со своим кешем, изменения остаются «Ожидают применения». Отзыв доступа действует после следующей связи с сервером. Команд перезагрузки ОС, удалённого shell или установки ПО нет.
Проверки: npm test --prefix backend, npm run build --prefix frontend. Отдельный PostgreSQL-тест устройств запускается с BOARD_DEVICE_TEST_DATABASE_URL (имя БД должно содержать test); он создаёт и удаляет изолированную схему, не использует рабочие таблицы. Совместный визуальный стенд находится в ridebus-board/test/fixtures/device-flow-server.mjs и использует явные тестовые данные.
Ночной режим табло
В настройках устройства доступны nightMode (auto / off) и nightWakeMinutes (45 / 60). По умолчанию — автоматически, возврат за 60 минут. Обновлённый Board включает режим после завершения движения, если весь ночной перерыв до первого следующего рейса длиннее двух часов, только по полному действующему расписанию. Перезагрузка в середине перерыва сохраняет ночной экран до выбранного порога возврата 45/60 минут. Ночные продолжения рейсов, фильтр маршрута и отмены учитываются. В ночном экране остаются первый рейс, часы без секунд и сообщения о движении; цикл информации и рекламы приостанавливается. Дневная тема возвращается при выходе. Лимиты, привязка и подтверждение версии сохраняются.
Поля находятся в существующем JSONB настроек устройств; новая SQL-миграция не требуется. Старые сохранённые конфигурации нормализуются при чтении (auto / 60) и не изменяются в базе автоматически. Для редактирования этих полей разверните обновлённые API, frontend админки и Board вместе.
Блок информации «Дальше по маршруту» заменяет первый и последний рейс. Его переключатель сохраняет существующий ключ infoServiceEnabled: ранее отключённый блок остаётся отключённым, SQL-миграция и изменение JSON устройства не нужны. Схему следующих остановок и время пути строит обновлённый Board из полного кеша расписания; в админке изменено только название переключателя.