Monorepo for Tangled tangled.org

avatar: generate a random color for users without a pfp

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.sh>

anirudh.fi 8aad4c6d f4bff878

verified
Changed files
+33 -4
avatar
src
+33 -4
avatar/src/index.js
··· 1 export default { 2 async fetch(request, env) { 3 const url = new URL(request.url); 4 const { pathname, searchParams } = url; 5 ··· 60 const profile = await profileResponse.json(); 61 const avatar = profile.avatar; 62 63 - if (!avatar) { 64 - return new Response(`avatar not found for ${actor}.`, { status: 404 }); 65 } 66 67 // Resize if requested 68 let avatarResponse; 69 if (resizeToTiny) { 70 - avatarResponse = await fetch(avatar, { 71 cf: { 72 image: { 73 width: 32, ··· 78 }, 79 }); 80 } else { 81 - avatarResponse = await fetch(avatar); 82 } 83 84 if (!avatarResponse.ok) {
··· 1 export default { 2 async fetch(request, env) { 3 + // Helper function to generate a color from a string 4 + const stringToColor = (str) => { 5 + let hash = 0; 6 + for (let i = 0; i < str.length; i++) { 7 + hash = str.charCodeAt(i) + ((hash << 5) - hash); 8 + } 9 + let color = "#"; 10 + for (let i = 0; i < 3; i++) { 11 + const value = (hash >> (i * 8)) & 0xff; 12 + color += ("00" + value.toString(16)).substr(-2); 13 + } 14 + return color; 15 + }; 16 + 17 const url = new URL(request.url); 18 const { pathname, searchParams } = url; 19 ··· 74 const profile = await profileResponse.json(); 75 const avatar = profile.avatar; 76 77 + let avatarUrl = profile.avatar; 78 + 79 + if (!avatarUrl) { 80 + // Generate a random color based on the actor string 81 + const bgColor = stringToColor(actor); 82 + const size = resizeToTiny ? 32 : 128; 83 + const svg = `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg"><rect width="${size}" height="${size}" fill="${bgColor}"/></svg>`; 84 + const svgData = new TextEncoder().encode(svg); 85 + 86 + response = new Response(svgData, { 87 + headers: { 88 + "Content-Type": "image/svg+xml", 89 + "Cache-Control": "public, max-age=43200", 90 + }, 91 + }); 92 + await cache.put(cacheKey, response.clone()); 93 + return response; 94 } 95 96 // Resize if requested 97 let avatarResponse; 98 if (resizeToTiny) { 99 + avatarResponse = await fetch(avatarUrl, { 100 cf: { 101 image: { 102 width: 32, ··· 107 }, 108 }); 109 } else { 110 + avatarResponse = await fetch(avatarUrl); 111 } 112 113 if (!avatarResponse.ok) {