diff --git a/app/__init__.py b/app/__init__.py index a20d23c..3961366 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -27,10 +27,12 @@ def create_app(): from .routes.auth import auth_bp from .routes.main import main_bp from .routes.friends import friends_bp + from .routes.presence import presence_bp app.register_blueprint(main_bp) app.register_blueprint(auth_bp) app.register_blueprint(friends_bp) + app.register_blueprint(presence_bp) init_db(app) diff --git a/app/db.py b/app/db.py index d0f9ae3..b14ff1a 100644 --- a/app/db.py +++ b/app/db.py @@ -66,4 +66,13 @@ def init_db(app): ); """ ) - db.commit() \ No newline at end of file + + # 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() diff --git a/app/routes/presence.py b/app/routes/presence.py new file mode 100644 index 0000000..26f7210 --- /dev/null +++ b/app/routes/presence.py @@ -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"}) + \ No newline at end of file diff --git a/app/static/js/presence.js b/app/static/js/presence.js new file mode 100644 index 0000000..e036a69 --- /dev/null +++ b/app/static/js/presence.js @@ -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); +})(); diff --git a/app/templates/base_app.html b/app/templates/base_app.html index 4e26ad0..6dae00f 100644 --- a/app/templates/base_app.html +++ b/app/templates/base_app.html @@ -49,5 +49,6 @@ {% endif %} {% endwith %} {% block content %}{% endblock %} + diff --git a/app/templates/base_public.html b/app/templates/base_public.html index dd16f9b..9d6a689 100644 --- a/app/templates/base_public.html +++ b/app/templates/base_public.html @@ -20,5 +20,6 @@ {% endif %} {% endwith %} {% block content %}{% endblock %} +