@smpx/koa-request 0.4.3 → 1.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.
package/GeoIP.js CHANGED
@@ -9,6 +9,7 @@ const fileName = 'GeoLite2-City';
9
9
  const fileUrl = `https://raw.githubusercontent.com/GitSquared/node-geolite2-redist/master/redist/${fileName}.tar.gz`;
10
10
  const fileDir = __dirname;
11
11
  const filePath = `${fileDir}/${fileName}.mmdb`;
12
+ let geoInitPromise;
12
13
  let geoip;
13
14
 
14
15
  function save(url, outDir) {
@@ -84,13 +85,21 @@ async function updateDb() {
84
85
  setTimeout(updateDb, 3 * 24 * 3600 * 1000);
85
86
  }
86
87
 
88
+ async function geoIpInit() {
89
+ await download();
90
+ geoip = await maxmind.open(filePath);
91
+ if (process.env.NODE_ENV === 'production') {
92
+ updateDb();
93
+ }
94
+ }
95
+
87
96
  async function getGeoIp() {
88
97
  if (!geoip) {
89
- await download();
90
- geoip = await maxmind.open(filePath);
91
- if (process.env.NODE_ENV === 'production') {
92
- updateDb();
98
+ if (!geoInitPromise) {
99
+ geoInitPromise = geoIpInit();
93
100
  }
101
+ await geoInitPromise;
102
+ geoInitPromise = null;
94
103
  }
95
104
  return geoip;
96
105
  }
package/IPRange.js CHANGED
@@ -11,6 +11,7 @@ const PRIVATE_IPS = [
11
11
  ];
12
12
 
13
13
  // Source: https://www.lifewire.com/what-is-the-ip-address-of-google-818153
14
+ // https://developers.google.com/search/apis/ipranges/googlebot.json
14
15
  const GOOGLE_IPS = [
15
16
  '64.233.160.0 - 64.233.191.255',
16
17
  '66.102.0.0 - 66.102.15.255',
@@ -27,11 +28,16 @@ const GOOGLE_IPS = [
27
28
  ];
28
29
 
29
30
  const SEARCH_IPS = [
30
- '178.154.128.0/17', // YANDAX
31
- '87.250.224.0/19', // YANDAX
32
- '157.55.0.0/16', // Microsoft
33
- '207.46.0.0/19', // Microsoft
34
- '17.0.0.0/8', // Apple
31
+ // BingBot (https://www.bing.com/toolbox/bingbot.json)
32
+ '157.55.0.0/16',
33
+ '207.46.0.0/19',
34
+ '52.167.144.0/24',
35
+ '40.77.128.0/17',
36
+ // YANDAX
37
+ '178.154.128.0/17',
38
+ '87.250.224.0/19',
39
+ // Apple
40
+ '17.0.0.0/8',
35
41
  ];
36
42
 
37
43
  let compiledPrivateIps;
package/Request.js CHANGED
@@ -22,7 +22,6 @@ const TEN_YEARS = 10 * ONE_YEAR;
22
22
 
23
23
  const PLATFORM_PARAM = 'platform';
24
24
  const PLATFORM_COOKIE = 'platform';
25
- const PLATFORM_COOKIE_DURATION = 4 * ONE_HOUR;
26
25
  const APPINFO_PARAM = 'sm_app';
27
26
  const APPINFO_COOKIE = 'sm_app';
28
27
  const APPINFO_HEADER = 'sm-app';
@@ -399,7 +398,6 @@ class Request {
399
398
  this.ctx.set('Content-Security-Policy', `frame-ancestors https://*.${domain}`);
400
399
  }
401
400
 
402
- this.handlePlatformModification();
403
401
  if (!this.isAjax()) {
404
402
  this.setUTMCookie();
405
403
  this.setAffidCookie();
@@ -653,17 +651,17 @@ class Request {
653
651
  _getAppInfoFromString(infoStr, separator = '#') {
654
652
  if (!infoStr) return null;
655
653
  // eslint-disable-next-line prefer-const
656
- let [platform, appVersion, installId] = infoStr.split(separator);
657
- platform = platform.toLowerCase();
654
+ let [os, appVersion, installId] = infoStr.split(separator);
655
+ os = os.toLowerCase();
658
656
 
659
657
  const appInfo = {
660
- platform,
658
+ os,
661
659
  appVersion,
662
660
  installId,
663
661
  };
664
662
 
665
- if (this.appPlatforms().has(platform)) {
666
- appInfo.isMobileApp = true;
663
+ if (this.appPlatforms().has(os)) {
664
+ appInfo.appType = 'app';
667
665
  }
668
666
 
669
667
  return appInfo;
@@ -693,17 +691,75 @@ class Request {
693
691
  return this._getAppInfoFromString(this.ctx.headers[APPINFO_HEADER]);
694
692
  }
695
693
 
696
- getAppInfo() {
697
- const appInfoHeader = this.getAppInfoFromHeader();
698
- if (appInfoHeader) return appInfoHeader;
694
+ getAppInfoCustom() {
695
+ return null;
696
+ }
699
697
 
700
- const appInfoParam = this.getAppInfoFromParam();
701
- if (appInfoParam) return appInfoParam;
698
+ _getDeviceTypeFromUA() {
699
+ const ua = this.parseUserAgent();
700
+ const deviceType = ua.device?.type;
701
+ if (deviceType === 'mobile') return 'mobile';
702
+ if (deviceType === 'tablet') return 'tablet';
703
+ return 'desktop';
704
+ }
702
705
 
703
- const appInfoUserAgent = this.getAppInfoFromUserAgent();
704
- if (appInfoUserAgent) return appInfoUserAgent;
706
+ _getDeviceType() {
707
+ const secHeader = this.ctx.headers['sec-ch-ua-mobile'];
708
+ if (secHeader) {
709
+ if (secHeader === '?1') return 'mobile';
710
+ return this._getDeviceTypeFromUA();
711
+ }
712
+ const vwCookie = this.cookie(VIEWPORT_WIDTH_COOKIE);
713
+ if (vwCookie) {
714
+ if (Number(vwCookie) <= 750) return 'mobile';
715
+ return this._getDeviceTypeFromUA();
716
+ }
717
+ return this._getDeviceTypeFromUA();
718
+ }
719
+
720
+ computeAppInfo() {
721
+ const ctx = this.ctx;
705
722
 
706
- return {};
723
+ const appInfo1 = this.getAppInfoFromHeader() || {};
724
+ const appInfo2 = this.getAppInfoFromParam() || {};
725
+ const appInfo3 = this.getAppInfoFromUserAgent() || {};
726
+ const appInfo4 = this.getAppInfoCustom() || {};
727
+
728
+ const appInfo = {
729
+ installId: ctx.query.installId,
730
+ appVersion: ctx.query.appVersion,
731
+ ...appInfo4,
732
+ ...appInfo3,
733
+ ...appInfo2,
734
+ ...appInfo1,
735
+ };
736
+
737
+ let xPlatform = (
738
+ ctx.query[PLATFORM_PARAM] ||
739
+ this.cookie(PLATFORM_COOKIE) ||
740
+ ctx.headers['x-sm-platform']
741
+ );
742
+ if (xPlatform) {
743
+ xPlatform = xPlatform.toLowerCase();
744
+ if (xPlatform === 'mobile_app') {
745
+ if (!appInfo.deviceType) appInfo.deviceType = 'mobile';
746
+ if (!appInfo.appType) appInfo.appType = 'app';
747
+ }
748
+ else {
749
+ const os = xPlatform.match(/android|ios|wp|tizen|jio/i)?.[0];
750
+ if (os) {
751
+ if (!appInfo.deviceType) appInfo.deviceType = 'mobile';
752
+ if (!appInfo.appType) appInfo.appType = 'app';
753
+ if (!appInfo.os) appInfo.os = os.toLowerCase();
754
+ }
755
+ else if (!appInfo.deviceType) {
756
+ appInfo.deviceType = xPlatform.includes('mobile') ? 'mobile' : 'desktop';
757
+ }
758
+ }
759
+ }
760
+ else if (!appInfo.deviceType) {
761
+ appInfo.deviceType = this._getDeviceType();
762
+ }
707
763
  }
708
764
 
709
765
  /*
@@ -711,156 +767,71 @@ class Request {
711
767
  * this means that you can have the url as sm_app=android
712
768
  * and it'll automatically identify it as an android app
713
769
  */
714
- appInfo(param = null) {
770
+ appInfo() {
715
771
  if (!this._appInfo) {
716
- this._appInfo = this.getAppInfo() || {};
772
+ this._appInfo = this.computeAppInfo() || {};
717
773
  }
718
- return param ? this._appInfo[param] : this._appInfo;
774
+ return this._appInfo;
719
775
  }
720
776
 
721
777
  installId() {
722
- return this.appInfo('installId') || this.ctx.query.installId || '';
778
+ return this.appInfo().installId || '';
723
779
  }
724
780
 
725
781
  appVersion() {
726
- return this.appInfo('appVersion') || this.ctx.query.appVersion || '';
782
+ return this.appInfo().appVersion || '';
783
+ }
784
+
785
+ isApp() {
786
+ return this.appInfo().type === 'app';
727
787
  }
728
788
 
729
789
  isAndroidApp() {
730
- return this.appInfo('platform') === 'android';
790
+ return this.isApp() && this.appInfo().os === 'android';
731
791
  }
732
792
 
733
793
  isIOSApp() {
734
- return this.appInfo('platform') === 'ios';
794
+ return this.isApp() && this.appInfo().os === 'ios';
735
795
  }
736
796
 
737
797
  isWPApp() {
738
- return this.appInfo('platform') === 'wp';
798
+ return this.isApp() && this.appInfo().os === 'wp';
739
799
  }
740
800
 
741
801
  isTizenApp() {
742
- return this.appInfo('platform') === 'tizen';
802
+ return this.isApp() && this.appInfo().os === 'tizen';
743
803
  }
744
804
 
745
805
  isJIOApp() {
746
- return this.appInfo('platform') === 'jio';
806
+ return this.isApp() && this.appInfo().os === 'jio';
747
807
  }
748
808
 
749
809
  isMobileApp() {
750
- // for setPlatform cases
751
- const platform = this._platform;
752
- if (platform) {
753
- if (platform === 'mobile_app') return true;
754
- return false;
755
- }
756
-
757
- return !!this.appInfo('isMobileApp');
810
+ return this.isApp() && this.isMobile();
758
811
  }
759
812
 
760
813
  isMobileWeb() {
761
- // for setPlatform cases
762
- const platform = this._platform;
763
- if (platform) {
764
- if (platform === 'mobile_web') return true;
765
- return false;
766
- }
767
-
768
- if (this._isMobileWeb == null) {
769
- const secHeader = this.ctx.headers['sec-ch-ua-mobile'];
770
- if (secHeader) {
771
- this._isMobileWeb = secHeader === '?1';
772
- }
773
- else {
774
- const vwCookie = this.cookie(VIEWPORT_WIDTH_COOKIE);
775
- if (vwCookie) {
776
- this._isMobileWeb = Number(vwCookie) <= 750;
777
- }
778
- else {
779
- const ua = this.parseUserAgent();
780
- this._isMobileWeb = (ua && ua.device && ua.device.type === 'mobile') || false;
781
- }
782
- }
783
- }
784
- return this._isMobileWeb;
785
- }
786
-
787
- isMobile() {
788
- return this.isMobileApp() || this.isMobileWeb();
814
+ return this.isMobile() && !this.isApp();
789
815
  }
790
816
 
791
- isAPI() {
792
- return false;
817
+ deviceType() {
818
+ return this.appInfo().deviceType || 'desktop';
793
819
  }
794
820
 
795
- platform() {
796
- if (!this._platform) {
797
- if (this.isMobileApp()) {
798
- this._platform = 'mobile_app';
799
- }
800
- else if (this.isMobileWeb()) {
801
- this._platform = 'mobile_web';
802
- }
803
- else if (this.isAPI()) {
804
- this._platform = 'api';
805
- }
806
- else {
807
- this._platform = 'desktop';
808
- }
809
- }
810
-
811
- return this._platform;
812
- }
813
-
814
- setPlatform(platform) {
815
- if (!platform) return false;
816
- platform = handleArray(platform);
817
- const appPlatform = platform.replace('_app', '');
818
-
819
- if (this.appPlatforms().has(appPlatform)) {
820
- this._platform = 'mobile_app';
821
- this._subPlatform = `${appPlatform}_app`;
822
- return false;
823
- }
824
-
825
- switch (platform) {
826
- case 'mobile':
827
- case 'mobile_web':
828
- this._platform = 'mobile_web';
829
- return true;
830
- case 'www':
831
- case 'desktop':
832
- this._platform = 'desktop';
833
- return true;
834
- case 'mobile_app':
835
- this._platform = 'mobile_app';
836
- return true;
837
- default:
838
- return false;
839
- }
821
+ isMobile() {
822
+ return this.appInfo().deviceType === 'mobile';
840
823
  }
841
824
 
842
- subPlatform() {
843
- if (!this._subPlatform) {
844
- const appPlatform = this.appInfo('platform');
845
- if (appPlatform && this.isMobileApp()) {
846
- this._subPlatform = `${appPlatform}_app`;
847
- }
848
- else {
849
- this._subPlatform = this.platform();
850
- }
851
- }
852
- return this._subPlatform;
825
+ isTablet() {
826
+ return this.appInfo().deviceType === 'tablet';
853
827
  }
854
828
 
855
829
  isDesktop() {
856
- // for setPlatform cases
857
- const platform = this._platform;
858
- if (platform) {
859
- if (platform === 'desktop') return true;
860
- return false;
861
- }
830
+ return !this.isMobile();
831
+ }
862
832
 
863
- return this.platform() === 'desktop';
833
+ isAPI() {
834
+ return false;
864
835
  }
865
836
 
866
837
  /**
@@ -1152,14 +1123,6 @@ class Request {
1152
1123
  return '/signup?next=' + encodeURIComponent(this.nextUrl());
1153
1124
  }
1154
1125
 
1155
- mobileUrl() {
1156
- return addQuery(this.ctx.url, {[PLATFORM_PARAM]: 'mobile'});
1157
- }
1158
-
1159
- desktopUrl() {
1160
- return addQuery(this.ctx.url, {[PLATFORM_PARAM]: 'desktop'});
1161
- }
1162
-
1163
1126
  redirect(url, qs = true) {
1164
1127
  if (qs === true) {
1165
1128
  url = addQuery(url, this.ctx.querystring);
@@ -1402,30 +1365,16 @@ class Request {
1402
1365
  }
1403
1366
  }
1404
1367
 
1405
- handlePlatformModification() {
1406
- let setPlatformCookie = false;
1407
- if (!this.isMobileApp() && !this.isAjax()) {
1408
- // don't change platform in mobile apps (and ajax requests) from query or cookie
1409
- const platform = this.ctx.query[PLATFORM_PARAM] || this.cookie(PLATFORM_COOKIE);
1410
- setPlatformCookie = this.setPlatform(platform);
1411
- if (setPlatformCookie) {
1412
- this.cookie(PLATFORM_COOKIE, platform, {
1413
- maxAge: PLATFORM_COOKIE_DURATION,
1414
- domain: '*',
1415
- });
1416
- }
1417
- }
1418
- if (!setPlatformCookie) {
1419
- const smPlatform = this.header('x-sm-platform');
1420
- if (smPlatform) {
1421
- this._platform = smPlatform;
1422
- }
1368
+ geoipLoc() {
1369
+ if (this._giploc === undefined) {
1370
+ this._giploc = GeoIP.getSync(this.ip()) || {};
1423
1371
  }
1372
+ return this._giploc;
1424
1373
  }
1425
1374
 
1426
1375
  ipLocation() {
1427
1376
  if (this._ipLoc === undefined) {
1428
- const loc = GeoIP.getSync(this.ip()) || {};
1377
+ const loc = this.geoipLoc();
1429
1378
  const country = loc.country;
1430
1379
  const city = loc.city;
1431
1380
  const state = loc.subdivisions && loc.subdivisions[0];
package/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  const Request = require('./Request');
2
2
  const IPRange = require('./IPRange');
3
+ const GeoIP = require('./GeoIP');
3
4
 
4
5
  module.exports = {
5
6
  Request,
6
7
  IPRange,
8
+ GeoIP,
7
9
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smpx/koa-request",
3
- "version": "0.4.3",
3
+ "version": "1.0.0",
4
4
  "description": "Handle basic tasks for koajs",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -30,14 +30,14 @@
30
30
  },
31
31
  "homepage": "https://github.com/smartprix/koa-request#readme",
32
32
  "dependencies": {
33
- "ip": "^1.1.8",
33
+ "ip": "^2.0.1",
34
34
  "koa-basic-auth": "^4.0.0",
35
35
  "koa-body": "^5.0.0",
36
36
  "koa-send": "^5.0.1",
37
- "koa2-ratelimit": "^1.1.1",
38
- "maxmind": "^4.3.6",
39
- "tar": "^6.1.11",
40
- "ua-parser-js": "^1.0.2"
37
+ "koa2-ratelimit": "^1.1.3",
38
+ "maxmind": "^4.3.20",
39
+ "tar": "^6.2.1",
40
+ "ua-parser-js": "^1.0.38"
41
41
  },
42
42
  "devDependencies": {
43
43
  "eslint": "^5.7.0",