diff --git a/openapi.yaml b/openapi.yaml index c82f32b..a9c41af 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -305,7 +305,6 @@ components: type: "string" description: txid of the transaction where this address is one of the outputs - PushNotificationOnchainAddressGotUnconfirmedTransaction: allOf: # Combines PushNotificationBase and the inline model - $ref: "#/components/schemas/PushNotificationBase" @@ -355,7 +354,7 @@ components: txid: type: "string" description: txid of the transaction that got confirmed - + PushNotificationMessage: allOf: # Combines PushNotificationBase and the inline model - $ref: "#/components/schemas/PushNotificationBase" @@ -368,4 +367,4 @@ components: enum: [5] text: type: "string" - description: custom text thats displayed on push notification bubble \ No newline at end of file + description: custom text thats displayed on push notification bubble diff --git a/scripts/mass_send.js b/scripts/mass_send.js index f9f023c..5d4e27c 100644 --- a/scripts/mass_send.js +++ b/scripts/mass_send.js @@ -3,14 +3,15 @@ const fs = require("fs"); const text = fs.readFileSync("all_tokens_unique.csv", { encoding: "utf8" }); const lines = text.split("\n"); -console.log('# got', lines.length, 'tokens'); +console.log("# got", lines.length, "tokens"); console.log("INSERT INTO send_queue_2 VALUES "); let fisrt = true; for (const line of lines.slice(0, 1000000)) { const [token, os] = line.split("\t"); - const sql = (fisrt ? '' : ', ') + `(null, '{"type":5,"token":"${token}","os":"${os}","text":"If you are using Lightning, please read our blog post. The service is sunsetting. Balances should be moved to another service"}', null)`; + const sql = + (fisrt ? "" : ", ") + `(null, '{"type":5,"token":"${token}","os":"${os}","text":"If you are using Lightning, please read our blog post. The service is sunsetting. Balances should be moved to another service"}', null)`; console.log(sql); fisrt = false; } diff --git a/src/class/GroundControlToMajorTom.ts b/src/class/GroundControlToMajorTom.ts index 9b91025..ce0f91b 100644 --- a/src/class/GroundControlToMajorTom.ts +++ b/src/class/GroundControlToMajorTom.ts @@ -91,7 +91,12 @@ 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<[object, object]> { const fcmPayload = { data: {}, notification: { @@ -143,7 +148,12 @@ 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<[object, object]> { const fcmPayload = { data: {}, notification: { diff --git a/src/openapi/api.ts b/src/openapi/api.ts index 48bb285..147be89 100644 --- a/src/openapi/api.ts +++ b/src/openapi/api.ts @@ -4,413 +4,244 @@ */ export interface paths { - "/lightningInvoiceGotSettled": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - post: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["LightningInvoiceSettledNotification"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; + "/lightningInvoiceGotSettled": { + post: { + responses: { + /** OK */ + 200: unknown; + }; + requestBody: { + content: { + "application/json": components["schemas"]["LightningInvoiceSettledNotification"]; + }; + }; }; - "/majorTomToGroundControl": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** Associate bitcoin addressess / ln preimage hashes / txids that you wish to be notified about to specific push token. Token serves as unique identifier of a device/user. Also, OS of the token */ - post: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - addresses?: string[]; - hashes?: string[]; - txids?: string[]; - token?: string; - os?: string; - }; - }; - }; - responses: { - /** @description Created */ - 201: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; + }; + "/majorTomToGroundControl": { + post: { + responses: { + /** Created */ + 201: unknown; + }; + requestBody: { + content: { + "application/json": { + addresses?: string[]; + hashes?: string[]; + txids?: string[]; + token?: string; + os?: string; + }; + }; + }; }; - "/unsubscribe": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** This call is a reverse call for /majorTomToGroundControl. In case user doesnt want to track addresses/txids/ln invoice hashes, he makes this call along with his token/os so GroundControl can remove them from the database */ - post: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - addresses?: string[]; - hashes?: string[]; - txids?: string[]; - token?: string; - os?: string; - }; - }; - }; - responses: { - /** @description Created */ - 201: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; + }; + "/unsubscribe": { + post: { + responses: { + /** Created */ + 201: unknown; + }; + requestBody: { + content: { + "application/json": { + addresses?: string[]; + hashes?: string[]; + txids?: string[]; + token?: string; + os?: string; + }; + }; + }; }; - "/ping": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["ServerInfo"]; - }; - }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; + }; + "/ping": { + get: { + responses: { + /** OK */ + 200: { + content: { + "application/json": components["schemas"]["ServerInfo"]; + }; + }; + }; }; - "/getTokenConfiguration": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** Returns levels of notifications user currently subscribed to */ - post: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": { - token?: string; - os?: string; - }; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["TokenConfiguration"]; - }; - }; - }; - }; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; + }; + "/getTokenConfiguration": { + post: { + responses: { + /** OK */ + 200: { + content: { + "application/json": components["schemas"]["TokenConfiguration"]; + }; + }; + }; + requestBody: { + content: { + "application/json": { + token?: string; + os?: string; + }; + }; + }; }; - "/setTokenConfiguration": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** Sets levels of notifications user is subscribed to; also saves some user info, like lang or app version */ - post: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["TokenConfiguration"] & { - token: string; - os: string; - }; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; + }; + "/setTokenConfiguration": { + post: { + responses: { + /** OK */ + 200: unknown; + }; + requestBody: { + content: { + "application/json": components["schemas"]["TokenConfiguration"] & { + token: string; + os: string; + }; }; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; + }; }; - "/enqueue": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - /** Puts push in send queue. Push body should be any descendant of `/components/schemas/PushNotificationBase` */ - post: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["PushNotificationLightningInvoicePaid"] | components["schemas"]["PushNotificationOnchainAddressGotPaid"] | components["schemas"]["PushNotificationOnchainAddressGotUnconfirmedTransaction"] | components["schemas"]["PushNotificationTxidGotConfirmed"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; - }; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; + }; + "/enqueue": { + post: { + responses: { + /** OK */ + 200: unknown; + }; + requestBody: { + content: { + "application/json": + | components["schemas"]["PushNotificationLightningInvoicePaid"] + | components["schemas"]["PushNotificationOnchainAddressGotPaid"] + | components["schemas"]["PushNotificationOnchainAddressGotUnconfirmedTransaction"] + | components["schemas"]["PushNotificationTxidGotConfirmed"]; + }; + }; }; + }; } -export type webhooks = Record; + export interface components { - schemas: { - ServerInfo: { - name?: string; - description?: string; - version?: string; - uptime?: number; - last_processed_block?: number; - send_queue_size?: number; - sent_24h?: number; - }; - /** @enum {string} */ - NotificationLevel: "transactions" | "news" | "price" | "tips"; - TokenConfiguration: { - level_all?: boolean; - level_transactions?: boolean; - level_news?: boolean; - level_price?: boolean; - level_tips?: boolean; - lang?: string; - app_version?: string; - }; - /** @description object thats posted to GroundControl to notify end-user that his specific invoice was paid by someone */ - LightningInvoiceSettledNotification: { - /** @description text that was embedded in invoice paid */ - memo?: string; - /** @description hex string preimage */ - preimage?: string; - /** @description hex string preimage hash */ - hash?: string; - /** @description exactly how much satoshis was paid to make this invoice settked (>= invoice amount) */ - amt_paid_sat?: number; - }; - /** @description payload for push notification delivered to phone */ - PushNotificationBase: { - /** - * @description type: - * * `1` - Your lightning invoice was paid - * * `2` - New transaction to one of your addresses - * * `3` - New unconfirmed transaction to one of your addresses - * * `4` - Transaction confirmed - * * `5` - Arbitrary text message - * - * @enum {integer} - */ - type: 1 | 2 | 3 | 4 | 5; - token: string; - /** @enum {string} */ - os: "android" | "ios"; - badge?: number; - level: components["schemas"]["NotificationLevel"]; - }; - PushNotificationLightningInvoicePaid: components["schemas"]["PushNotificationBase"] & { - /** @enum {integer} */ - type?: 1; - /** @enum {string} */ - level?: "transactions"; - /** @description amount of satoshis */ - sat: number; - /** @description hash of specific ln invoice preimage */ - hash: string; - /** @description text attached to bolt11 */ - memo: string; - }; - PushNotificationOnchainAddressGotPaid: components["schemas"]["PushNotificationBase"] & { - /** @enum {integer} */ - type?: 2; - /** @enum {string} */ - level?: "transactions"; - /** - * @description Only included if type is 2, 3, or 4 - * @default TRANSACTION_CATEGORY - */ - category: string; - /** @description amount of satoshis */ - sat: number; - /** @description user's onchain address that has incoming transaction */ - address: string; - /** @description txid of the transaction where this address is one of the outputs */ - txid: string; - }; - PushNotificationOnchainAddressGotUnconfirmedTransaction: components["schemas"]["PushNotificationBase"] & { - /** @enum {integer} */ - type?: 3; - /** @enum {string} */ - level?: "transactions"; - /** - * @description Only included if type is 2, 3, or 4 - * @default TRANSACTION_CATEGORY - */ - category: string; - /** @description amount of satoshis */ - sat: number; - /** @description user's onchain address that has incoming transaction */ - address: string; - /** @description txid of the transaction where this address is one of the outputs */ - txid: string; - }; - PushNotificationTxidGotConfirmed: components["schemas"]["PushNotificationBase"] & { - /** @enum {integer} */ - type?: 4; - /** @enum {string} */ - level?: "transactions"; - /** - * @description Only included if type is 2, 3, or 4 - * @default TRANSACTION_CATEGORY - */ - category: string; - /** @description txid of the transaction that got confirmed */ - txid: string; - }; - PushNotificationMessage: components["schemas"]["PushNotificationBase"] & { - /** @enum {integer} */ - type?: 5; - /** @description custom text thats displayed on push notification bubble */ - text: string; - }; + schemas: { + ServerInfo: { + name?: string; + description?: string; + version?: string; + uptime?: number; + last_processed_block?: number; + send_queue_size?: number; + sent_24h?: number; + }; + /** @enum {string} */ + NotificationLevel: "transactions" | "news" | "price" | "tips"; + TokenConfiguration: { + level_all?: boolean; + level_transactions?: boolean; + level_news?: boolean; + level_price?: boolean; + level_tips?: boolean; + lang?: string; + app_version?: string; + }; + /** @description object thats posted to GroundControl to notify end-user that his specific invoice was paid by someone */ + LightningInvoiceSettledNotification: { + /** @description text that was embedded in invoice paid */ + memo?: string; + /** @description hex string preimage */ + preimage?: string; + /** @description hex string preimage hash */ + hash?: string; + /** @description exactly how much satoshis was paid to make this invoice settked (>= invoice amount) */ + amt_paid_sat?: number; }; - responses: never; - parameters: never; - requestBodies: never; - headers: never; - pathItems: never; + /** @description payload for push notification delivered to phone */ + PushNotificationBase: { + /** + * @description type: + * * `1` - Your lightning invoice was paid + * * `2` - New transaction to one of your addresses + * * `3` - New unconfirmed transaction to one of your addresses + * * `4` - Transaction confirmed + * * `5` - Arbitrary text message + * + * @enum {integer} + */ + type: 1 | 2 | 3 | 4 | 5; + token: string; + /** @enum {string} */ + os: "android" | "ios"; + badge?: number; + level: components["schemas"]["NotificationLevel"]; + }; + PushNotificationLightningInvoicePaid: components["schemas"]["PushNotificationBase"] & { + /** @enum {integer} */ + type?: 1; + /** @enum {string} */ + level?: "transactions"; + /** @description amount of satoshis */ + sat: number; + /** @description hash of specific ln invoice preimage */ + hash: string; + /** @description text attached to bolt11 */ + memo: string; + }; + PushNotificationOnchainAddressGotPaid: components["schemas"]["PushNotificationBase"] & { + /** @enum {integer} */ + type?: 2; + /** @enum {string} */ + level?: "transactions"; + /** + * @description Only included if type is 2, 3, or 4 + * @default TRANSACTION_CATEGORY + */ + category?: string; + /** @description amount of satoshis */ + sat: number; + /** @description user's onchain address that has incoming transaction */ + address: string; + /** @description txid of the transaction where this address is one of the outputs */ + txid: string; + }; + PushNotificationOnchainAddressGotUnconfirmedTransaction: components["schemas"]["PushNotificationBase"] & { + /** @enum {integer} */ + type?: 3; + /** @enum {string} */ + level?: "transactions"; + /** + * @description Only included if type is 2, 3, or 4 + * @default TRANSACTION_CATEGORY + */ + category?: string; + /** @description amount of satoshis */ + sat: number; + /** @description user's onchain address that has incoming transaction */ + address: string; + /** @description txid of the transaction where this address is one of the outputs */ + txid: string; + }; + PushNotificationTxidGotConfirmed: components["schemas"]["PushNotificationBase"] & { + /** @enum {integer} */ + type?: 4; + /** @enum {string} */ + level?: "transactions"; + /** + * @description Only included if type is 2, 3, or 4 + * @default TRANSACTION_CATEGORY + */ + category?: string; + /** @description txid of the transaction that got confirmed */ + txid: string; + }; + PushNotificationMessage: components["schemas"]["PushNotificationBase"] & { + /** @enum {integer} */ + type?: 5; + /** @description custom text thats displayed on push notification bubble */ + text: string; + }; + }; } -export type $defs = Record; -export type operations = Record; + +export interface operations {} + +export interface external {} diff --git a/src/openapi/constants.ts b/src/openapi/constants.ts index 7797c35..c78c261 100644 --- a/src/openapi/constants.ts +++ b/src/openapi/constants.ts @@ -3,4 +3,4 @@ import { components } from "./api"; export const NOTIFICATION_LEVEL_TRANSACTIONS: components["schemas"]["NotificationLevel"] = "transactions"; export const NOTIFICATION_LEVEL_NEWS: components["schemas"]["NotificationLevel"] = "news"; export const NOTIFICATION_LEVEL_PRICE: components["schemas"]["NotificationLevel"] = "price"; -export const NOTIFICATION_LEVEL_TIPS: components["schemas"]["NotificationLevel"] = "tips"; \ No newline at end of file +export const NOTIFICATION_LEVEL_TIPS: components["schemas"]["NotificationLevel"] = "tips"; diff --git a/src/worker-blockprocessor.ts b/src/worker-blockprocessor.ts index 010d1c0..d6b8cc1 100644 --- a/src/worker-blockprocessor.ts +++ b/src/worker-blockprocessor.ts @@ -89,12 +89,7 @@ async function processBlock(blockNum, sendQueueRepository: Repository } // batch insert via a raw query as its faster - await sendQueueRepository - .createQueryBuilder() - .insert() - .into(SendQueue) - .values(entities2save) - .execute(); + await sendQueueRepository.createQueryBuilder().insert().into(SendQueue).values(entities2save).execute(); // now, checking if there is a subscription to one of the mined txids: const query2 = dataSource.getRepository(TokenToTxid).createQueryBuilder().where("txid IN (:...txids)", { txids }); @@ -116,14 +111,8 @@ async function processBlock(blockNum, sendQueueRepository: Repository }); } - // batch insert via a raw query as its faster - await sendQueueRepository - .createQueryBuilder() - .insert() - .into(SendQueue) - .values(entities2save) - .execute(); + await sendQueueRepository.createQueryBuilder().insert().into(SendQueue).values(entities2save).execute(); } dataSource diff --git a/src/worker-processmempool.ts b/src/worker-processmempool.ts index a9b662c..8c26f87 100644 --- a/src/worker-processmempool.ts +++ b/src/worker-processmempool.ts @@ -131,7 +131,7 @@ dataSource try { await processMempool(); } catch (error) { - console.warn('Exception in processMempool():', error); + console.warn("Exception in processMempool():", error); } const end = +new Date(); process.env.VERBOSE && console.log("processing mempool took", (end - start) / 1000, "sec"); @@ -142,4 +142,4 @@ dataSource .catch((error) => { console.error("exception in mempool processor:", error, "committing suicide"); process.exit(1); - }); \ No newline at end of file + }); diff --git a/src/worker-sender.ts b/src/worker-sender.ts index 663c83d..843772e 100644 --- a/src/worker-sender.ts +++ b/src/worker-sender.ts @@ -34,10 +34,11 @@ dataSource while (1) { // getting random record so multiple workers wont fight for the same record to send - const [record] = await sendQueueRepository .createQueryBuilder() - .orderBy('RAND()') // mysql-specific - .limit(1) - .getMany(); + const [record] = await sendQueueRepository + .createQueryBuilder() + .orderBy("RAND()") // mysql-specific + .limit(1) + .getMany(); // ^^^ 'order by rand' is suboptimal, but will have to do for now, especially if we are aiming to keep // queue table near-empty @@ -51,7 +52,7 @@ dataSource const query = `SELECT GET_LOCK(?, ?) as result`; const result = await sendQueueRepository.query(query, [`send${record.id}`, 0]); if (result[0].result !== 1) { - process.env.VERBOSE && console.log('could not acquire lock, skipping record'); + process.env.VERBOSE && console.log("could not acquire lock, skipping record"); continue; }