-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathProfilePictureController.ts
More file actions
140 lines (126 loc) · 4.55 KB
/
ProfilePictureController.ts
File metadata and controls
140 lines (126 loc) · 4.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import { Request, Response, NextFunction } from 'express';
import { TraineesRepository } from '../../repositories';
import {
StorageServiceType,
UploadServiceType,
UploadServiceError,
ImageServiceType,
AccessControl,
} from '../../services';
import { ResponseError } from '../../models';
import * as Sentry from '@sentry/node';
import fs from 'fs';
export interface ProfilePictureControllerType {
setProfilePicture(req: Request, res: Response, next: NextFunction): Promise<void>;
deleteProfilePicture(req: Request, res: Response, next: NextFunction): Promise<void>;
}
export class ProfilePictureController implements ProfilePictureControllerType {
private readonly traineesRepository: TraineesRepository;
private readonly storageService: StorageServiceType;
private readonly uploadService: UploadServiceType;
private readonly imageService: ImageServiceType;
constructor(
traineesRepository: TraineesRepository,
storageService: StorageServiceType,
uploadService: UploadServiceType,
imageService: ImageServiceType
) {
this.traineesRepository = traineesRepository;
this.storageService = storageService;
this.uploadService = uploadService;
this.imageService = imageService;
}
async setProfilePicture(req: Request, res: Response, next: NextFunction) {
const trainee = await this.traineesRepository.getTrainee(String(req.params.id));
if (!trainee) {
res.status(404).send(new ResponseError('Trainee not found'));
return;
}
// Handle file upload.
try {
await this.uploadService.uploadImage(req, res, 'profilePicture');
} catch (error: any) {
if (error instanceof UploadServiceError) {
res.status(400).send(new ResponseError(error.message));
} else {
next(error);
}
return;
}
if (!req.file?.path) {
res.status(400).send(new ResponseError('No file was uploaded'));
return;
}
// Resize image to reduce file size and create a smaller version
const originalFilePath = req.file.path;
const largeFilePath = originalFilePath + '_large';
const smallFilePath = originalFilePath + '_small';
try {
await this.imageService.resizeImage(originalFilePath, largeFilePath, 700, 700);
await this.imageService.resizeImage(largeFilePath, smallFilePath, 70, 70);
} catch (error: any) {
next(error);
return;
}
try {
// Upload image to storage
const baseURL = process.env.STORAGE_BASE_URL ?? '';
const imageURL = new URL(this.getImageKey(trainee.id), baseURL).href;
const thumbnailURL = new URL(this.getThumbnailKey(trainee.id), baseURL).href;
// Upload images to storage
const largeFileStream = fs.createReadStream(largeFilePath);
const smallFileStream = fs.createReadStream(smallFilePath);
await this.storageService.upload(this.getImageKey(trainee.id), largeFileStream, AccessControl.Public);
await this.storageService.upload(this.getThumbnailKey(trainee.id), smallFileStream, AccessControl.Public);
// update the trainee object with the new image URL
trainee.imageURL = imageURL;
trainee.thumbnailURL = thumbnailURL;
this.traineesRepository.updateTrainee(trainee);
res.status(201).send({ imageURL, thumbnailURL });
} catch (error: any) {
next(error);
return;
}
// Cleanup
fs.unlink(originalFilePath, (error) => {
if (error) {
Sentry.captureException(error);
}
});
fs.unlink(largeFilePath, (error) => {
if (error) {
Sentry.captureException(error);
}
});
fs.unlink(smallFilePath, (error) => {
if (error) {
Sentry.captureException(error);
}
});
}
async deleteProfilePicture(req: Request, res: Response, next: NextFunction) {
const trainee = await this.traineesRepository.getTrainee(String(req.params.id));
if (!trainee) {
res.status(404).send(new ResponseError('Trainee not found'));
return;
}
try {
await this.storageService.delete(this.getImageKey(trainee.id));
await this.storageService.delete(this.getThumbnailKey(trainee.id));
// update the trainee object with the new image URL
trainee.imageURL = undefined;
trainee.thumbnailURL = undefined;
this.traineesRepository.updateTrainee(trainee);
} catch (error: any) {
next(error);
return;
}
res.status(204).end();
}
private getImageKey(traineeId: string) {
return `images/profile/${traineeId}.jpeg`;
}
private getThumbnailKey(traineeId: string) {
return `images/profile/${traineeId}_thumb.jpeg`;
}
}