Standardize bridge send/receive params
This commit is contained in:
parent
69abe9bee1
commit
c32c26088f
23 changed files with 7042 additions and 1276 deletions
|
|
@ -18,12 +18,12 @@
|
||||||
"@emotion/react": "^11.11.4",
|
"@emotion/react": "^11.11.4",
|
||||||
"@emotion/styled": "^11.11.5",
|
"@emotion/styled": "^11.11.5",
|
||||||
"@mui/icons-material": "^5",
|
"@mui/icons-material": "^5",
|
||||||
"@mui/lab": "^5.0.0-alpha.170",
|
"@mui/lab": "^5.0.0-alpha.172",
|
||||||
"@mui/material": "^5",
|
"@mui/material": "^5",
|
||||||
"@mui/material-nextjs": "^5.15.11",
|
"@mui/material-nextjs": "^5.16.4",
|
||||||
"@mui/x-data-grid-pro": "^7.8.0",
|
"@mui/x-data-grid-pro": "^7.10.0",
|
||||||
"@mui/x-date-pickers-pro": "^7.8.0",
|
"@mui/x-date-pickers-pro": "^7.10.0",
|
||||||
"@mui/x-license": "^7.8.0",
|
"@mui/x-license": "^7.10.0",
|
||||||
"@link-stack/bridge-common": "*",
|
"@link-stack/bridge-common": "*",
|
||||||
"@link-stack/bridge-ui": "*",
|
"@link-stack/bridge-ui": "*",
|
||||||
"@link-stack/signal-api": "*",
|
"@link-stack/signal-api": "*",
|
||||||
|
|
@ -32,8 +32,8 @@
|
||||||
"graphile-worker": "^0.16.6",
|
"graphile-worker": "^0.16.6",
|
||||||
"kysely": "0.26.1",
|
"kysely": "0.26.1",
|
||||||
"material-ui-popup-state": "^5.1.2",
|
"material-ui-popup-state": "^5.1.2",
|
||||||
"mui-chips-input": "^2.1.4",
|
"mui-chips-input": "^2.1.5",
|
||||||
"next": "14.2.4",
|
"next": "14.2.5",
|
||||||
"next-auth": "^4.24.7",
|
"next-auth": "^4.24.7",
|
||||||
"pg": "^8.12.0",
|
"pg": "^8.12.0",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
|
|
|
||||||
|
|
@ -95,10 +95,6 @@ export default class WhatsappService extends Service {
|
||||||
const botDirectory = this.getBotDirectory(botID);
|
const botDirectory = this.getBotDirectory(botID);
|
||||||
const qrPath = `${botDirectory}/qr.txt`;
|
const qrPath = `${botDirectory}/qr.txt`;
|
||||||
fs.writeFileSync(qrPath, qr, "utf8");
|
fs.writeFileSync(qrPath, qr, "utf8");
|
||||||
const verifiedFile = `${botDirectory}/verified`;
|
|
||||||
if (fs.existsSync(verifiedFile)) {
|
|
||||||
fs.rmSync(verifiedFile);
|
|
||||||
}
|
|
||||||
} else if (isNewLogin) {
|
} else if (isNewLogin) {
|
||||||
console.log("got new login");
|
console.log("got new login");
|
||||||
const botDirectory = this.getBotDirectory(botID);
|
const botDirectory = this.getBotDirectory(botID);
|
||||||
|
|
@ -110,7 +106,6 @@ export default class WhatsappService extends Service {
|
||||||
console.log("connection closed due to ", lastDisconnect?.error);
|
console.log("connection closed due to ", lastDisconnect?.error);
|
||||||
const disconnectStatusCode = (lastDisconnect?.error as any)?.output
|
const disconnectStatusCode = (lastDisconnect?.error as any)?.output
|
||||||
?.statusCode;
|
?.statusCode;
|
||||||
|
|
||||||
if (disconnectStatusCode === DisconnectReason.restartRequired) {
|
if (disconnectStatusCode === DisconnectReason.restartRequired) {
|
||||||
console.log("reconnecting after got new login");
|
console.log("reconnecting after got new login");
|
||||||
await this.createConnection(botID, server, options);
|
await this.createConnection(botID, server, options);
|
||||||
|
|
@ -181,26 +176,26 @@ export default class WhatsappService extends Service {
|
||||||
|
|
||||||
const messageContent = Object.values(message)[0];
|
const messageContent = Object.values(message)[0];
|
||||||
let messageType: MediaType;
|
let messageType: MediaType;
|
||||||
let attachment: string;
|
let attachment: string | null | undefined;
|
||||||
let filename: string | null | undefined;
|
let filename: string | null | undefined;
|
||||||
let mimetype: string | null | undefined;
|
let mimeType: string | null | undefined;
|
||||||
if (isMediaMessage) {
|
if (isMediaMessage) {
|
||||||
if (audioMessage) {
|
if (audioMessage) {
|
||||||
messageType = "audio";
|
messageType = "audio";
|
||||||
filename = id + "." + audioMessage.mimetype?.split("/").pop();
|
filename = id + "." + audioMessage.mimetype?.split("/").pop();
|
||||||
mimetype = audioMessage.mimetype;
|
mimeType = audioMessage.mimetype;
|
||||||
} else if (documentMessage) {
|
} else if (documentMessage) {
|
||||||
messageType = "document";
|
messageType = "document";
|
||||||
filename = documentMessage.fileName;
|
filename = documentMessage.fileName;
|
||||||
mimetype = documentMessage.mimetype;
|
mimeType = documentMessage.mimetype;
|
||||||
} else if (imageMessage) {
|
} else if (imageMessage) {
|
||||||
messageType = "image";
|
messageType = "image";
|
||||||
filename = id + "." + imageMessage.mimetype?.split("/").pop();
|
filename = id + "." + imageMessage.mimetype?.split("/").pop();
|
||||||
mimetype = imageMessage.mimetype;
|
mimeType = imageMessage.mimetype;
|
||||||
} else if (videoMessage) {
|
} else if (videoMessage) {
|
||||||
messageType = "video";
|
messageType = "video";
|
||||||
filename = id + "." + videoMessage.mimetype?.split("/").pop();
|
filename = id + "." + videoMessage.mimetype?.split("/").pop();
|
||||||
mimetype = videoMessage.mimetype;
|
mimeType = videoMessage.mimetype;
|
||||||
}
|
}
|
||||||
|
|
||||||
const stream = await downloadContentFromMessage(
|
const stream = await downloadContentFromMessage(
|
||||||
|
|
@ -217,17 +212,6 @@ export default class WhatsappService extends Service {
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (messageContent || attachment) {
|
if (messageContent || attachment) {
|
||||||
const receivedMessage = {
|
|
||||||
waMessageId: id,
|
|
||||||
waMessage: JSON.stringify(webMessageInfo),
|
|
||||||
waTimestamp: new Date((messageTimestamp as number) * 1000),
|
|
||||||
// @ts-ignore
|
|
||||||
attachment,
|
|
||||||
filename,
|
|
||||||
mimetype,
|
|
||||||
whatsappBotId: botID,
|
|
||||||
};
|
|
||||||
|
|
||||||
const message =
|
const message =
|
||||||
webMessageInfo?.message?.conversation ??
|
webMessageInfo?.message?.conversation ??
|
||||||
webMessageInfo?.message?.extendedTextMessage?.text ??
|
webMessageInfo?.message?.extendedTextMessage?.text ??
|
||||||
|
|
@ -235,8 +219,14 @@ export default class WhatsappService extends Service {
|
||||||
webMessageInfo?.message?.videoMessage?.caption;
|
webMessageInfo?.message?.videoMessage?.caption;
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
|
to: botID,
|
||||||
|
from: webMessageInfo.key.remoteJid?.split("@")[0],
|
||||||
|
messageId: id,
|
||||||
|
sentAt: new Date((messageTimestamp as number) * 1000).toISOString(),
|
||||||
message,
|
message,
|
||||||
sender: webMessageInfo.key.remoteJid?.split("@")[0],
|
attachment,
|
||||||
|
filename,
|
||||||
|
mimeType,
|
||||||
};
|
};
|
||||||
|
|
||||||
await fetch(
|
await fetch(
|
||||||
|
|
|
||||||
|
|
@ -20,20 +20,20 @@
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"kysely": "^0.27.3",
|
"kysely": "^0.27.3",
|
||||||
"pg": "^8.12.0",
|
"pg": "^8.12.0",
|
||||||
"remeda": "^2.2.2",
|
"remeda": "^2.6.0",
|
||||||
"twilio": "^5.2.2"
|
"twilio": "^5.2.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.24.7",
|
"@babel/core": "7.24.9",
|
||||||
"@babel/preset-env": "7.24.7",
|
"@babel/preset-env": "7.24.8",
|
||||||
"@babel/preset-typescript": "7.24.7",
|
"@babel/preset-typescript": "7.24.7",
|
||||||
"@types/fluent-ffmpeg": "^2.1.24",
|
"@types/fluent-ffmpeg": "^2.1.24",
|
||||||
"dotenv-cli": "^7.4.2",
|
"dotenv-cli": "^7.4.2",
|
||||||
"@link-stack/eslint-config": "*",
|
"@link-stack/eslint-config": "*",
|
||||||
"prettier": "^3.3.2",
|
"prettier": "^3.3.3",
|
||||||
"@link-stack/typescript-config": "*",
|
"@link-stack/typescript-config": "*",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typedoc": "^0.26.3",
|
"typedoc": "^0.26.4",
|
||||||
"typescript": "^5.5.3"
|
"typescript": "^5.5.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,11 @@ const receiveFacebookMessageTask = async ({
|
||||||
.executeTakeFirstOrThrow();
|
.executeTakeFirstOrThrow();
|
||||||
const backendId = row.id;
|
const backendId = row.id;
|
||||||
const payload = {
|
const payload = {
|
||||||
text: messaging.message.text,
|
to: pageId,
|
||||||
recipient: messaging.sender.id,
|
from: messaging.sender.id,
|
||||||
|
sent_at: new Date(messaging.timestamp).toISOString(),
|
||||||
|
message: messaging.message.text,
|
||||||
|
message_id: messaging.message.mid,
|
||||||
};
|
};
|
||||||
|
|
||||||
await worker.addJob("common/notify-webhooks", { backendId, payload });
|
await worker.addJob("common/notify-webhooks", { backendId, payload });
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@ import { db } from "@link-stack/bridge-common";
|
||||||
|
|
||||||
interface SendFacebookMessageTaskOptions {
|
interface SendFacebookMessageTaskOptions {
|
||||||
token: string;
|
token: string;
|
||||||
recipient: string;
|
to: string;
|
||||||
text: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sendFacebookMessageTask = async (
|
const sendFacebookMessageTask = async (
|
||||||
options: SendFacebookMessageTaskOptions,
|
options: SendFacebookMessageTaskOptions,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const { token, text, recipient } = options;
|
const { token, to, message } = options;
|
||||||
const { pageId, pageAccessToken } = await db
|
const { pageId, pageAccessToken } = await db
|
||||||
.selectFrom("FacebookBot")
|
.selectFrom("FacebookBot")
|
||||||
.selectAll()
|
.selectAll()
|
||||||
|
|
@ -19,17 +19,23 @@ const sendFacebookMessageTask = async (
|
||||||
const endpoint = `https://graph.facebook.com/v19.0/${pageId}/messages`;
|
const endpoint = `https://graph.facebook.com/v19.0/${pageId}/messages`;
|
||||||
|
|
||||||
const outgoingMessage = {
|
const outgoingMessage = {
|
||||||
recipient: { id: recipient },
|
recipient: { id: to },
|
||||||
message: { text },
|
message: { text: message },
|
||||||
messaging_type: "RESPONSE",
|
messaging_type: "RESPONSE",
|
||||||
access_token: pageAccessToken,
|
access_token: pageAccessToken,
|
||||||
};
|
};
|
||||||
|
|
||||||
await fetch(endpoint, {
|
try {
|
||||||
method: "POST",
|
const response = await fetch(endpoint, {
|
||||||
headers: { "Content-Type": "application/json" },
|
method: "POST",
|
||||||
body: JSON.stringify(outgoingMessage),
|
headers: { "Content-Type": "application/json" },
|
||||||
});
|
body: JSON.stringify(outgoingMessage),
|
||||||
|
});
|
||||||
|
console.log({ response });
|
||||||
|
} catch (error) {
|
||||||
|
console.error({ error });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sendFacebookMessageTask;
|
export default sendFacebookMessageTask;
|
||||||
|
|
|
||||||
|
|
@ -17,16 +17,23 @@ const fetchSignalMessagesTask = async (): Promise<void> => {
|
||||||
for (const msg of messages) {
|
for (const msg of messages) {
|
||||||
const { envelope } = msg as any;
|
const { envelope } = msg as any;
|
||||||
const { source, sourceUuid, dataMessage } = envelope;
|
const { source, sourceUuid, dataMessage } = envelope;
|
||||||
const messageID = sourceUuid;
|
const messageId = sourceUuid;
|
||||||
const message = dataMessage?.message;
|
const message = dataMessage?.message;
|
||||||
const timestamp = new Date(dataMessage?.timestamp);
|
const timestamp = new Date(dataMessage?.timestamp);
|
||||||
|
const attachment = undefined;
|
||||||
|
const mimeType = undefined;
|
||||||
|
const filename = undefined;
|
||||||
if (source !== number && message) {
|
if (source !== number && message) {
|
||||||
await worker.addJob("signal/receive-signal-message", {
|
await worker.addJob("signal/receive-signal-message", {
|
||||||
token: id,
|
token: id,
|
||||||
sender: source,
|
to: number,
|
||||||
messageID,
|
from: source,
|
||||||
|
messageId,
|
||||||
message,
|
message,
|
||||||
timestamp: timestamp.toISOString(),
|
sentAt: timestamp.toISOString(),
|
||||||
|
attachment,
|
||||||
|
filename,
|
||||||
|
mimeType,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,28 @@ import { db, getWorkerUtils } from "@link-stack/bridge-common";
|
||||||
|
|
||||||
interface ReceiveSignalMessageTaskOptions {
|
interface ReceiveSignalMessageTaskOptions {
|
||||||
token: string;
|
token: string;
|
||||||
sender: string;
|
to: string;
|
||||||
|
from: string;
|
||||||
|
messageId: string;
|
||||||
|
sentAt: string;
|
||||||
message: string;
|
message: string;
|
||||||
messageID: string;
|
attachment?: string;
|
||||||
timestamp: string;
|
filename?: string;
|
||||||
|
mimeType?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const receiveSignalMessageTask = async ({
|
const receiveSignalMessageTask = async ({
|
||||||
token,
|
token,
|
||||||
sender,
|
to,
|
||||||
|
from,
|
||||||
|
messageId,
|
||||||
|
sentAt,
|
||||||
message,
|
message,
|
||||||
messageID,
|
attachment,
|
||||||
timestamp,
|
filename,
|
||||||
|
mimeType,
|
||||||
}: ReceiveSignalMessageTaskOptions): Promise<void> => {
|
}: ReceiveSignalMessageTaskOptions): Promise<void> => {
|
||||||
console.log({ token, sender, message, messageID, timestamp });
|
console.log({ token, to, from });
|
||||||
const worker = await getWorkerUtils();
|
const worker = await getWorkerUtils();
|
||||||
const row = await db
|
const row = await db
|
||||||
.selectFrom("SignalBot")
|
.selectFrom("SignalBot")
|
||||||
|
|
@ -23,15 +31,16 @@ const receiveSignalMessageTask = async ({
|
||||||
.where("id", "=", token)
|
.where("id", "=", token)
|
||||||
.executeTakeFirstOrThrow();
|
.executeTakeFirstOrThrow();
|
||||||
|
|
||||||
console.log(row);
|
|
||||||
console.log(message);
|
|
||||||
const backendId = row.id;
|
const backendId = row.id;
|
||||||
const payload = {
|
const payload = {
|
||||||
to: row.phoneNumber,
|
to,
|
||||||
from: sender,
|
from,
|
||||||
sent_at: timestamp,
|
message_id: messageId,
|
||||||
|
sent_at: sentAt,
|
||||||
message,
|
message,
|
||||||
message_id: messageID,
|
attachment,
|
||||||
|
filename,
|
||||||
|
mime_type: mimeType,
|
||||||
};
|
};
|
||||||
|
|
||||||
await worker.addJob("common/notify-webhooks", { backendId, payload });
|
await worker.addJob("common/notify-webhooks", { backendId, payload });
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,16 @@ const { Configuration, MessagesApi } = signalApi;
|
||||||
|
|
||||||
interface SendSignalMessageTaskOptions {
|
interface SendSignalMessageTaskOptions {
|
||||||
token: string;
|
token: string;
|
||||||
recipient: string;
|
to: string;
|
||||||
message: any;
|
message: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sendSignalMessageTask = async ({
|
const sendSignalMessageTask = async ({
|
||||||
message,
|
|
||||||
recipient,
|
|
||||||
token,
|
token,
|
||||||
|
to,
|
||||||
|
message,
|
||||||
}: SendSignalMessageTaskOptions): Promise<void> => {
|
}: SendSignalMessageTaskOptions): Promise<void> => {
|
||||||
|
console.log({ token, to });
|
||||||
const bot = await db
|
const bot = await db
|
||||||
.selectFrom("SignalBot")
|
.selectFrom("SignalBot")
|
||||||
.selectAll()
|
.selectAll()
|
||||||
|
|
@ -24,15 +25,20 @@ const sendSignalMessageTask = async ({
|
||||||
basePath: process.env.BRIDGE_SIGNAL_URL,
|
basePath: process.env.BRIDGE_SIGNAL_URL,
|
||||||
});
|
});
|
||||||
const messagesClient = new MessagesApi(config);
|
const messagesClient = new MessagesApi(config);
|
||||||
const response = await messagesClient.v2SendPost({
|
|
||||||
data: {
|
|
||||||
number,
|
|
||||||
recipients: [recipient],
|
|
||||||
message,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log({ response });
|
try {
|
||||||
|
const response = await messagesClient.v2SendPost({
|
||||||
|
data: {
|
||||||
|
number,
|
||||||
|
recipients: [to],
|
||||||
|
message,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log({ response });
|
||||||
|
} catch (error) {
|
||||||
|
console.error({ error });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sendSignalMessageTask;
|
export default sendSignalMessageTask;
|
||||||
|
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
/* eslint-disable camelcase */
|
|
||||||
// import logger from "../logger";
|
|
||||||
// import { IncomingMessagev1 } from "@digiresilience/node-signald/build/main/generated";
|
|
||||||
import { withDb, AppDatabase } from "../../lib/db.js";
|
|
||||||
import workerUtils from "../../lib/utils.js";
|
|
||||||
|
|
||||||
type IncomingMessagev1 = any;
|
|
||||||
|
|
||||||
interface WebhookPayload {
|
|
||||||
to: string;
|
|
||||||
from: string;
|
|
||||||
message_id: string;
|
|
||||||
sent_at: string;
|
|
||||||
message: string;
|
|
||||||
attachment: string | null;
|
|
||||||
filename: string | null;
|
|
||||||
mime_type: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SignaldMessageTaskOptions {
|
|
||||||
message: IncomingMessagev1;
|
|
||||||
botId: string;
|
|
||||||
botPhoneNumber: string;
|
|
||||||
attachment: string;
|
|
||||||
filename: string;
|
|
||||||
mimetype: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatPayload = (opts: SignaldMessageTaskOptions): WebhookPayload => {
|
|
||||||
const { botId, botPhoneNumber, message, attachment, filename, mimetype } =
|
|
||||||
opts;
|
|
||||||
const { source, timestamp, data_message: dataMessage } = message;
|
|
||||||
|
|
||||||
const { number }: any = source;
|
|
||||||
|
|
||||||
const { body, attachments }: any = dataMessage;
|
|
||||||
|
|
||||||
return {
|
|
||||||
to: botPhoneNumber,
|
|
||||||
from: number,
|
|
||||||
message_id: `${botId}-${timestamp}`,
|
|
||||||
sent_at: `${timestamp}`,
|
|
||||||
message: body,
|
|
||||||
attachment,
|
|
||||||
filename,
|
|
||||||
mime_type: mimetype,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const notifyWebhooks = async (
|
|
||||||
db: AppDatabase,
|
|
||||||
messageInfo: SignaldMessageTaskOptions,
|
|
||||||
) => {
|
|
||||||
const {
|
|
||||||
botId,
|
|
||||||
message: { timestamp },
|
|
||||||
} = messageInfo;
|
|
||||||
const webhooks = await db.webhooks.findAllByBackendId("signal", botId);
|
|
||||||
if (webhooks && webhooks.length === 0) {
|
|
||||||
// logger.debug({ botId }, "no webhooks registered for signal bot");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
webhooks.forEach(({ id }: any) => {
|
|
||||||
const payload = formatPayload(messageInfo);
|
|
||||||
// logger.debug(
|
|
||||||
// { payload },
|
|
||||||
// "formatted signal bot payload for notify-webhook",
|
|
||||||
// );
|
|
||||||
workerUtils.addJob(
|
|
||||||
"notify-webhook",
|
|
||||||
{
|
|
||||||
payload,
|
|
||||||
webhookId: id,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// this de-deduplicates the job
|
|
||||||
jobKey: `webhook-${id}-message-${botId}-${timestamp}`,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const signaldMessageTask = async (
|
|
||||||
options: SignaldMessageTaskOptions,
|
|
||||||
): Promise<void> => {
|
|
||||||
console.log(options);
|
|
||||||
withDb(async (db: AppDatabase) => {
|
|
||||||
await notifyWebhooks(db, options);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default signaldMessageTask;
|
|
||||||
|
|
@ -2,130 +2,48 @@ import { db, getWorkerUtils } from "@link-stack/bridge-common";
|
||||||
|
|
||||||
interface ReceiveWhatsappMessageTaskOptions {
|
interface ReceiveWhatsappMessageTaskOptions {
|
||||||
token: string;
|
token: string;
|
||||||
sender: string;
|
to: string;
|
||||||
|
from: string;
|
||||||
|
messageId: string;
|
||||||
|
sentAt: string;
|
||||||
message: string;
|
message: string;
|
||||||
|
attachment?: string;
|
||||||
|
filename?: string;
|
||||||
|
mimeType?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const receiveWhatsappMessageTask = async ({
|
const receiveWhatsappMessageTask = async ({
|
||||||
token,
|
token,
|
||||||
sender,
|
to,
|
||||||
|
from,
|
||||||
|
messageId,
|
||||||
|
sentAt,
|
||||||
message,
|
message,
|
||||||
|
attachment,
|
||||||
|
filename,
|
||||||
|
mimeType,
|
||||||
}: ReceiveWhatsappMessageTaskOptions): Promise<void> => {
|
}: ReceiveWhatsappMessageTaskOptions): Promise<void> => {
|
||||||
console.log({ token, sender, message });
|
console.log({ token, to, from });
|
||||||
|
|
||||||
const worker = await getWorkerUtils();
|
const worker = await getWorkerUtils();
|
||||||
const row = await db
|
const row = await db
|
||||||
.selectFrom("WhatsappBot")
|
.selectFrom("WhatsappBot")
|
||||||
.selectAll()
|
.selectAll()
|
||||||
.where("id", "=", token)
|
.where("id", "=", token)
|
||||||
.executeTakeFirstOrThrow();
|
.executeTakeFirstOrThrow();
|
||||||
|
|
||||||
console.log(row);
|
|
||||||
|
|
||||||
const backendId = row.id;
|
const backendId = row.id;
|
||||||
const payload = {
|
const payload = {
|
||||||
|
to,
|
||||||
|
from,
|
||||||
|
message_id: messageId,
|
||||||
|
sent_at: sentAt,
|
||||||
message,
|
message,
|
||||||
recipient: sender,
|
attachment,
|
||||||
|
filename,
|
||||||
|
mime_type: mimeType,
|
||||||
};
|
};
|
||||||
|
|
||||||
await worker.addJob("common/notify-webhooks", { backendId, payload });
|
await worker.addJob("common/notify-webhooks", { backendId, payload });
|
||||||
};
|
};
|
||||||
|
|
||||||
export default receiveWhatsappMessageTask;
|
export default receiveWhatsappMessageTask;
|
||||||
|
|
||||||
/* eslint-disable camelcase */
|
|
||||||
/*
|
|
||||||
import { withDb, AppDatabase } from "../../lib/db";
|
|
||||||
import workerUtils from "../../lib/utils";
|
|
||||||
|
|
||||||
interface WebhookPayload {
|
|
||||||
to: string;
|
|
||||||
from: string;
|
|
||||||
message_id: string;
|
|
||||||
sent_at: string;
|
|
||||||
message: string;
|
|
||||||
attachment: string;
|
|
||||||
filename: string;
|
|
||||||
mime_type: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface WhatsappMessageTaskOptions {
|
|
||||||
waMessageId: string;
|
|
||||||
waMessage: string;
|
|
||||||
waTimestamp: string;
|
|
||||||
attachment: string;
|
|
||||||
filename: string;
|
|
||||||
mimetype: string;
|
|
||||||
botPhoneNumber: string;
|
|
||||||
whatsappBotId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatPayload = (
|
|
||||||
messageInfo: WhatsappMessageTaskOptions,
|
|
||||||
): WebhookPayload => {
|
|
||||||
const {
|
|
||||||
waMessageId,
|
|
||||||
waMessage,
|
|
||||||
waTimestamp,
|
|
||||||
attachment,
|
|
||||||
filename,
|
|
||||||
mimetype,
|
|
||||||
botPhoneNumber,
|
|
||||||
} = messageInfo;
|
|
||||||
const parsedMessage = JSON.parse(waMessage);
|
|
||||||
const message =
|
|
||||||
parsedMessage.message?.conversation ??
|
|
||||||
parsedMessage.message?.extendedTextMessage?.text ??
|
|
||||||
parsedMessage.message?.imageMessage?.caption ??
|
|
||||||
parsedMessage.message?.videoMessage?.caption;
|
|
||||||
|
|
||||||
return {
|
|
||||||
to: botPhoneNumber,
|
|
||||||
from: parsedMessage.key.remoteJid,
|
|
||||||
message_id: waMessageId,
|
|
||||||
sent_at: waTimestamp,
|
|
||||||
message,
|
|
||||||
attachment,
|
|
||||||
filename,
|
|
||||||
mime_type: mimetype,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const notifyWebhooks = async (
|
|
||||||
db: AppDatabase,
|
|
||||||
messageInfo: WhatsappMessageTaskOptions,
|
|
||||||
) => {
|
|
||||||
const { waMessageId, whatsappBotId } = messageInfo;
|
|
||||||
const webhooks = await db.webhooks.findAllByBackendId(
|
|
||||||
"whatsapp",
|
|
||||||
whatsappBotId,
|
|
||||||
);
|
|
||||||
if (webhooks && webhooks.length === 0) return;
|
|
||||||
|
|
||||||
webhooks.forEach(({ id }) => {
|
|
||||||
const payload = formatPayload(messageInfo);
|
|
||||||
console.log({ payload });
|
|
||||||
workerUtils.addJob(
|
|
||||||
"notify-webhook",
|
|
||||||
{
|
|
||||||
payload,
|
|
||||||
webhookId: id,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// this de-deduplicates the job
|
|
||||||
jobKey: `webhook-${id}-message-${waMessageId}`,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const whatsappMessageTask = async (
|
|
||||||
options: WhatsappMessageTaskOptions,
|
|
||||||
): Promise<void> => {
|
|
||||||
console.log(options);
|
|
||||||
withDb(async (db: AppDatabase) => {
|
|
||||||
await notifyWebhooks(db, options);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export default whatsappMessageTask;
|
|
||||||
*/
|
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@ import { db } from "@link-stack/bridge-common";
|
||||||
|
|
||||||
interface SendWhatsappMessageTaskOptions {
|
interface SendWhatsappMessageTaskOptions {
|
||||||
token: string;
|
token: string;
|
||||||
recipient: string;
|
to: string;
|
||||||
message: any;
|
message: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sendWhatsappMessageTask = async ({
|
const sendWhatsappMessageTask = async ({
|
||||||
message,
|
message,
|
||||||
recipient,
|
to,
|
||||||
token,
|
token,
|
||||||
}: SendWhatsappMessageTaskOptions): Promise<void> => {
|
}: SendWhatsappMessageTaskOptions): Promise<void> => {
|
||||||
const bot = await db
|
const bot = await db
|
||||||
|
|
@ -18,15 +18,18 @@ const sendWhatsappMessageTask = async ({
|
||||||
.executeTakeFirstOrThrow();
|
.executeTakeFirstOrThrow();
|
||||||
|
|
||||||
const url = `${process.env.BRIDGE_WHATSAPP_URL}/api/bots/${bot.id}/send`;
|
const url = `${process.env.BRIDGE_WHATSAPP_URL}/api/bots/${bot.id}/send`;
|
||||||
const params = { message, phoneNumber: recipient };
|
const params = { message, phoneNumber: to };
|
||||||
console.log({ params });
|
try {
|
||||||
const result = await fetch(url, {
|
const result = await fetch(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify(params),
|
body: JSON.stringify(params),
|
||||||
});
|
});
|
||||||
|
console.log({ result });
|
||||||
console.log({ result });
|
} catch (error) {
|
||||||
|
console.error({ error });
|
||||||
|
throw new Error("Failed to send message");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default sendWhatsappMessageTask;
|
export default sendWhatsappMessageTask;
|
||||||
|
|
|
||||||
|
|
@ -18,17 +18,17 @@
|
||||||
"@emotion/server": "^11.11.0",
|
"@emotion/server": "^11.11.0",
|
||||||
"@emotion/styled": "^11.11.5",
|
"@emotion/styled": "^11.11.5",
|
||||||
"@mui/icons-material": "^5",
|
"@mui/icons-material": "^5",
|
||||||
"@mui/lab": "^5.0.0-alpha.170",
|
"@mui/lab": "^5.0.0-alpha.172",
|
||||||
"@mui/material": "^5",
|
"@mui/material": "^5",
|
||||||
"@mui/x-data-grid-pro": "^7.8.0",
|
"@mui/x-data-grid-pro": "^7.10.0",
|
||||||
"@mui/x-date-pickers-pro": "^7.8.0",
|
"@mui/x-date-pickers-pro": "^7.10.0",
|
||||||
"@opensearch-project/opensearch": "^2.10.0",
|
"@opensearch-project/opensearch": "^2.10.0",
|
||||||
"cryptr": "^6.3.0",
|
"cryptr": "^6.3.0",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
"http-proxy-middleware": "^3.0.0",
|
"http-proxy-middleware": "^3.0.0",
|
||||||
"@link-stack/leafcutter-ui": "*",
|
"@link-stack/leafcutter-ui": "*",
|
||||||
"material-ui-popup-state": "^5.1.2",
|
"material-ui-popup-state": "^5.1.2",
|
||||||
"next": "14.2.4",
|
"next": "14.2.5",
|
||||||
"next-auth": "^4.24.7",
|
"next-auth": "^4.24.7",
|
||||||
"next-http-proxy-middleware": "^1.2.6",
|
"next-http-proxy-middleware": "^1.2.6",
|
||||||
"@link-stack/opensearch-common": "*",
|
"@link-stack/opensearch-common": "*",
|
||||||
|
|
@ -46,18 +46,18 @@
|
||||||
"uuid": "^10.0.0"
|
"uuid": "^10.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.24.7",
|
"@babel/core": "^7.24.9",
|
||||||
"@types/node": "^20.14.9",
|
"@types/node": "^20.14.11",
|
||||||
"@types/react": "18.3.3",
|
"@types/react": "18.3.3",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"babel-loader": "^9.1.3",
|
"babel-loader": "^9.1.3",
|
||||||
"eslint": "^8.0.0",
|
"eslint": "^8.0.0",
|
||||||
"eslint-config-next": "^14.2.4",
|
"eslint-config-next": "^14.2.5",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"eslint-plugin-jsx-a11y": "^6.9.0",
|
"eslint-plugin-jsx-a11y": "^6.9.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-react": "^7.34.3",
|
"eslint-plugin-react": "^7.34.4",
|
||||||
"typescript": "5.5.3"
|
"typescript": "5.5.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,10 @@
|
||||||
"@emotion/server": "^11.11.0",
|
"@emotion/server": "^11.11.0",
|
||||||
"@emotion/styled": "^11.11.5",
|
"@emotion/styled": "^11.11.5",
|
||||||
"@mui/icons-material": "^5",
|
"@mui/icons-material": "^5",
|
||||||
"@mui/lab": "^5.0.0-alpha.170",
|
"@mui/lab": "^5.0.0-alpha.172",
|
||||||
"@mui/material": "^5",
|
"@mui/material": "^5",
|
||||||
"@mui/x-data-grid-pro": "^7.8.0",
|
"@mui/x-data-grid-pro": "^7.10.0",
|
||||||
"@mui/x-date-pickers-pro": "^7.8.0",
|
"@mui/x-date-pickers-pro": "^7.10.0",
|
||||||
"@link-stack/bridge-common": "*",
|
"@link-stack/bridge-common": "*",
|
||||||
"@link-stack/bridge-ui": "*",
|
"@link-stack/bridge-ui": "*",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
|
|
@ -28,8 +28,8 @@
|
||||||
"graphql-request": "^7.1.0",
|
"graphql-request": "^7.1.0",
|
||||||
"@link-stack/leafcutter-ui": "*",
|
"@link-stack/leafcutter-ui": "*",
|
||||||
"material-ui-popup-state": "^5.1.2",
|
"material-ui-popup-state": "^5.1.2",
|
||||||
"mui-chips-input": "^2.1.4",
|
"mui-chips-input": "^2.1.5",
|
||||||
"next": "14.2.4",
|
"next": "14.2.5",
|
||||||
"next-auth": "^4.24.7",
|
"next-auth": "^4.24.7",
|
||||||
"@link-stack/opensearch-common": "*",
|
"@link-stack/opensearch-common": "*",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
|
|
@ -44,18 +44,18 @@
|
||||||
"@link-stack/ui": "*"
|
"@link-stack/ui": "*"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.24.7",
|
"@babel/core": "^7.24.9",
|
||||||
"@types/node": "^20.14.9",
|
"@types/node": "^20.14.11",
|
||||||
"@types/react": "18.3.3",
|
"@types/react": "18.3.3",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"babel-loader": "^9.1.3",
|
"babel-loader": "^9.1.3",
|
||||||
"eslint": "^8.0.0",
|
"eslint": "^8.0.0",
|
||||||
"eslint-config-next": "^14.2.4",
|
"eslint-config-next": "^14.2.5",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"eslint-plugin-jsx-a11y": "^6.9.0",
|
"eslint-plugin-jsx-a11y": "^6.9.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-react": "^7.34.3",
|
"eslint-plugin-react": "^7.34.4",
|
||||||
"typescript": "5.5.3"
|
"typescript": "5.5.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ x-zammad-vars: &common-zammad-variables
|
||||||
ELASTICSEARCH_HOST: ${OPENSEARCH_HOST}
|
ELASTICSEARCH_HOST: ${OPENSEARCH_HOST}
|
||||||
ELASTICSEARCH_USER: ${OPENSEARCH_USER}
|
ELASTICSEARCH_USER: ${OPENSEARCH_USER}
|
||||||
ELASTICSEARCH_PASS: ${OPENSEARCH_ADMIN_PASSWORD}
|
ELASTICSEARCH_PASS: ${OPENSEARCH_ADMIN_PASSWORD}
|
||||||
ELASTICSEARCH_SSL_VERIFY: false # this doesn't set es_ssl_verify as expected, but ideally it would
|
ELASTICSEARCH_SSL_VERIFY: "false" # this doesn't set es_ssl_verify as expected, but ideally it would
|
||||||
ELASTICSEARCH_SCHEMA: "https"
|
ELASTICSEARCH_SCHEMA: "https"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
|
|
||||||
7803
package-lock.json
generated
7803
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -15,10 +15,10 @@
|
||||||
"pg": "^8.12.0"
|
"pg": "^8.12.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "7.24.7",
|
"@babel/core": "7.24.9",
|
||||||
"@babel/preset-env": "7.24.7",
|
"@babel/preset-env": "7.24.8",
|
||||||
"@babel/preset-typescript": "7.24.7",
|
"@babel/preset-typescript": "7.24.7",
|
||||||
"prettier": "^3.3.2",
|
"prettier": "^3.3.3",
|
||||||
"@link-stack/typescript-config": "*",
|
"@link-stack/typescript-config": "*",
|
||||||
"tsx": "^4.16.2",
|
"tsx": "^4.16.2",
|
||||||
"typescript": "^5.5.3"
|
"typescript": "^5.5.3"
|
||||||
|
|
|
||||||
|
|
@ -95,9 +95,7 @@ export class Service {
|
||||||
req: NextRequest,
|
req: NextRequest,
|
||||||
{ params: { service, token } }: ServiceParams,
|
{ params: { service, token } }: ServiceParams,
|
||||||
): Promise<NextResponse> {
|
): Promise<NextResponse> {
|
||||||
console.log("INTO receiveMessage");
|
|
||||||
const json = await req.json();
|
const json = await req.json();
|
||||||
console.log({ json });
|
|
||||||
const worker = await getWorkerUtils();
|
const worker = await getWorkerUtils();
|
||||||
await worker.addJob(`${service}/receive-${service}-message`, {
|
await worker.addJob(`${service}/receive-${service}-message`, {
|
||||||
token,
|
token,
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,14 @@
|
||||||
"@emotion/styled": "^11.11.5",
|
"@emotion/styled": "^11.11.5",
|
||||||
"@link-stack/signal-api": "*",
|
"@link-stack/signal-api": "*",
|
||||||
"@mui/icons-material": "^5",
|
"@mui/icons-material": "^5",
|
||||||
"@mui/lab": "^5.0.0-alpha.170",
|
"@mui/lab": "^5.0.0-alpha.172",
|
||||||
"@mui/material": "^5",
|
"@mui/material": "^5",
|
||||||
"@mui/x-data-grid-pro": "^7.8.0",
|
"@mui/x-data-grid-pro": "^7.10.0",
|
||||||
"@mui/x-date-pickers-pro": "^7.8.0",
|
"@mui/x-date-pickers-pro": "^7.10.0",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
"kysely": "0.26.1",
|
"kysely": "0.26.1",
|
||||||
"material-ui-popup-state": "^5.1.2",
|
"material-ui-popup-state": "^5.1.2",
|
||||||
"next": "14.2.4",
|
"next": "14.2.5",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-cookie": "^7.1.4",
|
"react-cookie": "^7.1.4",
|
||||||
"react-cookie-consent": "^9.0.0",
|
"react-cookie-consent": "^9.0.0",
|
||||||
|
|
@ -32,19 +32,19 @@
|
||||||
"uuid": "^10.0.0"
|
"uuid": "^10.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.24.7",
|
"@babel/core": "^7.24.9",
|
||||||
"@types/node": "^20.14.9",
|
"@types/node": "^20.14.11",
|
||||||
"@types/react": "18.3.3",
|
"@types/react": "18.3.3",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"babel-loader": "^9.1.3",
|
"babel-loader": "^9.1.3",
|
||||||
"eslint": "^8.0.0",
|
"eslint": "^8.0.0",
|
||||||
"eslint-config-next": "^14.2.4",
|
"eslint-config-next": "^14.2.5",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"eslint-plugin-jsx-a11y": "^6.9.0",
|
"eslint-plugin-jsx-a11y": "^6.9.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-react": "^7.34.3",
|
"eslint-plugin-react": "^7.34.4",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"typescript": "5.5.3"
|
"typescript": "5.5.3"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rushstack/eslint-patch": "^1.10.3",
|
"@rushstack/eslint-patch": "^1.10.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.15.0",
|
"@typescript-eslint/eslint-plugin": "^7.16.1",
|
||||||
"@typescript-eslint/parser": "^7.15.0",
|
"@typescript-eslint/parser": "^7.16.1",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-config-xo-space": "^0.35.0",
|
"eslint-config-xo-space": "^0.35.0",
|
||||||
"eslint-plugin-cypress": "^3.3.0",
|
"eslint-plugin-cypress": "^3.3.0",
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
"eslint-plugin-jest": "^28.6.0",
|
"eslint-plugin-jest": "^28.6.0",
|
||||||
"eslint-plugin-promise": "^6.4.0",
|
"eslint-plugin-promise": "^6.4.0",
|
||||||
"eslint-plugin-unicorn": "54.0.0",
|
"eslint-plugin-unicorn": "54.0.0",
|
||||||
"@babel/eslint-parser": "7.24.7"
|
"@babel/eslint-parser": "7.24.8"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^4.9.5"
|
"typescript": "^4.9.5"
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ export const QueryListSelector: FC<QueryListSelectorProps> = ({
|
||||||
disableColumnMenu
|
disableColumnMenu
|
||||||
scrollbarSize={10}
|
scrollbarSize={10}
|
||||||
onRowSelectionModelChange={(newSelectionModel) => {
|
onRowSelectionModelChange={(newSelectionModel) => {
|
||||||
setSelectionModel(newSelectionModel);
|
setSelectionModel(newSelectionModel as any);
|
||||||
updateQuery({
|
updateQuery({
|
||||||
[keyName]: { values: newSelectionModel },
|
[keyName]: { values: newSelectionModel },
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -10,14 +10,14 @@
|
||||||
"@emotion/server": "^11.11.0",
|
"@emotion/server": "^11.11.0",
|
||||||
"@emotion/styled": "^11.11.5",
|
"@emotion/styled": "^11.11.5",
|
||||||
"@mui/icons-material": "^5",
|
"@mui/icons-material": "^5",
|
||||||
"@mui/lab": "^5.0.0-alpha.170",
|
"@mui/lab": "^5.0.0-alpha.172",
|
||||||
"@mui/material": "^5",
|
"@mui/material": "^5",
|
||||||
"@mui/x-data-grid-pro": "^7.8.0",
|
"@mui/x-data-grid-pro": "^7.10.0",
|
||||||
"@mui/x-date-pickers-pro": "^7.8.0",
|
"@mui/x-date-pickers-pro": "^7.10.0",
|
||||||
"@link-stack/opensearch-common": "*",
|
"@link-stack/opensearch-common": "*",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
"material-ui-popup-state": "^5.1.2",
|
"material-ui-popup-state": "^5.1.2",
|
||||||
"next": "14.2.4",
|
"next": "14.2.5",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-cookie": "^7.1.4",
|
"react-cookie": "^7.1.4",
|
||||||
"react-cookie-consent": "^9.0.0",
|
"react-cookie-consent": "^9.0.0",
|
||||||
|
|
@ -29,18 +29,18 @@
|
||||||
"uuid": "^10.0.0"
|
"uuid": "^10.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.24.7",
|
"@babel/core": "^7.24.9",
|
||||||
"@types/node": "^20.14.9",
|
"@types/node": "^20.14.11",
|
||||||
"@types/react": "18.3.3",
|
"@types/react": "18.3.3",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"babel-loader": "^9.1.3",
|
"babel-loader": "^9.1.3",
|
||||||
"eslint": "^8.0.0",
|
"eslint": "^8.0.0",
|
||||||
"eslint-config-next": "^14.2.4",
|
"eslint-config-next": "^14.2.5",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"eslint-plugin-jsx-a11y": "^6.9.0",
|
"eslint-plugin-jsx-a11y": "^6.9.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-react": "^7.34.3",
|
"eslint-plugin-react": "^7.34.4",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
"typescript": "5.5.3"
|
"typescript": "5.5.3"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@
|
||||||
"uuid": "^10.0.0"
|
"uuid": "^10.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.24.7",
|
"@babel/core": "^7.24.9",
|
||||||
"@types/node": "^20.14.9",
|
"@types/node": "^20.14.11",
|
||||||
"@types/react": "18.3.3",
|
"@types/react": "18.3.3",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"babel-loader": "^9.1.3",
|
"babel-loader": "^9.1.3",
|
||||||
|
|
|
||||||
|
|
@ -9,16 +9,16 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mui/icons-material": "^5",
|
"@mui/icons-material": "^5",
|
||||||
"@mui/lab": "^5.0.0-alpha.170",
|
"@mui/lab": "^5.0.0-alpha.172",
|
||||||
"@mui/material": "^5",
|
"@mui/material": "^5",
|
||||||
"@mui/x-data-grid-pro": "^7.8.0",
|
"@mui/x-data-grid-pro": "^7.10.0",
|
||||||
"@mui/x-date-pickers-pro": "^7.8.0",
|
"@mui/x-date-pickers-pro": "^7.10.0",
|
||||||
"next": "14.2.4",
|
"next": "14.2.5",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-dom": "18.3.1"
|
"react-dom": "18.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.14.9",
|
"@types/node": "^20.14.11",
|
||||||
"@types/react": "18.3.3",
|
"@types/react": "18.3.3",
|
||||||
"typescript": "^5.5.3"
|
"typescript": "^5.5.3"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue