Проблема с обновлением фронтенда Flask-приложения на виртуальной машине
Всем привет! Столкнулся с проблемой при развертывании Flask-приложения на виртуальной машине. При локальном запуске всё работает корректно: фронтенд отображается как задумано, элементы интерфейса (кнопки, статус-бар) функционируют, данные обновляются в реальном времени. Однако при запуске на виртуальной машине (Ubuntu) фронтенд работает некорректно: не отображается кнопка "Остановить", статус-бар не обновляется, прогресс обработки не виден.
Backend: Flask, раздаётся через Gunicorn (-w 4 --timeout 300), проксируется через Nginx. Frontend: HTML-шаблон (index.html) с Tailwind CSS и jQuery, загружаемыми через CDN (cdn.jsdelivr.net, code.jquery.com). AJAX-запросы (/get_status) используются для обновления статуса обработки в реальном времени.
Логика: Пользователь вводит ID вакансии, отправляется POST-запрос на /process_vacancy, запускается обработка в отдельном потоке, а фронтенд опрашивает /get_status каждую секунду для отображения прогресса.
Проблема
Локально: При нажатии кнопки "Обработать" появляется кнопка "Остановить". Статус-бар и счётчик соискателей обновляются в реальном времени. Логи консоли браузера показывают успешные AJAX-запросы (/get_status).
На ВМ: Кнопка "Остановить" не появляется. Статус-бар и счётчик не обновляются.
В логах приложения (app.log) запросы к API обрабатываются
server {
listen 80;
server_name;
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_read_timeout 300;
proxy_connect_timeout 300;
}
}
app.py (упрощённый фрагмент)
from flask import Flask, render_template, request, jsonify
import threading
import queue
app = Flask(__name__)
processing_status = {
'is_processing': False,
'stop_processing': False,
'applicant_count': 0,
'progress': 0,
'message': '',
'current_vacancy_id': None
}
status_lock = threading.Lock()
progress_queue = queue.Queue()
@app.route('/')
def index():
with status_lock:
status = processing_status.copy()
return render_template('index.html', **status)
@app.route('/process_vacancy', methods=['POST'])
def process_vacancy():
with status_lock:
if processing_status['is_processing']:
return jsonify({'error': 'Обработка уже выполняется'}), 409
processing_status['is_processing'] = True
processing_status['stop_processing'] = False
processing_status['applicant_count'] = 0
processing_status['progress'] = 0
processing_status['message'] = 'Обработка начата'
processing_status['current_vacancy_id'] = request.form.get('vacancy_id')
def process(vacancy_id):
# Обработка вакансии (запросы к API, запись в Google Sheets)
# ...
with status_lock:
processing_status['is_processing'] = False
processing_status['message'] = f"Обработано: {inserted_count} записей"
processing_status['current_vacancy_id'] = None
threading.Thread(target=process, args=(request.form.get('vacancy_id'),), daemon=True).start()
return jsonify({'status': 'Processing started'}), 202
@app.route('/get_status')
def get_status():
with status_lock:
status = processing_status.copy()
try:
status['progress'] = progress_queue.get_nowait()
except queue.Empty:
pass
return jsonify(status)
index.html (упрощённый фрагмент)
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Обработка вакансий</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/tailwind.min.css" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>
<body class="bg-gray-100 flex items-center justify-center h-screen">
<div class="bg-white p-8 rounded-lg shadow-lg w-full max-w-md">
<form id="process-form" class="space-y-4">
<input type="text" id="vacancy_id" name="vacancy_id" required>
<button type="submit" id="process-btn" class="w-full bg-blue-500 text-white p-2 rounded-md">Обработать</button>
</form>
<form id="stop-form" class="mt-4 {% if not is_processing %}hidden{% endif %}">
<button type="submit" class="w-full bg-red-500 text-white p-2 rounded-md">Остановить</button>
</form>
<div class="mt-4">
<p>Найдено соискателей: <span id="applicant_count">{{ applicant_count }}</span></p>
<div class="w-full bg-gray-200 rounded-full h-4">
<div id="progress_bar" class="bg-blue-500 h-4 rounded-full" style="width: {{ progress }}%"></div>
</div>
<p><span id="progress_text">{{ progress | round(2) }}</span>%</p>
</div>
</div>
<script>
function updateStatus() {
console.log('Sending AJAX request to /get_status at ' + new Date().toISOString());
$.ajax({
url: '/get_status',
method: 'GET',
timeout: 5000,
success: function(data) {
console.log('Received status at ' + new Date().toISOString() + ':', data);
$('#applicant_count').text(data.applicant_count || 0);
$('#progress_bar').css('width', (data.progress || 0) + '%');
$('#progress_text').text((data.progress || 0).toFixed(2));
if (data.is_processing) {
$('#process-btn').prop('disabled', true);
$('#stop-form').removeClass('hidden');
setTimeout(updateStatus, 1000);
} else {
$('#process-btn').prop('disabled', false);
$('#stop-form').addClass('hidden');
}
},
error: function(xhr, status, error) {
console.error('Error in /get_status:', {status: status, error: error});
}
});
}
$(document).ready(function() {
$('#process-form').on('submit', function(e) {
e.preventDefault();
console.log('Submitting process form');
$.ajax({
url: '/process_vacancy',
method: 'POST',
data: $(this).serialize(),
success: function(response) {
console.log('Process started:', response);
updateStatus();
},
error: function(xhr) {
console.error('Error in /process_vacancy');
}
});
});
});
</script>
</body>
</html>
Проверил загрузку статики (Tailwind, jQuery) через CDN — в Network-вкладке статус 200. Добавлял отладочные логи в JavaScript — запрос /get_status отправляется, но ответ не приходит. Увеличивал таймауты в Gunicorn и Nginx до 300 секунд. Проверял логи сервера — обработка вакансий завершается (иногда быстро, если данные уже есть), ошибок нет. Проверял сетевые настройки: порт 80 открыт, curl http://localhost:5000/get_status возвращает корректный JSON.
Почему фронтенд работает корректно локально, но на виртуальной машине не обновляется (кнопка "Остановить" не появляется, статус-бар не движется)? Может ли это быть связано с настройками Gunicorn/Nginx, сетевыми ограничениями или поведением AJAX-запросов? Какие шаги можно предпринять для диагностики и исправления?
Заранее спасибо за помощь!