api: draft for upload avatars

Pedro Lucas Porcellis porcellis@eletrotupi.com 1 month ago aa9f316078c22a3cd681d3cef4743472fe1d1dad
Parents: c74790e
4 file(s) changed
  • api/src/controllers/users.ts +24 -2
  • api/src/lib/s3.ts +12 -0
  • api/src/routes/users.ts +2 -0
  • api/src/services/avatar.ts +24 -0
api/src/controllers/users.ts
@@ -16,6 +16,11 @@ findUserByEmail,
16 16 updateUser
17 17 } from '@app/services/user.service';
18 18
19 + // XXX: I've regreted already
20 + interface SingleFileRequest extends Request {
21 + file?: any;
22 + }
23 +
19 24 export const UsersController = {
20 25 create: async (req: Request, res: Response, next: NextFunction) => {
21 26 try {
@@ -45,7 +50,7 @@ update: async (req: Request, res: Response, next: NextFunction) => {
45 50 try {
46 51 const { id } = req.params;
47 52
48 - const parsed = UpdateUserSchema.safeParse(req.body);
53 + const parsed = UpdateUserSchema.safeParse(req.body.user);
49 54 if (!parsed.success) {
50 55 return res.status(400).json({
51 56 errors: parsed.error!.issues
@@ -69,5 +74,22 @@ });
69 74 } catch (err) {
70 75 next(err);
71 76 }
77 + },
78 +
79 + updateAvatar: async (req: SingleFileRequest, res: Response, next: NextFunction) => {
80 + try {
81 + if (!req.file) {
82 + return res.status(400).json({ error: 'No file uploaded' });
83 + }
84 +
85 + res.json({
86 + message: 'File uploaded successfully',
87 + fileLocation: req.file.location,
88 + filename: req.file.originalname,
89 + size: req.file.size
90 + });
91 + } catch (err) {
92 + next(err);
93 + }
72 94 }
73 - };
95 + }
api/src/lib/s3.ts
@@ -0,0 +1,12 @@
1 + import { S3Client } from '@aws-sdk/client-s3';
2 +
3 + const s3 = new S3Client({
4 + region: process.env.S3_REGION,
5 + endpoint: `https://${process.env.S3_URL}`,
6 + credentials: {
7 + accessKeyId: process.env.S3_ACCESS_KEY!,
8 + secretAccessKey: process.env.S3_ACCESS_SECRET!
9 + }
10 + });
11 +
12 + export default s3;
api/src/routes/users.ts
@@ -1,9 +1,11 @@
1 1 import { Router } from "express";
2 2 import { UsersController } from '@app/controllers/users'
3 + import multer from '@app/services/avatar';
3 4
4 5 const router = Router();
5 6
6 7 router.post('/', UsersController.create);
7 8 router.put('/:id', UsersController.update);
9 + router.post('/:id/avatar', multer.single('avatar'), UsersController.updateAvatar);
8 10
9 11 export default router;
api/src/services/avatar.ts
@@ -0,0 +1,24 @@
1 + import s3 from '@app/lib/s3';
2 + import multerBase from 'multer';
3 + import multerS3 from 'multer-s3';
4 + import path from 'path';
5 + import crypto from 'crypto';
6 +
7 + const avatarMulter = multerBase({
8 + storage: multerS3({
9 + s3,
10 + acl: "public-read",
11 + bucket: process.env.S3_BUCKET!,
12 + metadata: function (req, file, cb) {
13 + cb(null, { fieldName: file.fieldname });
14 + },
15 + key: function (req, file, cb) {
16 + const ext = path.extname(file.originalname).slice(1);
17 + const uuid = crypto.randomUUID();
18 +
19 + cb(null, `avatars/${uuid}.${ext}`)
20 + }
21 + })
22 + })
23 +
24 + export default avatarMulter;