aspans_patrol/README.md
2026-10-22 11:09:38 +05:00

82 lines
8.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Aspans · Обходы
Сервис обходов охраняемых объектов: периметры и маршруты на карте, график дежурств, мобильный экран сотрудника с офлайн-очередью, отчёты с фото, видео и геометками, вебхуки для интеграций.
Стек: React 18 + TypeScript + MUI (Vite, PWA), NestJS 11 + TypeORM, PostgreSQL 17 + PostGIS, SeaweedFS (S3 API) для медиа, tileserver-gl для своей карты Казахстана. Всё поднимается через docker compose.
## Запуск
```bash
cp .env.example .env
./scripts/build-tiles.sh
docker compose up -d --build
```
Откройте http://localhost:8090 и войдите владельцем платформы — учётной записью паспорта Aspans из `PLATFORM_ADMIN_USERNAMES` (по умолчанию `admin`, с его паролем из паспорта). Компанию можно зарегистрировать там же («Организации») или самостоятельно на странице входа → «Зарегистрировать».
Перед реальным использованием смените в `.env` `JWT_SECRET`, `POSTGRES_PASSWORD`, `S3_SECRET_KEY`.
## Паспорт Aspans, Aspans ID и Kafka
Люди и пароли живут в паспорте Aspans, «Обходы» — платформа `patrol` в нём. Организации, роли и доступ к объектам — свои, внутри «Обходов».
1. В паспорте через интерфейс «Платформы и группы» создать платформу `patrol` (без домена) и её группу `patrol_user`; выложить метод `v1/users/findUsers`.
2. В `.env`: `PASSPORT_URL`, `PASSPORT_PLATFORM_TOKEN` (= PLATFORM_SALT паспорта), `ASPANS_ID_BASE_URL`. Локально паспорт — контейнер в docker-сети `aspans` (см. `docker-compose.override.yml`), на сервере — `https://passport.aspans.com`.
3. Синхронизация через Kafka (как у symfony): в проекте kafka
`make service NAME=platform-patrol` и `./bin/service.sh grant-shared platform-patrol aspans.`,
пароль — в `KAFKA_PASSWORD`, затем `docker compose --profile kafka up -d users_sync`.
Как это работает:
- **Вход** — логином и паролем паспорта или через Aspans ID (SSO). Если человек уже вошёл в другой сервис Aspans, «Обходы» входят тихо (`prompt=none`). Пускают только тех, кто заведён в «Обходах» и состоит в организации с ролью; владельцы платформы — по `PLATFORM_ADMIN_USERNAMES`.
- **Новый сотрудник** заводится в паспорте (`createOrGetUserSimple`, группа `patrol_user`). **Существующий** — ищется в паспорте (`findUsers`, контакты замаскированы) и привязывается к платформе. Один человек — одна организация в «Обходах».
- **Профиль** (ФИО, email, телефон) правится в обе стороны через `aspans.users.changed`: правки из «Обходов» уходят событием `UserProfileUpsertedEvent` (очередь `users_sync_pending`), правки из паспорта применяются по версии (LWW). Пароль меняется сразу в паспорте.
- **Удаление**: убрали с платформы в паспорте (`UserRemovedFromPlatformEvent`) — доступ закрывается, сессии гаснут, человек пропадает из сотрудников; запись остаётся для отчётов. «Убрать из организации» в «Обходах» отвязывает человека от платформы в паспорте.
`build-tiles.sh` один раз скачивает OSM-данные Казахстана и собирает `tiles/kazakhstan.mbtiles` (planetiler, нужно ~4 ГБ RAM, несколько минут). Без этого файла сервис работает, но карта будет пустой. Повторный запуск обновляет карту, после него — `docker compose restart tiles`.
## Сервер
Сервис отдаётся по http на `WEB_PORT` (8090). HTTPS делает прокси перед сервером. Пока его нет, на телефонах охранников не работают геолокация и офлайн-режим PWA: браузеры дают их только по HTTPS.
1. Нужен `docker compose` v2 (не старый `docker-compose` 1.x). Проверка сервера, только чтение: `ssh user@IP 'bash -s' < scripts/server-check.sh`.
2. Склонировать репозиторий, `cp .env.production.example .env`, заполнить.
3. Карту на сервере **не собирать**: скопировать готовый файл `tiles/kazakhstan.mbtiles` (~0,5 ГБ) в `tiles/` проекта. Сборка planetiler требует ~4 ГБ RAM и качает исходные данные.
4. `docker compose up -d --build` (`COMPOSE_FILE` из `.env` сам подключит `docker-compose.prod.yml`).
5. В Aspans ID у клиента `patrol` разрешить redirect_uri `${WEBSITE_URL}/auth/sso/callback`.
Наружу открыт только web на `WEB_PORT`. База слушает 127.0.0.1, api, tiles и s3 доступны только внутри docker.
## Порты
| Что | Адрес |
|---|---|
| Веб и API | http://localhost:8090 (`/api/…`, карта — `/tiles/…`) |
| PostgreSQL | localhost:5470 |
## Разработка без Docker для фронта
```bash
npm install
docker compose up -d db s3 tiles api web
npm run dev:web
```
Vite на http://localhost:5173 проксирует `/api` и `/tiles` на :8090.
## Устройство
```
apps/api NestJS: auth, organizations, users/roles, sites, routes, schedules, rounds, media, reports, webhooks
apps/web React: панель управления и экран сотрудника /guard (PWA, офлайн-очередь в IndexedDB)
packages/shared права, статусы, флаги отметок, события вебхуков — общие для фронта и API
docker/ Dockerfile'ы и nginx
scripts/ сборка тайлов карты
```
- Обходы создаются из графиков на 7 дней вперёд (cron раз в час) для дней, где назначены дежурные. Начать обход может любой дежурный этого дня.
- Незакрытые к концу окна обходы становятся «Пропущен» или «Не полностью». Отметки, снятые офлайн до конца окна и пришедшие позже, пересчитывают статус.
- GPS только записывается: отметку «я тут» сотрудник ставит на карте, координаты телефона сохраняются рядом. Расхождения видны в отчёте флагами и ничего не блокируют.
- Медиа грузятся кусками по 8 МБ через API в SeaweedFS и докачиваются после обрыва связи.
- Вебхуки: `POST` с JSON, подпись `X-Patrol-Signature: sha256=<HMAC тела секретом>`; события `round.completed`, `round.partial`, `round.missed`, `visit.issue`.
- Реквизиты по БИН — из pk.uchet.kz: публичный поиск без токена даёт название, адрес и руководителя. С токеном из личного кабинета pk.uchet.kz (`UCHET_KZ_TOKEN` в `.env`) добавляются телефон и email. Выключить обращения к uchet.kz: `UCHET_KZ_DISABLED=true`.