Add Gravatar-style avatar endpoint, rename user-based requests to /avatar/user/<uid>
parent
0ae77291cc
commit
6f51489194
|
@ -11,6 +11,7 @@ import time
|
|||
import io
|
||||
import logging
|
||||
import random
|
||||
import hashlib
|
||||
|
||||
from PIL import Image, ImageDraw
|
||||
import flask
|
||||
|
@ -234,6 +235,68 @@ class AvatarCache:
|
|||
|
||||
cache = AvatarCache()
|
||||
|
||||
@bp.route('/avatar/<uid>', methods=['GET'])
|
||||
def hash_for_uid(uid):
|
||||
# NOTE: Gravatar documentation says to use SHA256, but everyone passes MD5 instead
|
||||
email = f'{uid}@hackerspace.pl'.strip().lower()
|
||||
hasher = hashlib.md5()
|
||||
hasher.update(email.encode())
|
||||
return hasher.hexdigest()
|
||||
|
||||
def get_all_user_uids(conn):
|
||||
all_uids = []
|
||||
|
||||
results = conn.search_s(config.ldap_people, ldap.SCOPE_SUBTREE, 'uid=*', attrlist=['uid'])
|
||||
for user, attrs in results:
|
||||
uid = attrs['uid'][0].decode()
|
||||
all_uids.append(uid)
|
||||
|
||||
return all_uids
|
||||
|
||||
class HashCache:
|
||||
# email hash -> uid mapping
|
||||
entries: dict[str, str] = {}
|
||||
# deadline when this cache expires
|
||||
deadline: float = 0
|
||||
|
||||
def get(self, email_hash: str) -> str:
|
||||
self.rebuild_if_needed()
|
||||
return self.entries.get(email_hash, 'default')
|
||||
|
||||
def reset(self):
|
||||
self.entries = {}
|
||||
self.deadline = 0
|
||||
|
||||
def rebuild_if_needed(self):
|
||||
now = time.time()
|
||||
if now > self.deadline:
|
||||
self.rebuild()
|
||||
|
||||
def rebuild(self):
|
||||
log.info("Rebuilding email hash cache")
|
||||
conn = context.get_admin_connection()
|
||||
users = get_all_user_uids(conn)
|
||||
self.deadline = time.time() + config.avatar_cache_timeout
|
||||
self.entries = { hash_for_uid(uid): uid for uid in users }
|
||||
|
||||
hash_cache = HashCache()
|
||||
|
||||
def sanitize_email_hash(hash: str):
|
||||
"""
|
||||
lowercases, removes file extension (probably)
|
||||
"""
|
||||
hash = hash.lower()
|
||||
if hash.endswith('.png') or hash.endswith('.jpg'):
|
||||
hash = hash[:-4]
|
||||
return hash
|
||||
|
||||
@bp.route('/avatar/<email_hash>', methods=['GET'])
|
||||
def gravatar_serve(email_hash):
|
||||
"""
|
||||
Serves avatar in a Gravatar-compatible(ish) way, i.e. by email hash, not user name.
|
||||
"""
|
||||
uid = hash_cache.get(sanitize_email_hash(email_hash))
|
||||
return cache.get(uid)
|
||||
|
||||
@bp.route('/avatar/user/<uid>', methods=['GET'])
|
||||
def avatar_serve(uid):
|
||||
return cache.get(uid)
|
||||
|
|
|
@ -52,5 +52,6 @@ def refresh_profile(dn=None):
|
|||
# bust avatar cache
|
||||
if user_uid:
|
||||
avatar.cache.reset_user(user_uid)
|
||||
avatar.hash_cache.reset()
|
||||
|
||||
return profile
|
||||
|
|
Loading…
Reference in New Issue