@k03mad/ip2geo 19.3.3 → 20.0.0

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.
Files changed (41) hide show
  1. package/.vscode/settings.json +2 -1
  2. package/app/api.ts +120 -0
  3. package/app/cli.ts +102 -0
  4. package/app/helpers/cache.ts +257 -0
  5. package/app/helpers/utils.ts +1 -0
  6. package/app/types.ts +15 -0
  7. package/cli.js +3 -0
  8. package/package.json +14 -11
  9. package/pnpm-workspace.yaml +4 -42
  10. package/tests/cache-file-entries.ts +37 -0
  11. package/tests/cache-file-modify.ts +34 -0
  12. package/tests/cache-file-subfolder.ts +34 -0
  13. package/tests/cache-map-entries.ts +41 -0
  14. package/tests/cache-map-max-entries.ts +50 -0
  15. package/tests/cache-map-off.ts +34 -0
  16. package/tests/default.ts +55 -0
  17. package/tests/helpers/path.ts +5 -0
  18. package/tests/ip-multi-requests-cache.ts +39 -0
  19. package/tests/ip-no-data.ts +33 -0
  20. package/tests/ip-v6.ts +31 -0
  21. package/tests/shared/consts.ts +49 -0
  22. package/tests/shared/fs.ts +44 -0
  23. package/tsconfig.json +9 -0
  24. package/app/api.js +0 -142
  25. package/app/cli.js +0 -104
  26. package/app/helpers/cache.js +0 -274
  27. package/app/helpers/utils.js +0 -2
  28. package/tests/cache-file-entries.js +0 -37
  29. package/tests/cache-file-modify.js +0 -34
  30. package/tests/cache-file-subfolder.js +0 -34
  31. package/tests/cache-map-entries.js +0 -41
  32. package/tests/cache-map-max-entries.js +0 -50
  33. package/tests/cache-map-off.js +0 -35
  34. package/tests/default.js +0 -55
  35. package/tests/helpers/path.js +0 -13
  36. package/tests/ip-multi-requests-cache.js +0 -39
  37. package/tests/ip-no-data.js +0 -35
  38. package/tests/ip-v6.js +0 -31
  39. package/tests/shared/consts.js +0 -47
  40. package/tests/shared/fs.js +0 -42
  41. /package/app/helpers/{colors.js → colors.ts} +0 -0
