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 io
|
||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
|
import hashlib
|
||||||
|
|
||||||
from PIL import Image, ImageDraw
|
from PIL import Image, ImageDraw
|
||||||
import flask
|
import flask
|
||||||
|
@ -234,6 +235,68 @@ class AvatarCache:
|
||||||
|
|
||||||
cache = 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):
|
def avatar_serve(uid):
|
||||||
return cache.get(uid)
|
return cache.get(uid)
|
||||||
|
|
|
@ -52,5 +52,6 @@ def refresh_profile(dn=None):
|
||||||
# bust avatar cache
|
# bust avatar cache
|
||||||
if user_uid:
|
if user_uid:
|
||||||
avatar.cache.reset_user(user_uid)
|
avatar.cache.reset_user(user_uid)
|
||||||
|
avatar.hash_cache.reset()
|
||||||
|
|
||||||
return profile
|
return profile
|
||||||
|
|
Loading…
Reference in New Issue