I've build a test app (testing for RESTfull Api) to process some POST requests from another app. When both apps are run localy from PyCharm everything works ok - POST request activates a function which a frontend socketIO is monitoring, and thus triggering a Frontend element update.
My main goal is, however, to have some clients that run localy which will be sending POST requests to a server app in the cloud (Koyeb).
The platform needs to be run using Gunicorn WSGI server, and how ever I try to make it run, the socketIO part just wont work. POST requests are getting through and activate the required function, but socketIO doesn't work.
app.py code:
import eventlet
eventlet.monkey_patch()
from flask import Flask, render_template, request, redirect, send_file, url_for, jsonify
from flask_sqlalchemy import SQLAlchemy
import secrets, os, requests, json
from flask_login import LoginManager, UserMixin, login_user, logout_user, current_user, login_required
from flask_bcrypt import Bcrypt
import feedparser
from flask_socketio import SocketIO, emit
app = Flask(__name__)
socketio = SocketIO(app, async_mode='eventlet')
#
#other code
#
@app.route("/endpoint", methods=['GET', 'POST'])
def endpoint():
if request.method == 'POST':
#ApiDb.__table__.drop(db.engine)
#db.session.query(ApiDb).delete()
# dbVisits2 = db.session.query(ApiDb).filter_by(id=1).first()
try:
dbVisits = ApiDb.query.filter_by(id=1).first()
dbVisits.nrVisits += 1
except:
db.session.add(ApiDb(id=1, nrVisits=1))
db.session.commit()
#trigger_update()
handle_trigger_update()
return {'odgovor': 'OK'}
@socketio.on('trigger_update')
def handle_trigger_update():
dbVisits = ApiDb.query.filter_by(id=1).first()
total = dbVisits.nrVisits
socketio.emit('update', {'total': total})
@app.route('/trigger_backend')
def trigger_backend():
handle_trigger_update() # Poziv backend funkcije direktno
return 'Backend function triggered successfully'
if __name__=='__main__':
#socketio.run(app, debug=True, allow_unsafe_werkzeug=True)
#socketio.run(app)
#socketio.run(app, port=8000)
wsgi.server(eventlet.listen(('', 8000)), app)
html code:
<!DOCTYPE html>
<html lang="hr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %} Naslov {% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/w3css.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/fontawesome/css/all.min.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
</head>
<body>
<div class="outside-container">
<div class="form-container" style="width: clamp(600px, 30vw, 1000px);">
<h3>Comms</h3>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.3.1/socket.io.js" integrity="sha512-Y5MDU6RaF5h5HE5BgqJlKkV12kbkbIgWHutcT+XHHNOUzr+HHjWZGC02sqEguuPglmFms3cc08WH2PhQ5rF8Cw==" crossorigin="anonymous"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const socket = io.connect('http://' + document.domain + ':' + location.port, {
transports: ['websocket']
});
console.log('sckIO start');
socket.on('update', function(data) {
console.log('sckIO update');
document.getElementById('output3').innerText = data.total;
});
function triggerBackend() {
fetch('/trigger_backend')
.then(response => response.text())
.then(data => console.log(data)) // Log the response from the server
.catch(error => console.error('Error:', error));
}
triggerBackend();
});
</script>
<div style="display: flex; justify-content: center;">
Number of visits - periodicaly: <strong><div style="margin-left: 15px;" id="output"></div></strong>
</div>
<div style="display: flex; justify-content: center;">
Number of visits - onStart: <strong><div style="margin-left: 15px;" id="output2"></div></strong>
</div>
<div style="display: flex; justify-content: center;">
Number of visits - direct from userComm load: <strong><div style="margin-left: 15px;">{{ total }}</div></strong>
</div>
<div style="display: flex; justify-content: center;">
Number of visits - SocketIO: <strong><div style="margin-left: 15px;" id="output3"></div></strong>
</div>
</div>
</div>
</body>
</html>
Koyeb buildpack run command override: (Procfile)
gunicorn --worker-class eventlet -w 1 app:app
Anyone have some idea? It doesnt matter if it uses eventlet or gunicorn, I just want the socketIO to work
After days of searching the net, testing and experimenting, here is the solution: (the trick was to configure socketIO connect properly in your html)
app.py:
from flask import Flask, render_template, request, redirect, url_for
from flask_socketio import SocketIO, send, emit
app = Flask(__name__)
socketio = SocketIO(app)
#socketio = SocketIO(app, cors_allowed_origins='*', engineio_logger=True, logger=True) # for debugging
#
# other code
#
@app.route("/userComm", methods=['GET', 'POST'])
def userComm():
return render_template("userComm.html")
@app.route("/endpoint", methods=['GET', 'POST'])
def endpoint():
if request.method == 'POST':
dbVisits = ApiDb.query.filter_by(id=1).first()
visits = dbVisits.nrVisits
socketio.emit("update_visits", visits, namespace='/userComm')
return {'response': 'OK'}
@socketio.on('connect', namespace='/userComm') # not neccesary
def test_connect():
print('SocketIO client on /userComm connected')
@socketio.on('disconnect', namespace='/userComm') # not neccesary
def test_disconnect():
print('SocketIO client on /userComm disconnected')
if __name__=='__main__':
#socketio.run(app, debug=True, host='127.0.0.1', port=5000) #for testing as a local app
socketio.run(app, debug=True, host='0.0.0.0', port=8000)
userComm.html (as an extend to your basefile):
<h3>Comms</h3>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.3.1/socket.io.js" integrity="sha512-Y5MDU6RaF5h5HE5BgqJlKkV12kbkbIgWHutcT+XHHNOUzr+HHjWZGC02sqEguuPglmFms3cc08WH2PhQ5rF8Cw==" crossorigin="anonymous"></script>
<script>
socket = io('/userComm');
socket.on("update_visits", (visits)=>{
console.log(visits);
document.getElementById("output").innerHTML=visits;
});
</script>
<div style="display: flex; justify-content: center;">
Number of visits - SocketIO: <strong><div style="margin-left: 15px;" id="output"></div></strong>
</div>
Procfile:
web: gunicorn --worker-class eventlet -b 0.0.0.0:8000 -w 1 app:app