@go-mailer/jarvis 5.0.5 → 5.0.7
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/.eslintignore +1 -0
- package/.eslintrc.yml +10 -0
- package/index.js +11 -11
- package/lib/clients/go-flags.js +12 -12
- package/lib/clients/iam.js +33 -19
- package/lib/constants/automation.js +1 -1
- package/lib/env.js +12 -12
- package/lib/flag.js +4 -4
- package/lib/middlewares/auth.js +110 -95
- package/lib/middlewares/http.js +26 -26
- package/lib/middlewares/logger.js +29 -31
- package/lib/query.js +4 -4
- package/lib/redis/index.js +2 -2
- package/lib/utilitiy/chart.js +37 -37
- package/lib/utilitiy/date.js +104 -104
- package/lib/utilitiy/index.js +5 -5
- package/lib/utilitiy/number.js +5 -5
- package/package.json +8 -2
package/.eslintignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
test/
|
package/.eslintrc.yml
ADDED
package/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
const EnvVar = require(
|
|
2
|
-
const FeatureFlag = require(
|
|
3
|
-
const Redis = require(
|
|
4
|
-
const QueryBuilder = require(
|
|
5
|
-
const HTTP = require(
|
|
6
|
-
const Authenticator = require(
|
|
7
|
-
const AutomationConstants = require(
|
|
8
|
-
const { RequestLogger, ProcessLogger } = require(
|
|
9
|
-
const Utility = require(
|
|
1
|
+
const EnvVar = require('./lib/env')
|
|
2
|
+
const FeatureFlag = require('./lib/flag')
|
|
3
|
+
const Redis = require('./lib/redis/index')
|
|
4
|
+
const QueryBuilder = require('./lib/query')
|
|
5
|
+
const HTTP = require('./lib/middlewares/http')
|
|
6
|
+
const Authenticator = require('./lib/middlewares/auth')
|
|
7
|
+
const AutomationConstants = require('./lib/constants/automation')
|
|
8
|
+
const { RequestLogger, ProcessLogger } = require('./lib/middlewares/logger')
|
|
9
|
+
const Utility = require('./lib/utilitiy/index')
|
|
10
10
|
|
|
11
11
|
module.exports = {
|
|
12
12
|
Authenticator,
|
|
@@ -18,5 +18,5 @@ module.exports = {
|
|
|
18
18
|
QueryBuilder,
|
|
19
19
|
Redis,
|
|
20
20
|
RequestLogger,
|
|
21
|
-
Utility
|
|
22
|
-
}
|
|
21
|
+
Utility
|
|
22
|
+
}
|
package/lib/clients/go-flags.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const axios = require(
|
|
2
|
-
const Env = require(
|
|
3
|
-
const BASE_URI = Env.fetch(
|
|
4
|
-
const API_KEY = Env.fetch(
|
|
1
|
+
const axios = require('axios')
|
|
2
|
+
const Env = require('../env')
|
|
3
|
+
const BASE_URI = Env.fetch('GO_FLAGS_URI', true)
|
|
4
|
+
const API_KEY = Env.fetch('GO_FLAGS_KEY', true)
|
|
5
5
|
|
|
6
6
|
const verifyFeatureFlag = async (flag_name, criteria = {}, environment = '') => {
|
|
7
7
|
const { error, payload } = (
|
|
@@ -9,19 +9,19 @@ const verifyFeatureFlag = async (flag_name, criteria = {}, environment = '') =>
|
|
|
9
9
|
`${BASE_URI}/api/v1/flags/${flag_name}`,
|
|
10
10
|
{
|
|
11
11
|
payload: criteria,
|
|
12
|
-
environment: environment || Env.fetch(
|
|
12
|
+
environment: environment || Env.fetch('GO_FLAGS_ENV', true)
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
headers: {
|
|
16
|
-
authorization: `Bearer ${API_KEY}
|
|
17
|
-
}
|
|
16
|
+
authorization: `Bearer ${API_KEY}`
|
|
17
|
+
}
|
|
18
18
|
}
|
|
19
19
|
)
|
|
20
|
-
).data
|
|
20
|
+
).data
|
|
21
21
|
|
|
22
|
-
if (error) throw new Error(error)
|
|
22
|
+
if (error) throw new Error(error)
|
|
23
23
|
|
|
24
|
-
return payload.is_permitted
|
|
25
|
-
}
|
|
24
|
+
return payload.is_permitted
|
|
25
|
+
}
|
|
26
26
|
|
|
27
|
-
module.exports = { verifyFeatureFlag }
|
|
27
|
+
module.exports = { verifyFeatureFlag }
|
package/lib/clients/iam.js
CHANGED
|
@@ -1,21 +1,35 @@
|
|
|
1
|
-
const axios = require(
|
|
2
|
-
const Env = require(
|
|
3
|
-
const IAM_URI = Env.fetch(
|
|
4
|
-
const DEFAULT_TOKEN = Env.fetch(
|
|
1
|
+
const axios = require('axios').default
|
|
2
|
+
const Env = require('../env')
|
|
3
|
+
const IAM_URI = Env.fetch('IAM_SERVICE_URI', true)
|
|
4
|
+
const DEFAULT_TOKEN = Env.fetch('DEFAULT_TOKEN', true)
|
|
5
|
+
|
|
6
|
+
const checkAuthority = async ({ action, resource, user_id, tenant_id }) => {
|
|
7
|
+
const { error, payload } = (
|
|
8
|
+
await axios.post(`${IAM_URI}/authorizer`, {
|
|
9
|
+
action,
|
|
10
|
+
resource,
|
|
11
|
+
tenant_id,
|
|
12
|
+
user_id
|
|
13
|
+
})
|
|
14
|
+
).data
|
|
15
|
+
|
|
16
|
+
if (error || !payload.is_permitted) throw new Error('Unauthorized')
|
|
17
|
+
return payload.is_permitted
|
|
18
|
+
}
|
|
5
19
|
|
|
6
20
|
const verifyAPIKey = async (key) => {
|
|
7
21
|
const { error, payload } = (
|
|
8
22
|
await axios.get(`${IAM_URI}/keys/verify/${key}`, {
|
|
9
23
|
headers: {
|
|
10
|
-
authorization: `Bearer ${DEFAULT_TOKEN}
|
|
11
|
-
}
|
|
24
|
+
authorization: `Bearer ${DEFAULT_TOKEN}`
|
|
25
|
+
}
|
|
12
26
|
})
|
|
13
|
-
).data
|
|
27
|
+
).data
|
|
14
28
|
|
|
15
|
-
if (error) throw new Error(
|
|
29
|
+
if (error) throw new Error('Unauthorized')
|
|
16
30
|
|
|
17
|
-
return payload.org_id
|
|
18
|
-
}
|
|
31
|
+
return payload.org_id
|
|
32
|
+
}
|
|
19
33
|
|
|
20
34
|
const verifyFeatureFlag = async (flag_name, criteria = {}) => {
|
|
21
35
|
const { error, payload } = (
|
|
@@ -23,20 +37,20 @@ const verifyFeatureFlag = async (flag_name, criteria = {}) => {
|
|
|
23
37
|
`${IAM_URI}/flags`,
|
|
24
38
|
{
|
|
25
39
|
criteria,
|
|
26
|
-
environment: Env.fetch(
|
|
27
|
-
name: flag_name
|
|
40
|
+
environment: Env.fetch('NODE_ENV'),
|
|
41
|
+
name: flag_name
|
|
28
42
|
},
|
|
29
43
|
{
|
|
30
44
|
headers: {
|
|
31
|
-
authorization: `Bearer ${DEFAULT_TOKEN}
|
|
32
|
-
}
|
|
45
|
+
authorization: `Bearer ${DEFAULT_TOKEN}`
|
|
46
|
+
}
|
|
33
47
|
}
|
|
34
48
|
)
|
|
35
|
-
).data
|
|
49
|
+
).data
|
|
36
50
|
|
|
37
|
-
if (error) throw new Error(error)
|
|
51
|
+
if (error) throw new Error(error)
|
|
38
52
|
|
|
39
|
-
return payload.is_permitted
|
|
40
|
-
}
|
|
53
|
+
return payload.is_permitted
|
|
54
|
+
}
|
|
41
55
|
|
|
42
|
-
module.exports = { verifyAPIKey, verifyFeatureFlag }
|
|
56
|
+
module.exports = { checkAuthority, verifyAPIKey, verifyFeatureFlag }
|
|
@@ -7,7 +7,7 @@ module.exports = {
|
|
|
7
7
|
EVENT_OPENED_TRANSACTIONAL: 'opened_transactional',
|
|
8
8
|
EVENT_SUBSCRIPTION: 'joined_audience',
|
|
9
9
|
EVENT_UNSUBSCRIPTION: 'left_audience',
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
//
|
|
12
12
|
EFFECT_REMOVE_CONTACT: 'delete_contact',
|
|
13
13
|
EFFECT_SEND_TRANSACTIONAL: 'send_transactional_email',
|
package/lib/env.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
require(
|
|
1
|
+
require('dotenv').config()
|
|
2
2
|
|
|
3
3
|
/** */
|
|
4
4
|
class EnvVar {
|
|
5
|
-
constructor() {
|
|
6
|
-
this.config = {}
|
|
5
|
+
constructor () {
|
|
6
|
+
this.config = {}
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
set(config = {}) {
|
|
9
|
+
set (config = {}) {
|
|
10
10
|
this.config = {
|
|
11
11
|
...this.config,
|
|
12
|
-
...config
|
|
13
|
-
}
|
|
12
|
+
...config
|
|
13
|
+
}
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
fetch(var_name =
|
|
17
|
-
if (!var_name) throw new Error(
|
|
18
|
-
const value = process.env[var_name] || this.config[var_name]
|
|
19
|
-
if (is_required && !value) throw new Error(`Required EnvVar ${var_name} not found`)
|
|
20
|
-
return value
|
|
16
|
+
fetch (var_name = '', is_required = false) {
|
|
17
|
+
if (!var_name) throw new Error('Variable name is required.')
|
|
18
|
+
const value = process.env[var_name] || this.config[var_name]
|
|
19
|
+
if (is_required && !value) throw new Error(`Required EnvVar ${var_name} not found`)
|
|
20
|
+
return value
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
module.exports = new EnvVar()
|
|
24
|
+
module.exports = new EnvVar()
|
package/lib/flag.js
CHANGED
|
@@ -4,10 +4,10 @@ const flagLogger = new ProcessLogger('FeatureFlag')
|
|
|
4
4
|
|
|
5
5
|
const verify = async (flag_name = '', criteria = {}) => {
|
|
6
6
|
try {
|
|
7
|
-
if (!flag_name) throw new Error('Unspecified flag name')
|
|
8
|
-
if (!Object.keys(criteria)) throw new Error('Unspecified criteria')
|
|
9
|
-
|
|
10
|
-
const result = await verifyFeatureFlag(flag_name, criteria)
|
|
7
|
+
if (!flag_name) throw new Error('Unspecified flag name')
|
|
8
|
+
if (!Object.keys(criteria)) throw new Error('Unspecified criteria')
|
|
9
|
+
|
|
10
|
+
const result = await verifyFeatureFlag(flag_name, criteria)
|
|
11
11
|
return result
|
|
12
12
|
} catch (e) {
|
|
13
13
|
flagLogger.error(e, 'verify')
|
package/lib/middlewares/auth.js
CHANGED
|
@@ -2,98 +2,113 @@
|
|
|
2
2
|
* User Authentication Middleware
|
|
3
3
|
*/
|
|
4
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
|
-
|
|
5
|
+
const jwt = require('jsonwebtoken')
|
|
6
|
+
const Env = require('../env')
|
|
7
|
+
const Errors = require('./errors')
|
|
8
|
+
const { ProcessLogger } = require('./logger')
|
|
9
|
+
const { checkAuthority, verifyAPIKey } = require('../clients/iam')
|
|
10
|
+
const authLogger = new ProcessLogger('Authenticator')
|
|
11
|
+
|
|
12
|
+
// helpers
|
|
13
|
+
const extractToken = (headers) => {
|
|
14
|
+
const { authorization } = headers
|
|
15
|
+
if (!authorization) throw new Error('Unauthorized')
|
|
16
|
+
|
|
17
|
+
const [, token] = authorization.split(' ')
|
|
18
|
+
if (!token) throw new Error('Unauthorized')
|
|
19
|
+
|
|
20
|
+
return token
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const extractId = (request, key) => {
|
|
24
|
+
const { params, query, body } = request
|
|
25
|
+
let id = { $exists: true }
|
|
26
|
+
if (query[key]) id = query[key]
|
|
27
|
+
if (params[key]) id = params[key]
|
|
28
|
+
if (body[key]) id = body[key]
|
|
29
|
+
|
|
30
|
+
return id
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// main
|
|
34
|
+
|
|
35
|
+
const authenticateAdmin = async (request, response, next) => {
|
|
36
|
+
const { is_admin } = request
|
|
37
|
+
if (!is_admin) return response.status(403).json(Errors.UNAUTHORIZED)
|
|
38
|
+
|
|
39
|
+
next()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const authenticateBearerKey = async (request, response, next) => {
|
|
43
|
+
try {
|
|
44
|
+
const token = extractToken(request.headers)
|
|
45
|
+
request.tenant_id = await verifyAPIKey(token)
|
|
46
|
+
next()
|
|
47
|
+
} catch (e) {
|
|
48
|
+
authLogger.error(e, 'authenticateBearerKey')
|
|
49
|
+
return response.status(403).json(Errors.UNAUTHORIZED)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const authenticateParamKey = async (request, response, next) => {
|
|
54
|
+
try {
|
|
55
|
+
const { api_key: token } = request.params
|
|
56
|
+
request.tenant_id = await verifyAPIKey(token)
|
|
57
|
+
next()
|
|
58
|
+
} catch (e) {
|
|
59
|
+
authLogger.error(e, 'authenticateParamKey')
|
|
60
|
+
return response.status(403).json(Errors.UNAUTHORIZED)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const authenticateUser = async (request, response, next) => {
|
|
65
|
+
try {
|
|
66
|
+
// env vars
|
|
67
|
+
const DEFAULT_TOKEN = Env.fetch('DEFAULT_TOKEN', true)
|
|
68
|
+
const ISSUER = Env.fetch('JWT_ISSUER', true)
|
|
69
|
+
const SECRET = Env.fetch('JWT_SECRET', true)
|
|
70
|
+
|
|
71
|
+
const token = extractToken(request.headers)
|
|
72
|
+
if (token === DEFAULT_TOKEN) {
|
|
73
|
+
// inter-service requests
|
|
74
|
+
request.is_service_request = true
|
|
75
|
+
// typically scope requests by tenant_id
|
|
76
|
+
request.tenant_id = request.body.tenant_id || request.query.tenant_id || { $exists: true }
|
|
77
|
+
request.user_id = request.body.user_id || request.query.user_id || { $exists: true }
|
|
78
|
+
return next()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const { id: user_id, is_admin, tenant_id } = await jwt.verify(token, SECRET, { issuer: ISSUER })
|
|
82
|
+
request.is_admin = !!is_admin
|
|
83
|
+
request.user_id = is_admin ? extractId(request, 'user_id') : user_id
|
|
84
|
+
request.tenant_id = is_admin ? extractId(request, 'tenant_id') : tenant_id
|
|
85
|
+
|
|
86
|
+
next()
|
|
87
|
+
} catch (e) {
|
|
88
|
+
authLogger.error(e, 'authenticateUser')
|
|
89
|
+
return response.status(403).json(Errors.UNAUTHORIZED)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const authorizeUser = ({ action, resource }) => {
|
|
94
|
+
return async (request, response, next) => {
|
|
95
|
+
try {
|
|
96
|
+
const { is_admin, tenant_id, user_id } = request
|
|
97
|
+
if (is_admin) next()
|
|
98
|
+
|
|
99
|
+
await checkAuthority({ action, resource, tenant_id, user_id })
|
|
100
|
+
next()
|
|
101
|
+
} catch (e) {
|
|
102
|
+
authLogger.error(e, 'authorizeUser')
|
|
103
|
+
return response.status(403).json(Errors.UNAUTHORIZED)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = {
|
|
109
|
+
authenticateAdmin,
|
|
110
|
+
authenticateBearerKey,
|
|
111
|
+
authenticateParamKey,
|
|
112
|
+
authenticateUser,
|
|
113
|
+
authorizeUser
|
|
114
|
+
}
|
package/lib/middlewares/http.js
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
1
|
/** **/
|
|
2
|
-
const { ProcessLogger } = require(
|
|
3
|
-
const HTTPLogger = new ProcessLogger(
|
|
2
|
+
const { ProcessLogger } = require('./logger')
|
|
3
|
+
const HTTPLogger = new ProcessLogger('HTTPSetup')
|
|
4
4
|
|
|
5
5
|
module.exports = {
|
|
6
|
-
handle404(_, __, next) {
|
|
6
|
+
handle404 (_, __, next) {
|
|
7
7
|
const return_data = {
|
|
8
8
|
status_code: 404,
|
|
9
9
|
success: false,
|
|
10
|
-
error:
|
|
11
|
-
payload: null
|
|
12
|
-
}
|
|
10
|
+
error: 'Resource not found',
|
|
11
|
+
payload: null
|
|
12
|
+
}
|
|
13
13
|
|
|
14
|
-
next(return_data)
|
|
14
|
+
next(return_data)
|
|
15
15
|
},
|
|
16
16
|
|
|
17
|
-
handleError(error, __, response, ____) {
|
|
17
|
+
handleError (error, __, response, ____) {
|
|
18
18
|
// Log errors
|
|
19
19
|
if (error.error) {
|
|
20
|
-
HTTPLogger.info(error.error,
|
|
20
|
+
HTTPLogger.info(error.error, 'handleError')
|
|
21
21
|
} else {
|
|
22
|
-
HTTPLogger.error(error,
|
|
22
|
+
HTTPLogger.error(error, 'handleError')
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
// return error
|
|
26
26
|
return response.status(error.status_code || 500).json({
|
|
27
27
|
success: false,
|
|
28
28
|
status_code: error.status_code || 500,
|
|
29
|
-
error: error.error ||
|
|
30
|
-
payload: null
|
|
31
|
-
})
|
|
29
|
+
error: error.error || 'Internal Server Error',
|
|
30
|
+
payload: null
|
|
31
|
+
})
|
|
32
32
|
},
|
|
33
33
|
|
|
34
|
-
processResponse(request, response, next) {
|
|
35
|
-
if (!request.payload) return next()
|
|
34
|
+
processResponse (request, response, next) {
|
|
35
|
+
if (!request.payload) return next()
|
|
36
36
|
|
|
37
|
-
const { status_code } = request.payload
|
|
38
|
-
return response.status(status_code).json(request.payload)
|
|
37
|
+
const { status_code } = request.payload
|
|
38
|
+
return response.status(status_code).json(request.payload)
|
|
39
39
|
},
|
|
40
40
|
|
|
41
|
-
setupRequest(request, response, next) {
|
|
42
|
-
request.headers[
|
|
43
|
-
request.headers[
|
|
41
|
+
setupRequest (request, response, next) {
|
|
42
|
+
request.headers['access-control-allow-origin'] = '*'
|
|
43
|
+
request.headers['access-control-allow-headers'] = '*'
|
|
44
44
|
|
|
45
|
-
if (request.method ===
|
|
46
|
-
request.headers[
|
|
47
|
-
response.status(200).json()
|
|
45
|
+
if (request.method === 'OPTIONS') {
|
|
46
|
+
request.headers['access-control-allow-methods'] = 'GET, POST, PUT, PATCH, DELETE'
|
|
47
|
+
response.status(200).json()
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
next()
|
|
51
|
-
}
|
|
52
|
-
}
|
|
50
|
+
next()
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -2,41 +2,38 @@
|
|
|
2
2
|
* @author Oguntuberu Nathan O. <nateoguns.work@gmail.com>
|
|
3
3
|
**/
|
|
4
4
|
|
|
5
|
-
const Env = require(
|
|
6
|
-
const { randomUUID } = require(
|
|
7
|
-
const { Logtail } = require("@logtail/node");
|
|
8
|
-
const LOGTAIL_SECRET = Env.fetch("LOGTAIL_SECRET", true);
|
|
9
|
-
const logtail = new Logtail(LOGTAIL_SECRET);
|
|
5
|
+
const Env = require('../env')
|
|
6
|
+
const { randomUUID } = require('crypto')
|
|
10
7
|
|
|
11
8
|
function hashLogData (log = {}) {
|
|
12
9
|
const hashed = JSON.stringify(log).replace(/\w+@/gi, '************')
|
|
13
10
|
return JSON.parse(hashed)
|
|
14
11
|
}
|
|
15
12
|
|
|
16
|
-
function RequestLogger() {
|
|
17
|
-
const app_name = Env.fetch(
|
|
13
|
+
function RequestLogger () {
|
|
14
|
+
const app_name = Env.fetch('APP_NAME', true)
|
|
18
15
|
return (request, response, next) => {
|
|
19
|
-
if (!request.request_id) request.request_id = randomUUID()
|
|
16
|
+
if (!request.request_id) request.request_id = randomUUID()
|
|
20
17
|
|
|
21
18
|
//
|
|
22
19
|
const {
|
|
23
20
|
query,
|
|
24
21
|
params,
|
|
25
|
-
headers: { host, origin,
|
|
22
|
+
headers: { host, origin, 'user-agent': user_agent, 'sec-ch-ua-platform': os, referer },
|
|
26
23
|
request_id,
|
|
27
|
-
tenant_id
|
|
28
|
-
} = request
|
|
24
|
+
tenant_id
|
|
25
|
+
} = request
|
|
29
26
|
|
|
30
|
-
response.on(
|
|
27
|
+
response.on('finish', () => {
|
|
31
28
|
const {
|
|
32
29
|
_parsedUrl: { pathname },
|
|
33
30
|
httpVersion,
|
|
34
31
|
_startTime,
|
|
35
32
|
_remoteAddress,
|
|
36
|
-
payload
|
|
37
|
-
} = response.req
|
|
38
|
-
const { statusCode, statusMessage } = response.req.res
|
|
39
|
-
const duration = Date.now() - Date.parse(_startTime)
|
|
33
|
+
payload
|
|
34
|
+
} = response.req
|
|
35
|
+
const { statusCode, statusMessage } = response.req.res
|
|
36
|
+
const duration = Date.now() - Date.parse(_startTime)
|
|
40
37
|
const log = {
|
|
41
38
|
app_name,
|
|
42
39
|
request_id,
|
|
@@ -52,11 +49,11 @@ function RequestLogger() {
|
|
|
52
49
|
_remoteAddress,
|
|
53
50
|
pathname,
|
|
54
51
|
duration,
|
|
55
|
-
type:
|
|
56
|
-
status_code: payload ? payload.status_code : statusCode
|
|
57
|
-
}
|
|
52
|
+
type: 'request',
|
|
53
|
+
status_code: payload ? payload.status_code : statusCode
|
|
54
|
+
}
|
|
58
55
|
|
|
59
|
-
|
|
56
|
+
log.error = payload ? payload.error : statusMessage
|
|
60
57
|
// if (error) {
|
|
61
58
|
// log.error = error;
|
|
62
59
|
// logtail.error(pathname, hashLogData(log));
|
|
@@ -65,18 +62,19 @@ function RequestLogger() {
|
|
|
65
62
|
// }
|
|
66
63
|
|
|
67
64
|
// logtail.flush();
|
|
68
|
-
|
|
65
|
+
console.log(pathname, hashLogData({ status_code: log.status_code, request_id: log.request_id }))
|
|
66
|
+
})
|
|
69
67
|
|
|
70
|
-
next()
|
|
71
|
-
}
|
|
68
|
+
next()
|
|
69
|
+
}
|
|
72
70
|
}
|
|
73
71
|
|
|
74
72
|
class ProcessLogger {
|
|
75
|
-
constructor(service =
|
|
76
|
-
this.service = service
|
|
73
|
+
constructor (service = 'System') {
|
|
74
|
+
this.service = service
|
|
77
75
|
}
|
|
78
76
|
|
|
79
|
-
error(error, method =
|
|
77
|
+
error (error, method = 'unspecified_method', params = {}) {
|
|
80
78
|
// logtail.error(`${this.service}:${method}:${error.message}`, hashLogData({
|
|
81
79
|
// app_name: Env.fetch("APP_NAME", true),
|
|
82
80
|
// type: "process",
|
|
@@ -85,12 +83,12 @@ class ProcessLogger {
|
|
|
85
83
|
// params,
|
|
86
84
|
// }));
|
|
87
85
|
// logtail.flush();
|
|
88
|
-
console.log(`${this.service}:${method}:${error.message}`, error.stack)
|
|
86
|
+
console.log(`${this.service}:${method}:${error.message}`, params, error.stack)
|
|
89
87
|
}
|
|
90
88
|
|
|
91
|
-
info(info, method =
|
|
92
|
-
const message_str = `${this.service}:${method}:${info}
|
|
93
|
-
console.log(message_str)
|
|
89
|
+
info (info, method = 'unspecified_method', params = {}) {
|
|
90
|
+
const message_str = `${this.service}:${method}:${info}`
|
|
91
|
+
console.log(message_str, params)
|
|
94
92
|
// logtail.info(message_str, hashLogData({
|
|
95
93
|
// app_name: Env.fetch("APP_NAME", true),
|
|
96
94
|
// type: "process",
|
|
@@ -101,4 +99,4 @@ class ProcessLogger {
|
|
|
101
99
|
}
|
|
102
100
|
}
|
|
103
101
|
|
|
104
|
-
module.exports = { RequestLogger, ProcessLogger }
|
|
102
|
+
module.exports = { RequestLogger, ProcessLogger }
|
package/lib/query.js
CHANGED
|
@@ -2,7 +2,7 @@ const buildQuery = (options) => {
|
|
|
2
2
|
const sort_condition = options.sort_by ? buildSortOrderString(options.sort_by) : {}
|
|
3
3
|
const fields_to_return = options.return_only ? buildReturnFieldsString(options.return_only) : ''
|
|
4
4
|
const count = options.count || false
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
const group_by = options.group_by
|
|
7
7
|
const sum = options.sum
|
|
8
8
|
const is_pipeline = group_by || sum
|
|
@@ -179,7 +179,7 @@ const generatePipeline = ({ group_by, seek_conditions, sort_condition, sum, fiel
|
|
|
179
179
|
})
|
|
180
180
|
|
|
181
181
|
let sort_options = sort_condition
|
|
182
|
-
if (sort_condition && typeof(sort_condition) === 'string') {
|
|
182
|
+
if (sort_condition && typeof (sort_condition) === 'string') {
|
|
183
183
|
sort_options = sort_condition.split(' ').reduce((sac, condition) => {
|
|
184
184
|
let key = condition
|
|
185
185
|
let value = 1
|
|
@@ -219,7 +219,7 @@ const generatePipeline = ({ group_by, seek_conditions, sort_condition, sum, fiel
|
|
|
219
219
|
const sum_field = sum.split(',')[0]
|
|
220
220
|
pipeline.push({
|
|
221
221
|
$group: {
|
|
222
|
-
_id: { tenant_id:
|
|
222
|
+
_id: { tenant_id: '$tenant_id' },
|
|
223
223
|
[sum_field]: { $sum: `$${sum_field}` }
|
|
224
224
|
}
|
|
225
225
|
})
|
|
@@ -227,7 +227,7 @@ const generatePipeline = ({ group_by, seek_conditions, sort_condition, sum, fiel
|
|
|
227
227
|
|
|
228
228
|
if (count) {
|
|
229
229
|
pipeline.push({
|
|
230
|
-
$count:
|
|
230
|
+
$count: 'size'
|
|
231
231
|
})
|
|
232
232
|
}
|
|
233
233
|
|
package/lib/redis/index.js
CHANGED
package/lib/utilitiy/chart.js
CHANGED
|
@@ -1,69 +1,69 @@
|
|
|
1
1
|
const processRangeInDays = (min = 0, seconds_in_day = 86400000) => {
|
|
2
|
-
const ranges = []
|
|
3
|
-
let x = min
|
|
2
|
+
const ranges = []
|
|
3
|
+
let x = min
|
|
4
4
|
for (let i = 0; i < 31; i++) {
|
|
5
|
-
const [, mmm, dd] = new Date(x).toDateString().split(
|
|
6
|
-
ranges.push({ start: x, end: x + seconds_in_day - 1000, label: `${mmm} ${dd}` })
|
|
7
|
-
x += seconds_in_day
|
|
5
|
+
const [, mmm, dd] = new Date(x).toDateString().split(' ')
|
|
6
|
+
ranges.push({ start: x, end: x + seconds_in_day - 1000, label: `${mmm} ${dd}` })
|
|
7
|
+
x += seconds_in_day
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
return ranges
|
|
11
|
-
}
|
|
10
|
+
return ranges
|
|
11
|
+
}
|
|
12
12
|
|
|
13
13
|
const processRangeInWeeks = (min = 0, seconds_in_day = 86400000) => {
|
|
14
|
-
const ranges = []
|
|
15
|
-
let x = min
|
|
16
|
-
const [, mmm, dd] = new Date(x).toDateString().split(
|
|
14
|
+
const ranges = []
|
|
15
|
+
let x = min
|
|
16
|
+
const [, mmm, dd] = new Date(x).toDateString().split(' ')
|
|
17
17
|
for (let i = 0; i < Math.ceil(100 / 7); i++) {
|
|
18
18
|
ranges[i] = {
|
|
19
19
|
start: x,
|
|
20
20
|
end: x + 6 * seconds_in_day - 1000,
|
|
21
|
-
label: `${mmm} ${dd}
|
|
22
|
-
}
|
|
23
|
-
x += 6 * seconds_in_day
|
|
21
|
+
label: `${mmm} ${dd}`
|
|
22
|
+
}
|
|
23
|
+
x += 6 * seconds_in_day
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
return ranges
|
|
27
|
-
}
|
|
26
|
+
return ranges
|
|
27
|
+
}
|
|
28
28
|
|
|
29
29
|
const processRangeInMonths = (min, max) => {
|
|
30
30
|
// group in months
|
|
31
|
-
const d = new Date(min)
|
|
32
|
-
const year = d.getFullYear()
|
|
33
|
-
const month = d.getUTCMonth()
|
|
31
|
+
const d = new Date(min)
|
|
32
|
+
const year = d.getFullYear()
|
|
33
|
+
const month = d.getUTCMonth()
|
|
34
34
|
|
|
35
|
-
const ranges = []
|
|
36
|
-
let x = min
|
|
35
|
+
const ranges = []
|
|
36
|
+
let x = min
|
|
37
37
|
for (let i = 1; x <= max; i++) {
|
|
38
|
-
const [, mmm, , yyyy] = new Date(x).toDateString().split(
|
|
38
|
+
const [, mmm, , yyyy] = new Date(x).toDateString().split(' ')
|
|
39
39
|
ranges.push({
|
|
40
40
|
start: x,
|
|
41
41
|
end: Date.parse(new Date(year, month + i, 1)) - 1000,
|
|
42
|
-
label: `${mmm} ${yyyy}
|
|
43
|
-
})
|
|
42
|
+
label: `${mmm} ${yyyy}`
|
|
43
|
+
})
|
|
44
44
|
|
|
45
|
-
x = Date.parse(new Date(year, month + i, 1))
|
|
45
|
+
x = Date.parse(new Date(year, month + i, 1))
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
return ranges
|
|
49
|
-
}
|
|
48
|
+
return ranges
|
|
49
|
+
}
|
|
50
50
|
|
|
51
51
|
module.exports = {
|
|
52
52
|
processDateRange: (start, stop) => {
|
|
53
|
-
const min = Date.parse(start)
|
|
54
|
-
const max = Date.parse(stop)
|
|
55
|
-
const difference = max - min
|
|
56
|
-
const seconds_in_day = 86400000
|
|
53
|
+
const min = Date.parse(start)
|
|
54
|
+
const max = Date.parse(stop)
|
|
55
|
+
const difference = max - min
|
|
56
|
+
const seconds_in_day = 86400000
|
|
57
57
|
|
|
58
|
-
let ranges = []
|
|
58
|
+
let ranges = []
|
|
59
59
|
if (difference / seconds_in_day <= 31) {
|
|
60
|
-
ranges = processRangeInDays(min, seconds_in_day)
|
|
60
|
+
ranges = processRangeInDays(min, seconds_in_day)
|
|
61
61
|
} else if (difference / seconds_in_day <= 100) {
|
|
62
|
-
ranges = processRangeInWeeks(min, seconds_in_day)
|
|
62
|
+
ranges = processRangeInWeeks(min, seconds_in_day)
|
|
63
63
|
} else {
|
|
64
|
-
ranges = processRangeInMonths(min, max)
|
|
64
|
+
ranges = processRangeInMonths(min, max)
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
return ranges
|
|
68
|
-
}
|
|
69
|
-
}
|
|
67
|
+
return ranges
|
|
68
|
+
}
|
|
69
|
+
}
|
package/lib/utilitiy/date.js
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
/** */
|
|
2
|
-
const MONTH_MAP = [
|
|
3
|
-
const NUM_OF_MILLISECONDS_IN_ONE_DAY = 86400000
|
|
2
|
+
const MONTH_MAP = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
|
3
|
+
const NUM_OF_MILLISECONDS_IN_ONE_DAY = 86400000
|
|
4
4
|
|
|
5
5
|
/** */
|
|
6
6
|
const convertDateFromIsoToHTMLFormat = (iso_date) => {
|
|
7
|
-
const date = new Date(iso_date)
|
|
8
|
-
const day = padDateValue(date.getDate())
|
|
9
|
-
const month = padDateValue(date.getMonth() + 1)
|
|
10
|
-
const year = date.getFullYear()
|
|
7
|
+
const date = new Date(iso_date)
|
|
8
|
+
const day = padDateValue(date.getDate())
|
|
9
|
+
const month = padDateValue(date.getMonth() + 1)
|
|
10
|
+
const year = date.getFullYear()
|
|
11
11
|
|
|
12
|
-
const converted_date = `${year}-${month}-${day}
|
|
13
|
-
return iso_date ? converted_date :
|
|
14
|
-
}
|
|
12
|
+
const converted_date = `${year}-${month}-${day}`
|
|
13
|
+
return iso_date ? converted_date : ''
|
|
14
|
+
}
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Extracts day string of the given timestamp for graphs. Return ex. 'Jan 10'
|
|
18
18
|
* @param {Number} timestamp
|
|
19
19
|
*/
|
|
20
20
|
const extractDayStringForGraph = (timestamp) => {
|
|
21
|
-
const date = new Date(timestamp)
|
|
22
|
-
const month = MONTH_MAP[date.getMonth()]
|
|
23
|
-
const day = padDateValue(date.getDate())
|
|
21
|
+
const date = new Date(timestamp)
|
|
22
|
+
const month = MONTH_MAP[date.getMonth()]
|
|
23
|
+
const day = padDateValue(date.getDate())
|
|
24
24
|
|
|
25
|
-
return `${month} ${day}
|
|
26
|
-
}
|
|
25
|
+
return `${month} ${day}`
|
|
26
|
+
}
|
|
27
27
|
|
|
28
28
|
const generateHTMLFormDateTimeDefaults = () => {
|
|
29
|
-
const date = new Date().toISOString()
|
|
30
|
-
const generated_date = `${convertDateFromIsoToHTMLFormat(date)}
|
|
31
|
-
return generated_date
|
|
32
|
-
}
|
|
29
|
+
const date = new Date().toISOString()
|
|
30
|
+
const generated_date = `${convertDateFromIsoToHTMLFormat(date)}`
|
|
31
|
+
return generated_date
|
|
32
|
+
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* Generates a configuration of back-counted days starting from a base time.
|
|
@@ -38,20 +38,20 @@ const generateHTMLFormDateTimeDefaults = () => {
|
|
|
38
38
|
* @param {*} spread The numbers of days to count back.
|
|
39
39
|
*/
|
|
40
40
|
const generateDaysConfigurationForGraph = (base_timestamp, spread = 7, key_values) => {
|
|
41
|
-
let curr_timestamp = getDayTimestampForRawTimestamp(base_timestamp)
|
|
42
|
-
const config = {}
|
|
41
|
+
let curr_timestamp = getDayTimestampForRawTimestamp(base_timestamp)
|
|
42
|
+
const config = {}
|
|
43
43
|
for (let i = 0; i < spread; i++) {
|
|
44
|
-
const date_string = extractDayStringForGraph(curr_timestamp)
|
|
44
|
+
const date_string = extractDayStringForGraph(curr_timestamp)
|
|
45
45
|
// key_values = [subscribers , unsubscribers]; [opens, bounces]
|
|
46
46
|
config[date_string] = {
|
|
47
47
|
date: date_string,
|
|
48
48
|
[key_values[0]]: 0,
|
|
49
|
-
[key_values[1]]: 0
|
|
50
|
-
}
|
|
51
|
-
curr_timestamp -= NUM_OF_MILLISECONDS_IN_ONE_DAY
|
|
49
|
+
[key_values[1]]: 0
|
|
50
|
+
}
|
|
51
|
+
curr_timestamp -= NUM_OF_MILLISECONDS_IN_ONE_DAY
|
|
52
52
|
}
|
|
53
|
-
return config
|
|
54
|
-
}
|
|
53
|
+
return config
|
|
54
|
+
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* Generates the number of milliseconds at (12:00AM) for the date specified in the raw_timestamp.
|
|
@@ -59,83 +59,83 @@ const generateDaysConfigurationForGraph = (base_timestamp, spread = 7, key_value
|
|
|
59
59
|
* @param {Number} raw_timestamp number of milliseconds
|
|
60
60
|
*/
|
|
61
61
|
const getDayTimestampForRawTimestamp = (raw_timestamp) => {
|
|
62
|
-
const date = new Date(raw_timestamp)
|
|
63
|
-
const day = `${padDateValue(date.getMonth() + 1)}/${padDateValue(date.getDate())}/${date.getFullYear()}
|
|
64
|
-
return Date.parse(day)
|
|
65
|
-
}
|
|
62
|
+
const date = new Date(raw_timestamp)
|
|
63
|
+
const day = `${padDateValue(date.getMonth() + 1)}/${padDateValue(date.getDate())}/${date.getFullYear()}`
|
|
64
|
+
return Date.parse(day)
|
|
65
|
+
}
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
68
|
* Get the timestamp of the first and last days of the month
|
|
69
69
|
*/
|
|
70
70
|
|
|
71
71
|
const getMonthTimeRange = () => {
|
|
72
|
-
const year = new Date().getFullYear()
|
|
73
|
-
const current_month = new Date().getMonth()
|
|
74
|
-
const start = Date.parse(new Date(year, current_month, 1))
|
|
75
|
-
const end = Date.parse(new Date(year, current_month + 1, 1)) - 1000
|
|
72
|
+
const year = new Date().getFullYear()
|
|
73
|
+
const current_month = new Date().getMonth()
|
|
74
|
+
const start = Date.parse(new Date(year, current_month, 1))
|
|
75
|
+
const end = Date.parse(new Date(year, current_month + 1, 1)) - 1000
|
|
76
76
|
|
|
77
|
-
return { start, end }
|
|
78
|
-
}
|
|
77
|
+
return { start, end }
|
|
78
|
+
}
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
81
|
* Get today's midnight time stamp
|
|
82
82
|
*/
|
|
83
83
|
|
|
84
84
|
const getCurrentDayTimestamp = () => {
|
|
85
|
-
const date = new Date()
|
|
86
|
-
const year = date.getFullYear()
|
|
87
|
-
const month = padDateValue(date.getMonth() + 1)
|
|
88
|
-
const day = padDateValue(date.getDate())
|
|
89
|
-
const date_string = `${year}-${month}-${day}
|
|
90
|
-
return Date.parse(date_string)
|
|
91
|
-
}
|
|
85
|
+
const date = new Date()
|
|
86
|
+
const year = date.getFullYear()
|
|
87
|
+
const month = padDateValue(date.getMonth() + 1)
|
|
88
|
+
const day = padDateValue(date.getDate())
|
|
89
|
+
const date_string = `${year}-${month}-${day}`
|
|
90
|
+
return Date.parse(date_string)
|
|
91
|
+
}
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
94
|
* prefixes 0 on numbers with single digits.
|
|
95
95
|
* @param {Number} value
|
|
96
96
|
*/
|
|
97
97
|
const padDateValue = (value) => {
|
|
98
|
-
return value < 10 ? `0${value}` : value
|
|
99
|
-
}
|
|
98
|
+
return value < 10 ? `0${value}` : value
|
|
99
|
+
}
|
|
100
100
|
|
|
101
101
|
const isCampaignSendDateValid = (chosen_date) => {
|
|
102
|
-
const today = new Date().toDateString()
|
|
103
|
-
return new Date(`${chosen_date}`) >= new Date(today)
|
|
104
|
-
}
|
|
102
|
+
const today = new Date().toDateString()
|
|
103
|
+
return new Date(`${chosen_date}`) >= new Date(today)
|
|
104
|
+
}
|
|
105
105
|
|
|
106
|
-
const toDateString = (date =
|
|
107
|
-
if (!date) return
|
|
108
|
-
return new Date(date).toDateString()
|
|
109
|
-
}
|
|
106
|
+
const toDateString = (date = '') => {
|
|
107
|
+
if (!date) return ''
|
|
108
|
+
return new Date(date).toDateString()
|
|
109
|
+
}
|
|
110
110
|
|
|
111
|
-
const formatDateWithoutDayOfWeek = (date =
|
|
112
|
-
if (!date) return
|
|
113
|
-
const options = { year:
|
|
114
|
-
return new Date(date).toLocaleDateString(undefined, options)
|
|
115
|
-
}
|
|
111
|
+
const formatDateWithoutDayOfWeek = (date = '') => {
|
|
112
|
+
if (!date) return ''
|
|
113
|
+
const options = { year: 'numeric', month: 'short', day: 'numeric' }
|
|
114
|
+
return new Date(date).toLocaleDateString(undefined, options)
|
|
115
|
+
}
|
|
116
116
|
|
|
117
117
|
const formatDateForDisplay = (raw_value) => {
|
|
118
|
-
const date = new Date(raw_value)
|
|
119
|
-
if (!raw_value || !date) return raw_value
|
|
120
|
-
const [, mon, day, year, time] = date.toString().split(
|
|
121
|
-
return [mon, `${day},`, year, time].join(
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const toTimeString = (date =
|
|
125
|
-
if (!date) return
|
|
118
|
+
const date = new Date(raw_value)
|
|
119
|
+
if (!raw_value || !date) return raw_value
|
|
120
|
+
const [, mon, day, year, time] = date.toString().split(' ')
|
|
121
|
+
return [mon, `${day},`, year, time].join(' ')
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const toTimeString = (date = '') => {
|
|
125
|
+
if (!date) return ''
|
|
126
126
|
const options = {
|
|
127
|
-
hour:
|
|
128
|
-
minute:
|
|
129
|
-
hour12: true
|
|
130
|
-
}
|
|
131
|
-
return new Date(date).toLocaleTimeString(
|
|
132
|
-
}
|
|
127
|
+
hour: 'numeric',
|
|
128
|
+
minute: 'numeric',
|
|
129
|
+
hour12: true
|
|
130
|
+
}
|
|
131
|
+
return new Date(date).toLocaleTimeString('en-US', options)
|
|
132
|
+
}
|
|
133
133
|
|
|
134
134
|
const generateDefaultRange = () => {
|
|
135
|
-
const [today] = new Date().toISOString().split(
|
|
136
|
-
const [thirty_days_ago] = new Date(Date.parse(today) - 30 * 86400000).toISOString().split(
|
|
137
|
-
return [thirty_days_ago, today]
|
|
138
|
-
}
|
|
135
|
+
const [today] = new Date().toISOString().split('T')
|
|
136
|
+
const [thirty_days_ago] = new Date(Date.parse(today) - 30 * 86400000).toISOString().split('T')
|
|
137
|
+
return [thirty_days_ago, today]
|
|
138
|
+
}
|
|
139
139
|
|
|
140
140
|
/**
|
|
141
141
|
*
|
|
@@ -143,45 +143,45 @@ const generateDefaultRange = () => {
|
|
|
143
143
|
* @returns {startDate, endDate}
|
|
144
144
|
*/
|
|
145
145
|
const getYearRange = (year) => {
|
|
146
|
-
const startDate = new Date(year, 0, 1).getTime()
|
|
147
|
-
const endDate = new Date(year, 11, 31).getTime() + 86400000 - 1
|
|
148
|
-
return { startDate, endDate }
|
|
149
|
-
}
|
|
146
|
+
const startDate = new Date(year, 0, 1).getTime() // January 1st
|
|
147
|
+
const endDate = new Date(year, 11, 31).getTime() + 86400000 - 1 // December 31st (23:59:59)
|
|
148
|
+
return { startDate, endDate }
|
|
149
|
+
}
|
|
150
150
|
|
|
151
151
|
const getMonthTimestamps = (year) => {
|
|
152
|
-
const timestamps = []
|
|
152
|
+
const timestamps = []
|
|
153
153
|
|
|
154
154
|
for (let month = 0; month < 12; month++) {
|
|
155
|
-
const startDate = new Date(year, month, 1)
|
|
156
|
-
const endDate = new Date(year, month + 1, 0, 23, 59, 59, 999)
|
|
155
|
+
const startDate = new Date(year, month, 1)
|
|
156
|
+
const endDate = new Date(year, month + 1, 0, 23, 59, 59, 999) // Last day of the month
|
|
157
157
|
|
|
158
158
|
timestamps.push({
|
|
159
159
|
start: startDate.getTime(),
|
|
160
|
-
end: endDate.getTime()
|
|
161
|
-
})
|
|
160
|
+
end: endDate.getTime()
|
|
161
|
+
})
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
-
return timestamps
|
|
165
|
-
}
|
|
164
|
+
return timestamps
|
|
165
|
+
}
|
|
166
166
|
|
|
167
167
|
const getMonthNameFromTimestamp = (timestamp) => {
|
|
168
|
-
const date = new Date(timestamp)
|
|
168
|
+
const date = new Date(timestamp)
|
|
169
169
|
const months = [
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
]
|
|
183
|
-
return months[date.getMonth()]
|
|
184
|
-
}
|
|
170
|
+
'January',
|
|
171
|
+
'February',
|
|
172
|
+
'March',
|
|
173
|
+
'April',
|
|
174
|
+
'May',
|
|
175
|
+
'June',
|
|
176
|
+
'July',
|
|
177
|
+
'August',
|
|
178
|
+
'September',
|
|
179
|
+
'October',
|
|
180
|
+
'November',
|
|
181
|
+
'December'
|
|
182
|
+
]
|
|
183
|
+
return months[date.getMonth()]
|
|
184
|
+
}
|
|
185
185
|
|
|
186
186
|
module.exports = {
|
|
187
187
|
convertDateFromIsoToHTMLFormat,
|
|
@@ -200,5 +200,5 @@ module.exports = {
|
|
|
200
200
|
toTimeString,
|
|
201
201
|
getYearRange,
|
|
202
202
|
getMonthTimestamps,
|
|
203
|
-
getMonthNameFromTimestamp
|
|
204
|
-
}
|
|
203
|
+
getMonthNameFromTimestamp
|
|
204
|
+
}
|
package/lib/utilitiy/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
const Chart = require(
|
|
2
|
-
const Date = require(
|
|
3
|
-
const Number = require(
|
|
1
|
+
const Chart = require('./chart')
|
|
2
|
+
const Date = require('./date')
|
|
3
|
+
const Number = require('./number')
|
|
4
4
|
|
|
5
5
|
module.exports = {
|
|
6
6
|
Chart,
|
|
7
7
|
Date,
|
|
8
|
-
Number
|
|
9
|
-
}
|
|
8
|
+
Number
|
|
9
|
+
}
|
package/lib/utilitiy/number.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
toReadableNumber: (value) => {
|
|
3
|
-
if (!value) return 0
|
|
4
|
-
if (isNaN(value)) return value
|
|
5
|
-
return Number(Number(value).toFixed(2)).toLocaleString()
|
|
6
|
-
}
|
|
7
|
-
}
|
|
3
|
+
if (!value) return 0
|
|
4
|
+
if (isNaN(value)) return value
|
|
5
|
+
return Number(Number(value).toFixed(2)).toLocaleString()
|
|
6
|
+
}
|
|
7
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@go-mailer/jarvis",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.7",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"repository": "git@github.com:go-mailer-ltd/jarvis-node.git",
|
|
6
6
|
"author": "Nathan Oguntuberu <nateoguns.work@gmail.com>",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"scripts": {
|
|
9
|
-
"test": "mocha --recursive -w -c || true"
|
|
9
|
+
"test": "mocha --recursive -w -c || true",
|
|
10
|
+
"lint:fix": "eslint --fix . --ext .js"
|
|
10
11
|
},
|
|
11
12
|
"dependencies": {
|
|
12
13
|
"@logtail/node": "^0.3.3",
|
|
@@ -18,6 +19,11 @@
|
|
|
18
19
|
},
|
|
19
20
|
"devDependencies": {
|
|
20
21
|
"chai": "^4.3.7",
|
|
22
|
+
"eslint": "^7.32.0",
|
|
23
|
+
"eslint-config-standard": "^16.0.3",
|
|
24
|
+
"eslint-plugin-import": "^2.25.4",
|
|
25
|
+
"eslint-plugin-node": "^11.1.0",
|
|
26
|
+
"eslint-plugin-promise": "^5.2.0",
|
|
21
27
|
"mocha": "^10.2.0"
|
|
22
28
|
}
|
|
23
29
|
}
|