@azteam/express 1.2.277 → 1.2.278
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +12 -2
- package/src/AdminController.js +0 -311
- package/src/Controller.js +0 -38
- package/src/Server.js +0 -398
- package/src/SocketServer.js +0 -186
- package/src/constant.js +0 -12
- package/src/index.js +0 -12
- package/src/middleware/adminRoleMiddleware.js +0 -7
- package/src/middleware/authMiddleware.js +0 -53
- package/src/middleware/cacheMiddleware.js +0 -37
- package/src/middleware/etagMiddleware.js +0 -20
- package/src/middleware/index.js +0 -11
- package/src/middleware/limitRequestMiddleware.js +0 -18
- package/src/middleware/paginateMiddleware.js +0 -128
- package/src/middleware/roleMiddleware.js +0 -16
- package/src/middleware/signMiddleware.js +0 -14
- package/src/middleware/systemRoleMiddleware.js +0 -7
- package/src/middleware/validateMiddleware.js +0 -58
- package/src/middleware/verifyGoogleAppMiddleware.js +0 -12
- package/src/validate.js +0 -161
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import etag from 'etag';
|
|
2
|
-
|
|
3
|
-
function floorToMinute(time, minutes) {
|
|
4
|
-
const roundSecond = minutes * 60;
|
|
5
|
-
time = time - (time % (Math.floor(time / roundSecond) * roundSecond));
|
|
6
|
-
return time;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export default function (mTimeout = 5) {
|
|
10
|
-
return async function (req, res, next) {
|
|
11
|
-
if (req.method === 'GET') {
|
|
12
|
-
const etag_hash = etag(req.url + floorToMinute(Math.floor(Date.now() / 1000), mTimeout));
|
|
13
|
-
if (req.headers['if-none-match'] === etag_hash) {
|
|
14
|
-
return res.status(304).send();
|
|
15
|
-
}
|
|
16
|
-
res.setHeader('ETag', etag_hash);
|
|
17
|
-
}
|
|
18
|
-
return next();
|
|
19
|
-
};
|
|
20
|
-
}
|
package/src/middleware/index.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
export {default as signMiddleware} from './signMiddleware';
|
|
2
|
-
export {default as etagMiddleware} from './etagMiddleware';
|
|
3
|
-
export {default as authMiddleware} from './authMiddleware';
|
|
4
|
-
export {default as roleMiddleware} from './roleMiddleware';
|
|
5
|
-
export {default as adminRoleMiddleware} from './adminRoleMiddleware';
|
|
6
|
-
export {default as systemRoleMiddleware} from './systemRoleMiddleware';
|
|
7
|
-
export {default as paginateMiddleware} from './paginateMiddleware';
|
|
8
|
-
export {default as validateMiddleware} from './validateMiddleware';
|
|
9
|
-
export {default as limitRequestMiddleware} from './limitRequestMiddleware';
|
|
10
|
-
export {default as cacheMiddleware} from './cacheMiddleware';
|
|
11
|
-
export {default as verifyGoogleAppMiddleware} from './verifyGoogleAppMiddleware';
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import rateLimit from 'express-rate-limit';
|
|
2
|
-
import {BLOCKED} from '@azteam/error';
|
|
3
|
-
|
|
4
|
-
export default function limitRequest(max = 10, seconds = 30) {
|
|
5
|
-
return rateLimit({
|
|
6
|
-
max,
|
|
7
|
-
windowMs: seconds * 1000, // default 30 seconds
|
|
8
|
-
message: {
|
|
9
|
-
success: false,
|
|
10
|
-
errors: [
|
|
11
|
-
{
|
|
12
|
-
code: BLOCKED,
|
|
13
|
-
message: 'Too many request',
|
|
14
|
-
},
|
|
15
|
-
],
|
|
16
|
-
},
|
|
17
|
-
});
|
|
18
|
-
}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import _ from 'lodash';
|
|
2
|
-
import {ErrorException, INVALID} from '@azteam/error';
|
|
3
|
-
|
|
4
|
-
function omitData(data) {
|
|
5
|
-
Object.keys(data).map(function (key) {
|
|
6
|
-
let value = data[key];
|
|
7
|
-
if (typeof value === 'string') {
|
|
8
|
-
value = value.trim();
|
|
9
|
-
if (value === '' || value === 'NaN' || value === 'null' || value === 'undefined') {
|
|
10
|
-
delete data[key];
|
|
11
|
-
} else {
|
|
12
|
-
data[key] = value;
|
|
13
|
-
}
|
|
14
|
-
} else if (value === null || value === undefined || Number.isNaN(value)) {
|
|
15
|
-
delete data[key];
|
|
16
|
-
}
|
|
17
|
-
return true;
|
|
18
|
-
});
|
|
19
|
-
return data;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export default function (options = {}) {
|
|
23
|
-
options = {
|
|
24
|
-
limit: 20,
|
|
25
|
-
allowSearchFields: [],
|
|
26
|
-
allowSortFields: ['created_at', 'modified_at', 'status'],
|
|
27
|
-
allowLimits: [20, 40, 80],
|
|
28
|
-
...options,
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
return async function (req, res, next) {
|
|
32
|
-
req.query = omitData(req.query);
|
|
33
|
-
|
|
34
|
-
res.resOptions = options;
|
|
35
|
-
req.paginate = {
|
|
36
|
-
limit: options.limit,
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
if (req.query.limit) {
|
|
40
|
-
const limit = Number(req.query.limit);
|
|
41
|
-
|
|
42
|
-
if (options.allowLimits.includes(limit)) {
|
|
43
|
-
req.paginate.limit = limit;
|
|
44
|
-
delete req.query.limit;
|
|
45
|
-
} else {
|
|
46
|
-
throw new ErrorException(INVALID, [
|
|
47
|
-
{
|
|
48
|
-
field: 'limit',
|
|
49
|
-
message: `limit just accept ${options.allowLimits.toString()}`,
|
|
50
|
-
expected: options.allowLimits,
|
|
51
|
-
actual: limit,
|
|
52
|
-
},
|
|
53
|
-
]);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
req.paginate.page = req.query.page ? Number(req.query.page) : 1;
|
|
58
|
-
req.paginate.offset = (req.paginate.page - 1) * req.paginate.limit;
|
|
59
|
-
|
|
60
|
-
if (req.query.sort_by && options.allowSortFields.includes(req.query.sort_by)) {
|
|
61
|
-
req.paginate.sort = {
|
|
62
|
-
[req.query.sort_by]: req.query.sort_type === 'asc' ? 'asc' : 'desc',
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
delete req.query.sort_by;
|
|
67
|
-
delete req.query.sort_type;
|
|
68
|
-
delete req.query.page;
|
|
69
|
-
|
|
70
|
-
if (req.query.autocomplete) {
|
|
71
|
-
if (!options.autocompleteField) {
|
|
72
|
-
throw new ErrorException(INVALID, [{field: 'autocomplete', message: 'Not exists autocomplete field'}]);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
req.query = {
|
|
76
|
-
[options.autocompleteField]: {
|
|
77
|
-
$regex: req.query.autocomplete,
|
|
78
|
-
$options: 'i',
|
|
79
|
-
},
|
|
80
|
-
};
|
|
81
|
-
} else {
|
|
82
|
-
_.map(req.query, (value, key) => {
|
|
83
|
-
if (key.endsWith('_start')) {
|
|
84
|
-
const newKey = key.replace('_start', '');
|
|
85
|
-
|
|
86
|
-
req.query[newKey] = {
|
|
87
|
-
...req.query[newKey],
|
|
88
|
-
$gte: value,
|
|
89
|
-
};
|
|
90
|
-
} else if (key.endsWith('_end')) {
|
|
91
|
-
const newKey = key.replace('_end', '');
|
|
92
|
-
|
|
93
|
-
req.query[newKey] = {
|
|
94
|
-
...req.query[newKey],
|
|
95
|
-
$lte: value,
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (!options.allowSearchFields.includes(key)) {
|
|
100
|
-
throw new ErrorException(INVALID, [
|
|
101
|
-
{
|
|
102
|
-
field: 'key',
|
|
103
|
-
message: `Not exists search ${key}`,
|
|
104
|
-
actual: key,
|
|
105
|
-
expected: options.allowSearchFields,
|
|
106
|
-
},
|
|
107
|
-
]);
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
if (req.query.keywords) {
|
|
112
|
-
const arrKeywords = _.words(req.query.keywords);
|
|
113
|
-
const keywords = `"${arrKeywords.join('" "')}"`;
|
|
114
|
-
|
|
115
|
-
delete req.query.keywords;
|
|
116
|
-
|
|
117
|
-
req.query = {
|
|
118
|
-
...req.query,
|
|
119
|
-
$text: {
|
|
120
|
-
$search: keywords,
|
|
121
|
-
},
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return next();
|
|
127
|
-
};
|
|
128
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import {ErrorException, PERMISSION, UNAUTHORIZED} from '@azteam/error';
|
|
2
|
-
|
|
3
|
-
export default function (roles = null, minLevel = 1) {
|
|
4
|
-
return async function (req, res, next) {
|
|
5
|
-
if (!req.user) {
|
|
6
|
-
throw new ErrorException(UNAUTHORIZED);
|
|
7
|
-
}
|
|
8
|
-
if (req.user.level < minLevel) {
|
|
9
|
-
throw new ErrorException(PERMISSION);
|
|
10
|
-
}
|
|
11
|
-
if (!roles || req.user.level === 100 || req.user.roles.some((r) => roles.includes(r))) {
|
|
12
|
-
return next();
|
|
13
|
-
}
|
|
14
|
-
throw new ErrorException(PERMISSION);
|
|
15
|
-
};
|
|
16
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import {isValidSign} from '@azteam/crypto';
|
|
2
|
-
import {ErrorException, SIGNATURE_FAILED} from '@azteam/error';
|
|
3
|
-
|
|
4
|
-
export default function (mTimeout = 5) {
|
|
5
|
-
return async function (req, res, next) {
|
|
6
|
-
if (req.query.sign) {
|
|
7
|
-
const url = `${req.protocol}://${req.hostname}${req.originalUrl}`;
|
|
8
|
-
if (isValidSign(url, process.env.SECRET_KEY, mTimeout)) {
|
|
9
|
-
return next();
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
throw new ErrorException(SIGNATURE_FAILED);
|
|
13
|
-
};
|
|
14
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import {ErrorException, INVALID} from '@azteam/error';
|
|
2
|
-
import Validator from 'fastest-validator';
|
|
3
|
-
|
|
4
|
-
const v = new Validator({
|
|
5
|
-
messages: {
|
|
6
|
-
jsonString: "The '{field}' field must be an json string! Actual: {actual}",
|
|
7
|
-
},
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
v.add('json', function ({schema, messages}, path, context) {
|
|
11
|
-
return {
|
|
12
|
-
source: `
|
|
13
|
-
try {
|
|
14
|
-
JSON.parse(value);
|
|
15
|
-
} catch (e) {
|
|
16
|
-
${this.makeError({type: 'jsonString', actual: 'value', messages})}
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
return value;
|
|
20
|
-
`,
|
|
21
|
-
};
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
/* remove empty field or not have validate field */
|
|
25
|
-
function omitData(data, inKeys = []) {
|
|
26
|
-
Object.keys(data).map(function (key, index) {
|
|
27
|
-
if (inKeys.includes(key)) {
|
|
28
|
-
let value = data[key];
|
|
29
|
-
if (typeof value === 'string') {
|
|
30
|
-
value = value.trim();
|
|
31
|
-
if (value === '' || value === 'NaN' || value === 'null' || value === 'undefined') {
|
|
32
|
-
delete data[key];
|
|
33
|
-
} else {
|
|
34
|
-
data[key] = value;
|
|
35
|
-
}
|
|
36
|
-
} else if (value === null || value === undefined || Number.isNaN(value)) {
|
|
37
|
-
delete data[key];
|
|
38
|
-
}
|
|
39
|
-
} else {
|
|
40
|
-
delete data[key];
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
return data;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export default function (type, rules) {
|
|
47
|
-
return async function (req, res, next) {
|
|
48
|
-
const reqData = omitData(req[type], Object.keys(rules));
|
|
49
|
-
|
|
50
|
-
/* validate field */
|
|
51
|
-
const errors = v.validate(reqData, rules);
|
|
52
|
-
|
|
53
|
-
if (Array.isArray(errors)) {
|
|
54
|
-
throw new ErrorException(INVALID, errors);
|
|
55
|
-
}
|
|
56
|
-
return next();
|
|
57
|
-
};
|
|
58
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import {decryptAES} from '@azteam/crypto';
|
|
2
|
-
import {ErrorException, CREDENTIAL_FAILED} from '@azteam/error';
|
|
3
|
-
|
|
4
|
-
export default function () {
|
|
5
|
-
return async function (req, res, next) {
|
|
6
|
-
if (!req.user.google_api_credential) {
|
|
7
|
-
return next(new ErrorException(CREDENTIAL_FAILED));
|
|
8
|
-
}
|
|
9
|
-
req.user.google_api_credential = JSON.parse(decryptAES(req.user.google_api_credential, process.env.SECRET_KEY));
|
|
10
|
-
return next();
|
|
11
|
-
};
|
|
12
|
-
}
|
package/src/validate.js
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
export const schemaId = (optional = false) => ({
|
|
2
|
-
type: 'string',
|
|
3
|
-
length: 24,
|
|
4
|
-
pattern: /^[A-Fa-f0-9]{24}$/,
|
|
5
|
-
optional,
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
export const schemaEmail = (optional = false) => ({
|
|
9
|
-
type: 'email',
|
|
10
|
-
optional,
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
export const schemaUrl = (optional = false) => ({
|
|
14
|
-
type: 'url',
|
|
15
|
-
optional,
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
export const schemaBoolean = (optional = false) => ({
|
|
19
|
-
type: 'boolean',
|
|
20
|
-
convert: true,
|
|
21
|
-
optional,
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
export const schemaDate = (optional = false) => ({
|
|
25
|
-
type: 'number',
|
|
26
|
-
convert: true,
|
|
27
|
-
integer: true,
|
|
28
|
-
optional,
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
export const schemaNumber = (optional = false) => ({
|
|
32
|
-
type: 'number',
|
|
33
|
-
convert: true,
|
|
34
|
-
optional,
|
|
35
|
-
});
|
|
36
|
-
export const schemaInteger = (min = 0, max = 999999999, optional = false) => ({
|
|
37
|
-
type: 'number',
|
|
38
|
-
integer: true,
|
|
39
|
-
convert: true,
|
|
40
|
-
min,
|
|
41
|
-
max,
|
|
42
|
-
optional,
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
export const schemaString = (min = 1, max = 255, optional = false) => ({
|
|
46
|
-
type: 'string',
|
|
47
|
-
convert: true,
|
|
48
|
-
trim: true,
|
|
49
|
-
min,
|
|
50
|
-
max,
|
|
51
|
-
optional,
|
|
52
|
-
});
|
|
53
|
-
export const schemaPassword = (optional = false) => ({
|
|
54
|
-
type: 'string',
|
|
55
|
-
convert: true,
|
|
56
|
-
min: 6,
|
|
57
|
-
max: 32,
|
|
58
|
-
optional,
|
|
59
|
-
});
|
|
60
|
-
export const schemaOTP = (optional = false) => ({
|
|
61
|
-
type: 'string',
|
|
62
|
-
length: 6,
|
|
63
|
-
pattern: /\d+/,
|
|
64
|
-
optional,
|
|
65
|
-
});
|
|
66
|
-
export const schemaIPv4 = (optional = false) => ({
|
|
67
|
-
type: 'string',
|
|
68
|
-
pattern: /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$/,
|
|
69
|
-
optional,
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
export const schemaImage = (optional = false) => ({
|
|
73
|
-
type: 'string',
|
|
74
|
-
max: 255,
|
|
75
|
-
optional,
|
|
76
|
-
});
|
|
77
|
-
export const schemaVideo = (optional = false) => ({
|
|
78
|
-
type: 'string',
|
|
79
|
-
max: 255,
|
|
80
|
-
optional,
|
|
81
|
-
});
|
|
82
|
-
export const schemaPhoneNumber = (optional = false) => ({
|
|
83
|
-
type: 'string',
|
|
84
|
-
length: 10,
|
|
85
|
-
pattern: /^((09|03|07|08|05)+([0-9]{8})\b)$/,
|
|
86
|
-
optional,
|
|
87
|
-
});
|
|
88
|
-
export const schemaSlug = (optional = false) => ({
|
|
89
|
-
type: 'string',
|
|
90
|
-
convert: true,
|
|
91
|
-
pattern: /[A-Za-z0-9_-]+/,
|
|
92
|
-
lowercase: true,
|
|
93
|
-
max: 300,
|
|
94
|
-
optional,
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
export const schemaKey = (optional = false) => ({
|
|
98
|
-
type: 'string',
|
|
99
|
-
convert: true,
|
|
100
|
-
pattern: /[A-Za-z0-9_-]+/,
|
|
101
|
-
uppercase: true,
|
|
102
|
-
max: 300,
|
|
103
|
-
optional,
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
export const schemaVersion = (optional = false) => ({
|
|
107
|
-
type: 'string',
|
|
108
|
-
pattern: /^(\d+)\.(\d+)\.(\d+)$/,
|
|
109
|
-
max: 300,
|
|
110
|
-
optional,
|
|
111
|
-
});
|
|
112
|
-
export const schemaArray = (items, optional = false) => ({
|
|
113
|
-
type: 'array',
|
|
114
|
-
items,
|
|
115
|
-
default: [],
|
|
116
|
-
optional,
|
|
117
|
-
});
|
|
118
|
-
export const schemaJSON = (optional = false) => ({
|
|
119
|
-
type: 'json',
|
|
120
|
-
optional,
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
export const schemaGallery = (optional = false) => ({
|
|
124
|
-
type: 'array',
|
|
125
|
-
items: schemaImage(),
|
|
126
|
-
optional,
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
export const schemaObject = (props = {}, optional = false) => ({
|
|
130
|
-
type: 'object',
|
|
131
|
-
strict: 'remove',
|
|
132
|
-
props,
|
|
133
|
-
optional,
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
export const schemaEnum = (values, optional = false) => ({
|
|
137
|
-
type: 'enum',
|
|
138
|
-
values,
|
|
139
|
-
optional,
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
/* rules */
|
|
143
|
-
export const rulesId = {
|
|
144
|
-
id: schemaId(),
|
|
145
|
-
};
|
|
146
|
-
export const rulesSlug = {
|
|
147
|
-
slug: schemaSlug(),
|
|
148
|
-
};
|
|
149
|
-
export const rulesKey = {
|
|
150
|
-
key: schemaKey(),
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
export const rulesMetadata = {
|
|
154
|
-
metadata_disable: schemaBoolean(true),
|
|
155
|
-
metadata_title: schemaString(0, 255, true),
|
|
156
|
-
metadata_title_og: schemaString(0, 255, true),
|
|
157
|
-
metadata_description: schemaString(0, 255, true),
|
|
158
|
-
metadata_description_og: schemaString(0, 255, true),
|
|
159
|
-
metadata_keywords: schemaString(0, 255, true),
|
|
160
|
-
metadata_image_url: schemaImage(true),
|
|
161
|
-
};
|