Изменения в /api/n8n/documents/attach:
✅ Принимает массив документов (не одиночный объект)
✅ Умная обработка S3 путей:
- /bucket/path → https://s3.twcstorage.ru/bucket/path
- bucket/path → https://s3.twcstorage.ru/bucket/path
- https://... → без изменений
✅ Поддержка обоих форматов полей:
- file / file_url
- filename / file_name
✅ Batch-обработка с детальной статистикой
✅ Возвращает результаты для каждого документа отдельно
✅ Логирование успешных и неуспешных операций
Формат ответа:
{
total_processed: N,
successful: M,
failed: K,
results: [...],
errors: [...]
}
Тесты:
- TEST_REAL_DATA.sh - тест с реальными данными из n8n
- TEST_QUICK.sh - быстрые тесты
Документация обновлена с примерами batch-обработки
Изменения:
✅ Новый endpoint: POST /api/n8n/documents/attach
✅ Поддерживает привязку к Project или HelpDesk
✅ Логика: если указан ticket_id → HelpDesk, иначе → Project
✅ Полное логирование всех операций
✅ Интеграция с upload_documents_to_crm.php
Входные данные:
- contact_id (обязательно)
- project_id (обязательно)
- file_url (обязательно)
- file_name (обязательно)
- ticket_id (опционально, для привязки к заявке)
- file_type (опционально, описание документа)
Готово к интеграции в n8n workflow!
Изменения:
✅ Добавлены переменные n8n_create_contact_webhook и n8n_create_claim_webhook в Settings
✅ Все webhook URLs теперь централизованно в .env
✅ Удалён хардкод из n8n_proxy.py
Теперь все N8N webhooks:
- N8N_POLICY_CHECK_WEBHOOK
- N8N_FILE_UPLOAD_WEBHOOK
- N8N_CREATE_CONTACT_WEBHOOK
- N8N_CREATE_CLAIM_WEBHOOK
Проблема:
- Backend не логировал что именно n8n возвращает для /api/n8n/policy/check
- Не видно откуда брать project_id в response
Исправление:
✅ Добавлено логирование response.text[:500] для policy/check
✅ Добавлена обработка ошибок парсинга JSON
Теперь в логах видно полный ответ от n8n!
Проблема:
- Step1Phone делал запрос НАПРЯМУЮ к n8n (палил webhook URL)
- В backend логах не было видно что n8n возвращает для контакта
- Нельзя было отследить contact_id, claim_id, is_new_contact
Решение:
✅ Добавлен endpoint /api/n8n/contact/create в n8n_proxy.py
✅ Step1Phone.tsx теперь использует proxy вместо прямого URL
✅ Backend логирует полный response от n8n (contact_id, claim_id и тд)
Теперь весь трафик к n8n идёт через backend proxy!
Проблема:
- TypeScript игнорировал project_id, is_new_project, ticket_id, ticket_number
- Они не были объявлены в interface FormData
Исправление:
✅ Добавлены в FormData:
- project_id?: string (ID проекта в vTiger)
- is_new_project?: boolean (флаг создания)
- ticket_id?: string (ID заявки HelpDesk)
- ticket_number?: string (номер заявки)
Теперь updateFormData корректно сохраняет все данные от n8n!
Проблема:
- n8n возвращает [{success: true, result: {claim_id, contact_id, ...}}]
- Код пытался взять data.claim_id вместо data.result.claim_id
Исправление:
- ✅ Обработка массива от n8n
- ✅ Извлечение данных из result: const result = crmResult.result || crmResult
- ✅ Улучшенное логирование для отладки
- ✅ Проверка crmResult.success перед обработкой
Теперь formData корректно получает:
- claim_id (от n8n)
- contact_id (от CreateWebContact)
- is_new_contact (флаг)
- ✅ Добавлена обработка массива от n8n (как в Step2EventType)
- ✅ Сохранение project_id, is_new_project в formData
- ✅ Сохранение contact_id для консистентности
- ✅ Работает как для найденного, так и для не найденного полиса
Теперь formData содержит полную информацию:
- claim_id (из n8n)
- contact_id (из CreateWebContact)
- project_id (из CreateWebProject) ← НОВОЕ
- is_new_project (флаг создания) ← НОВОЕ
Подробная документация:
- ✅ CreateWebClaim.php - операция vTiger для заявок
- ✅ n8n workflow get_claim_CRM_ERV (ID: qdYZqhIDGhK9E4DA)
- ✅ Backend proxy /api/n8n/claim/create
- ✅ Frontend интеграция Step2EventType
- ✅ Решенные проблемы: BOM, пустой ответ n8n, массив вместо объекта
- ✅ Полный флоу создания заявки
- ✅ Метрики и статистика
Создано заявок: 8 (последняя ЗАЯВКА_825)
Время работы: 4 часа 15 минут
- ✅ Новый endpoint: POST /api/n8n/claim/create
- ✅ Проксирует запросы к n8n webhook создания заявки
- ✅ Frontend теперь использует /api/n8n/claim/create вместо прямого URL
- ✅ Решает проблему CORS и скрывает webhook URL
- ✅ Логирование запросов и ошибок
- ✅ n8n может вернуть [{success: true, ...}] вместо {success: true, ...}
- ✅ Добавлена проверка Array.isArray и извлечение первого элемента
- ✅ Теперь корректно обрабатывается ответ от webhook создания заявки
- ✅ Вызов n8n webhook после выбора типа события
- ✅ Формирование title из event_type + voucher
- ✅ Передача всех данных: claim_id, contact_id, project_id, event_type
- ✅ Сохранение ticket_id и ticket_number в formData
- ✅ Loading состояние кнопки
- ✅ Debug события для отслеживания
- Step1Phone теперь вызывает n8n webhook после SMS верификации
- Webhook создаёт/находит контакт в CRM через CreateWebContact
- Возвращает: contact_id, claim_id, is_new_contact
- Данные сохраняются в formData для дальнейшей работы
- Исправлена нормализация телефона в sms_service (убираем +)
- Отключен rate limiting SMS для тестирования
- Backend подключён к внешнему Redis (crm.clientright.ru:6379)
- Добавлены поля contact_id, is_new_contact в FormData
- Frontend пересобран с новым кодом
- Перенос телефона на первый шаг с SMS верификацией
- Создана операция CreateWebContact в vTiger CRM
- N8N workflow: контакт + claim_id + Redis session
- Docker-compose: убраны локальные postgres/redis
- Backend подключён к внешним сервисам
- Флаг is_new_contact для UX (новый vs существующий клиент)
- Исправлено 7 проблем (Postgres v16, Redis, N8N webhooks, Gitea)
- Готовность к черновикам и личному кабинету
- docker-compose.yml: убраны локальные postgres/redis, только внешние
- Frontend: телефон в формате 79001234567 (без +)
- Готово к интеграции с n8n webhook для создания контакта в CRM
- CreateWebContact: только создание или возврат ID, БЕЗ обновления
Детальный лог работы по спрятыванию webhook URLs:
- Backend proxy для n8n
- Webhook URLs в .env
- Исправления проблем (относительные пути, event_type, пропущенные поля)
- Полная документация SECURITY_N8N_PROXY.md
- 4 коммита, все проблемы решены
Результат: Webhook URLs больше не видны в коде фронтенда
Проблема: Frontend ждал event_type='ocr_completed', но n8n отправлял 'policy_ocr_completed'
Из-за этого событие не обрабатывалось и показывалась ошибка
Решение: Гибкая проверка event_type:
- ocr_completed (старый формат)
- policy_ocr_completed (новый формат из n8n)
- любой *_ocr_completed (includes)
Теперь работает с любыми workflow которые шлют разные типы событий
Проблема: Backend proxy не передавал filename и upload_timestamp к n8n
Это ломало обработку файлов в workflow
Изменения:
- Добавлены параметры filename и upload_timestamp в proxy_file_upload()
- Теперь передаём все поля которые отправляет frontend
Было в n8n body: {claim_id, voucher, session_id, file_type}
Стало: {claim_id, voucher, session_id, file_type, filename, upload_timestamp}
Проблема: Frontend в Docker не мог достучаться до http://localhost:8100
Решение: Используем '/api/n8n/*' - Vite proxy автоматически перенаправит на backend
Изменения:
- Step1Policy.tsx: fetch('/api/n8n/policy/check')
- Step1Policy.tsx: fetch('/api/n8n/upload/file')
- StepDocumentUpload.tsx: fetch('/api/n8n/upload/file')
Vite proxy настроен в vite.config.ts:
/api -> host.docker.internal:8100
/events -> host.docker.internal:8100
- Создан n8n_proxy.py для безопасного проксирования запросов
- Webhook URLs перенесены в .env (скрыты от фронтенда)
- Frontend теперь использует /api/n8n/* endpoints
- Добавлена документация SECURITY_N8N_PROXY.md
Преимущества:
- Webhook URLs не видны в DevTools
- Централизованное логирование
- Возможность добавить rate limiting и auth
- Легко менять URLs без пересборки фронтенда
- Документирован полный рефакторинг визарда (Вариант B)
- Каждый документ = отдельный шаг в прогресс-баре
- Созданы Step2EventType.tsx, StepDocumentUpload.tsx, documentConfigs.ts
- Переделан ClaimForm.tsx на динамические шаги через useMemo
- Исправлены проблемы: URL n8n, FormData структура, SSE логирование
- Исправлены прогресс и навигация через useCallback
- Всего 9 коммитов, ~1500 строк кода
- Обернул nextStep, prevStep, updateFormData, handleSubmit в useCallback
- Теперь функции стабильны и не пересоздаются при ререндере
- nextStep и prevStep используют functional update для setState
- Добавлено логирование навигации: '⏩ nextStep' и '⏪ prevStep'
- Исправлены зависимости useMemo для steps
ПРОБЛЕМА: prevStep вызывался, но setCurrentStep не обновлял стейт
РЕШЕНИЕ: useCallback гарантирует что функции стабильны
- Добавлен disabled={false} для основной кнопки Назад
- Добавлен disabled={false} для DEV кнопок (Назад и Пропустить)
- Добавлено логирование при нажатии для отладки
- Теперь кнопки гарантированно кликабельны
- Изменён console.error на console.log для нормального закрытия SSE
- Теперь показывается '✅ SSE закрыто после получения результата - всё ОК'
- Реальная ошибка выводится только если данные не получены
- Консоль больше не пугает красными ошибками при успешной работе
- Upload: добавлен disabled во время загрузки
- Upload onChange: правильная обработка newFileList
- Кнопка Назад: убран disabled - теперь всегда доступна
- DEV кнопка Назад: также убран disabled
- Добавлено логирование handleUpload и onChange для отладки
- Создан Step2EventType.tsx для выбора типа страхового случая
- Создан StepDocumentUpload.tsx - универсальный компонент для загрузки одного документа
- Создан constants/documentConfigs.ts с конфигурацией документов для всех типов событий
- Переделан ClaimForm.tsx на динамическое создание шагов через useMemo
- Прогресс-бар теперь показывает: [Полис] → [Тип] → [Док1] → [Док2] → [Оплата]
- Бэкап старого Step2Details сохранён как Step2Details.OLD_WIZARD_INLINE.tsx
- Каждый документ загружается на отдельном шаге с модалкой обработки
- SSE для каждого документа с уникальным event_type
- DEV MODE кнопки для быстрой навигации на всех шагах
Проблема: После строки 657 весь код компонента дублировался,
что вызывало синтаксическую ошибку 'Unexpected token' на строке 659.
Решение: Удалены строки 659-820 (дубликат кода).
🎯 Изменения:
- Документы загружаются по очереди (один за другим)
- После загрузки каждого документа открывается модалка с крутилкой
- SSE слушает конкретный event_type: {file_type}_processed
- Модалка показывает результат распознавания с извлечёнными данными
- Кнопка 'Продолжить' → переход к следующему документу
- Опциональные документы можно пропустить
- После обработки всех обязательных → 'Далее на Step 3'
📊 UX флоу:
1. Выбор типа события → показываются нужные документы
2. Документ 1: Выбрать файл → Загрузить → Модалка → Результат → Продолжить
3. Документ 2: Выбрать файл → Загрузить → Модалка → Результат → Продолжить
4. Документ 3 (опц): Загрузить ИЛИ Пропустить
5. Все обязательные обработаны → Далее на Step 3
🔑 Каждый документ получает свой уникальный event_type:
- frontend отправляет file_type
- n8n возвращает event_type = {file_type}_processed
- frontend слушает этот конкретный event_type через SSE