Skip to content

Commit 1cce291

Browse files
committed
refactor: simplify the middleware checkToken
1 parent e90193c commit 1cce291

File tree

3 files changed

+124
-106
lines changed

3 files changed

+124
-106
lines changed

src/app.ts

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ import fs from 'fs';
22
import path from 'path';
33
import bodyParser from 'body-parser';
44
import cookieParser from 'cookie-parser';
5-
import express from 'express';
5+
import express, { NextFunction } from 'express';
66
import helmet from 'helmet';
77
import { logger } from 'kv-logger';
8-
import _ from 'lodash';
9-
108
import { AppError, NotFound } from './core/app-error';
119
import { config } from './core/config';
10+
import { Req, Res, withLogger } from './core/middleware';
1211

1312
const accessKeys = require('./routes/accessKeys');
1413
const account = require('./routes/account');
@@ -33,22 +32,23 @@ app.use(bodyParser.json());
3332
app.use(bodyParser.urlencoded({ extended: false }));
3433
app.use(cookieParser());
3534
app.use(express.static(path.join(__dirname, '../public')));
35+
app.use(withLogger);
3636

37-
logger.debug('use set Access-Control Header');
3837
app.all('*', (req, res, next) => {
3938
res.header('Access-Control-Allow-Origin', '*');
4039
res.header(
4140
'Access-Control-Allow-Headers',
42-
'Origin, X-Requested-With, Content-Type, Accept, Authorization, X-CodePush-Plugin-Version, X-CodePush-Plugin-Name, X-CodePush-SDK-Version',
41+
'Origin, X-Requested-With, Content-Type, Accept, Authorization, X-CodePush-Plugin-Version, X-CodePush-Plugin-Name, X-CodePush-SDK-Version, X-Request-Id',
4342
);
4443
res.header('Access-Control-Allow-Methods', 'PUT,POST,GET,PATCH,DELETE,OPTIONS');
4544
next();
4645
});
4746

48-
logger.debug(`config common.storageType value: ${_.get(config, 'common.storageType')}`);
47+
logger.debug(`config common.storageType value: ${config.common.storageType}`);
4948

