Skip to content

Conversation

@zherujiang
Copy link
Collaborator

@zherujiang zherujiang commented Feb 25, 2025

Description

This PR adds a new edit profile photo feature. Prior to this, RecNet displays a user's Google account profile photo and does not allow changes. This feature allows any RecNet user to upload a profile photo, which is stored in an AWS S3 bucket and make updates to the user profile information in the database.

Related Issue

  • See PR#392 for previous comments and requested changes. Reopening PR for the same issue to merge to dev before master.

Frontend Changes

  • Modified /apps/recnet/src/components/setting/profile/ProfileEditForm.tsx adding new form element and function:
    • Added profile photo <input> element with a photo preview image to accept and display the selected photo.
    • Added handleUploadS3 function to get a secure uploadUrl from the backend, put objects to the S3 bucket, and update the form data.
    • Modified the onSubmit function to call the handleUploadS3 function when a user saves the form.
    • Updated the ProfileEditSchema, useForm hook to include the photoUrl field.
    • Added a generateUploadUrlMutation hook to send a request to the backend to get a secure S3 upload URL.
    • Added new state hooks [isUploading, setIsUploading], [selectedFile, setSelectedFile], [photoPreviewUrl, setPhotoPreviewUrl] , [fileError, setFileError] to manage the selected photo and upload status.
  • Created a new router generateS3UploadUrl in the /apps/recnet/src/server/routers/user.ts
  • Modified the router updateUser in /apps/recnet/src/server/routers/user.ts to fetch the original photoUrl and delete the corresponding S3 file if a new profile photo is uploaded.

Backend Changes

  • Added a new folder photo-storage under /apps/recnet-api/src/modules
    • Created a photo-storage.controler.ts to handle requests to the photo-storage endpoint.
    • Created a photo-storage.service.ts. Included a generateS3UploadUrl method and a deleteS3Object method.
    • Created a photo-storage.module.ts to package and export the s3 class.
  • Modified common.config.ts under /apps/recnet-api/src/config to register AWS S3 configuration.
  • Updated env.schema.ts under /apps/recnet-api/src/config to include the s3 environment variable data types.
  • Updated app.module.ts under /apps/recnet-api/src to include and import the PhotoStorageModule.

Other Changes

  • Added photo-storage.ts under /libs/recnet-api-model/src/lib/api to document the new APIs related to photo-storage.
  • Updated package.json to include aws-sdk/client-s3 and aws-sdk/s3-request-presigner .
  • Install aws-sdk: @aws-sdk/client-s3 @aws-sdk/s3-request-presigner. Make sure to use aws-sdk v3.

How To Test

  1. Log in to your account.
  2. Navigate to the Profile page.
  3. Click on the "Settings" button and open the first tab "Edit profile".
  4. Click on "Choose file" to choose a file and attach to the form.
  5. Click on the "Save" button and wait for the upload to complete.
  6. Check your profile photo and it should be updated to the new photo you uploaded.

Screenshots (if appropriate):

1_profile-photo-before 2_edit-photo 3_photo-attached 4_uploading 5_photo-updated

TODO

  • Add AWS S3 configurations in the .env file under /apps/recnet-api for the dev and prod environment for deployment

Zheru Jiang added 13 commits February 18, 2025 16:42
… update s3 module to use aws-sdk v3 syntax, remove unused file s3.ts
Added AWS S3 config to .env.sample
Changed AWS config and secret names in .env.test
Added example time stamp to s3.service.ts
Defined RecnetError for S3 upload and delete errors and throw RecnetError in s3.service.ts
…edesign API endpoints to meet RESTful requirements

