From ab9c0f9ac092725c70ec3a963f57bc739f425d4f Mon Sep 17 00:00:00 2001 From: Elias Schneider Date: Tue, 11 Nov 2025 11:21:39 +0100 Subject: [PATCH 1/2] ci/cd: run checks on PR to `breaking/**` branches --- .github/workflows/backend-linter.yml | 4 ++-- .github/workflows/e2e-tests.yml | 4 ++-- .github/workflows/svelte-check.yml | 2 +- .github/workflows/unit-tests.yml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/backend-linter.yml b/.github/workflows/backend-linter.yml index b66aa005..47dcde1b 100644 --- a/.github/workflows/backend-linter.yml +++ b/.github/workflows/backend-linter.yml @@ -2,11 +2,11 @@ name: Run Backend Linter on: push: - branches: [main] + branches: [main, breaking/**] paths: - "backend/**" pull_request: - branches: [main] + branches: [main, breaking/**] paths: - "backend/**" diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 5bded2c0..acd002fb 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -1,13 +1,13 @@ name: E2E Tests on: push: - branches: [main] + branches: [main, breaking/**] paths-ignore: - "docs/**" - "**.md" - ".github/**" pull_request: - branches: [main] + branches: [main, breaking/**] paths-ignore: - "docs/**" - "**.md" diff --git a/.github/workflows/svelte-check.yml b/.github/workflows/svelte-check.yml index b49deddc..d3e5295f 100644 --- a/.github/workflows/svelte-check.yml +++ b/.github/workflows/svelte-check.yml @@ -2,7 +2,7 @@ name: Svelte Check on: push: - branches: [main] + branches: [main, breaking/**] paths: - "frontend/src/**" - ".github/svelte-check-matcher.json" diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index ca2b36a0..83ab9bb1 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -1,7 +1,7 @@ name: Unit Tests on: push: - branches: [main] + branches: [main, breaking/**] paths: - "backend/**" pull_request: From 12125713a2075dcc2b6b814a11f128c7fc336848 Mon Sep 17 00:00:00 2001 From: Elias Schneider Date: Tue, 11 Nov 2025 17:56:20 +0100 Subject: [PATCH 2/2] feat: add support for WEBP profile pictures (#1090) --- backend/internal/service/ldap_service.go | 9 ++++++-- backend/internal/service/user_service.go | 2 +- .../internal/utils/image/profile_picture.go | 22 ++++++++++++++----- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/backend/internal/service/ldap_service.go b/backend/internal/service/ldap_service.go index 51af4089..731c7e66 100644 --- a/backend/internal/service/ldap_service.go +++ b/backend/internal/service/ldap_service.go @@ -440,7 +440,7 @@ func (s *LdapService) SyncUsers(ctx context.Context, tx *gorm.DB, client *ldap.C } func (s *LdapService) saveProfilePicture(parentCtx context.Context, userId string, pictureString string) error { - var reader io.Reader + var reader io.ReadSeeker _, err := url.ParseRequestURI(pictureString) if err == nil { @@ -460,7 +460,12 @@ func (s *LdapService) saveProfilePicture(parentCtx context.Context, userId strin } defer res.Body.Close() - reader = res.Body + data, err := io.ReadAll(res.Body) + if err != nil { + return fmt.Errorf("failed to read profile picture: %w", err) + } + + reader = bytes.NewReader(data) } else if decodedPhoto, err := base64.StdEncoding.DecodeString(pictureString); err == nil { // If the photo is a base64 encoded string, decode it reader = bytes.NewReader(decodedPhoto) diff --git a/backend/internal/service/user_service.go b/backend/internal/service/user_service.go index 3ba8ab78..6cef3176 100644 --- a/backend/internal/service/user_service.go +++ b/backend/internal/service/user_service.go @@ -159,7 +159,7 @@ func (s *UserService) GetUserGroups(ctx context.Context, userID string) ([]model return user.UserGroups, nil } -func (s *UserService) UpdateProfilePicture(ctx context.Context, userID string, file io.Reader) error { +func (s *UserService) UpdateProfilePicture(ctx context.Context, userID string, file io.ReadSeeker) error { // Validate the user ID to prevent directory traversal err := uuid.Validate(userID) if err != nil { diff --git a/backend/internal/utils/image/profile_picture.go b/backend/internal/utils/image/profile_picture.go index 2e28585d..21d78797 100644 --- a/backend/internal/utils/image/profile_picture.go +++ b/backend/internal/utils/image/profile_picture.go @@ -12,24 +12,36 @@ import ( "golang.org/x/image/font" "golang.org/x/image/font/opentype" "golang.org/x/image/math/fixed" + "golang.org/x/image/webp" "github.com/pocket-id/pocket-id/backend/resources" ) const profilePictureSize = 300 -// CreateProfilePicture resizes the profile picture to a square -func CreateProfilePicture(file io.Reader) (io.ReadSeeker, error) { +// CreateProfilePicture resizes the profile picture to a square and encodes it as PNG +func CreateProfilePicture(file io.ReadSeeker) (io.ReadSeeker, error) { + // Attempt standard formats first img, _, err := imageorient.Decode(file) if err != nil { - return nil, fmt.Errorf("failed to decode image: %w", err) + if _, seekErr := file.Seek(0, io.SeekStart); seekErr != nil { + return nil, fmt.Errorf("failed to seek file: %w", seekErr) + } + // Try WebP + webpImg, webpErr := webp.Decode(file) + if webpErr != nil { + return nil, fmt.Errorf("failed to decode image: %w", err) + } + + img = webpImg } + // Resize to square img = imaging.Fill(img, profilePictureSize, profilePictureSize, imaging.Center, imaging.Lanczos) + // Encode back to PNG var buf bytes.Buffer - err = imaging.Encode(&buf, img, imaging.PNG) - if err != nil { + if err := imaging.Encode(&buf, img, imaging.PNG); err != nil { return nil, fmt.Errorf("failed to encode image: %w", err) }