Add global presence heartbeat and online tracking
This commit is contained in:
@@ -27,10 +27,12 @@ def create_app():
|
|||||||
from .routes.auth import auth_bp
|
from .routes.auth import auth_bp
|
||||||
from .routes.main import main_bp
|
from .routes.main import main_bp
|
||||||
from .routes.friends import friends_bp
|
from .routes.friends import friends_bp
|
||||||
|
from .routes.presence import presence_bp
|
||||||
|
|
||||||
app.register_blueprint(main_bp)
|
app.register_blueprint(main_bp)
|
||||||
app.register_blueprint(auth_bp)
|
app.register_blueprint(auth_bp)
|
||||||
app.register_blueprint(friends_bp)
|
app.register_blueprint(friends_bp)
|
||||||
|
app.register_blueprint(presence_bp)
|
||||||
|
|
||||||
init_db(app)
|
init_db(app)
|
||||||
|
|
||||||
|
|||||||
@@ -66,4 +66,13 @@ def init_db(app):
|
|||||||
);
|
);
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Lightweight migration support for existing databases.
|
||||||
|
user_columns = {
|
||||||
|
row["name"]
|
||||||
|
for row in db.execute("PRAGMA table_info(users)").fetchall()
|
||||||
|
}
|
||||||
|
if "last_seen_at" not in user_columns:
|
||||||
|
db.execute("ALTER TABLE users ADD COLUMN last_seen_at TIMESTAMP")
|
||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
from flask import Blueprint, jsonify
|
||||||
|
from flask_login import login_required, current_user
|
||||||
|
from app.db import get_db
|
||||||
|
|
||||||
|
presence_bp = Blueprint("presence", __name__)
|
||||||
|
|
||||||
|
def _mark_current_user_online() -> None:
|
||||||
|
db = get_db()
|
||||||
|
db.execute(
|
||||||
|
"UPDATE users SET last_seen_at = CURRENT_TIMESTAMP WHERE id = ?",
|
||||||
|
(current_user.id,),
|
||||||
|
)
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
|
||||||
|
@presence_bp.route("/api/presence/ping", methods=["POST"])
|
||||||
|
@login_required
|
||||||
|
def presence_ping():
|
||||||
|
if not current_user.is_authenticated: # should not happen due to @login_required, but just in case
|
||||||
|
return ("", 204)
|
||||||
|
|
||||||
|
_mark_current_user_online()
|
||||||
|
return jsonify({"status": "ok"})
|
||||||
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
(function startPresenceHeartbeat() {
|
||||||
|
const ping = function () {
|
||||||
|
fetch("/api/presence/ping", {
|
||||||
|
method: "POST",
|
||||||
|
credentials: "same-origin",
|
||||||
|
keepalive: true,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: "{}",
|
||||||
|
}).catch(function () {
|
||||||
|
// ignore network issues; next interval will retry
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
ping();
|
||||||
|
setInterval(ping, 15000);
|
||||||
|
})();
|
||||||
@@ -49,5 +49,6 @@
|
|||||||
{% endif %} {% endwith %} {% block content %}{% endblock %}
|
{% endif %} {% endwith %} {% block content %}{% endblock %}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
<script src="{{ url_for('static', filename='js/presence.js') }}"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -20,5 +20,6 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %} {% endwith %} {% block content %}{% endblock %}
|
{% endif %} {% endwith %} {% block content %}{% endblock %}
|
||||||
</main>
|
</main>
|
||||||
|
<script src="{{ url_for('static', filename='js/presence.js') }}"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user