forked from Cutlery/immich
		
	Upload profile picture and convert into webp
This commit is contained in:
		
							parent
							
								
									c28251b8b4
								
							
						
					
					
						commit
						e33566a04a
					
				| @ -19,7 +19,7 @@ import { | |||||||
| import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard'; | import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard'; | ||||||
| import { AssetService } from './asset.service'; | import { AssetService } from './asset.service'; | ||||||
| import { FileFieldsInterceptor } from '@nestjs/platform-express'; | import { FileFieldsInterceptor } from '@nestjs/platform-express'; | ||||||
| import { multerOption } from '../../config/multer-option.config'; | import { assetUploadOption } from '../../config/asset-upload.config'; | ||||||
| import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator'; | import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator'; | ||||||
| import { CreateAssetDto } from './dto/create-asset.dto'; | import { CreateAssetDto } from './dto/create-asset.dto'; | ||||||
| import { ServeFileDto } from './dto/serve-file.dto'; | import { ServeFileDto } from './dto/serve-file.dto'; | ||||||
| @ -48,7 +48,7 @@ export class AssetController { | |||||||
|         { name: 'assetData', maxCount: 1 }, |         { name: 'assetData', maxCount: 1 }, | ||||||
|         { name: 'thumbnailData', maxCount: 1 }, |         { name: 'thumbnailData', maxCount: 1 }, | ||||||
|       ], |       ], | ||||||
|       multerOption, |       assetUploadOption, | ||||||
|     ), |     ), | ||||||
|   ) |   ) | ||||||
|   async uploadFile( |   async uploadFile( | ||||||
|  | |||||||
| @ -19,7 +19,7 @@ export class AuthService { | |||||||
|   private async validateUser(loginCredential: LoginCredentialDto): Promise<UserEntity> { |   private async validateUser(loginCredential: LoginCredentialDto): Promise<UserEntity> { | ||||||
|     const user = await this.userRepository.findOne( |     const user = await this.userRepository.findOne( | ||||||
|       { email: loginCredential.email }, |       { email: loginCredential.email }, | ||||||
|       { select: ['id', 'email', 'password', 'salt', 'firstName', 'lastName', 'isAdmin'] }, |       { select: ['id', 'email', 'password', 'salt', 'firstName', 'lastName', 'isAdmin', 'profileImagePath', 'isFirstLoggedIn'] }, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     const isAuthenticated = await this.validatePassword(user.password, loginCredential.password, user.salt); |     const isAuthenticated = await this.validatePassword(user.password, loginCredential.password, user.salt); | ||||||
|  | |||||||
| @ -1,11 +1,12 @@ | |||||||
| import { Controller, Get, Post, Body, Patch, Param, Delete, UseGuards, ValidationPipe, Put, Query } from '@nestjs/common'; | import { Controller, Get, Post, Body, Patch, Param, Delete, UseGuards, ValidationPipe, Put, Query, UseInterceptors, UploadedFile } from '@nestjs/common'; | ||||||
| import { UserService } from './user.service'; | import { UserService } from './user.service'; | ||||||
| import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard'; | import { JwtAuthGuard } from '../../modules/immich-jwt/guards/jwt-auth.guard'; | ||||||
| import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator'; | import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator'; | ||||||
| import { CreateUserDto } from './dto/create-user.dto'; | import { CreateUserDto } from './dto/create-user.dto'; | ||||||
| import { AdminRolesGuard } from '../../middlewares/admin-role-guard.middleware'; | import { AdminRolesGuard } from '../../middlewares/admin-role-guard.middleware'; | ||||||
| import { UpdateUserDto } from './dto/update-user.dto'; | import { UpdateUserDto } from './dto/update-user.dto'; | ||||||
| import { boolean } from 'joi'; | import { FileInterceptor } from '@nestjs/platform-express'; | ||||||
|  | import { profileImageUploadOption } from '../../config/profile-image-upload.config'; | ||||||
| 
 | 
 | ||||||
| @Controller('user') | @Controller('user') | ||||||
| export class UserController { | export class UserController { | ||||||
| @ -35,4 +36,16 @@ export class UserController { | |||||||
|   async updateUser(@Body(ValidationPipe) updateUserDto: UpdateUserDto) { |   async updateUser(@Body(ValidationPipe) updateUserDto: UpdateUserDto) { | ||||||
|     return await this.userService.updateUser(updateUserDto) |     return await this.userService.updateUser(updateUserDto) | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   @UseGuards(JwtAuthGuard) | ||||||
|  |   @UseInterceptors(FileInterceptor('file', profileImageUploadOption)) | ||||||
|  |   @Post('/profile-image') | ||||||
|  |   async createProfileImage(@GetAuthUser() authUser: AuthUserDto, @UploadedFile() fileInfo: Express.Multer.File) { | ||||||
|  |     return await this.userService.createProfileImage(authUser, fileInfo); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   @Get('/profile-image/:userId') | ||||||
|  |   async getProfileImage(@Param('assetId') assetId: string) { | ||||||
|  | 
 | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ import { CreateUserDto } from './dto/create-user.dto'; | |||||||
| import { UpdateUserDto } from './dto/update-user.dto'; | import { UpdateUserDto } from './dto/update-user.dto'; | ||||||
| import { UserEntity } from './entities/user.entity'; | import { UserEntity } from './entities/user.entity'; | ||||||
| import * as bcrypt from 'bcrypt'; | import * as bcrypt from 'bcrypt'; | ||||||
| 
 | import sharp from 'sharp'; | ||||||
| 
 | 
 | ||||||
| @Injectable() | @Injectable() | ||||||
| export class UserService { | export class UserService { | ||||||
| @ -124,4 +124,35 @@ export class UserService { | |||||||
|       throw new InternalServerErrorException('Failed to register new user'); |       throw new InternalServerErrorException('Failed to register new user'); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   async createProfileImage(authUser: AuthUserDto, fileInfo: Express.Multer.File) { | ||||||
|  |     try { | ||||||
|  |       // Convert file to jpeg
 | ||||||
|  |       let filePath = '' | ||||||
|  |       const fileSave = await sharp(fileInfo.path).webp().resize(512, 512).toFile(fileInfo.path + '.webp') | ||||||
|  | 
 | ||||||
|  |       if (fileSave) { | ||||||
|  |         filePath = fileInfo.path + '.webp'; | ||||||
|  |         await this.userRepository.update(authUser.id, { | ||||||
|  |           profileImagePath: filePath | ||||||
|  |         }) | ||||||
|  |       } else { | ||||||
|  |         filePath = fileInfo.path; | ||||||
|  |         await this.userRepository.update(authUser.id, { | ||||||
|  |           profileImagePath: filePath | ||||||
|  | 
 | ||||||
|  |         }) | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |       return { | ||||||
|  |         userId: authUser.id, | ||||||
|  |         profileImagePath: filePath | ||||||
|  |       }; | ||||||
|  |     } catch (e) { | ||||||
|  |       Logger.error(e, 'Create User Profile Image'); | ||||||
|  |       throw new InternalServerErrorException('Failed to create new user profile image'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ import { APP_UPLOAD_LOCATION } from '../constants/upload_location.constant'; | |||||||
| import { randomUUID } from 'crypto'; | import { randomUUID } from 'crypto'; | ||||||
| import { CreateAssetDto } from '../api-v1/asset/dto/create-asset.dto'; | import { CreateAssetDto } from '../api-v1/asset/dto/create-asset.dto'; | ||||||
| 
 | 
 | ||||||
| export const multerOption: MulterOptions = { | export const assetUploadOption: MulterOptions = { | ||||||
|   fileFilter: (req: Request, file: any, cb: any) => { |   fileFilter: (req: Request, file: any, cb: any) => { | ||||||
|     if (file.mimetype.match(/\/(jpg|jpeg|png|gif|mp4|x-msvideo|quicktime|heic|heif|dng|webp)$/)) { |     if (file.mimetype.match(/\/(jpg|jpeg|png|gif|mp4|x-msvideo|quicktime|heic|heif|dng|webp)$/)) { | ||||||
|       cb(null, true); |       cb(null, true); | ||||||
| @ -19,14 +19,14 @@ export const multerOption: MulterOptions = { | |||||||
| 
 | 
 | ||||||
|   storage: diskStorage({ |   storage: diskStorage({ | ||||||
|     destination: (req: Request, file: Express.Multer.File, cb: any) => { |     destination: (req: Request, file: Express.Multer.File, cb: any) => { | ||||||
|       const uploadPath = APP_UPLOAD_LOCATION; |       const basePath = APP_UPLOAD_LOCATION; | ||||||
|       const fileInfo = req.body as CreateAssetDto; |       const fileInfo = req.body as CreateAssetDto; | ||||||
| 
 | 
 | ||||||
|       const yearInfo = new Date(fileInfo.createdAt).getFullYear(); |       const yearInfo = new Date(fileInfo.createdAt).getFullYear(); | ||||||
|       const monthInfo = new Date(fileInfo.createdAt).getMonth(); |       const monthInfo = new Date(fileInfo.createdAt).getMonth(); | ||||||
| 
 | 
 | ||||||
|       if (file.fieldname == 'assetData') { |       if (file.fieldname == 'assetData') { | ||||||
|         const originalUploadFolder = `${uploadPath}/${req.user['id']}/original/${req.body['deviceId']}`; |         const originalUploadFolder = `${basePath}/${req.user['id']}/original/${req.body['deviceId']}`; | ||||||
| 
 | 
 | ||||||
|         if (!existsSync(originalUploadFolder)) { |         if (!existsSync(originalUploadFolder)) { | ||||||
|           mkdirSync(originalUploadFolder, { recursive: true }); |           mkdirSync(originalUploadFolder, { recursive: true }); | ||||||
| @ -35,7 +35,7 @@ export const multerOption: MulterOptions = { | |||||||
|         // Save original to disk
 |         // Save original to disk
 | ||||||
|         cb(null, originalUploadFolder); |         cb(null, originalUploadFolder); | ||||||
|       } else if (file.fieldname == 'thumbnailData') { |       } else if (file.fieldname == 'thumbnailData') { | ||||||
|         const thumbnailUploadFolder = `${uploadPath}/${req.user['id']}/thumb/${req.body['deviceId']}`; |         const thumbnailUploadFolder = `${basePath}/${req.user['id']}/thumb/${req.body['deviceId']}`; | ||||||
| 
 | 
 | ||||||
|         if (!existsSync(thumbnailUploadFolder)) { |         if (!existsSync(thumbnailUploadFolder)) { | ||||||
|           mkdirSync(thumbnailUploadFolder, { recursive: true }); |           mkdirSync(thumbnailUploadFolder, { recursive: true }); | ||||||
| @ -56,3 +56,5 @@ export const multerOption: MulterOptions = { | |||||||
|     }, |     }, | ||||||
|   }), |   }), | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
							
								
								
									
										38
									
								
								server/src/config/profile-image-upload.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								server/src/config/profile-image-upload.config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | import { HttpException, HttpStatus } from '@nestjs/common'; | ||||||
|  | import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface'; | ||||||
|  | import { existsSync, mkdirSync } from 'fs'; | ||||||
|  | import { diskStorage } from 'multer'; | ||||||
|  | import { extname } from 'path'; | ||||||
|  | import { Request } from 'express'; | ||||||
|  | import { APP_UPLOAD_LOCATION } from '../constants/upload_location.constant'; | ||||||
|  | 
 | ||||||
|  | export const profileImageUploadOption: MulterOptions = { | ||||||
|  |   fileFilter: (req: Request, file: any, cb: any) => { | ||||||
|  |     if (file.mimetype.match(/\/(jpg|jpeg|png|heic|heif|dng|webp)$/)) { | ||||||
|  |       cb(null, true); | ||||||
|  |     } else { | ||||||
|  |       cb(new HttpException(`Unsupported file type ${extname(file.originalname)}`, HttpStatus.BAD_REQUEST), false); | ||||||
|  |     } | ||||||
|  |   }, | ||||||
|  | 
 | ||||||
|  |   storage: diskStorage({ | ||||||
|  |     destination: (req: Request, file: Express.Multer.File, cb: any) => { | ||||||
|  |       const basePath = APP_UPLOAD_LOCATION; | ||||||
|  |       const profileImageLocation = `${basePath}/${req.user['id']}/profile`; | ||||||
|  |       if (!existsSync(profileImageLocation)) { | ||||||
|  |         mkdirSync(profileImageLocation, { recursive: true }); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |       cb(null, profileImageLocation); | ||||||
|  |     }, | ||||||
|  | 
 | ||||||
|  |     filename: (req: Request, file: Express.Multer.File, cb: any) => { | ||||||
|  |       const userId = req.user['id']; | ||||||
|  | 
 | ||||||
|  |       cb(null, `${userId}`); | ||||||
|  |     }, | ||||||
|  |   }), | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user