From 6bfad6a8378878cfc1dbf5b11073389fb8346618 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Fri, 11 Jul 2025 18:58:02 -0400 Subject: [PATCH 1/3] fix: parse discord pfp and save as avatar or default --- apps/api/internal/oauth/discord.go | 45 +++++++++++++++++++++++++----- apps/api/internal/services/auth.go | 12 ++++++-- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/apps/api/internal/oauth/discord.go b/apps/api/internal/oauth/discord.go index 6c5336b7..4d029bc2 100644 --- a/apps/api/internal/oauth/discord.go +++ b/apps/api/internal/oauth/discord.go @@ -20,11 +20,16 @@ var ( ) type DiscordUser struct { - ID string `json:"id"` - Username string `json:"username"` - Avatar string `json:"avatar"` - Discriminator string `json:"discriminator"` - Email string `json:"email"` + ID string `json:"id"` + Username string `json:"username"` + Avatar *string `json:"avatar"` + Discriminator string `json:"discriminator"` + Email string `json:"email"` +} + +type DiscordUserWithAvatarURL struct { + DiscordUser + AvatarURL *string } // Note: expiresIn is in seconds @@ -71,7 +76,7 @@ func ExchangeDiscordCode(ctx context.Context, client *http.Client, oauthCfg *con } -func GetDiscordUserInfo(ctx context.Context, client *http.Client, accessToken string) (*DiscordUser, error) { +func GetDiscordUserInfo(ctx context.Context, client *http.Client, accessToken string) (*DiscordUserWithAvatarURL, error) { req, err := http.NewRequestWithContext(ctx, "GET", "https://discord.com/api/users/@me", nil) if err != nil { return nil, err @@ -96,7 +101,33 @@ func GetDiscordUserInfo(ctx context.Context, client *http.Client, accessToken st return nil, err } - return &user, nil + // Parse real avatar URL or make nil + userWithAvatar := DiscordUserWithAvatarURL{ + DiscordUser: user, + AvatarURL: user.AvatarURL(), + } + + return &userWithAvatar, nil +} + +func (u *DiscordUser) AvatarURL() *string { + // Only proceed if Avatar is non-nil *and* not the empty string + if u.Avatar != nil && *u.Avatar != "" { + hash := *u.Avatar + + ext := "png" + if strings.HasPrefix(hash, "a_") { + ext = "gif" + } + + url := fmt.Sprintf( + "https://cdn.discordapp.com/avatars/%s/%s.%s", + u.ID, hash, ext, + ) + return &url + } + + return nil } // TODO: Refactor more cleanly diff --git a/apps/api/internal/services/auth.go b/apps/api/internal/services/auth.go index f567f4a0..ba7e78ce 100644 --- a/apps/api/internal/services/auth.go +++ b/apps/api/internal/services/auth.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "net/http" + "net/url" "time" "github.com/google/uuid" @@ -109,7 +110,7 @@ func (s *AuthService) authenticateWithDiscord(ctx context.Context, code string, return s.createSessionForExistingUser(ctx, account.UserID, ipAddress, userAgent) } -func (s *AuthService) registerNewDiscordUser(ctx context.Context, userInfo *oauth.DiscordUser, oauthResp *oauth.DiscordExchangeResponse, ipAddress, userAgent *string) (*sqlc.AuthSession, error) { +func (s *AuthService) registerNewDiscordUser(ctx context.Context, userInfo *oauth.DiscordUserWithAvatarURL, oauthResp *oauth.DiscordExchangeResponse, ipAddress, userAgent *string) (*sqlc.AuthSession, error) { var session *sqlc.AuthSession err := s.txm.WithTx(ctx, func(tx pgx.Tx) error { @@ -117,10 +118,17 @@ func (s *AuthService) registerNewDiscordUser(ctx context.Context, userInfo *oaut txAccountRepo := s.accountRepo.NewTx(tx) txSessionRepo := s.sessionRepo.NewTx(tx) + // Default avatar if no discord avatar + avatar := userInfo.AvatarURL + if avatar == nil { + custom := fmt.Sprintf("https://api.dicebear.com/9.x/initials/svg?seed=%s", url.QueryEscape(userInfo.Username)) + avatar = &custom + } + user, err := txUserRepo.Create(ctx, sqlc.CreateUserParams{ Name: userInfo.Username, Email: &userInfo.Email, - Image: &userInfo.Avatar, + Image: avatar, }) if err != nil { return err From 08071e515bb72e1863d8909b769c39a17dfb71ea Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Fri, 11 Jul 2025 18:59:15 -0400 Subject: [PATCH 2/3] fix: change default from svg to png --- apps/api/internal/services/auth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api/internal/services/auth.go b/apps/api/internal/services/auth.go index ba7e78ce..2c0a901b 100644 --- a/apps/api/internal/services/auth.go +++ b/apps/api/internal/services/auth.go @@ -121,7 +121,7 @@ func (s *AuthService) registerNewDiscordUser(ctx context.Context, userInfo *oaut // Default avatar if no discord avatar avatar := userInfo.AvatarURL if avatar == nil { - custom := fmt.Sprintf("https://api.dicebear.com/9.x/initials/svg?seed=%s", url.QueryEscape(userInfo.Username)) + custom := fmt.Sprintf("https://api.dicebear.com/9.x/initials/png?seed=%s", url.QueryEscape(userInfo.Username)) avatar = &custom } From 364a374a59269264d919375468496aec55cb21db Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Fri, 11 Jul 2025 19:04:49 -0400 Subject: [PATCH 3/3] fix: fix non nestable workflows --- .github/workflows/{api => }/build-and-push.yml | 2 +- .github/workflows/{docs => }/docs.yml | 0 .github/workflows/{api => }/lint-api.yml | 0 .github/workflows/{discord-bot => }/lint-discord-bot.yml | 0 .github/workflows/{web => }/lint-web.yml | 0 .github/workflows/{api => }/sqlc_ci.yml | 0 .github/workflows/{web => }/test-web.yml | 0 7 files changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{api => }/build-and-push.yml (98%) rename .github/workflows/{docs => }/docs.yml (100%) rename .github/workflows/{api => }/lint-api.yml (100%) rename .github/workflows/{discord-bot => }/lint-discord-bot.yml (100%) rename .github/workflows/{web => }/lint-web.yml (100%) rename .github/workflows/{api => }/sqlc_ci.yml (100%) rename .github/workflows/{web => }/test-web.yml (100%) diff --git a/.github/workflows/api/build-and-push.yml b/.github/workflows/build-and-push.yml similarity index 98% rename from .github/workflows/api/build-and-push.yml rename to .github/workflows/build-and-push.yml index e81335ff..e488d9ea 100644 --- a/.github/workflows/api/build-and-push.yml +++ b/.github/workflows/build-and-push.yml @@ -3,7 +3,7 @@ name: Build and Push Docker Image to GHCR on: push: branches: - - main + - master paths: - 'apps/api/**' diff --git a/.github/workflows/docs/docs.yml b/.github/workflows/docs.yml similarity index 100% rename from .github/workflows/docs/docs.yml rename to .github/workflows/docs.yml diff --git a/.github/workflows/api/lint-api.yml b/.github/workflows/lint-api.yml similarity index 100% rename from .github/workflows/api/lint-api.yml rename to .github/workflows/lint-api.yml diff --git a/.github/workflows/discord-bot/lint-discord-bot.yml b/.github/workflows/lint-discord-bot.yml similarity index 100% rename from .github/workflows/discord-bot/lint-discord-bot.yml rename to .github/workflows/lint-discord-bot.yml diff --git a/.github/workflows/web/lint-web.yml b/.github/workflows/lint-web.yml similarity index 100% rename from .github/workflows/web/lint-web.yml rename to .github/workflows/lint-web.yml diff --git a/.github/workflows/api/sqlc_ci.yml b/.github/workflows/sqlc_ci.yml similarity index 100% rename from .github/workflows/api/sqlc_ci.yml rename to .github/workflows/sqlc_ci.yml diff --git a/.github/workflows/web/test-web.yml b/.github/workflows/test-web.yml similarity index 100% rename from .github/workflows/web/test-web.yml rename to .github/workflows/test-web.yml