UPdate device tracking cookie

This commit is contained in:
Jonny Ervine 2025-11-16 12:47:19 +08:00
parent 0cf8cf7fc0
commit 00f6c6576a
9 changed files with 61 additions and 4 deletions

View File

@ -364,3 +364,6 @@ For issues and questions:

View File

@ -244,3 +244,6 @@ For issues and questions, please refer to the application documentation or creat

View File

@ -261,3 +261,6 @@ main "$@"

View File

@ -70,3 +70,6 @@ Create the name of the service account to use

View File

@ -37,3 +37,6 @@ spec:

View File

@ -23,3 +23,6 @@ spec:

View File

@ -26,3 +26,6 @@ spec:

View File

@ -17,3 +17,6 @@ metadata:

View File

@ -19,7 +19,7 @@ importlib.reload(database)
importlib.reload(db_config) importlib.reload(db_config)
from app import app, randomUrlSuffix from app import app, randomUrlSuffix
from flask import Flask, flash, render_template, request, redirect, url_for, jsonify from flask import Flask, flash, render_template, request, redirect, url_for, jsonify, make_response
from sqlalchemy import text from sqlalchemy import text
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from flask_bootstrap import Bootstrap from flask_bootstrap import Bootstrap
@ -36,6 +36,9 @@ from fixture_scraper import FixtureScraper, get_next_hkfc_c_fixture, get_opponen
from club_scraper import ClubScraper, get_hk_hockey_clubs, expand_club_abbreviation from club_scraper import ClubScraper, get_hk_hockey_clubs, expand_club_abbreviation
from s3_config import s3_config_manager, s3_asset_service from s3_config import s3_config_manager, s3_asset_service
# Persistent device ID cookie name
DEVICE_COOKIE_NAME = 'motm_device_id'
# Custom authentication class that uses database # Custom authentication class that uses database
class DatabaseBasicAuth(BasicAuth): class DatabaseBasicAuth(BasicAuth):
def check_credentials(self, username, password): def check_credentials(self, username, password):
@ -146,6 +149,23 @@ def generate_device_id(request):
return device_id return device_id
def get_or_create_device_id(request):
"""
Return a persistent device identifier using a long-lived cookie.
Falls back to a header/IP fingerprint only if absolutely necessary.
Returns a tuple of (device_id, created) where created indicates whether
a new cookie needs to be set on the response.
"""
# Prefer existing cookie to uniquely identify a device/browser
cookie_device_id = request.cookies.get(DEVICE_COOKIE_NAME)
if cookie_device_id:
return cookie_device_id, False
# Create a new random UUID (more stable than header/IP fingerprints)
new_device_id = uuid.uuid4().hex
return new_device_id, True
def is_admin_authenticated(request): def is_admin_authenticated(request):
"""Check if the current request is authenticated as admin""" """Check if the current request is authenticated as admin"""
@ -490,8 +510,8 @@ def vote_thanks():
update_player_totals(_motm) update_player_totals(_motm)
update_player_totals(_dotd) update_player_totals(_dotd)
# Generate device identifier and record vote for tracking # Generate or retrieve persistent device identifier and record vote for tracking
device_id = generate_device_id(request) device_id, device_created = get_or_create_device_id(request)
sql_device = text(""" sql_device = text("""
INSERT INTO device_votes (device_id, fixture_date, motm_player_number, dotd_player_number, INSERT INTO device_votes (device_id, fixture_date, motm_player_number, dotd_player_number,
motm_player_name, dotd_player_name, ip_address, user_agent) motm_player_name, dotd_player_name, ip_address, user_agent)
@ -531,7 +551,20 @@ def vote_thanks():
# Fallback to static URL # Fallback to static URL
simpsons_url = "/static/images/simpsons-monkeys.jpg" simpsons_url = "/static/images/simpsons-monkeys.jpg"
return render_template('vote_thanks.html', simpsons_image_url=simpsons_url) # Build response and set device ID cookie if newly created
response = make_response(render_template('vote_thanks.html', simpsons_image_url=simpsons_url))
if device_created:
# Two years in seconds
max_age_seconds = 60 * 60 * 24 * 730
response.set_cookie(
DEVICE_COOKIE_NAME,
device_id,
max_age=max_age_seconds,
httponly=True,
samesite='Lax',
secure=bool(request.is_secure)
)
return response
else: else:
return 'Ouch ... something went wrong here' return 'Ouch ... something went wrong here'
except Exception as e: except Exception as e: