82 lines
8.6 KiB
Markdown
82 lines
8.6 KiB
Markdown
# 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`.
|
||
|
||
## Сервер
|
||
|
||
Без своего домена сервис открывается по `https://<ip-через-дефисы>.sslip.io`: sslip.io указывает на IP из имени, Caddy сам получает сертификат Let's Encrypt. HTTPS обязателен для телефонов охранников: без него браузер не даёт геолокацию и не включает офлайн-режим PWA.
|
||
|
||
1. Проверить сервер (только читает): `ssh user@IP 'bash -s' < scripts/server-check.sh`. Нужно: Docker с `docker compose` v2, свободные 80 и 443 (и открытые в файрволе), от 2 ГБ RAM (сборка фронта), от 10 ГБ свободного места плюс место под фото и видео.
|
||
2. Склонировать репозиторий, `cp .env.production.example .env`, заполнить.
|
||
3. Карту на сервере **не собирать**: скопировать готовый файл — `scp tiles/kazakhstan.mbtiles user@IP:<папка проекта>/tiles/` (~0,5 ГБ). Сборка planetiler требует ~4 ГБ RAM и качает исходные данные.
|
||
4. `docker compose up -d --build` (`COMPOSE_FILE` из `.env` сам подключит `docker-compose.prod.yml`).
|
||
5. В Aspans ID у клиента `patrol` разрешить redirect_uri `https://<ip-через-дефисы>.sslip.io/auth/sso/callback`.
|
||
|
||
Наружу открыты только 80 и 443 (Caddy). База слушает 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`.
|