Skip to content

Commit a2deca9

Browse files
committed
added files for resume in resources
1 parent 38f0ec7 commit a2deca9

File tree

7 files changed

+505
-152
lines changed

7 files changed

+505
-152
lines changed

nextstep-backend/src/app.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ app.use(authenticateToken.unless({
6262
{ url: '/comment', methods: ['GET'] },
6363
{ url: '/post', methods: ['GET'] }, // Allow GET to /post
6464
{ url: /^\/resource\/image\/[^\/]+$/, methods: ['GET'] }, // Allow GET to /resource/image/{anything}
65+
{ url: /^\/resource\/file\/[^\/]+$/, methods: ['GET'] }, // Allow GET to /resource/image/{anything}
6566
]
6667
}));
6768

nextstep-backend/src/config/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export const config = {
1515
refresh_token_secret: () => process.env.REFRESH_TOKEN_SECRET || 'secret'
1616
},
1717
resources: {
18+
filesDirectoryPath: () => 'resources/files',
19+
fileMaxSize: () => 10 * 1024 * 1024, // Max file size: 10MB
1820
imagesDirectoryPath: () => 'resources/images',
1921
imageMaxSize: () => 10 * 1024 * 1024, // Max file size: 10MB
2022
resumesDirectoryPath: () => 'resources/resumes',

nextstep-backend/src/controllers/resources_controller.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Request, Response } from 'express';
22
import { config } from '../config/config';
33
import fs from 'fs';
44
import path from 'path';
5-
import { uploadResume, uploadImage } from '../services/resources_service';
5+
import { uploadResume, uploadImage, uploadFile } from '../services/resources_service';
66
import multer from 'multer';
77
import {CustomRequest} from "types/customRequest";
88
import {updateUserById} from "../services/users_service";
@@ -37,6 +37,19 @@ const createImageResource = async (req: Request, res: Response) => {
3737
}
3838
};
3939

40+
const createFileResource = async (req: Request, res: Response) => {
41+
try {
42+
const filename = await uploadFile(req);
43+
return res.status(201).send(filename);
44+
} catch (error) {
45+
if (error instanceof multer.MulterError || error instanceof TypeError) {
46+
return res.status(400).send({ message: error.message });
47+
} else {
48+
handleError(error, res);
49+
}
50+
}
51+
};
52+
4053
const getImageResource = async (req: Request, res: Response) => {
4154
try {
4255
const { filename } = req.params;
@@ -52,6 +65,22 @@ const getImageResource = async (req: Request, res: Response) => {
5265
}
5366
};
5467

68+
const getFileResource = async (req: Request, res: Response) => {
69+
try {
70+
const { filename } = req.params;
71+
const filePath = path.resolve(config.resources.filesDirectoryPath(), filename);
72+
73+
if (!fs.existsSync(filePath)) {
74+
return res.status(404).send('File not found');
75+
}
76+
77+
// res.sendFile(filePath);
78+
return res.download(filePath);
79+
} catch (error) {
80+
handleError(error, res);
81+
}
82+
};
83+
5584
const createResumeResource = async (req: Request, res: Response) => {
5685
try {
5786
const resumeFilename = await uploadResume(req);
@@ -85,7 +114,9 @@ const getResumeResource = async (req: Request, res: Response) => {
85114
export default {
86115
createUserImageResource,
87116
createImageResource,
117+
createFileResource,
88118
getImageResource,
119+
getFileResource,
89120
getResumeResource,
90121
createResumeResource
91122
};

nextstep-backend/src/routes/resources_routes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ const router = express.Router();
77
router.post('/image/user', (req: Request, res: Response) => Resource.createUserImageResource(req as CustomRequest, res));
88

99
router.post('/image', Resource.createImageResource);
10+
router.post('/file', Resource.createFileResource);
1011

1112
router.get('/image/:filename', Resource.getImageResource);
13+
router.get('/file/:filename', Resource.getFileResource);
1214

1315
router.post('/resume', Resource.createResumeResource);
1416

nextstep-backend/src/services/resources_service.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,43 @@ interface MulterRequest extends Request {
99
file?: Express.Multer.File;
1010
}
1111

12+
const createFilesStorage = () => {
13+
// Ensure the directory exists
14+
const filesResourcesDir = config.resources.filesDirectoryPath();
15+
if (!fs.existsSync(filesResourcesDir)) {
16+
fs.mkdirSync(filesResourcesDir, { recursive: true });
17+
}
18+
19+
const filesStorage = multer.diskStorage({
20+
destination: (req, file, cb) => {
21+
cb(null, `${filesResourcesDir}/`);
22+
},
23+
filename: (req, file, cb) => {
24+
const ext = path.extname(file.originalname);
25+
const id = randomUUID();
26+
cb(null, id + ext);
27+
}
28+
});
29+
30+
return multer({
31+
storage: filesStorage,
32+
limits: {
33+
fileSize: config.resources.fileMaxSize()
34+
},
35+
fileFilter: (req, file, cb) => {
36+
const allowedTypes = /pdf|docx|docs/;
37+
const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
38+
const mimetype = allowedTypes.test(file.mimetype);
39+
40+
if (extname && mimetype) {
41+
return cb(null, true);
42+
} else {
43+
return cb(new TypeError(`Invalid file type. Only images are allowed: ${allowedTypes}`));
44+
}
45+
}
46+
});
47+
};
48+
1249
const createImagesStorage = () => {
1350
// Ensure the directory exists
1451
const imagesResourcesDir = config.resources.imagesDirectoryPath();
@@ -103,6 +140,26 @@ const uploadImage = (req: MulterRequest): Promise<string> => {
103140
});
104141
};
105142

143+
const uploadFile = (req: MulterRequest): Promise<string> => {
144+
return new Promise<string>((resolve, reject) => {
145+
createFilesStorage().single('file')(req, {} as any, (error) => {
146+
if (error) {
147+
if (error instanceof multer.MulterError || error instanceof TypeError) {
148+
return reject(error);
149+
} else if (!req.file) {
150+
return reject(new TypeError('No file uploaded.'));
151+
} else {
152+
return reject(new Error('Internal Server Error'));
153+
}
154+
}
155+
if (!req.file) {
156+
return reject(new TypeError('No file uploaded.'));
157+
}
158+
resolve(req.file.filename);
159+
});
160+
});
161+
};
162+
106163
const uploadResume = (req: MulterRequest): Promise<string> => {
107164
return new Promise<string>((resolve, reject) => {
108165
createResumesStorage().single('file')(req, {} as any, (error) => {
@@ -151,4 +208,4 @@ const resumeExists = (filename: string): boolean => {
151208
};
152209

153210

154-
export { uploadImage, uploadResume, getResumeBuffer, resumeExists };
211+
export { uploadImage, uploadResume, getResumeBuffer, resumeExists, uploadFile };

0 commit comments

Comments
 (0)