From f4a3031c5e714ae233809f51fd9e4c4245d48c45 Mon Sep 17 00:00:00 2001 From: Abel Luck Date: Mon, 13 Mar 2023 16:40:04 +0000 Subject: [PATCH] metamigo-api: fix linter errors --- .../src/app/plugins/cloudflare-jwt.ts | 18 +- apps/metamigo-api/src/app/plugins/index.ts | 3 +- .../src/app/plugins/nextauth-jwt.ts | 8 +- .../src/app/routes/whatsapp/index.ts | 1 - .../metamigo-api/src/app/services/settings.ts | 1 - apps/metamigo-api/src/app/services/signald.ts | 16 +- .../metamigo-api/src/app/services/whatsapp.ts | 187 ++++++++++-------- apps/metamigo-api/src/app/types/index.ts | 1 + turbo.json | 54 ++++- 9 files changed, 181 insertions(+), 108 deletions(-) diff --git a/apps/metamigo-api/src/app/plugins/cloudflare-jwt.ts b/apps/metamigo-api/src/app/plugins/cloudflare-jwt.ts index c6644d0..9886376 100644 --- a/apps/metamigo-api/src/app/plugins/cloudflare-jwt.ts +++ b/apps/metamigo-api/src/app/plugins/cloudflare-jwt.ts @@ -9,13 +9,16 @@ import type { IAppConfig } from "../../config"; const CF_JWT_HEADER_NAME = "cf-access-jwt-assertion"; const CF_JWT_ALGOS = ["RS256"]; -const verifyToken = (settings: any) => { +type VerifyFn = (token: string) => Promise; + +const verifyToken = (settings) => { const { audience, issuer } = settings; const client = jwksClient({ jwksUri: `${issuer}/cdn-cgi/access/certs`, }); - return async (token: any) => { + return async (token: string) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any const getKey = (header: any, callback: any) => { client.getSigningKey(header.kid, (err, key) => { if (err) @@ -37,7 +40,8 @@ const verifyToken = (settings: any) => { }; const handleCfJwt = - (verify: any) => async (request: Hapi.Request, h: Hapi.ResponseToolkit) => { + (verify: VerifyFn) => + async (request: Hapi.Request, h: Hapi.ResponseToolkit) => { const token = request.headers[CF_JWT_HEADER_NAME]; if (token) { try { @@ -60,6 +64,7 @@ const defaultOpts = { const cfJwtRegister = async ( server: Hapi.Server, + // eslint-disable-next-line @typescript-eslint/no-explicit-any options: any ): Promise => { server.dependency(["hapi-auth-jwt2"]); @@ -69,7 +74,11 @@ const cfJwtRegister = async ( const { validate, strategyName, audience, issuer } = settings; server.ext("onPreAuth", handleCfJwt(verify)); - server.auth.strategy(strategyName!, "jwt", { + if (!strategyName) { + throw new Error("Missing strategyName for cloudflare-jwt hapi plugin!"); + } + + server.auth.strategy(strategyName, "jwt", { key: hapiJwt2KeyAsync({ jwksUri: `${issuer}/cdn-cgi/access/certs`, }), @@ -102,6 +111,7 @@ export const registerCloudflareAccessJwt = async ( options: { issuer: `https://${domain}`, audience, + // eslint-disable-next-line @typescript-eslint/no-explicit-any validate(decoded: any, _request: any) { const { email, name } = decoded; return { diff --git a/apps/metamigo-api/src/app/plugins/index.ts b/apps/metamigo-api/src/app/plugins/index.ts index ca9891e..053fed4 100644 --- a/apps/metamigo-api/src/app/plugins/index.ts +++ b/apps/metamigo-api/src/app/plugins/index.ts @@ -1,6 +1,5 @@ import type * as Hapi from "@hapi/hapi"; -import { ServerRegisterPluginObjectDirect } from "@hapi/hapi/lib/types/plugin"; -import type { IInitOptions, IDatabase } from "pg-promise"; +import type { IInitOptions } from "pg-promise"; import Schmervice from "@hapipal/schmervice"; import { makePlugin } from "@digiresilience/hapi-pg-promise"; diff --git a/apps/metamigo-api/src/app/plugins/nextauth-jwt.ts b/apps/metamigo-api/src/app/plugins/nextauth-jwt.ts index e2ccaa6..b843b6d 100644 --- a/apps/metamigo-api/src/app/plugins/nextauth-jwt.ts +++ b/apps/metamigo-api/src/app/plugins/nextauth-jwt.ts @@ -22,10 +22,14 @@ const jwtDefaults = { const jwtRegister = async (server: Hapi.Server, options): Promise => { server.dependency(["hapi-auth-jwt2"]); - const settings: any = Hoek.applyToDefaults(jwtDefaults, options); + const settings = Hoek.applyToDefaults(jwtDefaults, options); const key = settings.jwkeysB64.map((k) => jwkToHapiAuthJwt2(k)); - server.auth.strategy(settings.strategyName!, "jwt", { + if (!settings.strategyName) { + throw new Error("Missing strategy name in nextauth-jwt pluginsettings!"); + } + + server.auth.strategy(settings.strategyName, "jwt", { key, cookieKey: false, urlKey: false, diff --git a/apps/metamigo-api/src/app/routes/whatsapp/index.ts b/apps/metamigo-api/src/app/routes/whatsapp/index.ts index ce124bb..6f250f4 100644 --- a/apps/metamigo-api/src/app/routes/whatsapp/index.ts +++ b/apps/metamigo-api/src/app/routes/whatsapp/index.ts @@ -161,7 +161,6 @@ export const UnverifyBotRoute = Helpers.withDefaults({ }, }); - export const RefreshBotRoute = Helpers.withDefaults({ method: "get", path: "/api/whatsapp/bots/{id}/refresh", diff --git a/apps/metamigo-api/src/app/services/settings.ts b/apps/metamigo-api/src/app/services/settings.ts index 151168b..d80e63f 100644 --- a/apps/metamigo-api/src/app/services/settings.ts +++ b/apps/metamigo-api/src/app/services/settings.ts @@ -9,7 +9,6 @@ export const VoicemailUseTextPrompt = settingInfo( ); export { ISettingsService } from "@digiresilience/metamigo-db"; -// @ts-expect-error const service = (server: Hapi.Server): Schmervice.ServiceFunctionalInterface => SettingsService(server.db().settings); diff --git a/apps/metamigo-api/src/app/services/signald.ts b/apps/metamigo-api/src/app/services/signald.ts index 6a05c76..994a03d 100644 --- a/apps/metamigo-api/src/app/services/signald.ts +++ b/apps/metamigo-api/src/app/services/signald.ts @@ -1,6 +1,6 @@ import { Server } from "@hapi/hapi"; import { Service } from "@hapipal/schmervice"; -import { promises as fs } from "fs"; +import { promises as fs } from "node:fs"; import { SignaldAPI, SendResponsev1, @@ -185,7 +185,7 @@ export default class SignaldService extends Service { await this.queueMessage(bot, message); } - private async getAttachmentInfo(dataMessage: any) { + private async getAttachmentInfo(dataMessage: IncomingMessagev1) { if (dataMessage.attachments?.length > 0) { const attachmentInfo = dataMessage.attachments[0]; const buffer = await fs.readFile(attachmentInfo.storedFilename); @@ -193,14 +193,12 @@ export default class SignaldService extends Service { const mimetype = attachmentInfo.contentType ?? "application/octet-stream"; const filename = attachmentInfo.customFilename ?? "unknown-filename"; - - return { attachment, mimetype, filename } + return { attachment, mimetype, filename }; } - return { attachment: null, mimetype: null, filename: null }; + return { attachment: undefined, mimetype: undefined, filename: undefined }; } - private async queueMessage(bot: Bot, message: IncomingMessagev1) { const { timestamp, account, data_message: dataMessage } = message; if (!dataMessage?.body && !dataMessage?.attachments) { @@ -212,7 +210,9 @@ export default class SignaldService extends Service { this.server.logger.debug({ message }, "invalid message received"); } - const { attachment, mimetype, filename } = await this.getAttachmentInfo(dataMessage); + const { attachment, mimetype, filename } = await this.getAttachmentInfo( + dataMessage + ); const receivedMessage = { message, @@ -220,7 +220,7 @@ export default class SignaldService extends Service { botPhoneNumber: bot.phoneNumber, attachment, mimetype, - filename + filename, }; workerUtils.addJob("signald-message", receivedMessage, { diff --git a/apps/metamigo-api/src/app/services/whatsapp.ts b/apps/metamigo-api/src/app/services/whatsapp.ts index 4c3a679..de16414 100644 --- a/apps/metamigo-api/src/app/services/whatsapp.ts +++ b/apps/metamigo-api/src/app/services/whatsapp.ts @@ -3,7 +3,17 @@ import { Server } from "@hapi/hapi"; import { Service } from "@hapipal/schmervice"; import { SavedWhatsappBot as Bot } from "@digiresilience/metamigo-db"; -import makeWASocket, { DisconnectReason, proto, downloadContentFromMessage, MediaType, fetchLatestBaileysVersion, isJidBroadcast, isJidStatusBroadcast, MessageRetryMap, useMultiFileAuthState } from "@adiwajshing/baileys"; +import makeWASocket, { + DisconnectReason, + proto, + downloadContentFromMessage, + MediaType, + fetchLatestBaileysVersion, + isJidBroadcast, + isJidStatusBroadcast, + MessageRetryMap, + useMultiFileAuthState, +} from "@adiwajshing/baileys"; import fs from "fs"; import workerUtils from "../../worker-utils"; @@ -36,14 +46,14 @@ export default class WhatsappService extends Service { } private async sleep(ms: number): Promise { - console.log(`pausing ${ms}`) - return new Promise(resolve => setTimeout(resolve, ms)); + console.log(`pausing ${ms}`); + return new Promise((resolve) => setTimeout(resolve, ms)); } private async resetConnections() { for (const connection of Object.values(this.connections)) { try { - connection.end(null) + connection.end(null); } catch (error) { console.log(error); } @@ -51,65 +61,74 @@ export default class WhatsappService extends Service { this.connections = {}; } - - private async createConnection(bot: Bot, server: Server, options: any, authCompleteCallback?: any) { + private async createConnection( + bot: Bot, + server: Server, + options: any, + authCompleteCallback?: any + ) { const directory = this.getAuthDirectory(bot); const { state, saveCreds } = await useMultiFileAuthState(directory); - const msgRetryCounterMap: MessageRetryMap = {} + const msgRetryCounterMap: MessageRetryMap = {}; const socket = makeWASocket({ ...options, auth: state, msgRetryCounterMap, - shouldIgnoreJid: jid => isJidBroadcast(jid) || isJidStatusBroadcast(jid), + shouldIgnoreJid: (jid) => + isJidBroadcast(jid) || isJidStatusBroadcast(jid), }); let pause = 5000; - socket.ev.process( - async (events) => { - if (events['connection.update']) { - const update = events['connection.update'] - const { connection: connectionState, lastDisconnect, qr, isNewLogin } = update - if (qr) { - console.log('got qr code') - await this.server.db().whatsappBots.updateQR(bot, qr); - } else if (isNewLogin) { - console.log("got new login") - await this.server.db().whatsappBots.updateVerified(bot, true); - } else if (connectionState === 'open') { - console.log('opened connection') - } else if (connectionState === "close") { - console.log('connection closed due to ', lastDisconnect.error) - const disconnectStatusCode = (lastDisconnect?.error as any)?.output?.statusCode + socket.ev.process(async (events) => { + if (events["connection.update"]) { + const update = events["connection.update"]; + const { + connection: connectionState, + lastDisconnect, + qr, + isNewLogin, + } = update; + if (qr) { + console.log("got qr code"); + await this.server.db().whatsappBots.updateQR(bot, qr); + } else if (isNewLogin) { + console.log("got new login"); + await this.server.db().whatsappBots.updateVerified(bot, true); + } else if (connectionState === "open") { + console.log("opened connection"); + } else if (connectionState === "close") { + console.log("connection closed due to ", lastDisconnect.error); + const disconnectStatusCode = (lastDisconnect?.error as any)?.output + ?.statusCode; - if (disconnectStatusCode === DisconnectReason.restartRequired) { - console.log('reconnecting after got new login') - const updatedBot = await this.findById(bot.id); - await this.createConnection(updatedBot, server, options) - authCompleteCallback?.() - } else if (disconnectStatusCode !== DisconnectReason.loggedOut) { - console.log('reconnecting') - await this.sleep(pause) - pause *= 2 - this.createConnection(bot, server, options) - } - } - } - - if (events['creds.update']) { - console.log("creds update") - await saveCreds() - } - - if (events['messages.upsert']) { - console.log("messages upsert") - const upsert = events['messages.upsert'] - const { messages } = upsert - if (messages) { - await this.queueUnreadMessages(bot, messages); + if (disconnectStatusCode === DisconnectReason.restartRequired) { + console.log("reconnecting after got new login"); + const updatedBot = await this.findById(bot.id); + await this.createConnection(updatedBot, server, options); + authCompleteCallback?.(); + } else if (disconnectStatusCode !== DisconnectReason.loggedOut) { + console.log("reconnecting"); + await this.sleep(pause); + pause *= 2; + this.createConnection(bot, server, options); } } } - ) + + if (events["creds.update"]) { + console.log("creds update"); + await saveCreds(); + } + + if (events["messages.upsert"]) { + console.log("messages upsert"); + const upsert = events["messages.upsert"]; + const { messages } = upsert; + if (messages) { + await this.queueUnreadMessages(bot, messages); + } + } + }); this.connections[bot.id] = { socket, msgRetryCounterMap }; } @@ -120,31 +139,31 @@ export default class WhatsappService extends Service { const bots = await this.server.db().whatsappBots.findAll(); for await (const bot of bots) { if (bot.isVerified) { - const { version, isLatest } = await fetchLatestBaileysVersion() - console.log(`using WA v${version.join('.')}, isLatest: ${isLatest}`) + const { version, isLatest } = await fetchLatestBaileysVersion(); + console.log(`using WA v${version.join(".")}, isLatest: ${isLatest}`); - await this.createConnection( - bot, - this.server, - { - browser: WhatsappService.browserDescription, - printQRInTerminal: false, - version - }) + await this.createConnection(bot, this.server, { + browser: WhatsappService.browserDescription, + printQRInTerminal: false, + version, + }); } } } - private async queueMessage(bot: Bot, webMessageInfo: proto.WebMessageInfo) { - const { key: { id, fromMe, remoteJid }, message, messageTimestamp } = webMessageInfo; + private async queueMessage(bot: Bot, webMessageInfo: proto.IWebMessageInfo) { + const { + key: { id, fromMe, remoteJid }, + message, + messageTimestamp, + } = webMessageInfo; if (!fromMe && message && remoteJid !== "status@broadcast") { - const { audioMessage, documentMessage, imageMessage, videoMessage } = message; - const isMediaMessage = audioMessage || - documentMessage || - imageMessage || - videoMessage; + const { audioMessage, documentMessage, imageMessage, videoMessage } = + message; + const isMediaMessage = + audioMessage || documentMessage || imageMessage || videoMessage; - const messageContent = Object.values(message)[0] + const messageContent = Object.values(message)[0]; let messageType: MediaType; let attachment: string; let filename: string; @@ -152,8 +171,7 @@ export default class WhatsappService extends Service { if (isMediaMessage) { if (audioMessage) { messageType = "audio"; - filename = - id + "." + audioMessage.mimetype.split("/").pop(); + filename = id + "." + audioMessage.mimetype.split("/").pop(); mimetype = audioMessage.mimetype; } else if (documentMessage) { messageType = "document"; @@ -161,20 +179,21 @@ export default class WhatsappService extends Service { mimetype = documentMessage.mimetype; } else if (imageMessage) { messageType = "image"; - filename = - id + "." + imageMessage.mimetype.split("/").pop(); + filename = id + "." + imageMessage.mimetype.split("/").pop(); mimetype = imageMessage.mimetype; } else if (videoMessage) { - messageType = "video" - filename = - id + "." + videoMessage.mimetype.split("/").pop(); + messageType = "video"; + filename = id + "." + videoMessage.mimetype.split("/").pop(); mimetype = videoMessage.mimetype; } - const stream = await downloadContentFromMessage(messageContent, messageType) - let buffer = Buffer.from([]) + const stream = await downloadContentFromMessage( + messageContent, + messageType + ); + let buffer = Buffer.from([]); for await (const chunk of stream) { - buffer = Buffer.concat([buffer, chunk]) + buffer = Buffer.concat([buffer, chunk]); } attachment = buffer.toString("base64"); } @@ -198,7 +217,10 @@ export default class WhatsappService extends Service { } } - private async queueUnreadMessages(bot: Bot, messages: proto.IWebMessageInfo[]) { + private async queueUnreadMessages( + bot: Bot, + messages: proto.IWebMessageInfo[] + ) { for await (const message of messages) { await this.queueMessage(bot, message); } @@ -244,7 +266,7 @@ export default class WhatsappService extends Service { } async register(bot: Bot, callback: AuthCompleteCallback): Promise { - const { version } = await fetchLatestBaileysVersion() + const { version } = await fetchLatestBaileysVersion(); await this.createConnection(bot, this.server, { version }, callback); } @@ -265,7 +287,10 @@ export default class WhatsappService extends Service { } } - async receive(bot: Bot, _lastReceivedDate: Date): Promise { + async receive( + bot: Bot, + _lastReceivedDate: Date + ): Promise { const connection = this.connections[bot.id]?.socket; const messages = await connection.loadAllUnreadMessages(); return messages; diff --git a/apps/metamigo-api/src/app/types/index.ts b/apps/metamigo-api/src/app/types/index.ts index 871fcb5..b84005d 100644 --- a/apps/metamigo-api/src/app/types/index.ts +++ b/apps/metamigo-api/src/app/types/index.ts @@ -30,4 +30,5 @@ declare module "@hapipal/schmervice" { interface SchmerviceDecorator { (namespace: "app"): AppServices; } + type ServiceFunctionalInterface = { name: string }; } diff --git a/turbo.json b/turbo.json index d886f5b..590f517 100644 --- a/turbo.json +++ b/turbo.json @@ -6,18 +6,54 @@ "persistent": true }, "build": { - "dependsOn": ["^build"], - "outputs": [".next/**"] + "dependsOn": [ + "^build" + ], + "outputs": [ + ".next/**" + ] }, "test": { - "dependsOn": ["build"], - "inputs": ["src/**/*.tsx", "src/**/*.ts", "test/**/*.ts", "test/**/*.tsx"] + "dependsOn": [ + "build" + ], + "inputs": [ + "src/**/*.tsx", + "src/**/*.ts", + "test/**/*.ts", + "test/**/*.tsx" + ] + }, + "lint": { + "inputs": [ + "src/**/*.tsx", + "src/**/*.ts", + "test/**/*.ts", + "test/**/*.tsx" + ] + }, + "fix:lint": { + "inputs": [ + "src/**/*.tsx", + "src/**/*.ts", + "test/**/*.ts", + "test/**/*.tsx" + ] + }, + "fmt": { + "inputs": [ + "src/**/*.tsx", + "src/**/*.ts", + "test/**/*.ts", + "test/**/*.tsx" + ] }, - "lint": {}, - "fix:lint": {}, - "fmt": {}, "deploy": { - "dependsOn": ["build", "test", "lint"] + "dependsOn": [ + "build", + "test", + "lint" + ] } } -} +} \ No newline at end of file