diff --git a/.prettierrc.json b/.prettierrc.json index 91a3517..a16ffc6 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,3 +1,3 @@ { - "printWidth": 160 + "printWidth": 230 } diff --git a/README.md b/README.md index b0fcfb8..8d8a13f 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ ## Can you hear me, Major Tom? Push notifications server for bitcoin wallets. Processes blocks & mempool in search of subscribed onchain addresses. -Built with typescript, expressjs, mariadb & openapi. +Built with typescript, expressjs, mariadb & [openapi](https://editor.swagger.io/?url=https://raw.githubusercontent.com/BlueWallet/GroundControl/master/openapi.yaml). In memory of David Bowie @@ -92,6 +92,7 @@ Set them as env variables or put them into `.env` file in project root dir. - `FCM_SERVER_KEY` hex encoded - `APNS_PEM` hex encoded - `BITCOIN_RPC` for example `http://username:password@host:8332` +- `APNS_TOPIC` for example `io.bluewallet.bluewallet` ### License diff --git a/openapi.yaml b/openapi.yaml index b18f1d5..269613f 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -5,6 +5,8 @@ info: version: 0.0.8 servers: - url: http://localhost:3001 + - url: https://groundcontrol-bluewallet-stg.herokuapp.com + - url: https://groundcontrol-bluewallet.herokuapp.com paths: /lightningInvoiceGotSettled: post: diff --git a/src/class/GroundControlToMajorTom.ts b/src/class/GroundControlToMajorTom.ts index 6faa786..8907c11 100644 --- a/src/class/GroundControlToMajorTom.ts +++ b/src/class/GroundControlToMajorTom.ts @@ -17,11 +17,7 @@ const http2 = require("http2"); * @see https://firebase.google.com/docs/cloud-messaging/http-server-ref */ export class GroundControlToMajorTom { - static async pushOnchainAddressGotUnconfirmedTransaction( - serverKey: string, - apnsPem: string, - pushNotification: Components.Schemas.PushNotificationOnchainAddressGotUnconfirmedTransaction - ): Promise<[object, object]> { + static async pushOnchainAddressGotUnconfirmedTransaction(serverKey: string, apnsPem: string, pushNotification: Components.Schemas.PushNotificationOnchainAddressGotUnconfirmedTransaction): Promise<[object, object]> { const fcmPayload = { data: {}, notification: { @@ -45,15 +41,10 @@ export class GroundControlToMajorTom { }; if (pushNotification.os === "android") return GroundControlToMajorTom._pushToFcm(serverKey, pushNotification.token, fcmPayload, pushNotification); - if (pushNotification.os === "ios") - return GroundControlToMajorTom._pushToApns(apnsPem, pushNotification.token, apnsPayload, pushNotification, pushNotification.txid); + if (pushNotification.os === "ios") return GroundControlToMajorTom._pushToApns(apnsPem, pushNotification.token, apnsPayload, pushNotification, pushNotification.txid); } - static async pushOnchainTxidGotConfirmed( - serverKey: string, - apnsPem: string, - pushNotification: Components.Schemas.PushNotificationTxidGotConfirmed - ): Promise<[object, object]> { + static async pushOnchainTxidGotConfirmed(serverKey: string, apnsPem: string, pushNotification: Components.Schemas.PushNotificationTxidGotConfirmed): Promise<[object, object]> { const fcmPayload = { data: {}, notification: { @@ -77,15 +68,10 @@ export class GroundControlToMajorTom { }; if (pushNotification.os === "android") return GroundControlToMajorTom._pushToFcm(serverKey, pushNotification.token, fcmPayload, pushNotification); - if (pushNotification.os === "ios") - return GroundControlToMajorTom._pushToApns(apnsPem, pushNotification.token, apnsPayload, pushNotification, pushNotification.txid); + if (pushNotification.os === "ios") return GroundControlToMajorTom._pushToApns(apnsPem, pushNotification.token, apnsPayload, pushNotification, pushNotification.txid); } - static async pushOnchainAddressWasPaid( - serverKey: string, - apnsPem: string, - pushNotification: Components.Schemas.PushNotificationOnchainAddressGotPaid - ): Promise<[object, object]> { + static async pushOnchainAddressWasPaid(serverKey: string, apnsPem: string, pushNotification: Components.Schemas.PushNotificationOnchainAddressGotPaid): Promise<[object, object]> { const fcmPayload = { data: {}, notification: { @@ -109,15 +95,10 @@ export class GroundControlToMajorTom { }; if (pushNotification.os === "android") return GroundControlToMajorTom._pushToFcm(serverKey, pushNotification.token, fcmPayload, pushNotification); - if (pushNotification.os === "ios") - return GroundControlToMajorTom._pushToApns(apnsPem, pushNotification.token, apnsPayload, pushNotification, pushNotification.txid); + if (pushNotification.os === "ios") return GroundControlToMajorTom._pushToApns(apnsPem, pushNotification.token, apnsPayload, pushNotification, pushNotification.txid); } - static async pushLightningInvoicePaid( - serverKey: string, - apnsPem: string, - pushNotification: Components.Schemas.PushNotificationLightningInvoicePaid - ): Promise<[object, object]> { + static async pushLightningInvoicePaid(serverKey: string, apnsPem: string, pushNotification: Components.Schemas.PushNotificationLightningInvoicePaid): Promise<[object, object]> { const fcmPayload = { data: {}, notification: { @@ -141,18 +122,12 @@ export class GroundControlToMajorTom { }; if (pushNotification.os === "android") return GroundControlToMajorTom._pushToFcm(serverKey, pushNotification.token, fcmPayload, pushNotification); - if (pushNotification.os === "ios") - return GroundControlToMajorTom._pushToApns(apnsPem, pushNotification.token, apnsPayload, pushNotification, pushNotification.hash); + if (pushNotification.os === "ios") return GroundControlToMajorTom._pushToApns(apnsPem, pushNotification.token, apnsPayload, pushNotification, pushNotification.hash); } - protected static async _pushToApns( - apnsPem: string, - token: string, - apnsPayload: object, - pushNotification: Components.Schemas.PushNotificationBase, - collapseId - ): Promise<[object, object]> { + protected static async _pushToApns(apnsPem: string, token: string, apnsPayload: object, pushNotification: Components.Schemas.PushNotificationBase, collapseId): Promise<[object, object]> { 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)) { if (["token", "os", "badge"].includes(dataKey)) continue; apnsPayload["data"][dataKey] = pushNotification[dataKey]; @@ -165,7 +140,7 @@ export class GroundControlToMajorTom { client.on("error", (err) => console.error(err)); const headers = { ":method": "POST", - "apns-topic": "io.bluewallet.bluewallet", + "apns-topic": process.env.APNS_TOPIC, "apns-collapse-id": collapseId, "apns-expiration": Math.floor(+new Date() / 1000 + 3600 * 24), ":scheme": "https", @@ -206,12 +181,7 @@ export class GroundControlToMajorTom { }); } - protected static async _pushToFcm( - serverKey: string, - token: string, - fcmPayload: object, - pushNotification: Components.Schemas.PushNotificationBase - ): Promise<[object, object]> { + protected static async _pushToFcm(serverKey: string, token: string, fcmPayload: object, pushNotification: Components.Schemas.PushNotificationBase): Promise<[object, object]> { const _api = new Frisbee({ baseURI: "https://fcm.googleapis.com" }); fcmPayload["to"] = token; diff --git a/src/index.ts b/src/index.ts index c6c97a3..2f656ea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,6 +8,10 @@ require("dotenv").config(); const cors = require("cors"); const url = require("url"); const parsed = url.parse(process.env.JAWSDB_MARIA_URL); +if (!process.env.JAWSDB_MARIA_URL || !process.env.FCM_SERVER_KEY || !process.env.APNS_PEM || !process.env.APNS_TOPIC) { + console.error("not all env variables set"); + process.exit(); +} createConnection({ type: "mariadb",