50-
if (_.get(config, 'common.storageType') === 'local') {
51-
const localStorageDir = _.get(config, 'local.storageDir');
49+
// config local storage
50+
if (config.common.storageType === 'local') {
51+
const localStorageDir = config.local.storageDir;
5252
if (localStorageDir) {
5353
logger.debug(`config common.storageDir value: ${localStorageDir}`);
5454

@@ -66,13 +66,14 @@ if (_.get(config, 'common.storageType') === 'local') {
6666
logger.error(e);
6767
throw e;
6868
}
69-
logger.debug(`static download uri value: ${_.get(config, 'local.public', '/download')}`);
70-
app.use(_.get(config, 'local.public', '/download'), express.static(localStorageDir));
69+
logger.debug(`static download uri value: ${config.local.public}`);
70+
app.use(config.local.public, express.static(localStorageDir));
7171
} else {
7272
logger.error('please config local storageDir');
7373
}
7474
}
7575

76+
// config routes
7677
app.use('/', routes);
7778
app.use('/v0.1/public/codepush', indexV1);
7879
app.use('/auth', auth);
@@ -89,12 +90,13 @@ app.use((req, res, next) => {
8990

9091
// error handler
9192
// eslint-disable-next-line @typescript-eslint/no-unused-vars
92-
app.use((err, req, res, next) => {
93+
app.use((err: Error, req: Req, res: Res, next: NextFunction) => {
94+
const thisLogger = req.logger || logger;
9395
if (err instanceof AppError) {
9496
res.status(err.status).send(err.message);
95-
logger.debug(err);
97+
thisLogger.debug(err);
9698
} else {
97-
res.status(err.status || 500).send(err.message);
98-
logger.error(err);
99+
res.status(500).send(err.message);
100+
thisLogger.error(err);
99101
}
100102
});

src/core/middleware.ts

Lines changed: 107 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,125 +1,141 @@
1+
import { randomUUID } from 'crypto';
2+
import type { Request, Response, NextFunction } from 'express';
13
import jwt from 'jsonwebtoken';
4+
import { logger, Logger } from 'kv-logger';
25
import _ from 'lodash';
36
import moment from 'moment';
47
import { Op } from 'sequelize';
58
import { UserTokens } from '../models/user_tokens';
6-
import { Users } from '../models/users';
9+
import { Users, UsersInterface } from '../models/users';
710
import { AppError, Unauthorized } from './app-error';
811
import { config } from './config';
912
import { parseToken, md5 } from './utils/security';
1013

11-
function checkAuthToken(authToken) {
14+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
15+
export interface Req<P = Record<string, string>, B = any, Q = Record<string, string | string[]>>
16+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
17+
extends Request<P, any, B, Q> {
18+
users: UsersInterface;
19+
logger: Logger;
20+
}
21+
22+
// eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-explicit-any
23+
export interface Res<B = any> extends Response<B> {}
24+
25+
/**
26+
* bind logger to request
27+
*/
28+
export function withLogger(req: Req, res: Res, next: NextFunction) {
29+
const { method, path, headers } = req;
30+
req.logger = logger.bindContext({
31+
path,
32+
method,
33+
requestId: headers['x-request-id'] || randomUUID(),
34+
});
35+
next();
36+
}
37+
38+
async function checkAuthToken(authToken: string) {
1239
const objToken = parseToken(authToken);
13-
return Users.findOne({
40+
const users = await Users.findOne({
1441
where: { identical: objToken.identical },
15-
})
16-
.then((users) => {
17-
if (_.isEmpty(users)) {
18-
throw new Unauthorized();
19-
}
20-
return UserTokens.findOne({
21-
where: {
22-
tokens: authToken,
23-
uid: users.id,
24-
expires_at: {
25-
[Op.gt]: moment().format('YYYY-MM-DD HH:mm:ss'),
26-
},
27-
},
28-
}).then((tokenInfo) => {
29-
if (_.isEmpty(tokenInfo)) {
30-
throw new Unauthorized();
31-
}
32-
return users;
33-
});
34-
})
35-
.then((users) => {
36-
return users;
37-
});
42+
});
43+
if (_.isEmpty(users)) {
44+
throw new Unauthorized();
45+
}
46+
47+
const tokenInfo = await UserTokens.findOne({
48+
where: {
49+
tokens: authToken,
50+
uid: users.id,
51+
expires_at: {
52+
[Op.gt]: moment().format('YYYY-MM-DD HH:mm:ss'),
53+
},
54+
},
55+
});
56+
if (_.isEmpty(tokenInfo)) {
57+
throw new Unauthorized();
58+
}
59+
60+
return users;
3861
}
3962

40-
function checkAccessToken(accessToken) {
41-
return new Promise((resolve, reject) => {
42-
if (_.isEmpty(accessToken)) {
43-
reject(new Unauthorized());
44-
return;
45-
}
46-
let authData;
47-
try {
48-
authData = jwt.verify(accessToken, config.jwt.tokenSecret);
49-
} catch (e) {
50-
reject(new Unauthorized());
51-
return;
52-
}
53-
const uid = _.get(authData, 'uid', null);
54-
const hash = _.get(authData, 'hash', null);
55-
if (parseInt(uid, 10) > 0) {
56-
Users.findOne({
57-
where: { id: uid },
58-
})
59-
.then((users) => {
60-
if (_.isEmpty(users)) {
61-
throw new Unauthorized();
62-
}
63-
if (!_.eq(hash, md5(users.get('ack_code')))) {
64-
throw new Unauthorized();
65-
}
66-
resolve(users);
67-
})
68-
.catch((e) => {
69-
reject(e);
70-
});
71-
return;
72-
}
73-
reject(new Unauthorized());
63+
async function checkAccessToken(accessToken: string) {
64+
if (_.isEmpty(accessToken)) {
65+
throw new Unauthorized();
66+
}
67+
68+
let authData: { uid: number; hash: string };
69+
try {
70+
authData = jwt.verify(accessToken, config.jwt.tokenSecret) as {
71+
uid: number;
72+
hash: string;
73+
};
74+
} catch (e) {
75+
throw new Unauthorized();
76+
}
77+
78+
const { uid, hash } = authData;
79+
if (uid <= 0) {
80+
throw new Unauthorized();
81+
}
82+
83+
const users = await Users.findOne({
84+
where: { id: uid },
7485
});
86+
if (_.isEmpty(users)) {
87+
throw new Unauthorized();
88+
}
89+
90+
if (hash !== md5(users.get('ack_code'))) {
91+
throw new Unauthorized();
92+
}
93+
return users;
7594
}
7695

77-
export function checkToken(req, res, next) {
78-
const authArr = _.split(req.get('Authorization'), ' ');
79-
let authType = 1;
96+
/**
97+
* check user token and bind user to request
98+
*/
99+
export function checkToken(req: Req, res: Res, next: NextFunction) {
100+
// get token and type
101+
let authType: 1 | 2 = 1;
80102
let authToken = '';
81-
if (_.eq(authArr[0], 'Bearer')) {
103+
const authArr = _.split(req.get('Authorization'), ' ');
104+
if (authArr[0] === 'Bearer') {
82105
[, authToken] = authArr; // Bearer
83106
if (authToken && authToken.length > 64) {
84107
authType = 2;
85108
} else {
86109
authType = 1;
87110
}
88-
} else if (_.eq(authArr[0], 'Basic')) {
111+
} else if (authArr[0] === 'Basic') {
89112
authType = 2;
90113
const b = Buffer.from(authArr[1], 'base64');
91114
const user = _.split(b.toString(), ':');
92115
[, authToken] = user;
93116
}
117+
118+
// do check token
119+
let checkTokenResult: Promise<UsersInterface>;
94120
if (authToken && authType === 1) {
95-
checkAuthToken(authToken)
96-
.then((users) => {
97-
req.users = users;
98-
next();
99-
return users;
100-
})
101-
.catch((e) => {
102-
if (e instanceof AppError) {
103-
res.status(e.status || 404).send(e.message);
104-
} else {
105-
next(e);
106-
}
107-
});
121+
checkTokenResult = checkAuthToken(authToken);
108122
} else if (authToken && authType === 2) {
109-
checkAccessToken(authToken)
110-
.then((users) => {
111-
req.users = users;
112-
next();
113-
return users;
114-
})
115-
.catch((e) => {
116-
if (e instanceof AppError) {
117-
res.status(e.status || 404).send(e.message);
118-
} else {
119-
next(e);
120-
}
121-
});
123+
checkTokenResult = checkAccessToken(authToken);
122124
} else {
123125
res.send(new Unauthorized(`Auth type not supported.`));
126+
return;
124127
}
128+
129+
checkTokenResult
130+
.then((users) => {
131+
req.users = users;
132+
next();
133+
})
134+
.catch((e) => {
135+
if (e instanceof AppError) {
136+
res.status(e.status || 404).send(e.message);
137+
} else {
138+
next(e);
139+
}
140+
});
125141
}

src/www.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ Versions.findOne({ where: { type: 1 } })
7777

7878
server.on('listening', () => {
7979
const addr = server.address();
80-
logger.info(`server is listening on ${addr}`);
80+
logger.info(`server is listening on ${JSON.stringify(addr)}`);
8181
});
8282
})
8383
.catch((e) => {

0 commit comments

Comments
 (0)