ac-geoip 1.4.2

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/.acsemver.js ADDED
@@ -0,0 +1,9 @@
1
+ module.exports = {
2
+ repository: {
3
+ url: 'https://github.com/admiralcloud/ac-geoip'
4
+ },
5
+ changelogFile: __dirname + '/CHANGELOG.md',
6
+ sections: [
7
+ {name: 'App' }
8
+ ],
9
+ }
package/.eslintrc.js ADDED
@@ -0,0 +1,27 @@
1
+ const config = {
2
+ root: true,
3
+ 'env': {
4
+ 'commonjs': true,
5
+ 'es6': true,
6
+ 'node': true
7
+ },
8
+ 'extends': 'eslint:recommended',
9
+ "rules": {
10
+ "space-before-function-paren": 0,
11
+ "no-extra-semi": 0,
12
+ "object-curly-spacing": ["error", "always"],
13
+ "brace-style": ["error", "stroustrup", { "allowSingleLine": true }],
14
+ "no-useless-escape": 0,
15
+ "standard/no-callback-literal": 0,
16
+ "new-cap": 0
17
+ },
18
+ globals: {
19
+ describe: true,
20
+ it: true
21
+ },
22
+ 'parserOptions': {
23
+ 'ecmaVersion': 2018
24
+ },
25
+ }
26
+
27
+ module.exports = config
package/CHANGELOG.md ADDED
@@ -0,0 +1,154 @@
1
+ <a name="1.4.2"></a>
2
+
3
+ ## [1.4.2](https://github.com/admiralcloud/ac-geoip/compare/v1.4.1..v1.4.2) (2021-10-09 10:25:40)
4
+
5
+
6
+ ### Bug Fix
7
+
8
+ * **App:** Package updates | MP | [08dd237ecc7274cfb083f3f6279036fa94d17398](https://github.com/admiralcloud/ac-geoip/commit/08dd237ecc7274cfb083f3f6279036fa94d17398)
9
+ Package updates
10
+ <a name="1.4.1"></a>
11
+
12
+ ## [1.4.1](https://github.com/mmpro/ac-geoip/compare/v1.4.0..v1.4.1) (2021-05-02 09:42:55)
13
+
14
+
15
+ ### Bug Fix
16
+
17
+ * **App:** Improved debug log | MP | [e5985e0a10d08979ab2f1ddadcc51217f43e0342](https://github.com/mmpro/ac-geoip/commit/e5985e0a10d08979ab2f1ddadcc51217f43e0342)
18
+ Improved debug log
19
+ ### Tests
20
+
21
+ * **App:** Minor fix | MP | [42f5ffec32d0fa9d54306ddc1281feef7ef6e3df](https://github.com/mmpro/ac-geoip/commit/42f5ffec32d0fa9d54306ddc1281feef7ef6e3df)
22
+ Minor fix
23
+ ### Chores
24
+
25
+ * **App:** Updated packages | MP | [39b1e855d0d8b7419868f84038163f142f5a4b26](https://github.com/mmpro/ac-geoip/commit/39b1e855d0d8b7419868f84038163f142f5a4b26)
26
+ Updated packages
27
+ <a name="1.4.0"></a>
28
+
29
+ # [1.4.0](https://github.com/mmpro/ac-geoip/compare/v1.3.0..v1.4.0) (2021-04-12 09:36:41)
30
+
31
+
32
+ ### Feature
33
+
34
+ * **App:** Use node-cache per default | MP | [1a5481a30245fc53ccb6333a60f4ae38a8245eb5](https://github.com/mmpro/ac-geoip/commit/1a5481a30245fc53ccb6333a60f4ae38a8245eb5)
35
+ If Redis is not defined, use node-cache (memory) to improve performance.
36
+ ### Bug Fix
37
+
38
+ * **App:** Allow Redis usage when using local database | MP | [943ab0cf914245cceab0c23da1e03078783a1b66](https://github.com/mmpro/ac-geoip/commit/943ab0cf914245cceab0c23da1e03078783a1b66)
39
+ To improve performance it makes sense to use Redis even for local database. Lookup is a little slow and consumes a lot of CPU
40
+ <a name="1.3.0"></a>
41
+
42
+ # [1.3.0](https://github.com/mmpro/ac-geoip/compare/v1.2.1..v1.3.0) (2021-04-02 09:52:27)
43
+
44
+
45
+ ### Feature
46
+
47
+ * **App:** LookupLocal for local database lookup | MP | [d6821229d6475e6bd2bb83822bee01bf474f1219](https://github.com/mmpro/ac-geoip/commit/d6821229d6475e6bd2bb83822bee01bf474f1219)
48
+ LookupLocal is now available as dedicated function to lookup a local geolite database. Lookup only uses webservice. You can use both functions in parallel (e.g. to compare results).
49
+ <a name="1.2.1"></a>
50
+
51
+ ## [1.2.1](https://github.com/mmpro/ac-geoip/compare/v1.2.0..v1.2.1) (2021-04-02 06:49:54)
52
+
53
+
54
+ ### Bug Fix
55
+
56
+ * **App:** Improved logging | MP | [7d664460d4109fc078e656a0fe264a23c7e2cf9d](https://github.com/mmpro/ac-geoip/commit/7d664460d4109fc078e656a0fe264a23c7e2cf9d)
57
+ Improved logging
58
+ ### Tests
59
+
60
+ * **App:** Added tests for geolite database | MP | [f6da987068a63c4cb43fb84729e4816875de9283](https://github.com/mmpro/ac-geoip/commit/f6da987068a63c4cb43fb84729e4816875de9283)
61
+ Added tests for geolite database
62
+ ### Chores
63
+
64
+ * **App:** Updated packages | MP | [66e8056c2dab7377b6040cb35551ee3bf352e98d](https://github.com/mmpro/ac-geoip/commit/66e8056c2dab7377b6040cb35551ee3bf352e98d)
65
+ Updated packages
66
+ <a name="1.2.0"></a>
67
+
68
+ # [1.2.0](https://github.com/mmpro/ac-geoip/compare/v1.1.0..v1.2.0) (2021-03-21 07:35:20)
69
+
70
+
71
+ ### Feature
72
+
73
+ * **App:** You can now use geolite2 local database | MP | [dd671ab0b6bcc19cee5aefeb3cf421c15974e864](https://github.com/mmpro/ac-geoip/commit/dd671ab0b6bcc19cee5aefeb3cf421c15974e864)
74
+ It is now possible to use geolite2 downloadble database instead of web service.
75
+ ### Chores
76
+
77
+ * **App:** Do not commit folder geolite2 | MP | [8e20cbba069222d9f25abe79111d604bc827b3e6](https://github.com/mmpro/ac-geoip/commit/8e20cbba069222d9f25abe79111d604bc827b3e6)
78
+ Do not commit folder geolite2
79
+ ### Chores
80
+
81
+ * **App:** Updated packages | MP | [8c44382bb0f0c60973fccd6f7e25feb01993e32b](https://github.com/mmpro/ac-geoip/commit/8c44382bb0f0c60973fccd6f7e25feb01993e32b)
82
+ Updated packages
83
+ <a name="1.1.0"></a>
84
+
85
+ # [1.1.0](https://github.com/mmpro/ac-geoip/compare/v1.0.4..v1.1.0) (2020-12-12 14:43:41)
86
+
87
+
88
+ ### Feature
89
+
90
+ * **App:** Function now supports async/await | MP | [f6b65c568ed34fb8149ee9e00d23b8946cb83256](https://github.com/mmpro/ac-geoip/commit/f6b65c568ed34fb8149ee9e00d23b8946cb83256)
91
+ You can use async/await in addition to classic callback
92
+ ### Chores
93
+
94
+ * **App:** Remove husky | MP | [cb52b5d3f8c38c6e8a9437725b5aeba35cf6de33](https://github.com/mmpro/ac-geoip/commit/cb52b5d3f8c38c6e8a9437725b5aeba35cf6de33)
95
+ Remove husky
96
+ * **App:** Package cleanup and updates | MP | [f661f118bd7897bce87de6c862d8497a19ec72f8](https://github.com/mmpro/ac-geoip/commit/f661f118bd7897bce87de6c862d8497a19ec72f8)
97
+ Package cleanup and updates
98
+ * **App:** Use ac-semantic-release | MP | [66c9797b3db0072559d62f16dcaa93acdad6859a](https://github.com/mmpro/ac-geoip/commit/66c9797b3db0072559d62f16dcaa93acdad6859a)
99
+ Use ac-semantic-release
100
+ <a name="1.0.4"></a>
101
+ ## [1.0.4](https://github.com/mmpro/ac-geoip/compare/v1.0.3...v1.0.4) (2019-10-13 06:40)
102
+
103
+
104
+ ### Bug Fixes
105
+
106
+ * **GeoIP:** Only log if debug parameter is set | MP ([b5466de](https://github.com/mmpro/ac-geoip/commit/b5466de))
107
+ Only log if debug parameter is set
108
+
109
+
110
+
111
+ <a name="1.0.3"></a>
112
+ ## [1.0.3](https://github.com/mmpro/ac-geoip/compare/v1.0.2...v1.0.3) (2019-10-06 09:57)
113
+
114
+
115
+ ### Bug Fixes
116
+
117
+ * **GeoIP:** Minor fixes: Redis, debugging | MP ([d692f62](https://github.com/mmpro/ac-geoip/commit/d692f62))
118
+ Fixed using Redis, added debug option per request
119
+
120
+
121
+
122
+ <a name="1.0.2"></a>
123
+ ## [1.0.2](https://github.com/mmpro/ac-geoip/compare/v1.0.1...v1.0.2) (2019-10-06 09:10)
124
+
125
+
126
+ ### Bug Fixes
127
+
128
+ * **GeoIP:** Improved functionality | MP ([000654f](https://github.com/mmpro/ac-geoip/commit/000654f))
129
+ Check for private IP, fixed using Redis as cache, allow mapping per request.
130
+
131
+
132
+
133
+ <a name="1.0.1"></a>
134
+ ## [1.0.1](https://github.com/mmpro/ac-geoip/compare/v1.0.0...v1.0.1) (2019-10-03 11:09)
135
+
136
+
137
+ ### Bug Fixes
138
+
139
+ * **GeoIP:** Removed location from default mapping | MP ([6c25ed4](https://github.com/mmpro/ac-geoip/commit/6c25ed4))
140
+ Removed location from default mapping
141
+
142
+
143
+
144
+ <a name="1.0.0"></a>
145
+ # 1.0.0 (2019-10-03 08:36)
146
+
147
+
148
+ ### Features
149
+
150
+ * **GeoIP:** Initial version | MP ([89771ac](https://github.com/mmpro/ac-geoip/commit/89771ac))
151
+ Initial version
152
+
153
+
154
+
package/Makefile ADDED
@@ -0,0 +1,16 @@
1
+ MOCHA_OPTS= --slow 0 -A
2
+ REPORTER = spec
3
+
4
+ lint-fix:
5
+ ./node_modules/.bin/eslint --fix index.js test/test.js
6
+
7
+ lint-check:
8
+ ./node_modules/.bin/eslint index.js test/test.js
9
+
10
+ commit:
11
+ @node ./node_modules/ac-semantic-release/lib/commit.js
12
+
13
+ release:
14
+ @node ./node_modules/ac-semantic-release/lib/release.js
15
+
16
+ .PHONY: check
package/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # AC GEOIP
2
+ Lookup IP addresses at Maxmind GEOIP services or local Maxmind (Geolite) database and prepare the response with custom field mapping. Optionally, you can store the response in Redis to improve performance (of your app).
3
+
4
+ GEOIP web service requires an account at Maxmind.
5
+
6
+ You can also use the Geolite2 database from Maxmind: https://dev.maxmind.com/geoip/geoip2/geolite2/
7
+
8
+ ## Usage
9
+
10
+ ### Using Webservice
11
+ ```
12
+ const acgeoip = require('./index')
13
+
14
+ let geoip = {
15
+ userId: 123456778,
16
+ licenseKey: 'abc-licensekey'
17
+ }
18
+ acgeoip.init(geoip)
19
+
20
+ // ASYNC/AWAIT
21
+ async() {
22
+ let response = await acgeoip.lookup({ ip: '1.2.3.4' })
23
+ }
24
+ ```
25
+
26
+ ### Using local (Geolite) database
27
+ ```
28
+ const acgeoip = require('./index')
29
+
30
+ let geoip = {
31
+ geolite: {
32
+ enabled: true,
33
+ path: '/path/to/GeoLite2-City.mmdb'
34
+ }
35
+ }
36
+ acgeoip.init(geoip)
37
+
38
+ // ASYNC/AWAIT
39
+ async() {
40
+ let response = await acgeoip.lookupLocal({ ip: '1.2.3.4' })
41
+ }
42
+ ```
43
+
44
+ ### Combined usage
45
+ ```
46
+ const acgeoip = require('./index')
47
+
48
+ let geoip = {
49
+ userId: 123456778,
50
+ licenseKey: 'abc-licensekey',
51
+ geolite: {
52
+ enabled: true,
53
+ path: '/path/to/GeoLite2-City.mmdb'
54
+ }
55
+ }
56
+ acgeoip.init(geoip)
57
+
58
+ // lookup using web service
59
+ async() {
60
+ let response = await acgeoip.lookup({ ip: '1.2.3.4' })
61
+ }
62
+
63
+ // lookup using local database
64
+ async() {
65
+ let response = await acgeoip.lookupLocal({ ip: '1.2.3.4' })
66
+ }
67
+ ```
68
+
69
+ ### Using callbacks
70
+ We recommend using modern async/await approach, but you can also use classic callbacks.
71
+ ```
72
+ // TRADITONAL CALLBACK is available for lookup and lookupLocal
73
+ acgeoip.lookup({
74
+ ip: '8.8.8.8'
75
+ }, (err, response) => {
76
+ console.log(response)
77
+ })
78
+
79
+ ```
80
+
81
+ ## Init Parameters
82
+ **Required**
83
+ Initiate the function with the required "userId" and "licenseKey" if you want to use the webservice. If you want to use local database, make sure to set geolite.enabled to true and provide the location of the database.
84
+
85
+ **Mapping**
86
+ Define how your response looks like using a mapping array. The array contains objects with properties "response" which is the property name in the response and "geoIP" which is the path to the GeoIP response.
87
+
88
+ See this default setup as example
89
+ ```
90
+ mapping: [
91
+ { response: 'iso2', geoIP: 'country.iso_code' },
92
+ { response: 'city', geoIP: 'city.names.en' },
93
+ { response: 'region', geoIP: 'subdivisions[0].names.en' },
94
+ // the following properties are only available using the paid webservice
95
+ { response: 'isp', geoIP: 'traits.isp' },
96
+ { response: 'organization', geoIP: 'traits.organization' },
97
+ { response: 'domain', geoIP: 'traits.domain' },
98
+ { response: 'location', geoIP: 'location' },
99
+ ]
100
+ ```
101
+
102
+ **Caching**
103
+ In order to cache the data, you need to provide:
104
+ + redis - a redis instance (from ioredis)
105
+ + environment - prefix (default development) for the redisKey (e.g development:geoip:8.8.8.8)
106
+ + cacheTime - seconds to cache the GeoIP response (default 7 days)
107
+
108
+
109
+ ## Links
110
+ - [Website](https://www.admiralcloud.com/)
111
+ - [Twitter (@admiralcloud)](https://twitter.com/admiralcloud)
112
+ - [Facebook](https://www.facebook.com/MediaAssetManagement/)
113
+
114
+ ## Thanks
115
+ Thanks to https://github.com/maxmind/GeoIP2-node
116
+
117
+ ## License
118
+ [MIT License](https://opensource.org/licenses/MIT) Copyright © 2009-present, AdmiralCloud AG, Mark Poepping
package/index.js ADDED
@@ -0,0 +1,289 @@
1
+ const _ = require('lodash')
2
+ const ipPackage = require('ip')
3
+ const fs = require('fs')
4
+
5
+ const WebServiceClient = require('@maxmind/geoip2-node').WebServiceClient;
6
+ const Reader = require('@maxmind/geoip2-node').Reader;
7
+
8
+ const NodeCache = require( "node-cache" );
9
+ const geoCache = new NodeCache( { stdTTL: 7*86400, checkperiod: 3600 } );
10
+
11
+ const acgeoip = () => {
12
+
13
+ let geoip = {
14
+ userId: 'userId',
15
+ licenseKey: 'licenseKey',
16
+ environment: 'development',
17
+ // redis, // instance of redis
18
+ // reader // initiated if useBuffer with local database
19
+ geolite: {
20
+ useBuffer: false,
21
+ enabled: false,
22
+ path: '/path/to/GeoLite2-City.mmdb'
23
+ },
24
+ cacheTime: 7 * 86400, // cache GEOIP response for 1 week
25
+ mapping: [
26
+ { response: 'iso2', geoIP: 'country.isoCode' },
27
+ { response: 'city', geoIP: 'city.names.en' },
28
+ { response: 'region', geoIP: 'subdivisions[0].names.en' },
29
+ { response: 'isp', geoIP: 'traits.isp' },
30
+ { response: 'organization', geoIP: 'traits.organization' },
31
+ { response: 'domain', geoIP: 'traits.domain' },
32
+ { response: 'latitude', geoIP: 'location.latitude' },
33
+ { response: 'longitude', geoIP: 'location.longitude' }
34
+ ]
35
+ }
36
+
37
+ const init = (params) => {
38
+ if (_.has(params, 'userId')) _.set(geoip, 'userId', _.get(params, 'userId'))
39
+ if (_.has(params, 'licenseKey')) _.set(geoip, 'licenseKey', _.get(params, 'licenseKey'))
40
+ if (_.has(params, 'env')) _.set(geoip, 'environment', _.get(params, 'env'))
41
+ if (_.has(params, 'redis')) _.set(geoip, 'redis', _.get(params, 'redis'))
42
+ if (_.has(params, 'geolite')) _.set(geoip, 'geolite', _.get(params, 'geolite'))
43
+
44
+ if (_.get(params, 'geolite.enabled') && _.get(params, 'geolite.useBuffer')) {
45
+ const dbBuffer = fs.readFileSync(_.get(geoip, 'geolite.path'))
46
+ geoip.reader = Reader.openBuffer(dbBuffer)
47
+ }
48
+ }
49
+
50
+
51
+
52
+ const lookupLocal = async(params, cb) => {
53
+ const functionName = 'ac-geoip | lookupLocal'
54
+ if (!_.get(geoip, 'geolite.enabled')) {
55
+ let message = 'acgeoip_geolite_notEnabled'
56
+ if (_.isFunction(cb)) return cb({ message })
57
+ throw Error(message)
58
+ }
59
+ const ip = _.get(params, 'ip')
60
+ if (ipPackage.isPrivate(ip)) {
61
+ if (_.isFunction(cb)) return cb()
62
+ return
63
+ }
64
+
65
+ const mapping = _.get(params, 'mapping', geoip.mapping)
66
+ const debug = _.get(params, 'debug')
67
+ const debugPerformance = _.get(params, 'debugPerforance')
68
+ const start = process.hrtime()
69
+
70
+ let response = {
71
+ ip
72
+ }
73
+ let geoipResponse
74
+
75
+ if (geoip.redis) {
76
+ geoipResponse = await checkRedis(params)
77
+ }
78
+ else {
79
+ geoipResponse = getFromMemory({ ip })
80
+ }
81
+ if (debugPerformance) console.log('%s | getFromCache %d', functionName, performanceHelper(start, process.hrtime()))
82
+
83
+ if (!geoipResponse) {
84
+ if (_.get(geoip, 'geolite.useBuffer') && geoip.reader) {
85
+ geoipResponse = geoip.reader.city(ip)
86
+ if (debugPerformance) console.log('%s | readFromBuffer %d', functionName, performanceHelper(start, process.hrtime()))
87
+ }
88
+ else {
89
+ try {
90
+ if (_.get(geoip, 'geolite.enabled')) {
91
+ geoipResponse = await new Promise((resolve, reject) => {
92
+ Reader.open(_.get(geoip, 'geolite.path')).then(reader => {
93
+ const response = reader.city(ip)
94
+ resolve(response)
95
+ }).catch(reject)
96
+ })
97
+ }
98
+ if (debugPerformance) console.log('%s | readFromDB %d', functionName, performanceHelper(start, process.hrtime()))
99
+
100
+ if (debug) {
101
+ console.log('AC-GEOIP | From Geolite | %j', geoipResponse)
102
+ }
103
+ }
104
+ catch(e) {
105
+ console.error('AC-GEOIP | From Geolite | Failed | %j', e)
106
+ }
107
+ }
108
+ if (geoipResponse) {
109
+ _.set(geoipResponse, 'origin', 'db')
110
+ if (geoip.redis) {
111
+ await storeRedis({ ip, geoipResponse })
112
+ }
113
+ else {
114
+ storeInMemory({ ip, geoipResponse })
115
+ }
116
+ if (debugPerformance) console.log('%s | storeInCache %d', functionName, performanceHelper(start, process.hrtime()))
117
+ }
118
+ }
119
+
120
+ // prepare response
121
+ if (!_.isEmpty(mapping)) {
122
+ _.forEach(mapping, item => {
123
+ if (_.get(geoipResponse, item.geoIP)) _.set(response, item.response, _.get(geoipResponse, item.geoIP))
124
+ })
125
+ }
126
+ else {
127
+ response = geoipResponse
128
+ }
129
+ _.set(response, 'origin', _.get(geoipResponse, 'origin'))
130
+ if (_.get(geoipResponse, 'fromCache')) _.set(response, 'fromCache', true)
131
+
132
+ if (debugPerformance) console.log('%s | Finished %d', functionName, performanceHelper(start, process.hrtime()))
133
+ if (_.isFunction(cb)) return cb(null, response)
134
+ return response
135
+ }
136
+
137
+ const lookup = async(params, cb) => {
138
+ const functionName = 'ac-geoip | lookup'
139
+ if (!_.get(geoip, 'licenseKey') || _.get(geoip, 'licenseKey') === 'licenseKey') {
140
+ let message = 'acgeoip_licenseKey_missing'
141
+ if (_.isFunction(cb)) return cb({ message })
142
+ throw Error(message)
143
+ }
144
+ const ip = _.get(params, 'ip')
145
+ if (ipPackage.isPrivate(ip)) {
146
+ if (_.isFunction(cb)) return cb()
147
+ return
148
+ }
149
+
150
+ const mapping = _.get(params, 'mapping', geoip.mapping)
151
+ const debug = _.get(params, 'debug')
152
+ const debugPerformance = _.get(params, 'debugPerforance')
153
+ const start = process.hrtime()
154
+
155
+ let response = {
156
+ ip
157
+ }
158
+ let geoipResponse
159
+
160
+ if (geoip.redis) {
161
+ geoipResponse = await checkRedis(params)
162
+ }
163
+ else {
164
+ geoipResponse = getFromMemory({ ip })
165
+ }
166
+ if (debugPerformance) console.log('%s | getFromCache %d', functionName, performanceHelper(start, process.hrtime()))
167
+
168
+ // fetch fresh
169
+ if (!_.get(geoipResponse, 'country')) {
170
+ try {
171
+ const client = new WebServiceClient(geoip.userId, geoip.licenseKey)
172
+ geoipResponse = await new Promise((resolve, reject) => {
173
+ client.city(ip).then(result => {
174
+ return resolve(result)
175
+ }).catch(reject)
176
+ })
177
+ if (debugPerformance) console.log('%s | readFromWebservice %d', functionName, performanceHelper(start, process.hrtime()))
178
+ if (geoipResponse) {
179
+ _.set(geoipResponse, 'origin', 'webservice')
180
+ }
181
+
182
+ if (debug) {
183
+ console.log('AC-GEOIP | From Maxmind | %j', geoipResponse)
184
+ }
185
+ }
186
+ catch(e) {
187
+ console.error('AC-GEOIP | From Maxmind | Failed | %j', e)
188
+ }
189
+ }
190
+
191
+ if (geoipResponse) {
192
+ if (geoip.redis) {
193
+ await storeRedis({ ip, geoipResponse })
194
+ }
195
+ else {
196
+ storeInMemory({ ip, geoipResponse })
197
+ }
198
+ if (debugPerformance) console.log('%s | storeInCache %d', functionName, performanceHelper(start, process.hrtime()))
199
+ }
200
+
201
+ // prepare response
202
+ if (!_.isEmpty(mapping)) {
203
+ _.forEach(mapping, item => {
204
+ if (_.get(geoipResponse, item.geoIP)) _.set(response, item.response, _.get(geoipResponse, item.geoIP))
205
+ })
206
+ }
207
+ else {
208
+ response = geoipResponse
209
+ }
210
+
211
+ _.set(response, 'origin', _.get(geoipResponse, 'origin'))
212
+ if (_.get(geoipResponse, 'fromCache')) _.set(response, 'fromCache', true)
213
+
214
+ if (_.isFunction(cb)) return cb(null, response)
215
+ return response
216
+ }
217
+
218
+ const storeInMemory = (params) => {
219
+ const geoipResponse = _.get(params, 'geoipResponse')
220
+ const ip = _.get(params, 'ip')
221
+ const storageKey = _.get(geoip, 'environment') + ':geoip:' + ip
222
+ geoCache.set(storageKey, geoipResponse)
223
+ }
224
+
225
+ const getFromMemory = (params) => {
226
+ const ip = _.get(params, 'ip')
227
+ const storageKey = _.get(geoip, 'environment') + ':geoip:' + ip
228
+ return geoCache.get(storageKey)
229
+ }
230
+
231
+
232
+ const checkRedis = async(params, cb) => {
233
+ const refresh = _.get(params, 'refresh')
234
+ if (!geoip.redis || refresh) {
235
+ if (_.isFunction(cb)) return cb()
236
+ return
237
+ }
238
+ const ip = _.get(params, 'ip')
239
+ const redisKey = _.get(geoip, 'environment') + ':geoip:' + ip
240
+ const debug = _.get(params, 'debug')
241
+
242
+ let geoipResponse
243
+ try {
244
+ geoipResponse = await geoip.redis.get(redisKey)
245
+ geoipResponse = JSON.parse(geoipResponse)
246
+ if (_.isPlainObject(geoipResponse)) {
247
+ geoipResponse.fromCache = true
248
+ }
249
+ if (debug) {
250
+ console.log('AC-GEOIP | From Cache | %j', geoipResponse)
251
+ }
252
+ }
253
+ catch(e) {
254
+ console.log(e)
255
+ console.error('AC-GEOIP | From Cache | Failed | %j', e)
256
+ }
257
+ return geoipResponse
258
+ }
259
+
260
+ const storeRedis = async(params) => {
261
+ const refresh = _.get(params, 'refresh')
262
+ if (!geoip.redis || refresh) {
263
+ return
264
+ }
265
+ const ip = _.get(params, 'ip')
266
+ const geoipResponse = _.get(params, 'geoipResponse')
267
+ const redisKey = _.get(geoip, 'environment') + ':geoip:' + ip
268
+
269
+ await geoip.redis.setex(redisKey, geoip.cacheTime, JSON.stringify(geoipResponse))
270
+ }
271
+
272
+ const performanceHelper = (t1, t2, options) => {
273
+ const accuracy = _.get(options, 'accuracy', 1e6)
274
+ const s = t2[0] - t1[0]
275
+ const mms = t2[1] - t1[1]
276
+ return (s*1e9 + mms)/accuracy
277
+ }
278
+
279
+
280
+ return {
281
+ init,
282
+ lookup,
283
+ lookupLocal
284
+ }
285
+ }
286
+
287
+ module.exports = acgeoip()
288
+
289
+
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "ac-geoip",
3
+ "author": "Mark Poepping (https://www.admiralcloud.com)",
4
+ "license": "MIT",
5
+ "repository": "admiralcloud/ac-geoip",
6
+ "version": "1.4.2",
7
+ "dependencies": {
8
+ "@maxmind/geoip2-node": "^3.2.0",
9
+ "ip": "^1.1.5",
10
+ "lodash": "^4.17.21",
11
+ "node-cache": "^5.1.2"
12
+ },
13
+ "devDependencies": {
14
+ "ac-semantic-release": "^0.2.6",
15
+ "chai": "^4.3.4",
16
+ "eslint": "^7.32.0",
17
+ "ioredis": "^4.27.10",
18
+ "mocha": "^9.1.2"
19
+ },
20
+ "scripts": {
21
+ "test": "./node_modules/.bin/mocha --bail --exit --slow 1000 ./test/test.js || :"
22
+ },
23
+ "engines": {
24
+ "node": ">=8.0.0"
25
+ }
26
+ }
package/test/test.js ADDED
@@ -0,0 +1,133 @@
1
+ const acgeoip = require('../index')
2
+ const _ = require('lodash')
3
+
4
+ const { expect } = require('chai');
5
+
6
+
7
+
8
+ const Redis = require("ioredis");
9
+ const redis = new Redis()
10
+
11
+ const credentials = require('./../credentials');
12
+
13
+
14
+ const ip = '35.184.130.59'
15
+ const expectedValue = {
16
+ ip: '35.184.130.59',
17
+ iso2: 'US',
18
+ city: 'Council Bluffs',
19
+ region: 'Iowa',
20
+ isp: 'Google Cloud',
21
+ organization: 'Google Cloud',
22
+ domain: 'googleusercontent.com'
23
+ }
24
+
25
+
26
+
27
+ describe('Test Webservice', () => {
28
+
29
+ it('Webservice is not yet enabled - should fail', async function() {
30
+ this.timeout(5000)
31
+ try {
32
+ await acgeoip.lookup({ ip, debug: false })
33
+ }
34
+ catch (e) {
35
+ expect(e).to.be.instanceOf(Error)
36
+ expect(e.message).to.eql('acgeoip_licenseKey_missing')
37
+ }
38
+ })
39
+
40
+ it('Init', done => {
41
+ const geoip = {
42
+ redis,
43
+ userId: credentials.userId,
44
+ licenseKey: credentials.licenseKey
45
+ }
46
+ acgeoip.init(geoip)
47
+ return done()
48
+ })
49
+
50
+ it('Shoud be tested with async/await', async function() {
51
+ this.timeout(5000)
52
+ const result = await acgeoip.lookup({ ip, debug: false, refresh: true })
53
+ _.forOwn(expectedValue, (val, key) => {
54
+ expect(result).to.have.property(key, val)
55
+ })
56
+ return
57
+ })
58
+
59
+ it('Shoud be tested with async/await - should be from cache', async function() {
60
+ this.timeout(5000)
61
+ const result = await acgeoip.lookup({ ip, debug: false })
62
+ _.forOwn(expectedValue, (val, key) => {
63
+ expect(result).to.have.property(key, val)
64
+ })
65
+ expect(result).to.have.property('fromCache', true)
66
+ return
67
+ })
68
+
69
+ it('End', done => {
70
+ return done()
71
+ })
72
+ })
73
+
74
+
75
+ describe('Test Geolite2 local database', () => {
76
+
77
+ it('Geolite local is not yet enabled - should fail', async function() {
78
+ this.timeout(5000)
79
+ try {
80
+ await acgeoip.lookupLocal({ ip, debug: false })
81
+ }
82
+ catch (e) {
83
+ expect(e).to.be.instanceOf(Error)
84
+ expect(e.message).to.eql('acgeoip_geolite_notEnabled')
85
+ }
86
+ })
87
+
88
+ it('Init geolite', done => {
89
+ const geoip = {
90
+ redis,
91
+ userId: undefined,
92
+ licenseKey: undefined,
93
+ geolite: {
94
+ enabled: true,
95
+ path: './geolite2/GeoLite2-City.mmdb'
96
+ }
97
+ }
98
+ acgeoip.init(geoip)
99
+ return done()
100
+ })
101
+
102
+ it('Test local ip async/await', async function() {
103
+ this.timeout(5000)
104
+ const result = await acgeoip.lookupLocal({ ip: '127.0.0.1', debug: false })
105
+ expect(result).to.be.undefined
106
+ })
107
+
108
+ it('Shoud be tested with async/await', async function() {
109
+ this.timeout(5000)
110
+ const result = await acgeoip.lookupLocal({ ip, debug: false, refresh: true })
111
+ let fields = ['iso2', 'city', 'region']
112
+ _.forEach(fields, key => {
113
+ let val = _.get(expectedValue, key)
114
+ expect(result).to.have.property(key, val)
115
+ })
116
+ expect(result).to.have.property('origin', 'db')
117
+ return
118
+ })
119
+
120
+ it('Test again - should be from cache', async function() {
121
+ this.timeout(5000)
122
+ const result = await acgeoip.lookupLocal({ ip, debug: false })
123
+ let fields = ['iso2', 'city', 'region']
124
+ _.forEach(fields, key => {
125
+ let val = _.get(expectedValue, key)
126
+ expect(result).to.have.property(key, val)
127
+ })
128
+ expect(result).to.have.property('origin', 'db')
129
+ expect(result).to.have.property('fromCache', true)
130
+ return
131
+ })
132
+ })
133
+