Skip to content

feat: add profile picture upload support with S3 presigned URLs#222

Open
sasidaran-99 wants to merge 3 commits into
roshankumar0036singh:SSOC26from
sasidaran-99:feat/profile-picture-upload
Open

feat: add profile picture upload support with S3 presigned URLs#222
sasidaran-99 wants to merge 3 commits into
roshankumar0036singh:SSOC26from
sasidaran-99:feat/profile-picture-upload

Conversation

@sasidaran-99

@sasidaran-99 sasidaran-99 commented Jun 23, 2026

Copy link
Copy Markdown

Summary

This PR adds profile picture support by enabling secure direct-to-cloud uploads using AWS S3 presigned URLs.

Changes Made

  • Added profile image support to the user profile model.
  • Added upload request and response DTOs.
  • Added StorageService for generating AWS S3 presigned upload URLs.
  • Added authenticated endpoint for requesting secure upload URLs.
  • Added storage-related configuration and environment variables.
  • Updated profile update functionality to support profile image URLs.
  • Updated route registration and handler dependencies.
  • Updated tests to support the new AuthHandler constructor.

How It Works

  1. The client requests a secure upload URL from the backend.
  2. The backend generates a short-lived AWS S3 presigned URL.
  3. The client uploads the image directly to S3.
  4. The backend never receives or buffers the image data.
  5. The uploaded image URL can then be saved as the user's profile picture.

Acceptance Criteria

  • Users can request a secure upload URL.
  • Users can update their profile picture.
  • Backend never buffers image uploads in memory.
  • AWS S3 presigned URL support implemented.

Testing

go build ./...
go test ./...

Both commands complete successfully.

Notes

This implementation follows a direct-to-cloud upload approach, reducing backend load and improving scalability while maintaining secure uploads through time-limited presigned URLs.

Summary by CodeRabbit

Release Notes

  • New Features
    • Users can now upload and update profile images with secure, pre-signed upload links

@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your contribution! Before we can merge this PR, we need you to sign our Contributor License Agreement.

To sign, please post a comment on this PR with the following exact text:

I have read the CLA and agree to its terms


I have read the CLA and agree to its terms.


You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@sasidaran-99, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 15 minutes and 58 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: eab747ad-23a2-4511-97c8-ae0f679fbc97

📥 Commits

Reviewing files that changed from the base of the PR and between 6ba7238 and 738b48f.

📒 Files selected for processing (3)
  • internal/config/config.go
  • internal/handler/auth_handler.go
  • internal/service/storage_service.go
📝 Walkthrough

Walkthrough

Adds AWS S3-backed profile image upload support: a new StorageService generates presigned S3 PutObject URLs, a POST /api/auth/profile/upload-url protected endpoint exposes this, UpdateProfileRequest gains a ProfileImage field persisted by AuthService, and config/env/dependencies are updated accordingly.

Changes

S3 Profile Image Upload

Layer / File(s) Summary
AWS config, env, and dependencies
internal/config/config.go, .env.example, go.mod
StorageConfig struct added with Bucket, Region, AccessKeyID, SecretAccessKey; Config extended with Storage field populated from env vars; .env.example adds AWS env placeholders; AWS SDK v2 and submodules added as indirect dependencies.
StorageService: S3 client and presigned URL generation
internal/service/storage_service.go
NewStorageService(bucket, region) loads AWS SDK default config and constructs an S3 client (panics on config load failure). GenerateUploadURL(userID, fileName) builds users/{userID}/{fileName} key, presigns a 15-minute PutObject URL, and returns both the presigned URL and the public S3 HTTPS URL.
Upload DTOs, ProfileImage field, and UpdateProfile logic
internal/dto/upload.go, internal/dto/auth_dto.go, internal/service/auth_service.go
UploadURLRequest and UploadURLResponse DTOs defined; ProfileImage field added to UpdateProfileRequest; UpdateProfile conditionally adds profile_image to the update map when non-empty.
AuthHandler wiring and GenerateProfileUploadURL endpoint
internal/handler/auth_handler.go, internal/routes/routes.go
AuthHandler gains storageService dependency and updated constructor; GenerateProfileUploadURL method added returning 401/500 on failures; SetupRoutes initializes StorageService from config and registers POST /api/auth/profile/upload-url under auth middleware.
Test constructor arity updates
internal/handler/auth_handler_test.go, internal/handler/auth_handler_protected_test.go
All NewAuthHandler calls updated from 3 to 4 arguments by appending nil for storageService.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant AuthHandler
    participant StorageService
    participant AWSS3

    Client->>AuthHandler: POST /api/auth/profile/upload-url {fileName}
    AuthHandler->>AuthHandler: Extract userID from Gin context
    AuthHandler->>StorageService: GenerateUploadURL(userID, fileName)
    StorageService->>AWSS3: PresignPutObject(users/{userID}/{fileName}, 15min)
    AWSS3-->>StorageService: presignedURL
    StorageService-->>AuthHandler: presignedURL, publicFileURL
    AuthHandler-->>Client: 200 {uploadUrl, fileUrl}

    Client->>AWSS3: PUT presignedURL (file bytes directly)
    AWSS3-->>Client: 200 OK

    Client->>AuthHandler: PATCH /api/auth/profile {profileImage: fileUrl}
    AuthHandler->>AuthHandler: UpdateProfile(req with profileImage)
    AuthHandler-->>Client: 200 updated profile
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

  • User Profile Image Uploads (Avatar) #166: The PR directly implements this issue by adding StorageService for presigned S3 URLs, UploadURLRequest/UploadURLResponse DTOs, the POST /api/auth/profile/upload-url endpoint, and UpdateProfile support for persisting the image URL — satisfying all described acceptance criteria.

