# Avatar
- @property
+ @lazy_property
def avatar_hash(self):
- payload = (
- self.uid,
- "%s" % self.modified_at,
- )
+ # Fetch the timestamp (or fall back to the last LDAP change)
+ t = self._fetch_avatar_timestamp() or self.modified_at
- # String the payload together
- payload = "-".join(payload)
+ # Create the payload
+ payload = "%s-%s" % (self.uid, t)
- # Run MD5() over the payload
- h = hashlib.new("md5", payload.encode())
+ # Compute a hash over the payload
+ h = hashlib.new("blake2b", payload.encode())
return h.hexdigest()[:7]
return url
async def get_avatar(self, size=None):
- photo = self._get_bytes("jpegPhoto")
+ # Check the PostgreSQL database
+ photo = self._fetch_avatar()
+
+ # Fall back to LDAP
+ if not photo:
+ photo = self._get_bytes("jpegPhoto")
# Exit if no avatar is available
if not photo:
if size is None:
return photo
+ # Compose the cache key
+ key = "accounts:%s:avatar:%s:%s" % (self.uid, self.avatar_hash, size)
+
# Try to retrieve something from the cache
- avatar = await self.backend.cache.get("accounts:%s:avatar:%s" % (self.dn, size))
+ avatar = await self.backend.cache.get(key)
if avatar:
return avatar
# Generate a new thumbnail
avatar = util.generate_thumbnail(photo, size, square=True)
- # Save to cache for 15m
- await self.backend.cache.set("accounts:%s:avatar:%s" % (self.dn, size), avatar, 900)
+ # Save to cache for 24h
+ await self.backend.cache.set(key, avatar, 86400)
return avatar
- def upload_avatar(self, avatar):
- self._set("jpegPhoto", avatar)
+ def _fetch_avatar(self):
+ """
+ Fetches the original avatar blob as being uploaded by the user
+ """
+ res = self.db.get("""
+ SELECT
+ blob
+ FROM
+ account_avatars
+ WHERE
+ uid = %s
+ AND
+ deleted_at IS NULL
+ """, self.uid,
+ )
+
+ if res:
+ return res.blob
+
+ def _fetch_avatar_timestamp(self):
+ res = self.db.get("""
+ SELECT
+ created_at
+ FROM
+ account_avatars
+ WHERE
+ uid = %s
+ AND
+ deleted_at IS NULL
+ """, self.uid,
+ )
+
+ if res:
+ return res.created_at
+
+ async def upload_avatar(self, avatar):
+ # Remove all previous avatars
+ self.db.execute("""
+ UPDATE
+ account_avatars
+ SET
+ deleted_at = CURRENT_TIMESTAMP
+ WHERE
+ uid = %s
+ AND
+ deleted_at IS NULL
+ """, self.uid,
+ )
+
+ # Store the new avatar in the database
+ self.db.execute("""
+ INSERT INTO
+ account_avatars
+ (
+ uid,
+ blob
+ )
+ VALUES
+ (
+ %s, %s
+ )
+ """, self.uid, avatar,
+ )
+
+ # Remove anything in the LDAP database
+ photo = self._get_bytes("jpegPhoto")
+ if photo:
+ self._delete("jpegPhoto", [photo])
# Consent to promotional emails