-
-
Notifications
You must be signed in to change notification settings - Fork 427
feat(edit-profile): allow switch gravar / Google avatar picture #1001
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
699f8f0
dd64a52
80146ab
0f7a6a8
f15e990
45c3644
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,56 @@ | ||||||||||||||||||||||||||
| import type { ApiHandler } from '../../types'; | ||||||||||||||||||||||||||
| import { error, success } from '../../utils/response'; | ||||||||||||||||||||||||||
| import { getUserBy, upsertUser } from '../../data/users'; | ||||||||||||||||||||||||||
| import { UserDto } from '../../common/dto/user.dto'; | ||||||||||||||||||||||||||
| import crypto from 'crypto'; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| function getGravatarUrl(email: string): string { | ||||||||||||||||||||||||||
| const hash = crypto | ||||||||||||||||||||||||||
| .createHash('md5') | ||||||||||||||||||||||||||
| .update(email.trim().toLowerCase()) | ||||||||||||||||||||||||||
| .digest('hex'); | ||||||||||||||||||||||||||
| return `https://www.gravatar.com/avatar/${hash}?s=200&d=identicon`; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| type ToggleAvatarRequest = { | ||||||||||||||||||||||||||
| useGravatar: boolean; | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| export const toggleAvatarHandler: ApiHandler<ToggleAvatarRequest> = async (event, context) => { | ||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||
| const { useGravatar } = event.parsedBody || {}; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (typeof useGravatar !== 'boolean') { | ||||||||||||||||||||||||||
| return error('Invalid request body: useGravatar must be a boolean', 400); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const auth0Id = context.user?.auth0Id; | ||||||||||||||||||||||||||
| if (!auth0Id) { | ||||||||||||||||||||||||||
| return error('Unauthorized', 401); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const currentUser = await getUserBy('auth0Id', auth0Id); | ||||||||||||||||||||||||||
| if (!currentUser) { | ||||||||||||||||||||||||||
| return error('User not found', 404); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const avatarUrl = useGravatar ? getGravatarUrl(currentUser.email) : context.user?.picture; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
Comment on lines
+37
to
+38
|
||||||||||||||||||||||||||
| const avatarUrl = useGravatar ? getGravatarUrl(currentUser.email) : context.user?.picture; | |
| const isGoogleOAuthUser = auth0Id.startsWith('google-oauth2|'); | |
| if (!useGravatar) { | |
| if (!isGoogleOAuthUser || !context.user?.picture) { | |
| return error( | |
| 'Switching to Google profile picture is only allowed for Google OAuth users.', | |
| 400 | |
| ); | |
| } | |
| } | |
| const avatarUrl = useGravatar ? getGravatarUrl(currentUser.email) : context.user.picture; |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,14 +1,17 @@ | ||||||||||||
| import React, { FC } from 'react'; | ||||||||||||
| import React, { FC, useState, useEffect } from 'react'; | ||||||||||||
|
||||||||||||
| import React, { FC, useState, useEffect } from 'react'; | |
| import React, { FC, useState } from 'react'; |
Copilot
AI
Jan 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import useEffect.
| import React, { FC, useState, useEffect } from 'react'; | |
| import React, { FC } from 'react'; |
Copilot
AI
Jan 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The User type import is unused in this file. It should be removed from the imports to keep the code clean.
| import type { User } from '../../../../types/models'; |
Copilot
AI
Jan 28, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for determining whether a user is using Gravatar relies on checking if the avatar URL contains 'gravatar.com'. However, this approach may not work correctly if the avatar URL is undefined or if a user switches from Google to Gravatar and the URL hasn't been updated yet. Consider checking the avatar URL against both the auth0Picture to determine which one is currently active, or storing the avatar preference explicitly in the database.
| const isUsingGravatar = currentUser.avatar?.includes('gravatar.com') || false; | |
| const isUsingGravatar = | |
| !!currentUser.avatar && | |
| !!(currentUser as User).auth0Picture && | |
| currentUser.avatar !== (currentUser as User).auth0Picture; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When useGravatar is false, the avatar URL is set to context.user?.picture, which could potentially be undefined. This could result in setting the avatar to undefined in the database. Consider adding validation to ensure context.user?.picture exists before allowing the switch, or falling back to generating a Gravatar URL if it's missing.