Poem

🐇 Hippity-hop, a bucket in the cloud,
Pre-signed URLs, oh so proud!
Upload your face to S3 with glee,
No server buffering — just you and me!
The rabbit stamps the key and hops away free. 🪣✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description covers all key changes, includes a clear workflow explanation, documents acceptance criteria, and provides testing instructions. However, it does not include the required CLA checklist items from the template. Add the CLA checklist section from the repository template, even if items are marked as not applicable or already completed.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the primary change: adding profile picture upload support using S3 presigned URLs, which is the main feature introduced across the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/config/config.go`:
- Around line 206-211: The StorageConfig initialization in LoadConfig() loads
AWS_S3_BUCKET and AWS_REGION from environment variables but does not validate
that these required fields are set. Add validation after loading these fields to
ensure they are not empty strings, using log.Fatal to fail at startup with a
descriptive error message, following the same validation pattern already
implemented for JWT secrets and encryption key validation elsewhere in the
LoadConfig() function. Extract the bucket and region values into variables, then
add conditional checks that call log.Fatal with appropriate error messages if
either field is empty.

In `@internal/handler/auth_handler.go`:
- Around line 199-233: The GenerateProfileUploadURL method in the AuthHandler
struct is missing Swagger annotations that document the API endpoint. Add
Swagger documentation comments above the GenerateProfileUploadURL method to
describe the endpoint, including request parameters, response types, possible
error codes, and any security requirements. After adding the annotations, run
the `make swagger` command to regenerate the API documentation files.
- Around line 202-220: The error responses in this handler are using
inconsistent utility functions (UnauthorizedResponse, ValidationErrorResponse,
ErrorResponse) that do not conform to the required JSON error schema. Replace
all error response calls with direct JSON structures that return the required
format with "error" and "code" fields. For the 401 Unauthorized response, use an
appropriate error code like UNAUTHORIZED; for the 400 Bad Request validation
error, use VALIDATION_ERROR or similar; and for the 500 Internal Server Error
when GenerateUploadURL fails, use an appropriate code like
UPLOAD_URL_GENERATION_FAILED. Ensure all three error response branches (around
StatusUnauthorized, StatusBadRequest, and StatusInternalServerError) follow the
consistent {"error": "message", "code": "ERROR_CODE"} format.
- Around line 200-215: The code checks if userID exists in the context but fails
to validate its type before assertion. After verifying that userID exists using
c.Get("userID"), add a type assertion with the comma-ok idiom to safely check if
userID is a string type. If the type assertion fails, return an appropriate
error response such as http.StatusUnauthorized before attempting to use userID
in the GenerateUploadURL method call, preventing a panic if the context value is
not a string.

In `@internal/routes/routes.go`:
- Around line 78-88: The SetupRoutes function calls
service.NewStorageService(...) unconditionally, which panics when AWS config
loading fails, causing a process crash during startup. Modify the
NewStorageService call to handle potential errors by checking the return value
for an error (the constructor likely returns both a service instance and an
error). Return or propagate this error from SetupRoutes instead of allowing the
panic to crash the application, ensuring graceful error handling rather than
panic-driven startup failures.

In `@internal/service/auth_service.go`:
- Around line 222-224: The profile image validation in the update block accepts
any non-empty string for req.ProfileImage without verifying it originates from
the configured S3 bucket/region, allowing clients to persist arbitrary external
URLs and bypass the presigned-upload mechanism. Before adding profile_image to
the updates map, validate that req.ProfileImage is a trusted S3 URL matching
your configured bucket and region (and user prefix if applicable) to ensure only
authorized presigned URLs are persisted.

In `@internal/service/storage_service.go`:
- Around line 37-59: The GenerateUploadURL method uses context.TODO() for the S3
presign operation, which decouples it from the HTTP request lifecycle and
prevents proper cancellation and timeout handling. Update the GenerateUploadURL
method signature to accept a context parameter as the first argument (after the
receiver), replace the context.TODO() call in the presign operation with the
passed context parameter, and then update the handler in
internal/handler/auth_handler.go to pass the request context when calling
GenerateUploadURL.
- Around line 37-46: The GenerateUploadURL method accepts fileName directly
without validation, creating security risks for path traversal, empty filenames,
and invalid characters. Add validation at the beginning of the GenerateUploadURL
function to check that fileName is not empty, does not contain path traversal
patterns like "../" or "./", does not contain forward slashes that would break
the intended key structure, and is within acceptable length limits. Return an
appropriate error if any validation fails. Add the "strings" package to the
imports if it is not already present to support these validation checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0536e886-5701-480f-8d4b-929cd425788c

📥 Commits

Reviewing files that changed from the base of the PR and between d61168a and 6ba7238.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (11)
  • .env.example
  • go.mod
  • internal/config/config.go
  • internal/dto/auth_dto.go
  • internal/dto/upload.go
  • internal/handler/auth_handler.go
  • internal/handler/auth_handler_protected_test.go
  • internal/handler/auth_handler_test.go
  • internal/routes/routes.go
  • internal/service/auth_service.go
  • internal/service/storage_service.go

Comment thread internal/config/config.go Outdated
Comment on lines +206 to +211
Storage: StorageConfig{
Bucket: getEnv("AWS_S3_BUCKET", ""),
Region: getEnv("AWS_REGION", ""),
AccessKeyID: getEnv("AWS_ACCESS_KEY_ID", ""),
SecretAccessKey: getEnv("AWS_SECRET_ACCESS_KEY", ""),
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Validate required AWS configuration fields before runtime errors occur.

The AWS_S3_BUCKET and AWS_REGION fields are not validated in LoadConfig(), unlike the JWT and encryption key validation at lines 135–140. An empty AWS_S3_BUCKET will silently reach the service layer and fail with a confusing S3 API error during presign requests, instead of failing loudly at startup.

Add validation to ensure these fields are set, mirroring the pattern used for JWT secrets:

bucket := getEnv("AWS_S3_BUCKET", "")
region := getEnv("AWS_REGION", "")
if bucket == "" {
    log.Fatal("AWS_S3_BUCKET must be set")
}
if region == "" {
    log.Fatal("AWS_REGION must be set")
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/config/config.go` around lines 206 - 211, The StorageConfig
initialization in LoadConfig() loads AWS_S3_BUCKET and AWS_REGION from
environment variables but does not validate that these required fields are set.
Add validation after loading these fields to ensure they are not empty strings,
using log.Fatal to fail at startup with a descriptive error message, following
the same validation pattern already implemented for JWT secrets and encryption
key validation elsewhere in the LoadConfig() function. Extract the bucket and
region values into variables, then add conditional checks that call log.Fatal
with appropriate error messages if either field is empty.

