Предыстория проекта: почему вообще пришлось это делать
Всё началось с того, что я разрабатывал ИИ-аналитика в n8n, который бы анализировал продажи компании по Excel-таблицам. Система должна была автоматически генерировать отчеты о проделанном анализе.
Но вот незадача: после анализа данных нужно было всё это красиво оформить в текстовый документ с таблицами и текстом.
Через Google Doc API заполнять таблицы практически невозможно: постоянные баги и ошибки. Я начал искать готовые решения для преобразования JSON в DOCX (в который также вставлялись бы и рандомные таблицы) и столкнулся с жестокой реальностью:
99% сервисов в сети имели критические недостатки:
- Платные - неподъемные тарифы для небольшого проекта
- Работают только через шаблоны - нужно заранее создавать шаблоны таблицы с переменными типа {{variable}} и только потом их передавать через http запрос
- Нет гибкости - невозможно работать с динамическими, случайными таблицами.
- Сложная автоматизация - для внутренней канцелярии компании, где каждый отчет уникален, шаблонный подход просто не работает
Представьте: у вас каждый день новые метрики, разные структуры таблиц, меняющиеся форматы данных. Создавать под каждый случай шаблон - это адская работа, которая убивает всю автоматизацию.
Именно поэтому я решил разработать свой микро-сервис на FastAPI, который:
- Принимает любой JSON с данными
- Динамически создает таблицы (может создавать неограниченное количество таблиц в одном документе за раз)
- Чередует текст и таблицы в произвольном порядке
- Возвращает готовый DOCX файл
Примерно так (итоговый результат работы микро сервиса):
Начало разработки и обманчивые успехи
Код был написан на Python с использованием:
- FastAPI
- python-docx для работы с Word документами
- uvicorn как ASGI-сервер
Сервер принимал JSON вида:
json
[
{"text": "Заголовочный текст"},
{"title": "Таблица фактов", "data": [{"metric": "value"}]},
{"text": "Пояснительный текст"},
{"title": "Топ товаров", "data": [{"product": "Name", "sales": 1000}]}
]
И возвращал готовый DOCX файл.
На тестовом стенде (Replit) всё работало идеально! Запросы из n8n обрабатывались, файлы генерировались. Казалось бы - победа!
Перенос на VPS: начало адской саги
Я развернул код на своем VPS, используя Docker. Запустил контейнер, проверил - сервер работает. Но когда я отправил ИДЕНТИЧНЫЙ запрос из n8n, который прекрасно работал на Replit, на моем VPS я получил:
"422 - Input should be a valid dictionary"
Диагностика проблемы
Первая теория: SSL сертификат
Так как на Replit был HTTPS, а на VPS изначально был HTTP, я предположил проблему с SSL.
Как решал: купил домен, привязал его к микро сервису, получил SSL.
bash
# Настройка nginx
sudo nano /etc/nginx/sites-available/json-docx.ru
server {
listen 80;
server_name json-docx.ru;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
server_name json-docx.ru;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Активация конфига и SSL
sudo ln -s /etc/nginx/sites-available/json-docx.ru /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d json-docx.ru
Результат: SSL заработал, но ошибка 422 осталась.
Вторая теория: права доступа
Проверил логи и обнаружил, что папка output для временных файлов не создавалась:
bash
# В контейнере
mkdir -p output
chmod 777 output
Результат: Папка создана, но ошибка осталась.
Третья теория: разные версии FastAPI
Сравнил версии пакетов - оказалось, что на VPS в контейнере вообще не было установленных пакетов FastAPI!
bash
# Проверка версий на VPS
pip show fastapi pydantic uvicorn
# WARNING: Package(s) not found: fastapi, pydantic, uvicorn
Вот оно! Проблема была в том, что контейнер многократно пересобирался, но Docker использовал кэш, и зависимости не устанавливались заново.
Решение: пересборка контейнера с зависимостями
bash
# Остановка и удаление старого контейнера
docker stop json_docx_api
docker rm json_docx_api
# Пересборка с очисткой кэша
docker build --no-cache -t json-docx .
# Запуск нового контейнера
docker run -d -p 5000:5000 --name json_docx_api json-docx
# Проверка логов
docker logs json_docx_api
После этого всё заработало! 🎉
Выводы и уроки на будущее
1. Всегда проверяйте зависимости в production-окружении
bash
# Всегда проверяйте, что пакеты установлены
docker exec -it container_name pip list
# Или добавляйте логирование версий при старте
import fastapi
print(f"FastAPI version: {fastapi.__version__}")
2. Docker кэш - друг и враг
Используйте "--no-cache" при пересборке, если есть подозрения на устаревшие зависимости:
bash
docker build --no-cache -t your_image .
3. Всегда мониторьте логи
Добавляйте подробное логирование в код:
python
import logging
logging.basicConfig(level=logging.INFO)
@app.post("/generate-docx")
async def generate_docx(data: Any = Body(...)):
logging.info(f"Received: {type(data)} - {data}")
4. Проверяйте права доступа к файловой системе
Убедитесь, что приложение имеет права на запись в нужные директории.
5. Тестируйте полный цикл
Не доверяйте тому, что "на тестовом стенде работает". Всегда тестируйте полный цикл в production-окружении.
Итог
Проблема, которая съела 2 дня жизни, оказалась банальной - отсутствие пакетов в Docker-контейнере из-за кэширования при сборке. Но путь к этому осознанию прошел через:
- Настройку nginx и SSL
- Регистрацию домена и настройку DNS
- Анализ логов и ошибок
- Понимание работы Docker кэша
Но все было не зря: теперь у меня работает микро-сервис, который автоматически генерирует текстовые документы с таблицами без заранее заготовленных шаблонов.
P.S.
Если у Вас есть вопросы по n8n, автоматизациям, разработке простых приложений с помощью ИИ и их деплою, пишите в ТГ. Буду рад помочь! https://t.me/evgen1992_1992