@@ -0,0 +1,34 @@
1
+ import assert from 'node:assert/strict';
2
+ import path from 'node:path';
3
+ import {describe, it} from 'node:test';
4
+
5
+ import {ip2geo} from '../app/api.ts';
6
+ import type {ReqOutput} from '../app/types.ts';
7
+
8
+ import {getCurrentFilename, getTestFolder} from './helpers/path.ts';
9
+ import {REQUEST_IPV4} from './shared/consts.ts';
10
+ import {checkCacheFile, removeCacheFolder} from './shared/fs.ts';
11
+
12
+ const testName = getCurrentFilename(import.meta.url);
13
+
14
+ describe(testName, () => {
15
+ const SUBFOLDERS = 5;
16
+
17
+ const opts = {
18
+ cacheDir: getTestFolder(path.join(...Array.from({length: SUBFOLDERS}, () => testName))),
19
+ cacheMap: new Map<string, ReqOutput>(),
20
+ };
21
+
22
+ it('should remove fs cache dir if exist', () => removeCacheFolder(opts.cacheDir));
23
+
24
+ it(`should return correct response for IP: "${REQUEST_IPV4.ip}"`, async () => {
25
+ const data = await ip2geo({ip: REQUEST_IPV4.ip ?? '', ...opts});
26
+ assert.deepEqual(data, REQUEST_IPV4);
27
+ });
28
+
29
+ it('should have cache file', () =>
30
+ checkCacheFile({
31
+ ...opts,
32
+ response: REQUEST_IPV4,
33
+ }));
34
+ });
@@ -0,0 +1,41 @@
1
+ import assert from 'node:assert/strict';
2
+ import {describe, it} from 'node:test';
3
+
4
+ import {ip2geo} from '../app/api.ts';
5
+ import type {ReqOutput} from '../app/types.ts';
6
+
7
+ import {getCurrentFilename, getTestFolder} from './helpers/path.ts';
8
+ import {REQUEST_IPV4, REQUEST_IPV6} from './shared/consts.ts';
9
+ import {removeCacheFolder} from './shared/fs.ts';
10
+
11
+ const testName = getCurrentFilename(import.meta.url);
12
+
13
+ describe(testName, () => {
14
+ const opts = {
15
+ cacheDir: getTestFolder(testName),
16
+ cacheMap: new Map<string, ReqOutput>(),
17
+ };
18
+
19
+ it('should remove fs cache dir if exist', () => removeCacheFolder(opts.cacheDir));
20
+
21
+ it(`should return correct response for IP: "${REQUEST_IPV4.ip}"`, async () => {
22
+ const data = await ip2geo({ip: REQUEST_IPV4.ip ?? '', ...opts});
23
+ assert.deepEqual(data, REQUEST_IPV4);
24
+ });
25
+
26
+ it('should have 1 correct cache entry', () => {
27
+ assert.equal(opts.cacheMap.size, 1);
28
+ assert.deepEqual(opts.cacheMap.get(REQUEST_IPV4.ip ?? ''), REQUEST_IPV4);
29
+ });
30
+
31
+ it.skip(`should return correct response for IP: "${REQUEST_IPV6.ip}"`, async () => {
32
+ const data = await ip2geo({ip: REQUEST_IPV6.ip ?? '', ...opts});
33
+ assert.deepEqual(data, REQUEST_IPV6);
34
+ });
35
+
36
+ it.skip('should have 2 correct cache entries', () => {
37
+ assert.equal(opts.cacheMap.size, 2);
38
+ assert.deepEqual(opts.cacheMap.get(REQUEST_IPV4.ip ?? ''), REQUEST_IPV4);
39
+ assert.deepEqual(opts.cacheMap.get(REQUEST_IPV6.ip ?? ''), REQUEST_IPV6);
40
+ });
41
+ });
@@ -0,0 +1,50 @@
1
+ import assert from 'node:assert/strict';
2
+ import {describe, it} from 'node:test';
3
+
4
+ import {ip2geo} from '../app/api.ts';
5
+ import type {ReqOutput} from '../app/types.ts';
6
+
7
+ import {getCurrentFilename, getTestFolder} from './helpers/path.ts';
8
+ import {removeCacheFolder} from './shared/fs.ts';
9
+
10
+ const testName = getCurrentFilename(import.meta.url);
11
+
12
+ describe(testName, () => {
13
+ const firstReqIps = ['10.10.10.10', '20.20.20.20', '30.30.30.30', '40.40.40.40', '50.50.50.50'];
14
+
15
+ const secondReqIps = ['60.60.60.60'];
16
+
17
+ const opts = {
18
+ cacheDir: getTestFolder(testName),
19
+ cacheMap: new Map<string, ReqOutput>(),
20
+ cacheMapMaxEntries: 2,
21
+ };
22
+
23
+ it('should remove fs cache dir if exist', () => removeCacheFolder(opts.cacheDir));
24
+
25
+ firstReqIps.forEach(ip => {
26
+ it(`should request geo for IP: "${ip}"`, async () => {
27
+ await ip2geo({ip, ...opts});
28
+ });
29
+ });
30
+
31
+ it(`should have ${opts.cacheMapMaxEntries} correct cache entries`, () => {
32
+ assert.equal(opts.cacheMap.size, opts.cacheMapMaxEntries);
33
+ assert.deepEqual([...opts.cacheMap.keys()], firstReqIps.slice(-opts.cacheMapMaxEntries));
34
+ });
35
+
36
+ secondReqIps.forEach(ip => {
37
+ it(`should request geo for IP: "${ip}"`, async () => {
38
+ await ip2geo({ip, ...opts});
39
+ });
40
+ });
41
+
42
+ it(`should have ${opts.cacheMapMaxEntries} correct cache entries`, () => {
43
+ assert.equal(opts.cacheMap.size, opts.cacheMapMaxEntries);
44
+
45
+ assert.deepEqual(
46
+ [...opts.cacheMap.keys()],
47
+ [firstReqIps, secondReqIps].flat().slice(-opts.cacheMapMaxEntries),
48
+ );
49
+ });
50
+ });
@@ -0,0 +1,34 @@
1
+ import assert from 'node:assert/strict';
2
+ import {describe, it} from 'node:test';
3
+
4
+ import {cacheStorage, ip2geo} from '../app/api.ts';
5
+
6
+ import {getCurrentFilename, getTestFolder} from './helpers/path.ts';
7
+ import {REQUEST_IPV4_MAP_OFF_ONLY} from './shared/consts.ts';
8
+ import {checkCacheFile, removeCacheFolder} from './shared/fs.ts';
9
+
10
+ const testName = getCurrentFilename(import.meta.url);
11
+
12
+ describe(testName, () => {
13
+ const opts = {
14
+ cacheDir: getTestFolder(testName),
15
+ cacheMapMaxEntries: 0,
16
+ };
17
+
18
+ it('should remove fs cache dir if exist', () => removeCacheFolder(opts.cacheDir));
19
+
20
+ it(`should return correct response for IP: "${REQUEST_IPV4_MAP_OFF_ONLY.ip}"`, async () => {
21
+ const data = await ip2geo({ip: REQUEST_IPV4_MAP_OFF_ONLY.ip ?? '', ...opts});
22
+ assert.deepEqual(data, REQUEST_IPV4_MAP_OFF_ONLY);
23
+ });
24
+
25
+ it('should have cache file', () =>
26
+ checkCacheFile({
27
+ ...opts,
28
+ response: REQUEST_IPV4_MAP_OFF_ONLY,
29
+ }));
30
+
31
+ it('should not have cache entries', () => {
32
+ assert.equal(cacheStorage.size, 0);
33
+ });
34
+ });
@@ -0,0 +1,55 @@
1
+ import assert from 'node:assert/strict';
2
+ import {describe, it} from 'node:test';
3
+
4
+ import {cacheStorage, ip2geo} from '../app/api.ts';
5
+ import type {ReqOutput} from '../app/types.ts';
6
+
7
+ import {getCurrentFilename} from './helpers/path.ts';
8
+ import {REQUEST_IPV4} from './shared/consts.ts';
9
+ import {checkCacheFile, removeCacheFolder} from './shared/fs.ts';
10
+
11
+ const testName = getCurrentFilename(import.meta.url);
12
+
13
+ describe(testName, () => {
14
+ it('should remove fs cache dir if exist', () => removeCacheFolder());
15
+
16
+ describe('with ip arg', () => {
17
+ it(`should return correct response for IP: "${REQUEST_IPV4.ip}"`, async () => {
18
+ const data = await ip2geo({ip: REQUEST_IPV4.ip ?? ''});
19
+ assert.deepEqual(data, REQUEST_IPV4);
20
+ });
21
+
22
+ it('should have cache file', () =>
23
+ checkCacheFile({
24
+ response: REQUEST_IPV4,
25
+ }));
26
+
27
+ it('should have 1 correct cache entry', () => {
28
+ assert.equal(cacheStorage.size, 1);
29
+ assert.deepEqual(cacheStorage.get(REQUEST_IPV4.ip ?? ''), REQUEST_IPV4);
30
+ });
31
+ });
32
+
33
+ describe('without ip arg', () => {
34
+ let data: ReqOutput;
35
+
36
+ it('should request geoip without ip arg', async () => {
37
+ data = await ip2geo();
38
+ });
39
+
40
+ Object.keys(REQUEST_IPV4).forEach(key => {
41
+ it(`should have "${key}" in request response`, () => {
42
+ assert.ok(Object.hasOwn(data, key));
43
+ });
44
+ });
45
+
46
+ it('should not have extra keys in request response', () => {
47
+ assert.deepEqual(Object.keys(data), Object.keys(REQUEST_IPV4));
48
+ });
49
+
50
+ it('should have 2 cache entries', () => {
51
+ assert.equal(cacheStorage.size, 2);
52
+ assert.deepEqual(cacheStorage.get(REQUEST_IPV4.ip ?? ''), REQUEST_IPV4);
53
+ });
54
+ });
55
+ });
@@ -0,0 +1,5 @@
1
+ import path from 'node:path';
2
+
3
+ export const getCurrentFilename = (file: string): string => path.basename(file, '.ts');
4
+
5
+ export const getTestFolder = (file: string): string => path.join('.geoip', file);
@@ -0,0 +1,39 @@
1
+ import assert from 'node:assert/strict';
2
+ import {describe, it} from 'node:test';
3
+
4
+ import {ip2geo} from '../app/api.ts';
5
+ import type {ReqOutput} from '../app/types.ts';
6
+
7
+ import {getCurrentFilename, getTestFolder} from './helpers/path.ts';
8
+ import {REQUEST_IPV4} from './shared/consts.ts';
9
+ import {checkCacheFile, removeCacheFolder} from './shared/fs.ts';
10
+
11
+ const testName = getCurrentFilename(import.meta.url);
12
+
13
+ describe(testName, () => {
14
+ const opts = {
15
+ cacheDir: getTestFolder(testName),
16
+ cacheMap: new Map<string, ReqOutput>(),
17
+ requestsCount: 5,
18
+ };
19
+
20
+ it('should remove fs cache dir if exist', () => removeCacheFolder(opts.cacheDir));
21
+
22
+ Array.from({length: opts.requestsCount}).forEach(() => {
23
+ it(`should return correct response for IP: "${REQUEST_IPV4.ip}"`, async () => {
24
+ const data = await ip2geo({ip: REQUEST_IPV4.ip ?? '', ...opts});
25
+ assert.deepEqual(data, REQUEST_IPV4);
26
+ });
27
+ });
28
+
29
+ it('should have cache file', () =>
30
+ checkCacheFile({
31
+ ...opts,
32
+ response: REQUEST_IPV4,
33
+ }));
34
+
35
+ it('should have 1 correct cache entry', () => {
36
+ assert.equal(opts.cacheMap.size, 1);
37
+ assert.deepEqual(opts.cacheMap.get(REQUEST_IPV4.ip ?? ''), REQUEST_IPV4);
38
+ });
39
+ });
@@ -0,0 +1,33 @@
1
+ import assert from 'node:assert/strict';
2
+ import {describe, it} from 'node:test';
3
+
4
+ import {ip2geo} from '../app/api.ts';
5
+ import type {ReqOutput} from '../app/types.ts';
6
+
7
+ import {getCurrentFilename, getTestFolder} from './helpers/path.ts';
8
+ import {REQUEST_IPV4} from './shared/consts.ts';
9
+ import {removeCacheFolder} from './shared/fs.ts';
10
+
11
+ const testName = getCurrentFilename(import.meta.url);
12
+
13
+ describe(testName, () => {
14
+ const responses = ['10.10.10.10', '192.168.1.100'];
15
+
16
+ const opts = {
17
+ cacheDir: getTestFolder(testName),
18
+ cacheMap: new Map<string, ReqOutput>(),
19
+ };
20
+
21
+ it('should remove fs cache dir if exist', () => removeCacheFolder(opts.cacheDir));
22
+
23
+ responses.forEach(ip => {
24
+ const expectedResponse = Object.fromEntries(
25
+ Object.keys(REQUEST_IPV4).map(key => [key, key === 'ip' ? ip : '']),
26
+ ) as ReqOutput;
27
+
28
+ it(`should return empty response for IP: "${ip}"`, async () => {
29
+ const data = await ip2geo({ip, ...opts});
30
+ assert.deepEqual(data, expectedResponse);
31
+ });
32
+ });
33
+ });
package/tests/ip-v6.ts ADDED
@@ -0,0 +1,31 @@
1
+ import assert from 'node:assert/strict';
2
+ import {describe, it} from 'node:test';
3
+
4
+ import {ip2geo} from '../app/api.ts';
5
+ import type {ReqOutput} from '../app/types.ts';
6
+
7
+ import {getCurrentFilename, getTestFolder} from './helpers/path.ts';
8
+ import {REQUEST_IPV6} from './shared/consts.ts';
9
+ import {checkCacheFile, removeCacheFolder} from './shared/fs.ts';
10
+
11
+ const testName = getCurrentFilename(import.meta.url);
12
+
13
+ describe.skip(testName, () => {
14
+ const opts = {
15
+ cacheDir: getTestFolder(testName),
16
+ cacheMap: new Map<string, ReqOutput>(),
17
+ };
18
+
19
+ it('should remove fs cache dir if exist', () => removeCacheFolder(opts.cacheDir));
20
+
21
+ it(`should return correct response for IP: "${REQUEST_IPV6.ip}"`, async () => {
22
+ const data = await ip2geo({ip: REQUEST_IPV6.ip ?? '', ...opts});
23
+ assert.deepEqual(data, REQUEST_IPV6);
24
+ });
25
+
26
+ it('should have cache file', () =>
27
+ checkCacheFile({
28
+ ...opts,
29
+ response: REQUEST_IPV6,
30
+ }));
31
+ });
@@ -0,0 +1,49 @@
1
+ import type {ReqOutput} from '../../app/types.ts';
2
+
3
+ export const REQUEST_IPV4: ReqOutput = {
4
+ ip: '8.8.8.8',
5
+ continent: 'North America',
6
+ continentCode: 'NA',
7
+ country: 'United States',
8
+ countryCode: 'US',
9
+ countryEmoji: '🇺🇸',
10
+ region: 'California',
11
+ regionCode: 'CA',
12
+ city: 'Mountain View',
13
+ connectionAsn: 15_169,
14
+ connectionOrg: 'Google LLC',
15
+ connectionIsp: 'Google LLC',
16
+ connectionDomain: 'google.com',
17
+ };
18
+
19
+ export const REQUEST_IPV4_MAP_OFF_ONLY: ReqOutput = {
20
+ ip: '9.9.9.9',
21
+ continent: 'North America',
22
+ continentCode: 'NA',
23
+ country: 'United States',
24
+ countryCode: 'US',
25
+ countryEmoji: '🇺🇸',
26
+ region: 'California',
27
+ regionCode: 'CA',
28
+ city: 'Berkeley',
29
+ connectionAsn: 19_281,
30
+ connectionOrg: 'Quad',
31
+ connectionIsp: 'Quad',
32
+ connectionDomain: 'quad9.net',
33
+ };
34
+
35
+ export const REQUEST_IPV6: ReqOutput = {
36
+ ip: '2a00:dd80:40:100::',
37
+ continent: 'North America',
38
+ continentCode: 'NA',
39
+ country: 'United States',
40
+ countryCode: 'US',
41
+ countryEmoji: '🇺🇸',
42
+ region: 'District of Columbia',
43
+ regionCode: 'DC',
44
+ city: 'Washington',
45
+ connectionAsn: 36_236,
46
+ connectionOrg: 'Netactuate INC',
47
+ connectionIsp: 'Netactuate, INC',
48
+ connectionDomain: 'netactuate.com',
49
+ };
@@ -0,0 +1,44 @@
1
+ import assert from 'node:assert/strict';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+
5
+ import {
6
+ DEFAULT_CACHE_FILE_DIR,
7
+ DEFAULT_CACHE_FILE_NAME,
8
+ DEFAULT_CACHE_FILE_NEWLINE,
9
+ DEFAULT_CACHE_FILE_SEPARATOR,
10
+ } from '../../app/api.ts';
11
+ import type {ReqOutput} from '../../app/types.ts';
12
+
13
+ interface CheckCacheFileOpts {
14
+ cacheDir?: string;
15
+ cacheFileName?: string;
16
+ cacheFileSeparator?: string;
17
+ cacheFileNewline?: string;
18
+ response: ReqOutput;
19
+ }
20
+
21
+ export const removeCacheFolder = async (
22
+ cacheDir: string = DEFAULT_CACHE_FILE_DIR,
23
+ ): Promise<void> => {
24
+ try {
25
+ await fs.rm(cacheDir, {recursive: true, force: true});
26
+ } catch (err) {
27
+ if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {
28
+ throw err;
29
+ }
30
+ }
31
+ };
32
+
33
+ export const checkCacheFile = async ({
34
+ cacheDir = DEFAULT_CACHE_FILE_DIR,
35
+ cacheFileName = DEFAULT_CACHE_FILE_NAME,
36
+ cacheFileSeparator = DEFAULT_CACHE_FILE_SEPARATOR,
37
+ cacheFileNewline = DEFAULT_CACHE_FILE_NEWLINE,
38
+ response,
39
+ }: CheckCacheFileOpts): Promise<void> => {
40
+ const cacheFile = `${response.ip?.split(/[.:]/)[0] ?? ''}_${cacheFileName}`;
41
+ const data = await fs.readFile(path.join(cacheDir, cacheFile), {encoding: 'utf8'});
42
+
43
+ assert.equal(data, cacheFileNewline + Object.values(response).join(cacheFileSeparator));
44
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "@tsconfig/strictest/tsconfig.json",
3
+ "compilerOptions": {
4
+ "types": ["node"],
5
+ "noEmit": true,
6
+ "allowImportingTsExtensions": true,
7
+ "erasableSyntaxOnly": true
8
+ }
9
+ }
package/app/api.js DELETED
@@ -1,142 +0,0 @@
1
- import os from 'node:os';
2
- import path from 'node:path';
3
-
4
- import {request} from '@k03mad/request';
5
-
6
- import {
7
- collectOutputData,
8
- readFromFsCache,
9
- readFromMapCache,
10
- writeToFsCache,
11
- writeToMapCache,
12
- } from './helpers/cache.js';
13
-
14
- const API = 'https://ipwho.is/';
15
-
16
- export const DEFAULT_CACHE_FILE_DIR = path.join(os.tmpdir(), '.ip2geo-cache');
17
- export const DEFAULT_CACHE_FILE_NAME = 'ip.log';
18
- export const DEFAULT_CACHE_FILE_SEPARATOR = ';;';
19
- export const DEFAULT_CACHE_FILE_NEWLINE = '\n';
20
- export const DEFAULT_CACHE_MAP_MAX_ENTRIES = Number.POSITIVE_INFINITY;
21
- export const DEFAULT_CONCURRENCY = 2;
22
-
23
- export const cacheStorage = new Map();
24
-
25
- /**
26
- * @typedef {object} ReqInput
27
- * @property {object} [opts]
28
- * @property {object} [opts.ip]
29
- * @property {string} [opts.cacheDir]
30
- * @property {string} [opts.cacheFileName]
31
- * @property {string} [opts.cacheFileSeparator]
32
- * @property {string} [opts.cacheFileNewline]
33
- * @property {Map} [opts.cacheMap]
34
- * @property {number} [opts.cacheMapMaxEntries]
35
- * @property {number} [opts.rps]
36
- * @property {number} [opts.concurrency]
37
- */
38
-
39
- /**
40
- * @typedef {object} ReqOutput
41
- * @property {string} [ip]
42
- * @property {string} [continent]
43
- * @property {string} [continentCode]
44
- * @property {string} [country]
45
- * @property {string} [countryCode]
46
- * @property {string} [countryEmoji]
47
- * @property {string} [region]
48
- * @property {string} [regionCode]
49
- * @property {string} [city]
50
- * @property {string} [connectionAsn]
51
- * @property {string} [connectionOrg]
52
- * @property {string} [connectionIsp]
53
- * @property {string} [connectionDomain]
54
- */
55
-
56
- /**
57
- * @param {ReqInput} opts
58
- * @returns {Promise<ReqOutput>}
59
- */
60
- export const ip2geo = async ({
61
- ip = '',
62
- cacheDir = DEFAULT_CACHE_FILE_DIR,
63
- cacheFileName = DEFAULT_CACHE_FILE_NAME,
64
- cacheFileSeparator = DEFAULT_CACHE_FILE_SEPARATOR,
65
- cacheFileNewline = DEFAULT_CACHE_FILE_NEWLINE,
66
- cacheMapMaxEntries = DEFAULT_CACHE_MAP_MAX_ENTRIES,
67
- cacheMap = cacheStorage,
68
- rps,
69
- concurrency,
70
- } = {}) => {
71
- if (ip) {
72
- const mapCache = readFromMapCache(ip, cacheMap, cacheMapMaxEntries);
73
-
74
- if (mapCache) {
75
- return mapCache;
76
- }
77
-
78
- const fsCache = await readFromFsCache(
79
- ip,
80
- cacheDir,
81
- cacheFileName,
82
- cacheFileSeparator,
83
- cacheFileNewline,
84
- );
85
-
86
- if (fsCache) {
87
- writeToMapCache(fsCache, cacheMap, cacheMapMaxEntries);
88
-
89
- return fsCache;
90
- }
91
- }
92
-
93
- const reqUrl = API + ip;
94
- const queueOpts = {};
95
-
96
- if (rps) {
97
- queueOpts.rps = rps;
98
- } else if (concurrency) {
99
- queueOpts.concurrency = concurrency;
100
- } else {
101
- queueOpts.concurrency = DEFAULT_CONCURRENCY;
102
- }
103
-
104
- const {body} = await request(reqUrl, {}, queueOpts);
105
-
106
- if (!body?.ip) {
107
- throw new Error(
108
- ['API error', `request: ${reqUrl}`, `response body: ${JSON.stringify(body)}`].join(
109
- '\n',
110
- ),
111
- );
112
- }
113
-
114
- const outputData = collectOutputData([
115
- body.ip,
116
- body?.continent,
117
- body?.continent_code,
118
- body?.country,
119
- body?.country_code,
120
- body?.flag?.emoji,
121
- body?.region,
122
- body?.region_code,
123
- body?.city,
124
- body?.connection?.asn,
125
- body?.connection?.org,
126
- body?.connection?.isp,
127
- body?.connection?.domain,
128
- ]);
129
-
130
- writeToMapCache(outputData, cacheMap, cacheMapMaxEntries);
131
-
132
- await writeToFsCache(
133
- body.ip,
134
- outputData,
135
- cacheDir,
136
- cacheFileName,
137
- cacheFileSeparator,
138
- cacheFileNewline,
139
- );
140
-
141
- return outputData;
142
- };
package/app/cli.js DELETED
@@ -1,104 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import chalk from 'chalk';
4
-
5
- import {
6
- DEFAULT_CACHE_FILE_DIR,
7
- DEFAULT_CACHE_FILE_NEWLINE,
8
- DEFAULT_CACHE_FILE_SEPARATOR,
9
- ip2geo,
10
- } from './api.js';
11
- import {pruneCache} from './helpers/cache.js';
12
- import {codeText, nameText} from './helpers/colors.js';
13
-
14
- const {blue, red, green, dim, bold} = chalk;
15
-
16
- const args = process.argv.slice(2);
17
- const argsExtra = args.filter(arg => !arg.startsWith('-'));
18
-
19
- const isHelp = args.includes('-h') || args.includes('--help');
20
- const isPrune = args.includes('-p') || args.includes('--prune');
21
-
22
- if (isHelp) {
23
- const cmd = `${codeText('$')} ${nameText('ip2geo')}`;
24
-
25
- console.log(
26
- [
27
- '',
28
- codeText('# current external ip'),
29
- cmd,
30
- `${cmd} 1.1.1.1`,
31
- '',
32
- `${cmd} -h`,
33
- `${cmd} --help`,
34
- '',
35
- `${cmd} 1.1.1.1 -j`,
36
- `${cmd} 1.1.1.1 --json`,
37
- '',
38
- `${cmd} 1.1.1.1 8.8.8.8`,
39
- `${cmd} 1.1.1.1,8.8.8.8`,
40
- '',
41
- codeText('# remove duplicate cache entries'),
42
- `${cmd} -p`,
43
- `${cmd} --prune`,
44
- ].join('\n'),
45
- );
46
-
47
- process.exit(0);
48
- }
49
-
50
- if (isPrune) {
51
- const cacheFolder = argsExtra[0] || DEFAULT_CACHE_FILE_DIR;
52
- console.log(blue(cacheFolder));
53
-
54
- try {
55
- const {different, duplicates, empty, entries, longLinesFiles} = await pruneCache(
56
- cacheFolder,
57
- DEFAULT_CACHE_FILE_SEPARATOR,
58
- DEFAULT_CACHE_FILE_NEWLINE,
59
- );
60
-
61
- console.log(
62
- [
63
- '',
64
- green(`Removed duplicate cache entries: ${bold(duplicates.length)}`),
65
- ...duplicates.map(elem => dim(`— ${elem}`)),
66
- green(`Removed different cache entries: ${bold(different.length)}`),
67
- ...different.map(elem => dim(`— ${elem}`)),
68
- green(`Removed empty cache entries: ${bold(empty.length)}`),
69
- ...empty.map(elem => dim(`— ${elem}`)),
70
- ].join('\n'),
71
- );
72
-
73
- if (longLinesFiles.length > 0) {
74
- console.log(
75
- [
76
- red(
77
- `Required manual check, some cache files has too long lines: ${bold(longLinesFiles.length)}`,
78
- ),
79
- ...longLinesFiles.map(({file, elem}) => dim(`— ${file}\n|— ${elem}`)),
80
- ].join('\n'),
81
- );
82
- }
83
-
84
- console.log(['', green(`Current cache entries: ${bold(entries)}`)].join('\n'));
85
- } catch (err) {
86
- console.error(red(err));
87
- process.exit(1);
88
- }
89
- } else {
90
- const output =
91
- argsExtra.length === 0
92
- ? [await ip2geo()]
93
- : await Promise.all(
94
- argsExtra.map(arg => Promise.all(arg.split(',').map(ip => ip2geo({ip})))),
95
- );
96
-
97
- const flatten = output.flat();
98
-
99
- if (args.includes('--json') || args.includes('-j')) {
100
- console.log(JSON.stringify(flatten.length > 1 ? flatten : flatten[0]));
101
- } else {
102
- flatten.forEach(elem => console.log(elem));
103
- }
104
- }