Comment thread internal/handler/auth_handler.go
Comment thread internal/handler/auth_handler.go
Comment on lines +202 to +220
c.JSON(http.StatusUnauthorized, utils.UnauthorizedResponse("Unauthorized"))
return
}

var req dto.UploadURLRequest

if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, utils.ValidationErrorResponse(err.Error()))
return
}

uploadURL, fileURL, err := h.storageService.GenerateUploadURL(
userID.(string),
req.FileName,
)

if err != nil {
c.JSON(http.StatusInternalServerError,
utils.ErrorResponse("Failed to generate upload URL", err))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Return handler errors using the required {error, code} JSON shape.

This endpoint currently emits helper responses with different schemas across 401/400/500 branches, which violates the handler error contract.

As per coding guidelines, internal/handler/**/*.go: Return JSON error responses in format {"error": "message", "code": "ERROR_CODE"} from HTTP handlers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/handler/auth_handler.go` around lines 202 - 220, The error responses
in this handler are using inconsistent utility functions (UnauthorizedResponse,
ValidationErrorResponse, ErrorResponse) that do not conform to the required JSON
error schema. Replace all error response calls with direct JSON structures that
return the required format with "error" and "code" fields. For the 401
Unauthorized response, use an appropriate error code like UNAUTHORIZED; for the
400 Bad Request validation error, use VALIDATION_ERROR or similar; and for the
500 Internal Server Error when GenerateUploadURL fails, use an appropriate code
like UPLOAD_URL_GENERATION_FAILED. Ensure all three error response branches
(around StatusUnauthorized, StatusBadRequest, and StatusInternalServerError)
follow the consistent {"error": "message", "code": "ERROR_CODE"} format.

Source: Coding guidelines

Comment thread internal/routes/routes.go
Comment on lines +78 to +88
storageService := service.NewStorageService(
cfg.Storage.Bucket,
cfg.Storage.Region,
)

authHandler := handler.NewAuthHandler(
authService,
oauthService,
oauthProviderService,
storageService,
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Avoid panic-driven startup failure from storage initialization.

SetupRoutes now unconditionally calls service.NewStorageService(...), and the provided constructor panics on AWS config load errors. A bad/missing storage config will crash the process during route setup.

Suggested direction
-// service/storage_service.go
-func NewStorageService(bucket, region string) *StorageService {
+func NewStorageService(bucket, region string) (*StorageService, error) {
 	cfg, err := awsconfig.LoadDefaultConfig(
 		context.TODO(),
 		awsconfig.WithRegion(region),
 	)
 	if err != nil {
-		panic(err)
+		return nil, err
 	}
 	client := s3.NewFromConfig(cfg)
-	return &StorageService{bucket: bucket, region: region, client: client}
+	return &StorageService{bucket: bucket, region: region, client: client}, nil
}

-// routes.go
-storageService := service.NewStorageService(cfg.Storage.Bucket, cfg.Storage.Region)
+storageService, err := service.NewStorageService(cfg.Storage.Bucket, cfg.Storage.Region)
+if err != nil {
+	log.Fatalf("failed to initialize storage service: %v", err)
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/routes/routes.go` around lines 78 - 88, The SetupRoutes function
calls service.NewStorageService(...) unconditionally, which panics when AWS
config loading fails, causing a process crash during startup. Modify the
NewStorageService call to handle potential errors by checking the return value
for an error (the constructor likely returns both a service instance and an
error). Return or propagate this error from SetupRoutes instead of allowing the
panic to crash the application, ensuring graceful error handling rather than
panic-driven startup failures.

Comment on lines +222 to +224
if req.ProfileImage != "" {
updates["profile_image"] = req.ProfileImage
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Restrict profileImage to trusted S3 URLs before persisting.

This branch stores any non-empty string, so clients can bypass the presigned-upload path and persist arbitrary external URLs as profile images. Validate against your configured bucket/region (and user prefix) before writing profile_image.

Suggested fix
+import (
+    "fmt"
+    "strings"
+)
...
 if req.ProfileImage != "" {
+    expectedPrefix := fmt.Sprintf(
+        "https://%s.s3.%s.amazonaws.com/users/%s/",
+        s.config.Storage.Bucket,
+        s.config.Storage.Region,
+        userID,
+    )
+    if !strings.HasPrefix(req.ProfileImage, expectedPrefix) {
+        return nil, errors.New("invalid profile image url")
+    }
     updates["profile_image"] = req.ProfileImage
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if req.ProfileImage != "" {
updates["profile_image"] = req.ProfileImage
}
import (
"fmt"
"strings"
)
...
if req.ProfileImage != "" {
expectedPrefix := fmt.Sprintf(
"https://%s.s3.%s.amazonaws.com/users/%s/",
s.config.Storage.Bucket,
s.config.Storage.Region,
userID,
)
if !strings.HasPrefix(req.ProfileImage, expectedPrefix) {
return nil, errors.New("invalid profile image url")
}
updates["profile_image"] = req.ProfileImage
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/service/auth_service.go` around lines 222 - 224, The profile image
validation in the update block accepts any non-empty string for req.ProfileImage
without verifying it originates from the configured S3 bucket/region, allowing
clients to persist arbitrary external URLs and bypass the presigned-upload
mechanism. Before adding profile_image to the updates map, validate that
req.ProfileImage is a trusted S3 URL matching your configured bucket and region
(and user prefix if applicable) to ensure only authorized presigned URLs are
persisted.

Comment thread internal/service/storage_service.go
Comment on lines +37 to +59
func (s *StorageService) GenerateUploadURL(
userID string,
fileName string,
) (string, string, error) {

objectKey := fmt.Sprintf(
"users/%s/%s",
userID,
fileName,
)

presignClient := s3.NewPresignClient(s.client)

req, err := presignClient.PresignPutObject(
context.TODO(),
&s3.PutObjectInput{
Bucket: &s.bucket,
Key: &objectKey,
},
func(opts *s3.PresignOptions) {
opts.Expires = 15 * time.Minute
},
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Pass request context from handler to service for proper lifecycle management.

GenerateUploadURL uses context.TODO() for the S3 presign operation (line 51), which decouples the presign request from the HTTP request lifecycle. This means:

  • HTTP request cancellation does not cancel the S3 operation (resource leak).
  • There is no timeout mechanism for the S3 call (potential hangs).
  • The service violates Go best practices for passing request context through layers.

Update the method signature to accept a context parameter:

💡 Proposed fix
 func (s *StorageService) GenerateUploadURL(
+	ctx context.Context,
 	userID string,
 	fileName string,
 ) (string, string, error) {
 	// ...
 	req, err := presignClient.PresignPutObject(
-		context.TODO(),
+		ctx,
 		&s3.PutObjectInput{

And update the handler call (in internal/handler/auth_handler.go) to pass the request context:

- uploadURL, fileURL, err := h.storageService.GenerateUploadURL(
+ uploadURL, fileURL, err := h.storageService.GenerateUploadURL(
+     c.Request.Context(),
      userID.(string),
      req.FileName,
  )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (s *StorageService) GenerateUploadURL(
userID string,
fileName string,
) (string, string, error) {
objectKey := fmt.Sprintf(
"users/%s/%s",
userID,
fileName,
)
presignClient := s3.NewPresignClient(s.client)
req, err := presignClient.PresignPutObject(
context.TODO(),
&s3.PutObjectInput{
Bucket: &s.bucket,
Key: &objectKey,
},
func(opts *s3.PresignOptions) {
opts.Expires = 15 * time.Minute
},
)
func (s *StorageService) GenerateUploadURL(
ctx context.Context,
userID string,
fileName string,
) (string, string, error) {
objectKey := fmt.Sprintf(
"users/%s/%s",
userID,
fileName,
)
presignClient := s3.NewPresignClient(s.client)
req, err := presignClient.PresignPutObject(
ctx,
&s3.PutObjectInput{
Bucket: &s.bucket,
Key: &objectKey,
},
func(opts *s3.PresignOptions) {
opts.Expires = 15 * time.Minute
},
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/service/storage_service.go` around lines 37 - 59, The
GenerateUploadURL method uses context.TODO() for the S3 presign operation, which
decouples it from the HTTP request lifecycle and prevents proper cancellation
and timeout handling. Update the GenerateUploadURL method signature to accept a
context parameter as the first argument (after the receiver), replace the
context.TODO() call in the presign operation with the passed context parameter,
and then update the handler in internal/handler/auth_handler.go to pass the
request context when calling GenerateUploadURL.

@sasidaran-99

Copy link
Copy Markdown
Author

I have read the CLA and agree to its terms

@sonarqubecloud

Copy link
Copy Markdown

@sasidaran-99

sasidaran-99 commented Jun 23, 2026

Copy link
Copy Markdown
Author

@roshankumar0036singh
I've addressed the review feedback in the latest commits:

  • Added validation for upload file names
  • Added safeguards around user ID handling in the upload endpoint
  • Resolved the SonarCloud maintainability warning
  • Verified the project builds and tests successfully

I have also signed the CLA by posting the required agreement comment. The CLA Assistant workflow appears to be awaiting maintainer approval to run. Could someone please approve and rerun it when convenient?

@dev-Aarish

dev-Aarish commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

@sasidaran-99 kindly do the following tasks to get your pr merged:

  1. there are still some of the suggestions which has not been resolved yet, please take care of them.
  2. there is a merge conflict coming as well, please attend that.
  3. please comment the cla terms with a full stop at the end of the comment.
  4. Link this pr with the issue you solved by keeping the issue number in the pr description

@dev-Aarish
dev-Aarish changed the base branch from main to SSOC26 July 18, 2026 18:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants