-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathserver.js
152 lines (125 loc) · 5.04 KB
/
server.js
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
141
142
143
144
145
146
147
148
149
150
151
152
const jsonServer = require('json-server');
const cors = require('cors');
const server = jsonServer.create();
const router = jsonServer.router('db.json');
const middlewares = jsonServer.defaults({ noCors: true });
const fs = require('fs');
const path = require('path');
const multer = require('multer');
const upload = multer({dest: 'uploads/'});
const jwt = require('jsonwebtoken');
const AUTH_JWT_SECRET = 'TOP-SECRET';
//TODO: change below line to change expire time of token (default time is 3hours 180mins)
const AUTH_JWT_OPTIONS = {expiresIn: 180*60};
// Load DB file for Authentication middleware and endpoints
const DB = JSON.parse(fs.readFileSync(path.join(__dirname, './db.json'), 'utf-8'));
server.use(cors());
// Authorization Middleware
server.use((req, res, next) => {
const protections = DB.protection || {};
const entity = req.url.split('/')[1];
const entityProtection = protections[entity];
const methodSpecificProtection = protections[entity + '.' + req.method.toLowerCase()];
const protectionRule = methodSpecificProtection === false ? false : (methodSpecificProtection || entityProtection);
const token = req.headers.token || req.headers.Token;
if (protectionRule && !token) return res.status(401).send();
if (token && !protectionRule) return next()
if (!token) return next();
jwt.verify(token, AUTH_JWT_SECRET, (err, decoded) => {
if (err) {
console.log('res: ', JSON.stringify(err));
return res.status(401).send(err.name === 'TokenExpiredError' ? 'Token Expired!' : 'Invalid Token');
}
req.user = decoded;
if (!protectionRule) return next(); // no authorization is needed
const authorized = protectionRule === true || protectionRule === decoded.role;
if (!authorized) return res.status(401).send();
next(); // authorized
});
});
// Set default middlewares (logger, static, cors and no-cache)
server.use(middlewares);
// general upload API (for test)
server.post('/upload', upload.single('image'), function (req, res, next) {
// req.file is the `image` file
// req.body will hold the text fields, if there were any
res.json(req.file);
});
// list all files API (for test)
server.get('/files', (req, res, next) => {
fs.readdir('./uploads/', (err, files) => {
if (err) return next(err);
res.json(files);
});
});
// download (preview) a file API
server.get('/files/:file_id', (req, res, next) => {
const {file_id} = req.params;
res.set('Content-Type', 'image/jpeg');
res.sendFile(path.join(__dirname, 'uploads/' + file_id));
});
// To handle POST, PUT and PATCH you need to use a body-parser
// You can use the one used by JSON Server
server.use(jsonServer.bodyParser);
// For all non-json POST and PATCH requests (create and edit endpoints using an image file)
// 1- Upload the file inside the `image` field
// 2- (do it in next middleware)
const imageFieldUploadMiddleware = upload.single('image');
server.use((req, res, next) => {
if ((req.method === 'POST' || req.method === 'PATCH') && req.headers['content-type'] != 'application/json') {
imageFieldUploadMiddleware(req, res, next);
} else {
next();
}
});
// If previous middle-ware worked, continue to next step
// 1- (previous middle-ware already did first step)
// 2- Validate uploaded file, and replace the `image` field value with the file path
server.use((req, res, next) => {
// if there was a file uploaded and previous middleware worked:
// req.file is the `image` file
// req.body will hold the text fields, if there were any
if (req.file) {
const {mimetype, size, filename} = req.file;
// validate uploaded image
if (mimetype != 'image/jpeg') throw new Error('image should be in image/jpeg type');
if (size > 2 * 1024 * 1024) throw new Error('image size should be less than 2MB');
// Replace image field value with the file's path
req.body.image = '/files/' + filename;
}
// continue to normal json-server router for actual creation
next();
});
// Add createdAt field with timestamp value when posting to any route
server.use((req, res, next) => {
if (req.method === 'POST') {
req.body.createdAt = Date.now();
}
// Continue to JSON Server router
next();
});
// Authentication Routes
server.post([
'/auth/login',
'/auth/refresh-token',
], function (req, res, next) {
if (req.url === '/auth/login') {
const {username, password} = req.body;
req.user = (DB.users || {}).find(u => u.username == username && u.password == password);
if (!req.user) return res.status(400).send('No user with those credentials!');
}
if (req.url === '/auth/refresh-token') {
if (!req.user) return res.status(400).send('Token Required!');
}
const {username, role, name} = req.user;
jwt.sign({username, role, name}, AUTH_JWT_SECRET, AUTH_JWT_OPTIONS, (err, token) => {
if (err) return next(error);
res.json({token});
});
});
// Use default router (CRUDs of db.json)
server.use(router);
//TODO: change below line to if you want to change backend port (default is : 3002 )
server.listen(3002, () => {
console.log('Customized JSON-Server is running at http://localhost:3002/');
});