FIX: FCM pushes fixed due to deprecation of legacy api (#292)

* FIX: FCM pushes fixed due to deprecation of legacy  api
This commit is contained in:
Overtorment 2024-12-15 16:35:40 +00:00 committed by GitHub
parent b28dd3eb2e
commit 5d3f630a01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 544 additions and 74 deletions

View File

@ -87,18 +87,22 @@ Works well on Heroku (you'll need `JawsDB Maria` addon)
Set them as env variables or put them into `.env` file in project root dir.
- `JAWSDB_MARIA_URL` for example `mysql://username:password@host:port/database`
- `FCM_SERVER_KEY` hex encoded
- ~~`FCM_SERVER_KEY` hex encoded~~ deprecated (in favor of json key file) after FCM XMPP and HTTP legacy APIs was deprecated; the new one is HTTP v1 API; theres also a new uri to post payload
- `APNS_P8` hex encoded
- `APNS_P8_KID` issuer key which is "key ID" of your p8 file
- `APPLE_TEAM_ID` "team ID" of your developer account
- `BITCOIN_RPC` for example `http://username:password@host:8332`
- `APNS_TOPIC` for example `io.bluewallet.bluewallet`
- `GOOGLE_KEY_FILE` - json file with Google key for FCM, in hex
- `GOOGLE_PROJECT_ID` - acquired with the key file
### Getting certificates
- outdated https://dev.to/jakubkoci/react-native-push-notifications-313i
- https://stackoverflow.com/questions/44631803/ios-swift-how-to-create-p8-file/67533665#67533665
- get P8 hex `xxd -p file.p8 | tr -d '\n'`
- get P8 hex `xxd -p file.p8 | tr -d '\n'` (google key file as well)
- https://firebase.google.com/docs/cloud-messaging/migrate-v1
### License

411
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "groundcontrol",
"version": "2.3.4",
"version": "3.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
@ -13,6 +13,7 @@
"dotenv": "^16.0.2",
"express": "^4.21.1",
"express-rate-limit": "^6.6.0",
"google-auth-library": "^9.15.0",
"helmet": "^5.1.0",
"jayson": "^3.6.6",
"jsonwebtoken": "^9.0.0",
@ -173,6 +174,15 @@
"node": ">=0.4.0"
}
},
"node_modules/agent-base": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
"license": "MIT",
"engines": {
"node": ">= 14"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@ -234,6 +244,15 @@
}
]
},
"node_modules/bignumber.js": {
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
"integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/body-parser": {
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
@ -720,6 +739,12 @@
}
]
},
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"license": "MIT"
},
"node_modules/eyes": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
@ -807,6 +832,68 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/gaxios": {
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
"integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==",
"license": "Apache-2.0",
"dependencies": {
"extend": "^3.0.2",
"https-proxy-agent": "^7.0.1",
"is-stream": "^2.0.0",
"node-fetch": "^2.6.9",
"uuid": "^9.0.1"
},
"engines": {
"node": ">=14"
}
},
"node_modules/gaxios/node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"license": "MIT",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/gaxios/node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"license": "MIT",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/gcp-metadata": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz",
"integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==",
"license": "Apache-2.0",
"dependencies": {
"gaxios": "^6.0.0",
"json-bigint": "^1.0.0"
},
"engines": {
"node": ">=14"
}
},
"node_modules/generate-function": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
@ -871,6 +958,44 @@
"integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
"dev": true
},
"node_modules/google-auth-library": {
"version": "9.15.0",
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.0.tgz",
"integrity": "sha512-7ccSEJFDFO7exFbO6NRyC+xH8/mZ1GZGG2xxx9iHxZWcjUjJpjWxIMw3cofAKcueZ6DATiukmmprD7yavQHOyQ==",
"license": "Apache-2.0",
"dependencies": {
"base64-js": "^1.3.0",
"ecdsa-sig-formatter": "^1.0.11",
"gaxios": "^6.1.1",
"gcp-metadata": "^6.1.0",
"gtoken": "^7.0.0",
"jws": "^4.0.0"
},
"engines": {
"node": ">=14"
}
},
"node_modules/google-auth-library/node_modules/jwa": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
"license": "MIT",
"dependencies": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/google-auth-library/node_modules/jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"license": "MIT",
"dependencies": {
"jwa": "^2.0.0",
"safe-buffer": "^5.0.1"
}
},
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
@ -882,6 +1007,40 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/gtoken": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
"integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==",
"license": "MIT",
"dependencies": {
"gaxios": "^6.0.0",
"jws": "^4.0.0"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/gtoken/node_modules/jwa": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
"license": "MIT",
"dependencies": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"node_modules/gtoken/node_modules/jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"license": "MIT",
"dependencies": {
"jwa": "^2.0.0",
"safe-buffer": "^5.0.1"
}
},
"node_modules/has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
@ -957,6 +1116,42 @@
"node": ">= 0.8"
}
},
"node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"license": "MIT",
"dependencies": {
"agent-base": "^7.1.2",
"debug": "4"
},
"engines": {
"node": ">= 14"
}
},
"node_modules/https-proxy-agent/node_modules/debug": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/https-proxy-agent/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -1022,6 +1217,18 @@
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
"integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ="
},
"node_modules/is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
"license": "MIT",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/isomorphic-ws": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz",
@ -1075,6 +1282,15 @@
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/json-bigint": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
"license": "MIT",
"dependencies": {
"bignumber.js": "^9.0.0"
}
},
"node_modules/json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
@ -1770,6 +1986,12 @@
"node": ">=0.6"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"license": "MIT"
},
"node_modules/ts-node": {
"version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
@ -2133,6 +2355,22 @@
"node": ">= 8"
}
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"license": "BSD-2-Clause"
},
"node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"license": "MIT",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
@ -2375,6 +2613,11 @@
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA=="
},
"agent-base": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
"integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="
},
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@ -2416,6 +2659,11 @@
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"bignumber.js": {
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
"integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug=="
},
"body-parser": {
"version": "1.20.3",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
@ -2759,6 +3007,11 @@
"integrity": "sha512-HFN2+4ZGdkQOS8Qli4z6knmJFnw6lZed67o6b7RGplWeb1Z0s8VXaj3dUgPIdm9hrhZXTRpCTHXA0/2Eqex0vA==",
"requires": {}
},
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"eyes": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
@ -2815,6 +3068,42 @@
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
},
"gaxios": {
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
"integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==",
"requires": {
"extend": "^3.0.2",
"https-proxy-agent": "^7.0.1",
"is-stream": "^2.0.0",
"node-fetch": "^2.6.9",
"uuid": "^9.0.1"
},
"dependencies": {
"node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
"requires": {
"whatwg-url": "^5.0.0"
}
},
"uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="
}
}
},
"gcp-metadata": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz",
"integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==",
"requires": {
"gaxios": "^6.0.0",
"json-bigint": "^1.0.0"
}
},
"generate-function": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
@ -2864,6 +3153,40 @@
"integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
"dev": true
},
"google-auth-library": {
"version": "9.15.0",
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.0.tgz",
"integrity": "sha512-7ccSEJFDFO7exFbO6NRyC+xH8/mZ1GZGG2xxx9iHxZWcjUjJpjWxIMw3cofAKcueZ6DATiukmmprD7yavQHOyQ==",
"requires": {
"base64-js": "^1.3.0",
"ecdsa-sig-formatter": "^1.0.11",
"gaxios": "^6.1.1",
"gcp-metadata": "^6.1.0",
"gtoken": "^7.0.0",
"jws": "^4.0.0"
},
"dependencies": {
"jwa": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
"requires": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"requires": {
"jwa": "^2.0.0",
"safe-buffer": "^5.0.1"
}
}
}
},
"gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
@ -2872,6 +3195,36 @@
"get-intrinsic": "^1.1.3"
}
},
"gtoken": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz",
"integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==",
"requires": {
"gaxios": "^6.0.0",
"jws": "^4.0.0"
},
"dependencies": {
"jwa": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz",
"integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==",
"requires": {
"buffer-equal-constant-time": "1.0.1",
"ecdsa-sig-formatter": "1.0.11",
"safe-buffer": "^5.0.1"
}
},
"jws": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz",
"integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==",
"requires": {
"jwa": "^2.0.0",
"safe-buffer": "^5.0.1"
}
}
}
},
"has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
@ -2920,6 +3273,30 @@
"toidentifier": "1.0.1"
}
},
"https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"requires": {
"agent-base": "^7.1.2",
"debug": "4"
},
"dependencies": {
"debug": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"requires": {
"ms": "^2.1.3"
}
},
"ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
}
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -2962,6 +3339,11 @@
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
"integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ="
},
"is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="
},
"isomorphic-ws": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz",
@ -3006,6 +3388,14 @@
"argparse": "^2.0.1"
}
},
"json-bigint": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
"requires": {
"bignumber.js": "^9.0.0"
}
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
@ -3518,6 +3908,11 @@
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
},
"tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"ts-node": {
"version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
@ -3709,6 +4104,20 @@
"resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
"integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q=="
},
"webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"requires": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "groundcontrol",
"version": "2.3.4",
"version": "3.0.0",
"description": "GroundControl push server API",
"devDependencies": {
"@types/node": "18.7.16",
@ -13,6 +13,7 @@
"dotenv": "^16.0.2",
"express": "^4.21.1",
"express-rate-limit": "^6.6.0",
"google-auth-library": "^9.15.0",
"helmet": "^5.1.0",
"jayson": "^3.6.6",
"jsonwebtoken": "^9.0.0",

View File

@ -1,3 +1,4 @@
import { GoogleAuth } from "google-auth-library";
import { DataSource } from "typeorm";
import { PushLog } from "../entity/PushLog";
import { TokenToAddress } from "../entity/TokenToAddress";
@ -8,11 +9,18 @@ const jwt = require("jsonwebtoken");
const http2 = require("http2");
require("dotenv").config();
if (!process.env.APNS_P8 || !process.env.APPLE_TEAM_ID || !process.env.APNS_P8_KID || !process.env.FCM_SERVER_KEY) {
if (!process.env.APNS_P8 || !process.env.APPLE_TEAM_ID || !process.env.APNS_P8_KID || !process.env.GOOGLE_KEY_FILE || !process.env.GOOGLE_PROJECT_ID) {
console.error("not all env variables set");
process.exit();
}
const keyFileStr = Buffer.from(process.env.GOOGLE_KEY_FILE, "hex").toString("ascii");
require('fs').writeFileSync('/tmp/google_key_file.json', keyFileStr, {encoding: "ascii"});
const auth = new GoogleAuth({
keyFile: '/tmp/google_key_file.json',
scopes: 'https://www.googleapis.com/auth/cloud-platform',
});
/**
* Since we cant attach any code to openapi schema definition, this is a repository of transforming pushnotification object
* (thats created from apenapi schema) to actual payload thats gona be pushed to fcm/apns. In most basic case we would
@ -29,8 +37,10 @@ export class GroundControlToMajorTom {
protected static _jwtToken: string = "";
protected static _jwtTokenMicroTimestamp: number = 0;
static getGoogleServerKey() {
return process.env.FCM_SERVER_KEY;
static async getGoogleCredentials() {
const client = await auth.getClient();
const accessTokenResponse = await client.getAccessToken();
return accessTokenResponse.token; // `accessTokenResponse.token` contains the short-lived access token
}
static getApnsJwtToken(): string {
@ -64,15 +74,19 @@ export class GroundControlToMajorTom {
serverKey: string,
apnsP8: string,
pushNotification: components["schemas"]["PushNotificationOnchainAddressGotUnconfirmedTransaction"]
): Promise<[object, object]> {
): Promise<void> {
const fcmPayload = {
data: {},
notification: {
title: "New unconfirmed transaction",
body: "You received new transfer on " + GroundControlToMajorTom.shortenAddress(pushNotification.address),
badge: pushNotification.badge,
tag: pushNotification.txid,
},
message: {
token: '',
data: {
badge: String(pushNotification.badge),
tag: pushNotification.txid,
},
notification: {
title: "New unconfirmed transaction",
body: "You received new transfer on " + GroundControlToMajorTom.shortenAddress(pushNotification.address),
},
}
};
const apnsPayload = {
@ -91,14 +105,17 @@ export class GroundControlToMajorTom {
if (pushNotification.os === "ios") return GroundControlToMajorTom._pushToApns(dataSource, apnsP8, pushNotification.token, apnsPayload, pushNotification, pushNotification.txid);
}
static async pushOnchainTxidGotConfirmed(dataSource: DataSource, serverKey: string, apnsP8: string, pushNotification: components["schemas"]["PushNotificationTxidGotConfirmed"]): Promise<[object, object]> {
static async pushOnchainTxidGotConfirmed(dataSource: DataSource, serverKey: string, apnsP8: string, pushNotification: components["schemas"]["PushNotificationTxidGotConfirmed"]): Promise<void> {
const fcmPayload = {
data: {},
notification: {
title: "Transaction - Confirmed",
body: "Your transaction " + GroundControlToMajorTom.shortenTxid(pushNotification.txid) + " has been confirmed",
badge: pushNotification.badge,
tag: pushNotification.txid,
message: {
data: {
badge: String(pushNotification.badge),
tag: pushNotification.txid,
},
notification: {
title: "Transaction - Confirmed",
body: "Your transaction " + GroundControlToMajorTom.shortenTxid(pushNotification.txid) + " has been confirmed",
},
},
};
@ -118,12 +135,14 @@ export class GroundControlToMajorTom {
if (pushNotification.os === "ios") return GroundControlToMajorTom._pushToApns(dataSource, apnsP8, pushNotification.token, apnsPayload, pushNotification, pushNotification.txid);
}
static async pushMessage(dataSource: DataSource, serverKey: string, apnsP8: string, pushNotification: components["schemas"]["PushNotificationMessage"]): Promise<[object, object]> {
static async pushMessage(dataSource: DataSource, serverKey: string, apnsP8: string, pushNotification: components["schemas"]["PushNotificationMessage"]): Promise<void> {
const fcmPayload = {
data: {},
notification: {
title: "Message",
body: pushNotification.text,
message: {
data: {},
notification: {
title: "Message",
body: pushNotification.text,
},
},
};
@ -143,15 +162,19 @@ export class GroundControlToMajorTom {
if (pushNotification.os === "ios") return GroundControlToMajorTom._pushToApns(dataSource, apnsP8, pushNotification.token, apnsPayload, pushNotification, pushNotification.txid);
}
static async pushOnchainAddressWasPaid(dataSource: DataSource, serverKey: string, apnsP8: string, pushNotification: components["schemas"]["PushNotificationOnchainAddressGotPaid"]): Promise<[object, object]> {
static async pushOnchainAddressWasPaid(dataSource: DataSource, serverKey: string, apnsP8: string, pushNotification: components["schemas"]["PushNotificationOnchainAddressGotPaid"]): Promise<void> {
const fcmPayload = {
data: {},
notification: {
title: "+" + pushNotification.sat + " sats",
body: "Received on " + GroundControlToMajorTom.shortenAddress(pushNotification.address),
badge: pushNotification.badge,
tag: pushNotification.txid,
},
message: {
token: '',
data: {
badge: String(pushNotification.badge),
tag: pushNotification.txid,
},
notification: {
title: "+" + pushNotification.sat + " sats",
body: "Received on " + GroundControlToMajorTom.shortenAddress(pushNotification.address),
},
}
};
const apnsPayload = {
@ -170,14 +193,17 @@ export class GroundControlToMajorTom {
if (pushNotification.os === "ios") return GroundControlToMajorTom._pushToApns(dataSource, apnsP8, pushNotification.token, apnsPayload, pushNotification, pushNotification.txid);
}
static async pushLightningInvoicePaid(dataSource: DataSource, serverKey: string, apnsP8: string, pushNotification: components["schemas"]["PushNotificationLightningInvoicePaid"]): Promise<[object, object]> {
static async pushLightningInvoicePaid(dataSource: DataSource, serverKey: string, apnsP8: string, pushNotification: components["schemas"]["PushNotificationLightningInvoicePaid"]): Promise<void> {
const fcmPayload = {
data: {},
notification: {
body: "Paid: " + (pushNotification.memo || "your invoice"),
title: "+" + pushNotification.sat + " sats",
badge: pushNotification.badge,
tag: pushNotification.hash,
message: {
data: {
badge: String(pushNotification.badge),
tag: pushNotification.hash,
},
notification: {
body: "Paid: " + (pushNotification.memo || "your invoice"),
title: "+" + pushNotification.sat + " sats",
},
},
};
@ -197,7 +223,7 @@ export class GroundControlToMajorTom {
if (pushNotification.os === "ios") return GroundControlToMajorTom._pushToApns(dataSource, apnsP8, pushNotification.token, apnsPayload, pushNotification, pushNotification.hash);
}
protected static async _pushToApns(dataSource: DataSource, apnsP8: string, token: string, apnsPayload: object, pushNotification: components["schemas"]["PushNotificationBase"], collapseId): Promise<[object, object]> {
protected static async _pushToApns(dataSource: DataSource, apnsP8: string, token: string, apnsPayload: object, pushNotification: components["schemas"]["PushNotificationBase"], collapseId): Promise<void> {
return new Promise(function (resolve) {
// we pass some of the notification properties as data properties to FCM payload:
for (let dataKey of Object.keys(pushNotification)) {
@ -239,7 +265,7 @@ export class GroundControlToMajorTom {
success: responseJson[":status"] === 200,
});
resolve([apnsPayload, responseJson]);
resolve();
});
request.setEncoding("utf8");
@ -267,48 +293,50 @@ export class GroundControlToMajorTom {
success: responseJson[":status"] === 200,
});
resolve([apnsPayload, responseJson]);
resolve();
});
request.end();
});
}
protected static async _pushToFcm(dataSource: DataSource, serverKey: string, token: string, fcmPayload: object, pushNotification: components["schemas"]["PushNotificationBase"]): Promise<[object, object]> {
fcmPayload["to"] = token;
fcmPayload["priority"] = "high";
protected static async _pushToFcm(dataSource: DataSource, bearer: string, token: string, fcmPayload: object, pushNotification: components["schemas"]["PushNotificationBase"]): Promise<void> {
fcmPayload["message"]["token"] = token;
// now, we pass some of the notification properties as data properties to FCM payload:
for (let dataKey of Object.keys(pushNotification)) {
if (["token", "os", "badge"].includes(dataKey)) continue;
fcmPayload["data"][dataKey] = pushNotification[dataKey];
fcmPayload["message"]["data"][dataKey] = String(pushNotification[dataKey]);
}
// @ts-ignore
const rawResponse = await fetch("https://fcm.googleapis.com/fcm/send", {
const rawResponse = await fetch(`https://fcm.googleapis.com/v1/projects/${process.env.GOOGLE_PROJECT_ID}/messages:send`, {
method: "POST",
headers: {
Authorization: "key=" + serverKey,
Authorization: `Bearer ${bearer}`,
"Content-Type": "application/json",
Host: "fcm.googleapis.com",
},
body: JSON.stringify(Object.assign({}, fcmPayload)),
});
const responseJson = await rawResponse.json();
delete fcmPayload["to"]; // compacting a bit, we dont need token in payload as well
let responseText: string;
try {
responseText = await rawResponse.text();
} catch (error) {
console.error("error getting response from FCM", error);
}
GroundControlToMajorTom.processFcmResponse(dataSource, responseJson, token);
delete fcmPayload["message"]["token"]; // compacting a bit, we dont need token in payload as well
const success = GroundControlToMajorTom.processFcmResponse(dataSource, responseText, token);
const PushLogRepository = dataSource.getRepository(PushLog);
await PushLogRepository.save({
token: token,
os: "android",
payload: JSON.stringify(fcmPayload),
response: JSON.stringify(responseJson),
success: !!responseJson["success"],
response: responseText,
success,
});
return [fcmPayload, responseJson];
}
static async killDeadToken(dataSource: DataSource, token: string) {
@ -318,21 +346,45 @@ export class GroundControlToMajorTom {
await dataSource.getRepository(TokenToHash).createQueryBuilder().delete().where("token = :token", { token }).execute();
}
static processFcmResponse(dataSource: DataSource, response, token: string) {
if (response && response.results && Array.isArray(response.results) && response.results.length === 1) {
if (response.results[0] && response.results[0].error && ["NotRegistered"].includes(response.results[0].error)) {
return GroundControlToMajorTom.killDeadToken(dataSource, token);
static processFcmResponse(dataSource: DataSource, responseText: string, token: string): boolean {
try {
const response = JSON.parse(responseText);
if (response?.error) {
if (response.error.code === 404) {
GroundControlToMajorTom.killDeadToken(dataSource, token);
return false;
}
// additonal check for the same thing, just for any case
if (Array.isArray(response?.error?.details)) {
for (const detail of response.error.details) {
if (detail.errorCode === "UNREGISTERED") {
GroundControlToMajorTom.killDeadToken(dataSource, token);
return false;
}
}
}
}
} else if (response && response.results && Array.isArray(response.results) && response.results.length !== 1) {
console.error("Expected only single result in FCM response, cant handle batch responses yet (zero response also means smth is wrong):", response);
if (response?.name) {
// thats a sign that payload is accepted and might be delivered
return true;
}
} catch (_) {
console.error("error parsing FCM response", responseText);
return false;
}
return false;
}
static processApnsResponse(dataSource: DataSource, response, token: string) {
if (response && response.data) {
try {
console.log('parsing', response.data);
const data = JSON.parse(response.data);
if (data && data.reason && ["Unregistered", "BadDeviceToken"].includes(data.reason)) return GroundControlToMajorTom.killDeadToken(dataSource, token);
if (data && data.reason && ["Unregistered", "BadDeviceToken", "DeviceTokenNotForTopic"].includes(data.reason)) return GroundControlToMajorTom.killDeadToken(dataSource, token);
} catch (_) {}
}
}

View File

@ -11,7 +11,7 @@ import dataSource from "../data-source";
import { paths, components } from "../openapi/api";
require("dotenv").config();
const pck = require("../../package.json");
if (!process.env.JAWSDB_MARIA_URL || !process.env.FCM_SERVER_KEY || !process.env.APNS_P8 || !process.env.APNS_TOPIC || !process.env.APPLE_TEAM_ID || !process.env.APNS_P8_KID) {
if (!process.env.JAWSDB_MARIA_URL || !process.env.GOOGLE_KEY_FILE || !process.env.APNS_P8 || !process.env.APNS_TOPIC || !process.env.APPLE_TEAM_ID || !process.env.APNS_P8_KID || !process.env.GOOGLE_PROJECT_ID) {
console.error("not all env variables set");
process.exit();
}
@ -19,6 +19,9 @@ if (!process.env.JAWSDB_MARIA_URL || !process.env.FCM_SERVER_KEY || !process.env
const LAST_PROCESSED_BLOCK = "LAST_PROCESSED_BLOCK";
const ADDRESS_IGNORE_LIST = [
"1NXNHZr6Pbzi3VStcgaxwEhspTWNXQ3Q4G",
"bc1qee7hk4a3k3km7j8hwclm0pkl76dhhgxay5nevu",
"bc1qclyfsxuu8vcwq38yygs5zrskwacq8sjlyvk9mx",
"bc1qltxjty7xfrnkzlrhmxpcekknr3uncne8sht7rn",
"bc1qwqdg6squsna38e46795at95yu9atm8azzmyvckulcc7kytlcckxswvvzej",
"bc1qw8wrek2m7nlqldll66ajnwr9mh64syvkt67zlu",

View File

@ -7,7 +7,7 @@ import dataSource from "./data-source";
require("dotenv").config();
const helmet = require("helmet");
const cors = require("cors");
if (!process.env.JAWSDB_MARIA_URL || !process.env.FCM_SERVER_KEY || !process.env.APNS_P8 || !process.env.APNS_TOPIC) {
if (!process.env.JAWSDB_MARIA_URL || !process.env.GOOGLE_KEY_FILE || !process.env.APNS_P8 || !process.env.APNS_TOPIC || !process.env.GOOGLE_PROJECT_ID) {
console.error("not all env variables set");
process.exit();
}

View File

@ -6,7 +6,7 @@ import { NOTIFICATION_LEVEL_NEWS, NOTIFICATION_LEVEL_PRICE, NOTIFICATION_LEVEL_T
import dataSource from "./data-source";
import { components } from "./openapi/api";
require("dotenv").config();
if (!process.env.FCM_SERVER_KEY || !process.env.APNS_P8 || !process.env.APNS_TOPIC || !process.env.APPLE_TEAM_ID || !process.env.APNS_P8_KID) {
if (!process.env.GOOGLE_KEY_FILE || !process.env.APNS_P8 || !process.env.APNS_TOPIC || !process.env.APPLE_TEAM_ID || !process.env.APNS_P8_KID || !process.env.GOOGLE_PROJECT_ID) {
console.error("not all env variables set");
process.exit();
}
@ -107,31 +107,31 @@ dataSource
case 2:
payload = <components["schemas"]["PushNotificationOnchainAddressGotPaid"]>payload;
process.env.VERBOSE && console.log("pushing to token", payload.token, payload.os);
await GroundControlToMajorTom.pushOnchainAddressWasPaid(connection, GroundControlToMajorTom.getGoogleServerKey(), GroundControlToMajorTom.getApnsJwtToken(), payload);
await GroundControlToMajorTom.pushOnchainAddressWasPaid(connection, await GroundControlToMajorTom.getGoogleCredentials(), GroundControlToMajorTom.getApnsJwtToken(), payload);
await sendQueueRepository.remove(record);
break;
case 3:
payload = <components["schemas"]["PushNotificationOnchainAddressGotUnconfirmedTransaction"]>payload;
process.env.VERBOSE && console.log("pushing to token", payload.token, payload.os);
await GroundControlToMajorTom.pushOnchainAddressGotUnconfirmedTransaction(connection, GroundControlToMajorTom.getGoogleServerKey(), GroundControlToMajorTom.getApnsJwtToken(), payload);
await GroundControlToMajorTom.pushOnchainAddressGotUnconfirmedTransaction(connection, await GroundControlToMajorTom.getGoogleCredentials(), GroundControlToMajorTom.getApnsJwtToken(), payload);
await sendQueueRepository.remove(record);
break;
case 1:
payload = <components["schemas"]["PushNotificationLightningInvoicePaid"]>payload;
process.env.VERBOSE && console.log("pushing to token", payload.token, payload.os);
await GroundControlToMajorTom.pushLightningInvoicePaid(connection, GroundControlToMajorTom.getGoogleServerKey(), GroundControlToMajorTom.getApnsJwtToken(), payload);
await GroundControlToMajorTom.pushLightningInvoicePaid(connection, await GroundControlToMajorTom.getGoogleCredentials(), GroundControlToMajorTom.getApnsJwtToken(), payload);
await sendQueueRepository.remove(record);
break;
case 4:
payload = <components["schemas"]["PushNotificationTxidGotConfirmed"]>payload;
process.env.VERBOSE && console.log("pushing to token", payload.token, payload.os);
await GroundControlToMajorTom.pushOnchainTxidGotConfirmed(connection, GroundControlToMajorTom.getGoogleServerKey(), GroundControlToMajorTom.getApnsJwtToken(), payload);
await GroundControlToMajorTom.pushOnchainTxidGotConfirmed(connection, await GroundControlToMajorTom.getGoogleCredentials(), GroundControlToMajorTom.getApnsJwtToken(), payload);
await sendQueueRepository.remove(record);
break;
case 5:
payload = <components["schemas"]["PushNotificationMessage"]>payload;
process.env.VERBOSE && console.log("pushing to token", payload.token, payload.os);
await GroundControlToMajorTom.pushMessage(connection, GroundControlToMajorTom.getGoogleServerKey(), GroundControlToMajorTom.getApnsJwtToken(), payload);
await GroundControlToMajorTom.pushMessage(connection, await GroundControlToMajorTom.getGoogleCredentials(), GroundControlToMajorTom.getApnsJwtToken(), payload);
await sendQueueRepository.remove(record);
break;
default:

View File

@ -1,5 +1,6 @@
{
"compilerOptions": {
"skipLibCheck": true,
"lib": ["es5", "es6"],
"target": "es5",
"module": "commonjs",