Документ — практический чеклист “пройти полный путь руками”: поднять стек, залогиниться в Portal через auth-proxy, создать сущности (project/experiment/run/capture session/sensor), отправить телеметрию и проверить результат в UI и в PostgreSQL.
См. также:
docs/demo-flow.md— короткий сценарий демо (5–7 минут, минимум шагов).docs/mvp-acceptance-checklist.md— технический чеклист приемки MVP.
- Docker +
docker-compose make- Порты свободны:
3000,8001,8002,8003,8080,5433,3001
Из корня репозитория:
cp env.docker.example .env
cp docker-compose.override.yml.example docker-compose.override.yml
make dev-upПроверки:
curl -fsS http://localhost:8001/health && echo "auth-service ok"
curl -fsS http://localhost:8002/health && echo "experiment-service ok"
curl -fsS http://localhost:8003/health && echo "telemetry-ingest ok"
curl -fsS http://localhost:8080/health && echo "auth-proxy ok"UI:
- Portal:
http://localhost:3000 - Grafana:
http://localhost:3001(по умолчаниюadmin/admin)
В Portal пока нет страницы регистрации, поэтому пользователя проще создать через Auth Service:
TS="$(date +%s)"
USERNAME="manual${TS}"
EMAIL="manual${TS}@example.com"
PASSWORD="manual12345"
curl -fsS -X POST http://localhost:8001/auth/register \
-H 'Content-Type: application/json' \
-d "{\"username\":\"${USERNAME}\",\"email\":\"${EMAIL}\",\"password\":\"${PASSWORD}\"}"
echo
echo "Created user: ${USERNAME} / ${PASSWORD}"Альтернатива: использовать дефолтного пользователя, который выводится в make dev-up (обычно admin/admin123), но он может требовать смену пароля при первом входе.
- Откройте
http://localhost:3000 - Перейдите на
/login(Portal сам редиректит туда, если вы не авторизованы) - Введите
USERNAME/PASSWORDиз шага выше
Ожидание:
- после успешного логина появляются защищённые страницы (
/projects,/experiments,/sensors) - в браузере появляются cookies от
auth-proxy, включаяcsrf_token(НЕ HttpOnly)
- Откройте страницу Projects:
http://localhost:3000/projects - Нажмите “Create” / “Новый проект” (название зависит от UI)
- Создайте проект (например
Manual Project) - Убедитесь, что проект выбран/активирован (Portal использует активный
project_idдля запросов к Experiment Service)
Ожидание:
- проект появляется в списке
- дальнейшие запросы (Experiments/Sensors) работают в контексте выбранного проекта
- Откройте
http://localhost:3000/sensors - Нажмите “New sensor” / “Создать сенсор”
- Заполните поля (пример):
- name:
temperature_raw - type:
thermocouple - input_unit:
mV - display_unit:
C
- name:
- После создания UI должен показать sensor token (одноразовая выдача) — скопируйте его.
Ожидание:
- сенсор в списке
- в деталях сенсора
last_heartbeatпустой (до первой телеметрии)
- Откройте
http://localhost:3000/experiments - Создайте эксперимент (например
Manual Experiment) - Откройте созданный эксперимент
- Создайте run (например
run-1) - Перейдите в run (страница вида
http://localhost:3000/runs/<run_id>)
Ожидание:
- эксперимент виден в списке, открывается детальная страница
- run создаётся и открывается
На странице run:
- На странице run найдите кнопку “Старт отсчёта” (в правой части шапки run, рядом со статусом).
Если у вас run в статусе «Черновик», эта кнопка всё равно должна быть доступна (Portal разрешает создавать capture session для
draft/running). Альтернатива: в блоке «Сессии отсчёта» можно нажать «Создать сессию». - Убедитесь, что в блоке Capture Sessions появилась активная сессия
- (Опционально) Нажмите “Остановить отсчёт” → статус меняется на
succeeded/failedв зависимости от выбора UI
Ожидание:
capture_session_idпоявляется в списке сессий
Нужно:
SENSOR_ID(из UI сенсора)SENSOR_TOKEN(скопирован на шаге 5)RUN_IDиCAPTURE_SESSION_ID(из страницы run / capture sessions)
Пример запроса (замените плейсхолдеры):
curl -fsS -X POST http://localhost:8003/api/v1/telemetry \
-H "Authorization: Bearer <SENSOR_TOKEN>" \
-H 'Content-Type: application/json' \
-d "{
\"sensor_id\": \"<SENSOR_ID>\",
\"run_id\": \"<RUN_ID>\",
\"capture_session_id\": \"<CAPTURE_SESSION_ID>\",
\"meta\": {\"source\": \"manual\"},
\"readings\": [
{\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\", \"raw_value\": 1.23, \"meta\": {\"step\": 1}}
]
}"
echoОжидание:
- ответ вида
{ "status": "accepted", "accepted": 1 }(код может быть200или202)
- Откройте страницу сенсора (
/sensors→ выбрать сенсор) - Убедитесь, что
last_heartbeatобновился (не пустой)
Проверить, что запись реально появилась в telemetry_records:
docker-compose exec -T postgres psql -U postgres -d experiment_db -c \
"select count(*) from telemetry_records where sensor_id='<SENSOR_ID>';"Ожидание: count >= 1.
Auth Proxy использует double-submit cookie:
- после
POST /auth/loginвыставляется cookiecsrf_token(НЕ HttpOnly) - для POST/PUT/PATCH/DELETE запросов при наличии session cookies нужен заголовок
X-CSRF-Tokenравныйcsrf_token
Быстрый тест (проверяем именно поведение прокси — upstream может вернуть другую ошибку, но не 403 от CSRF):
- Без
X-CSRF-Token(ожидаем403):
curl -i -sS -X POST http://localhost:8080/api/any/state-changing \
-H 'Content-Type: application/json' \
--cookie "access_token=dummy; csrf_token=dummy" \
-d '{"ping":1}' | head -n 20- С
X-CSRF-Token(CSRF-барьер снят; дальше ответ зависит от upstream):
curl -i -sS -X POST http://localhost:8080/api/any/state-changing \
-H 'Content-Type: application/json' \
-H 'X-CSRF-Token: dummy' \
--cookie "access_token=dummy; csrf_token=dummy" \
-d '{"ping":1}' | head -n 20Важно: в реальном браузерном флоу csrf_token берётся из cookie автоматически (см. src/utils/csrf.ts и интерсепторы axios).
- docker-compose ошибка
ContainerConfig:make dev-fix
- Portal не видит данные / 403:
- проверьте, что выбран/активирован нужный проект в UI
- обновите страницу и повторите запрос
- 403 CSRF от
auth-proxy:- проверьте наличие cookie
csrf_tokenи заголовкаX-CSRF-Tokenна state-changing запросах
- проверьте наличие cookie