Rename s3.service.ts to photo-storage.service.ts
Rename s3.controller.ts to photo-storage.controller.ts
Rename s3.module.ts to photo-storage.module.ts
Updated app.module.ts to import the PhotoStorageModule correctly
Modified getUploadUrl router to generateUploadUrl and use the POST method instead, because this request results in the creation of a new resource -the S3 URL which will be used later to upload a new photo. Hence POST is the appropriate method.
Updated the corresponding method generateS3UploadUrl in the backend photo-storage.service.ts and photo-storage.controller.ts, update to POST method.
… document the request and response schema for photo-storage API endpoints
@vercel
Copy link

vercel bot commented Feb 25, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
recnet ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 25, 2025 4:02am
recnet-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 25, 2025 4:02am

… Image component in ProfileEditForm.tsx to main square ratio
Copy link
Collaborator

@swh00tw swh00tw left a comment

Choose a reason for hiding this comment

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

LGTM

Comment on lines +1 to +27
import { Controller, Delete, Post, Query } from "@nestjs/common";
import { ApiOperation } from "@nestjs/swagger";

import { PhotoStorageService } from "./photo-storage.service";

@Controller("photo-storage")
export class PhotoStorageController {
constructor(private readonly photoStorageService: PhotoStorageService) {}
@ApiOperation({
summary: "Generate S3 Upload URL",
description:
"Generate a secure signed Url to upload profile photo to S3 bucket",
})
@Post("upload-url")
generateUploadUrl(): Promise<{ url: string }> {
return this.photoStorageService.generateS3UploadUrl();
}

@ApiOperation({
summary: "Delete S3 Object",
description: "Delete S3 Object (profile photo)",
})
@Delete("photo")
async deleteS3Object(@Query("fileUrl") fileUrl: string): Promise<void> {
return this.photoStorageService.deleteS3Object(fileUrl);
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would be better if we add access control for these APIs, for example, only authenticated user can hit these endpoints, otherwise, return error. You can simply add @Auth() decorator to achieve this.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Btw, you can use @UsePipes(new ZodValidationBodyPipe(theSchemaYouDefinedUnderRecnetAPIModel)) to check if the request fulfill the requirement (params, request body), if not, this decorator can help you handle the error throwing part.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Although missing some decorators won't affect functionality, you can read files like user.controller.ts to know more about our decorators.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I just saw these comments now! I'll try to make those edits after I set the env vars correctly and fix the conflicts.

@swh00tw
Copy link
Collaborator

swh00tw commented Feb 26, 2025

Btw, before merge into dev, please make sure to update env vars for staging environment using AWS portal.

@zherujiang zherujiang merged commit fd3b22b into dev Feb 26, 2025
4 checks passed
@github-actions github-actions bot mentioned this pull request Feb 26, 2025
zherujiang added a commit that referenced this pull request Mar 8, 2025
## RecNet auto-release action
This is an auto-generated PR by recnet-release-action 🤖
Please make sure to test your changes in staging before merging. 
## Related Issues
- #392
- #371
## Related PRs
- #394
- #391
## Staging links
recnet-web:
[https://vercel.live/link/recnet-git-dev-recnet-542617e7.vercel.app](https://vercel.live/link/recnet-git-dev-recnet-542617e7.vercel.app)
recnet-api:
[https://dev-api.recnet.io/api](https://dev-api.recnet.io/api)
@zherujiang zherujiang mentioned this pull request Mar 11, 2025
zherujiang added a commit that referenced this pull request Mar 12, 2025
## RecNet auto-release action
This is an auto-generated PR by recnet-release-action 🤖
Please make sure to test your changes in staging before merging. 
Recnet-api version bump for deployment. Related to PR#390.

## Related Issues
- #379
- #55 
- #371

## Related PRs
- #382 
- #391
- #392
- #394
- #390 

## Staging links
recnet-web:
[https://vercel.live/link/recnet-git-dev-recnet-542617e7.vercel.app](https://vercel.live/link/recnet-git-dev-recnet-542617e7.vercel.app)
recnet-api:
[https://dev-api.recnet.io/api](https://dev-api.recnet.io/api)
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.

4 participants