-
Notifications
You must be signed in to change notification settings - Fork 0
Ncto 141 add image upload on project settings #19
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
Merged
Louis-rollet
merged 3 commits into
main
from
NCTO-141-add-image-upload-on-project-settings
Mar 29, 2026
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
7f32e3d
[PROJECT] [UPDATE] Implement image upload functionality in project se…
Louis-rollet ba02b22
[PROJECT] [UPDATE] image upload functionality and public access for p…
Louis-rollet 6e0344c
[PROJECT] [UPDATE] Enhance image upload and retrieval with cache cont…
Louis-rollet File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import { SetMetadata } from "@nestjs/common"; | ||
|
|
||
| export const IS_PUBLIC_KEY = "isPublic"; | ||
| export const Public = (): ReturnType<typeof SetMetadata> => | ||
| SetMetadata(IS_PUBLIC_KEY, true); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,22 @@ | ||
| import { Injectable } from "@nestjs/common"; | ||
| import { ExecutionContext, Injectable } from "@nestjs/common"; | ||
| import { Reflector } from "@nestjs/core"; | ||
| import { AuthGuard } from "@nestjs/passport"; | ||
| import { IS_PUBLIC_KEY } from "@auth/decorators/public.decorator"; | ||
|
|
||
| @Injectable() | ||
| export class JwtAuthGuard extends AuthGuard("jwt") {} | ||
| export class JwtAuthGuard extends AuthGuard("jwt") { | ||
| constructor(private reflector: Reflector) { | ||
| super(); | ||
| } | ||
|
|
||
| override canActivate(context: ExecutionContext) { | ||
| const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [ | ||
| context.getHandler(), | ||
| context.getClass() | ||
| ]); | ||
| if (isPublic) { | ||
| return true; | ||
| } | ||
| return super.canActivate(context); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { ApiProperty } from "@nestjs/swagger"; | ||
|
|
||
| export class ImageUrlResponseDto { | ||
| @ApiProperty({ | ||
| example: "https://cdn.example.com/projects/42/image", | ||
| description: "The public CDN URL for the image" | ||
| }) | ||
| url!: string; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,9 +47,9 @@ import { FileInterceptor } from "@nestjs/platform-express"; | |
| import { Response } from "express"; | ||
| import { S3Service } from "@s3/s3.service"; | ||
| import { CloudfrontService } from "src/routes/s3/edge.service"; | ||
| import { ConfigService } from "@nestjs/config"; | ||
| import { SignedCdnResourceDto } from "@common/dto/signed-cdn-resource.dto"; | ||
| import { MissingEnvVarError } from "@auth/auth.error"; | ||
| import { Public } from "@auth/decorators/public.decorator"; | ||
| import { ImageUrlResponseDto } from "src/routes/common/dto/image-url-response.dto"; | ||
|
|
||
| @ApiTags("users") | ||
| @ApiExtraModels( | ||
|
|
@@ -66,8 +66,7 @@ export class UserController { | |
| constructor( | ||
| private readonly userService: UserService, | ||
| private readonly s3Service: S3Service, | ||
| private readonly cloudfrontService: CloudfrontService, | ||
| private readonly configService: ConfigService | ||
| private readonly cloudfrontService: CloudfrontService | ||
| ) {} | ||
|
|
||
| @Get("profile") | ||
|
|
@@ -109,6 +108,7 @@ export class UserController { | |
| @UploadedFile( | ||
| new ParseFilePipeBuilder() | ||
| .addMaxSizeValidator({ maxSize: 5 * 1024 * 1024 }) | ||
| .addFileTypeValidator({ fileType: /^image\/(jpeg|png|gif|webp)$/ }) | ||
| .build({ errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY }) | ||
| ) | ||
| file: Express.Multer.File, | ||
|
|
@@ -127,8 +127,10 @@ export class UserController { | |
| uploadedBy: req.user.id.toString(), | ||
| userId: id.toString(), | ||
| originalName: file.originalname | ||
| } | ||
| }, | ||
| cacheControl: "no-cache" | ||
| }); | ||
| await this.s3Service.setObjectPublicRead(key); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ehh we can ignore this
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not really otherwise we cannot have access to the ressource with the cdn |
||
|
|
||
| return { message: "Profile picture uploaded successfully", id }; | ||
| } | ||
|
|
@@ -148,18 +150,14 @@ export class UserController { | |
| @Res() res: Response | ||
| ): Promise<void> { | ||
| const key = `users/${id}/profile`; | ||
| const exists = await this.s3Service.fileExists(key); | ||
| if (!exists) { | ||
| const head = await this.s3Service.getFileMetadataOrNull(key); | ||
| if (!head) { | ||
| res.status(HttpStatus.NOT_FOUND).json({ message: "Profile picture not found" }); | ||
| return; | ||
| } | ||
|
|
||
| const cdnUrl = this.configService.get<string>("CDN_URL"); | ||
| if (!cdnUrl) { | ||
| throw new MissingEnvVarError("CDN_URL"); | ||
| } | ||
|
|
||
| const resourceUrl = this.cloudfrontService.generateSignedUrl(key); | ||
| const version = head.ETag?.replace(/"/g, "") ?? Date.now().toString(); | ||
| const resourceUrl = `${this.cloudfrontService.getCDNUrl(key)}?v=${version}`; | ||
|
|
||
| res.status(HttpStatus.OK).json({ | ||
| resourceUrl, | ||
|
|
@@ -324,4 +322,37 @@ export class UserController { | |
| message: "User deleted successfully" | ||
| }; | ||
| } | ||
|
|
||
| @Public() | ||
| @Get("public/:id/profile-picture") | ||
| @ApiOperation({ | ||
| summary: "Get public CDN URL for a user's profile picture" | ||
| }) | ||
| @ApiParam({ | ||
| name: "id", | ||
| type: "number", | ||
| description: "User ID" | ||
| }) | ||
| @ApiResponse({ | ||
| status: HttpStatus.OK, | ||
| description: "Returns the CDN URL for the profile picture", | ||
| type: ImageUrlResponseDto | ||
| }) | ||
| @ApiResponse({ | ||
| status: HttpStatus.NOT_FOUND, | ||
| description: "User not found or has no profile picture" | ||
| }) | ||
| async getPublicProfilePicture( | ||
| @Param("id", ParseIntPipe) id: number | ||
| ): Promise<ImageUrlResponseDto> { | ||
| const key = `users/${id}/profile`; | ||
| const head = await this.s3Service.getFileMetadataOrNull(key); | ||
| if (!head) { | ||
| throw new HttpException("Not found", HttpStatus.NOT_FOUND); | ||
| } | ||
|
|
||
| const version = head.ETag?.replace(/"/g, "") ?? Date.now().toString(); | ||
| const url = `${this.cloudfrontService.getCDNUrl(key)}?v=${version}`; | ||
| return { url }; | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Put this in
routes/common/dto/...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.
ba02b22