Allows users to hide their check-ins from other users

This commit is contained in:
Peter Stockings
2026-02-24 13:17:09 +11:00
parent d6885a8339
commit c21a7890f3
9 changed files with 267 additions and 51 deletions

View File

@@ -1,7 +1,7 @@
from flask import Blueprint, jsonify
from flask import Blueprint, jsonify, session
from app import SYDNEY_TZ
from app.auth import login_required
from app.db import query
from app.db import query, query_one
from datetime import timezone
bp = Blueprint("api", __name__, url_prefix="/api")
@@ -11,6 +11,12 @@ bp = Blueprint("api", __name__, url_prefix="/api")
@login_required
def chart_data(user_id):
"""Return weight & BMI over time for Chart.js."""
# Privacy guard: don't expose private user data to others
if user_id != session.get("user_id"):
target = query_one("SELECT is_private FROM users WHERE id = %s", (user_id,))
if target and target["is_private"]:
return jsonify({"labels": [], "weights": [], "bmis": []})
checkins = query(
"""SELECT weight_kg, bmi, checked_in_at
FROM checkins WHERE user_id = %s
@@ -43,6 +49,7 @@ def comparison():
(SELECT weight_kg FROM checkins WHERE user_id = u.id ORDER BY checked_in_at DESC LIMIT 1) as current_weight
FROM users u
WHERE (SELECT COUNT(*) FROM checkins WHERE user_id = u.id) > 0
AND u.is_private = FALSE
ORDER BY u.display_name
""")
@@ -74,6 +81,12 @@ def comparison():
@login_required
def weekly_change(user_id):
"""Return weekly weight changes for bar chart."""
# Privacy guard: don't expose private user data to others
if user_id != session.get("user_id"):
target = query_one("SELECT is_private FROM users WHERE id = %s", (user_id,))
if target and target["is_private"]:
return jsonify({"labels": [], "changes": []})
checkins = query(
"""SELECT weight_kg, checked_in_at
FROM checkins WHERE user_id = %s

View File

@@ -16,6 +16,7 @@ def signup():
gender = request.form.get("gender") or None
goal_weight_kg = request.form.get("goal_weight_kg") or None
starting_weight_kg = request.form.get("starting_weight_kg") or None
is_private = request.form.get("is_private") == "on"
# Validation
if not username or not password:
@@ -35,9 +36,9 @@ def signup():
# Create user
password_hash = generate_password_hash(password)
user = execute_returning(
"""INSERT INTO users (username, password_hash, display_name, height_cm, age, gender, goal_weight_kg, starting_weight_kg)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s) RETURNING id""",
(username, password_hash, display_name or username, height_cm, age, gender, goal_weight_kg, starting_weight_kg),
"""INSERT INTO users (username, password_hash, display_name, height_cm, age, gender, goal_weight_kg, starting_weight_kg, is_private)
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING id""",
(username, password_hash, display_name or username, height_cm, age, gender, goal_weight_kg, starting_weight_kg, is_private),
)
session["user_id"] = user["id"]

View File

@@ -48,9 +48,10 @@ def index():
SELECT c.*, u.display_name, u.username
FROM checkins c
JOIN users u ON c.user_id = u.id
WHERE u.is_private = FALSE OR u.id = %s
ORDER BY c.checked_in_at DESC
LIMIT 10
""")
""", (user["id"],))
# Milestones
milestones = query(

View File

@@ -21,6 +21,7 @@ def index():
(SELECT COUNT(*) FROM checkins WHERE user_id = u.id) as total_checkins,
(SELECT checked_in_at FROM checkins WHERE user_id = u.id ORDER BY checked_in_at DESC LIMIT 1) as last_checkin
FROM users u
WHERE u.is_private = FALSE
ORDER BY u.created_at
""")

View File

@@ -22,14 +22,15 @@ def update():
gender = request.form.get("gender") or None
goal_weight_kg = request.form.get("goal_weight_kg") or None
starting_weight_kg = request.form.get("starting_weight_kg") or None
is_private = request.form.get("is_private") == "on"
execute(
"""UPDATE users
SET display_name = %s, height_cm = %s, age = %s, gender = %s,
goal_weight_kg = %s, starting_weight_kg = %s
goal_weight_kg = %s, starting_weight_kg = %s, is_private = %s
WHERE id = %s""",
(display_name or user["username"], height_cm, age, gender,
goal_weight_kg, starting_weight_kg, user["id"]),
goal_weight_kg, starting_weight_kg, is_private, user["id"]),
)
if request.headers.get("HX-Request"):