Update logging
This commit is contained in:
parent
def602c05e
commit
810a333429
39 changed files with 85 additions and 139 deletions
|
|
@ -72,7 +72,7 @@ export const migrate = async (arg: string) => {
|
||||||
|
|
||||||
results?.forEach((it) => {
|
results?.forEach((it) => {
|
||||||
if (it.status === "Success") {
|
if (it.status === "Success") {
|
||||||
console.log(
|
console.info(
|
||||||
`Migration "${it.migrationName} ${it.direction.toLowerCase()}" was executed successfully`,
|
`Migration "${it.migrationName} ${it.direction.toLowerCase()}" was executed successfully`,
|
||||||
);
|
);
|
||||||
} else if (it.status === "Error") {
|
} else if (it.status === "Error") {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ export const SendMessageRoute = withDefaults({
|
||||||
description: "Send a message",
|
description: "Send a message",
|
||||||
async handler(request: Hapi.Request, _h: Hapi.ResponseToolkit) {
|
async handler(request: Hapi.Request, _h: Hapi.ResponseToolkit) {
|
||||||
const { id } = request.params;
|
const { id } = request.params;
|
||||||
console.log({ payload: request.payload });
|
|
||||||
const { phoneNumber, message } = request.payload as MessageRequest;
|
const { phoneNumber, message } = request.payload as MessageRequest;
|
||||||
const whatsappService = getService(request);
|
const whatsappService = getService(request);
|
||||||
await whatsappService.send(id, phoneNumber, message as string);
|
await whatsappService.send(id, phoneNumber, message as string);
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ export default class WhatsappService extends Service {
|
||||||
try {
|
try {
|
||||||
connection.end(null);
|
connection.end(null);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.connections = {};
|
this.connections = {};
|
||||||
|
|
@ -92,27 +92,27 @@ export default class WhatsappService extends Service {
|
||||||
isNewLogin,
|
isNewLogin,
|
||||||
} = update;
|
} = update;
|
||||||
if (qr) {
|
if (qr) {
|
||||||
console.log("got qr code");
|
console.info("got qr code");
|
||||||
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");
|
||||||
} else if (isNewLogin) {
|
} else if (isNewLogin) {
|
||||||
console.log("got new login");
|
console.info("got new login");
|
||||||
const botDirectory = this.getBotDirectory(botID);
|
const botDirectory = this.getBotDirectory(botID);
|
||||||
const verifiedFile = `${botDirectory}/verified`;
|
const verifiedFile = `${botDirectory}/verified`;
|
||||||
fs.writeFileSync(verifiedFile, "");
|
fs.writeFileSync(verifiedFile, "");
|
||||||
} else if (connectionState === "open") {
|
} else if (connectionState === "open") {
|
||||||
console.log("opened connection");
|
console.info("opened connection");
|
||||||
} else if (connectionState === "close") {
|
} else if (connectionState === "close") {
|
||||||
console.log("connection closed due to ", lastDisconnect?.error);
|
console.info("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.info("reconnecting after got new login");
|
||||||
await this.createConnection(botID, server, options);
|
await this.createConnection(botID, server, options);
|
||||||
authCompleteCallback?.();
|
authCompleteCallback?.();
|
||||||
} else if (disconnectStatusCode !== DisconnectReason.loggedOut) {
|
} else if (disconnectStatusCode !== DisconnectReason.loggedOut) {
|
||||||
console.log("reconnecting");
|
console.info("reconnecting");
|
||||||
await this.sleep(pause);
|
await this.sleep(pause);
|
||||||
pause *= 2;
|
pause *= 2;
|
||||||
this.createConnection(botID, server, options);
|
this.createConnection(botID, server, options);
|
||||||
|
|
@ -121,12 +121,12 @@ export default class WhatsappService extends Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events["creds.update"]) {
|
if (events["creds.update"]) {
|
||||||
console.log("creds update");
|
console.info("creds update");
|
||||||
await saveCreds();
|
await saveCreds();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (events["messages.upsert"]) {
|
if (events["messages.upsert"]) {
|
||||||
console.log("messages upsert");
|
console.info("messages upsert");
|
||||||
const upsert = events["messages.upsert"];
|
const upsert = events["messages.upsert"];
|
||||||
const { messages } = upsert;
|
const { messages } = upsert;
|
||||||
if (messages) {
|
if (messages) {
|
||||||
|
|
@ -143,13 +143,13 @@ export default class WhatsappService extends Service {
|
||||||
|
|
||||||
const baseDirectory = this.getBaseDirectory();
|
const baseDirectory = this.getBaseDirectory();
|
||||||
const botIDs = fs.readdirSync(baseDirectory);
|
const botIDs = fs.readdirSync(baseDirectory);
|
||||||
console.log({ botIDs });
|
|
||||||
for await (const botID of botIDs) {
|
for await (const botID of botIDs) {
|
||||||
const directory = this.getBotDirectory(botID);
|
const directory = this.getBotDirectory(botID);
|
||||||
const verifiedFile = `${directory}/verified`;
|
const verifiedFile = `${directory}/verified`;
|
||||||
if (fs.existsSync(verifiedFile)) {
|
if (fs.existsSync(verifiedFile)) {
|
||||||
const { version, isLatest } = await fetchLatestBaileysVersion();
|
const { version, isLatest } = await fetchLatestBaileysVersion();
|
||||||
console.log(`using WA v${version.join(".")}, isLatest: ${isLatest}`);
|
console.info(`using WA v${version.join(".")}, isLatest: ${isLatest}`);
|
||||||
|
|
||||||
await this.createConnection(botID, this.server, {
|
await this.createConnection(botID, this.server, {
|
||||||
browser: WhatsappService.browserDescription,
|
browser: WhatsappService.browserDescription,
|
||||||
|
|
@ -169,7 +169,10 @@ export default class WhatsappService extends Service {
|
||||||
message,
|
message,
|
||||||
messageTimestamp,
|
messageTimestamp,
|
||||||
} = webMessageInfo;
|
} = webMessageInfo;
|
||||||
console.log(webMessageInfo);
|
console.info("Message type debug");
|
||||||
|
for (const key in message) {
|
||||||
|
console.info(key, !!message[key as keyof proto.IMessage]);
|
||||||
|
}
|
||||||
const isValidMessage =
|
const isValidMessage =
|
||||||
message && remoteJid !== "status@broadcast" && !fromMe;
|
message && remoteJid !== "status@broadcast" && !fromMe;
|
||||||
if (isValidMessage) {
|
if (isValidMessage) {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
const startWorker = async () => {
|
const startWorker = async () => {
|
||||||
console.log("Starting worker...");
|
console.info("Starting worker...");
|
||||||
|
|
||||||
await run({
|
await run({
|
||||||
connectionString: process.env.DATABASE_URL,
|
connectionString: process.env.DATABASE_URL,
|
||||||
|
|
|
||||||
|
|
@ -62,9 +62,8 @@ export const createZammadTicket = async (
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.log(Object.keys(error));
|
|
||||||
if (error.isBoom) {
|
if (error.isBoom) {
|
||||||
console.log(error.output);
|
console.error(error.output);
|
||||||
throw new Error("Failed to create zamamd ticket");
|
throw new Error("Failed to create zamamd ticket");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ const defaultAudioConvertOpts = {
|
||||||
**/
|
**/
|
||||||
export const convert = (
|
export const convert = (
|
||||||
input: Buffer,
|
input: Buffer,
|
||||||
opts?: AudioConvertOpts
|
opts?: AudioConvertOpts,
|
||||||
): Promise<Buffer> => {
|
): Promise<Buffer> => {
|
||||||
const settings = { ...defaultAudioConvertOpts, ...opts };
|
const settings = { ...defaultAudioConvertOpts, ...opts };
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
@ -35,12 +35,8 @@ export const convert = (
|
||||||
.audioCodec(settings.audioCodec)
|
.audioCodec(settings.audioCodec)
|
||||||
.audioBitrate(settings.bitrate)
|
.audioBitrate(settings.bitrate)
|
||||||
.toFormat(settings.format)
|
.toFormat(settings.format)
|
||||||
.on("error", (err, stdout, stderr) => {
|
.on("error", (err, _stdout, _stderr) => {
|
||||||
console.error(err.message);
|
console.error(err.message);
|
||||||
console.log("FFMPEG OUTPUT");
|
|
||||||
console.log(stdout);
|
|
||||||
console.log("FFMPEG ERROR");
|
|
||||||
console.log(stderr);
|
|
||||||
reject(err);
|
reject(err);
|
||||||
})
|
})
|
||||||
.on("end", () => {
|
.on("end", () => {
|
||||||
|
|
@ -66,8 +62,12 @@ export const selfCheck = (): Promise<boolean> => {
|
||||||
resolve(false);
|
resolve(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
const preds = R.map(requiredCodecs, (codec) => (available: any) =>
|
const preds = R.map(
|
||||||
available[codec] && available[codec].canDemux && available[codec].canMux
|
requiredCodecs,
|
||||||
|
(codec) => (available: any) =>
|
||||||
|
available[codec] &&
|
||||||
|
available[codec].canDemux &&
|
||||||
|
available[codec].canMux,
|
||||||
);
|
);
|
||||||
|
|
||||||
resolve(R.allPass(codecs, preds));
|
resolve(R.allPass(codecs, preds));
|
||||||
|
|
@ -79,6 +79,6 @@ export const assertFfmpegAvailable = async (): Promise<void> => {
|
||||||
const r = await selfCheck();
|
const r = await selfCheck();
|
||||||
if (!r)
|
if (!r)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`ffmpeg is not installed, could not be located, or does not support the required codecs: ${requiredCodecs}`
|
`ffmpeg is not installed, could not be located, or does not support the required codecs: ${requiredCodecs}`,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,11 @@ const notifyWebhooksTask = async (
|
||||||
for (const webhook of webhooks) {
|
for (const webhook of webhooks) {
|
||||||
const { endpointUrl, httpMethod, headers } = webhook;
|
const { endpointUrl, httpMethod, headers } = webhook;
|
||||||
const finalHeaders = { "Content-Type": "application/json", ...headers };
|
const finalHeaders = { "Content-Type": "application/json", ...headers };
|
||||||
console.log({ endpointUrl, httpMethod, headers, finalHeaders });
|
|
||||||
const result = await fetch(endpointUrl, {
|
const result = await fetch(endpointUrl, {
|
||||||
method: httpMethod,
|
method: httpMethod,
|
||||||
headers: finalHeaders,
|
headers: finalHeaders,
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
});
|
});
|
||||||
console.log(result);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,6 @@ const sendFacebookMessageTask = async (
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify(outgoingMessage),
|
body: JSON.stringify(outgoingMessage),
|
||||||
});
|
});
|
||||||
console.log({ response });
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error({ error });
|
console.error({ error });
|
||||||
throw error;
|
throw error;
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,6 @@ const processMessage = async ({
|
||||||
message: msg,
|
message: msg,
|
||||||
}: ProcessMessageArgs): Promise<Record<string, any>[]> => {
|
}: ProcessMessageArgs): Promise<Record<string, any>[]> => {
|
||||||
const { envelope } = msg;
|
const { envelope } = msg;
|
||||||
console.log(envelope);
|
|
||||||
const { source, sourceUuid, dataMessage } = envelope;
|
const { source, sourceUuid, dataMessage } = envelope;
|
||||||
|
|
||||||
if (!dataMessage) return [];
|
if (!dataMessage) return [];
|
||||||
|
|
@ -125,7 +124,6 @@ const fetchSignalMessagesTask = async ({
|
||||||
phoneNumber,
|
phoneNumber,
|
||||||
message,
|
message,
|
||||||
});
|
});
|
||||||
console.log({ formattedMessages });
|
|
||||||
for (const formattedMessage of formattedMessages) {
|
for (const formattedMessage of formattedMessages) {
|
||||||
if (formattedMessage.to !== formattedMessage.from) {
|
if (formattedMessage.to !== formattedMessage.from) {
|
||||||
await worker.addJob(
|
await worker.addJob(
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ const getZammadTickets = async (
|
||||||
{ headers },
|
{ headers },
|
||||||
);
|
);
|
||||||
const tickets: any = await rawTickets.json();
|
const tickets: any = await rawTickets.json();
|
||||||
console.log({ tickets });
|
|
||||||
if (!tickets || tickets.length === 0) {
|
if (!tickets || tickets.length === 0) {
|
||||||
return [shouldContinue, docs];
|
return [shouldContinue, docs];
|
||||||
}
|
}
|
||||||
|
|
@ -49,22 +48,8 @@ const getZammadTickets = async (
|
||||||
shouldContinue = true;
|
shouldContinue = true;
|
||||||
|
|
||||||
if (source_closed_at <= minUpdatedTimestamp) {
|
if (source_closed_at <= minUpdatedTimestamp) {
|
||||||
console.log(`Skipping ticket`, {
|
|
||||||
source_id,
|
|
||||||
source_updated_at,
|
|
||||||
source_closed_at,
|
|
||||||
minUpdatedTimestamp,
|
|
||||||
});
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Processing ticket`, {
|
|
||||||
source_id,
|
|
||||||
source_updated_at,
|
|
||||||
source_closed_at,
|
|
||||||
minUpdatedTimestamp,
|
|
||||||
});
|
|
||||||
|
|
||||||
const rawArticles = await fetch(
|
const rawArticles = await fetch(
|
||||||
`${zammadApiUrl}/ticket_articles/by_ticket/${source_id}`,
|
`${zammadApiUrl}/ticket_articles/by_ticket/${source_id}`,
|
||||||
{ headers },
|
{ headers },
|
||||||
|
|
@ -178,8 +163,6 @@ const sendToLabelStudio = async (tickets: FormattedZammadTicket[]) => {
|
||||||
body: JSON.stringify([ticket]),
|
body: JSON.stringify([ticket]),
|
||||||
});
|
});
|
||||||
const importResult = await res.json();
|
const importResult = await res.json();
|
||||||
|
|
||||||
console.log(JSON.stringify(importResult, undefined, 2));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
@ -201,7 +184,6 @@ const importLabelStudioTask = async (): Promise<void> => {
|
||||||
await sendToLabelStudio(tickets);
|
await sendToLabelStudio(tickets);
|
||||||
const lastTicket = tickets.pop();
|
const lastTicket = tickets.pop();
|
||||||
const newLastTimestamp = lastTicket.data.source_closed_at;
|
const newLastTimestamp = lastTicket.data.source_closed_at;
|
||||||
console.log({ newLastTimestamp });
|
|
||||||
await db.settings.upsert(settingName, {
|
await db.settings.upsert(settingName, {
|
||||||
minUpdatedTimestamp: newLastTimestamp,
|
minUpdatedTimestamp: newLastTimestamp,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -43,14 +43,11 @@ const getLabelStudioTickets = async (
|
||||||
page_size: "50",
|
page_size: "50",
|
||||||
page: `${page}`,
|
page: `${page}`,
|
||||||
});
|
});
|
||||||
console.log({ url: `${labelStudioApiUrl}/projects/1/tasks?${ticketsQuery}` });
|
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`${labelStudioApiUrl}/projects/1/tasks?${ticketsQuery}`,
|
`${labelStudioApiUrl}/projects/1/tasks?${ticketsQuery}`,
|
||||||
{ headers },
|
{ headers },
|
||||||
);
|
);
|
||||||
console.log({ res });
|
|
||||||
const tasksResult: any = await res.json();
|
const tasksResult: any = await res.json();
|
||||||
console.log({ tasksResult });
|
|
||||||
|
|
||||||
return tasksResult;
|
return tasksResult;
|
||||||
};
|
};
|
||||||
|
|
@ -63,14 +60,11 @@ const fetchFromLabelStudio = async (
|
||||||
|
|
||||||
for await (const page of pages) {
|
for await (const page of pages) {
|
||||||
const docs = await getLabelStudioTickets(page + 1);
|
const docs = await getLabelStudioTickets(page + 1);
|
||||||
console.log({ page, docs });
|
|
||||||
|
|
||||||
if (docs && docs.length > 0) {
|
if (docs && docs.length > 0) {
|
||||||
for (const doc of docs) {
|
for (const doc of docs) {
|
||||||
const updatedAt = new Date(doc.updated_at);
|
const updatedAt = new Date(doc.updated_at);
|
||||||
console.log({ updatedAt, minUpdatedTimestamp });
|
|
||||||
if (updatedAt > minUpdatedTimestamp) {
|
if (updatedAt > minUpdatedTimestamp) {
|
||||||
console.log(`Adding doc`, { doc });
|
|
||||||
allDocs.push(doc);
|
allDocs.push(doc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -79,7 +73,6 @@ const fetchFromLabelStudio = async (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log({ allDocs });
|
|
||||||
return allDocs;
|
return allDocs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -93,9 +86,7 @@ const sendToLeafcutter = async (tickets: LabelStudioTicket[]) => {
|
||||||
},
|
},
|
||||||
} = config;
|
} = config;
|
||||||
|
|
||||||
console.log({ tickets });
|
|
||||||
const filteredTickets = tickets.filter((ticket) => ticket.is_labeled);
|
const filteredTickets = tickets.filter((ticket) => ticket.is_labeled);
|
||||||
console.log({ filteredTickets });
|
|
||||||
const finalTickets: LeafcutterTicket[] = filteredTickets.map((ticket) => {
|
const finalTickets: LeafcutterTicket[] = filteredTickets.map((ticket) => {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
|
|
@ -131,8 +122,7 @@ const sendToLeafcutter = async (tickets: LabelStudioTicket[]) => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("Sending to Leafcutter");
|
console.info("Sending to Leafcutter");
|
||||||
console.log({ finalTickets });
|
|
||||||
|
|
||||||
const result = await fetch(opensearchApiUrl, {
|
const result = await fetch(opensearchApiUrl, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|
@ -157,15 +147,7 @@ const importLeafcutterTask = async (): Promise<void> => {
|
||||||
? new Date(res.value.minUpdatedTimestamp as string)
|
? new Date(res.value.minUpdatedTimestamp as string)
|
||||||
: new Date("2023-03-01");
|
: new Date("2023-03-01");
|
||||||
const newLastTimestamp = new Date();
|
const newLastTimestamp = new Date();
|
||||||
console.log({
|
|
||||||
contributorName,
|
|
||||||
settingName,
|
|
||||||
res,
|
|
||||||
startTimestamp,
|
|
||||||
newLastTimestamp,
|
|
||||||
});
|
|
||||||
const tickets = await fetchFromLabelStudio(startTimestamp);
|
const tickets = await fetchFromLabelStudio(startTimestamp);
|
||||||
console.log({ tickets });
|
|
||||||
await sendToLeafcutter(tickets);
|
await sendToLeafcutter(tickets);
|
||||||
await db.settings.upsert(settingName, {
|
await db.settings.upsert(settingName, {
|
||||||
minUpdatedTimestamp: newLastTimestamp,
|
minUpdatedTimestamp: newLastTimestamp,
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ const receiveSignalMessageTask = async ({
|
||||||
filename,
|
filename,
|
||||||
mimeType,
|
mimeType,
|
||||||
}: ReceiveSignalMessageTaskOptions): Promise<void> => {
|
}: ReceiveSignalMessageTaskOptions): Promise<void> => {
|
||||||
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")
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ const sendSignalMessageTask = async ({
|
||||||
to,
|
to,
|
||||||
message,
|
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()
|
||||||
|
|
@ -34,7 +33,6 @@ const sendSignalMessageTask = async ({
|
||||||
message,
|
message,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
console.log({ response });
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error({ error });
|
console.error({ error });
|
||||||
throw error;
|
throw error;
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,6 @@ const receiveWhatsappMessageTask = async ({
|
||||||
filename,
|
filename,
|
||||||
mimeType,
|
mimeType,
|
||||||
}: ReceiveWhatsappMessageTaskOptions): Promise<void> => {
|
}: ReceiveWhatsappMessageTaskOptions): Promise<void> => {
|
||||||
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")
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ const sendWhatsappMessageTask = async ({
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify(params),
|
body: JSON.stringify(params),
|
||||||
});
|
});
|
||||||
console.log({ result });
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error({ error });
|
console.error({ error });
|
||||||
throw new Error("Failed to send message");
|
throw new Error("Failed to send message");
|
||||||
|
|
|
||||||
|
|
@ -69,19 +69,17 @@ export const getServerSideProps: GetServerSideProps = async (
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
console.log({ query });
|
|
||||||
const dataResponse = await client.search({
|
const dataResponse = await client.search({
|
||||||
index: "demo_data",
|
index: "demo_data",
|
||||||
size: 200,
|
size: 200,
|
||||||
body: { query },
|
body: { query },
|
||||||
});
|
});
|
||||||
console.log({ dataResponse });
|
|
||||||
res.props.data = dataResponse.body.hits.hits.map((hit) => ({
|
res.props.data = dataResponse.body.hits.hits.map((hit) => ({
|
||||||
id: hit._id,
|
id: hit._id,
|
||||||
...hit._source,
|
...hit._source,
|
||||||
}));
|
}));
|
||||||
console.log({ data: res.props.data });
|
|
||||||
console.log(res.props.data[0]);
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -22,20 +22,17 @@ export const authOptions: NextAuthOptions = {
|
||||||
Credentials({
|
Credentials({
|
||||||
name: "Link",
|
name: "Link",
|
||||||
credentials: {
|
credentials: {
|
||||||
authToken: { label: "AuthToken", type: "text", },
|
authToken: { label: "AuthToken", type: "text" },
|
||||||
},
|
},
|
||||||
async authorize(credentials, req) {
|
async authorize(credentials, req) {
|
||||||
const { headers } = req;
|
const { headers } = req;
|
||||||
console.log({ headers });
|
|
||||||
const leafcutterUser = headers?.["x-leafcutter-user"];
|
const leafcutterUser = headers?.["x-leafcutter-user"];
|
||||||
const authToken = credentials?.authToken;
|
const authToken = credentials?.authToken;
|
||||||
|
|
||||||
if (!leafcutterUser || leafcutterUser.trim() === "") {
|
if (!leafcutterUser || leafcutterUser.trim() === "") {
|
||||||
console.log("no leafcutter user");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log({ authToken });
|
|
||||||
return null;
|
return null;
|
||||||
/*
|
/*
|
||||||
try {
|
try {
|
||||||
|
|
@ -48,14 +45,13 @@ export const authOptions: NextAuthOptions = {
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log({ e });
|
console.error({ e });
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
*/
|
*/
|
||||||
}
|
},
|
||||||
})
|
}),
|
||||||
|
|
||||||
],
|
],
|
||||||
secret: process.env.NEXTAUTH_SECRET,
|
secret: process.env.NEXTAUTH_SECRET,
|
||||||
/*
|
/*
|
||||||
|
|
@ -77,4 +73,3 @@ export const authOptions: NextAuthOptions = {
|
||||||
}
|
}
|
||||||
},*/
|
},*/
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -302,8 +302,7 @@ export const updateUserVisualization = async (
|
||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// eslint-disable-next-line no-console
|
console.error({ e });
|
||||||
console.log({ e });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,12 @@ import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
export const GET = async (req: NextRequest) => {
|
export const GET = async (req: NextRequest) => {
|
||||||
const validDomains = "localhost";
|
const validDomains = "localhost";
|
||||||
console.log({ req });
|
|
||||||
|
|
||||||
return NextResponse.json({ response: "ok" });
|
return NextResponse.json({ response: "ok" });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const POST = async (req: NextRequest) => {
|
export const POST = async (req: NextRequest) => {
|
||||||
const validDomains = "localhost";
|
const validDomains = "localhost";
|
||||||
console.log({ req });
|
|
||||||
|
|
||||||
return NextResponse.json({ response: "ok" });
|
return NextResponse.json({ response: "ok" });
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,8 @@ import { getTrends } from "app/_lib/opensearch";
|
||||||
|
|
||||||
export const GET = async () => {
|
export const GET = async () => {
|
||||||
const results = await getTrends(5);
|
const results = await getTrends(5);
|
||||||
console.log({ results });
|
|
||||||
|
|
||||||
NextResponse.json(results);
|
NextResponse.json(results);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const dynamic = 'force-dynamic';
|
export const dynamic = "force-dynamic";
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ export const POST = async (req: NextRequest) => {
|
||||||
rejectUnauthorized: false,
|
rejectUnauthorized: false,
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
authorization
|
authorization,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const succeeded = [];
|
const succeeded = [];
|
||||||
|
|
@ -28,11 +28,15 @@ export const POST = async (req: NextRequest) => {
|
||||||
const country = ticket.country[0] ?? "none";
|
const country = ticket.country[0] ?? "none";
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
const translatedCountry = taxonomy.country[country]?.display ?? "none";
|
const translatedCountry = taxonomy.country[country]?.display ?? "none";
|
||||||
const countryDetails: any = unRegions.find((c) => c.name === translatedCountry);
|
const countryDetails: any = unRegions.find(
|
||||||
|
(c) => c.name === translatedCountry,
|
||||||
|
);
|
||||||
const augmentedTicket = {
|
const augmentedTicket = {
|
||||||
...ticket,
|
...ticket,
|
||||||
region: countryDetails['sub-region']?.toLowerCase().replace(" ", "-") ?? null,
|
region:
|
||||||
continent: countryDetails.region?.toLowerCase().replace(" ", "-") ?? null,
|
countryDetails["sub-region"]?.toLowerCase().replace(" ", "-") ?? null,
|
||||||
|
continent:
|
||||||
|
countryDetails.region?.toLowerCase().replace(" ", "-") ?? null,
|
||||||
};
|
};
|
||||||
const out = await client.create({
|
const out = await client.create({
|
||||||
id: uuid(),
|
id: uuid(),
|
||||||
|
|
@ -40,10 +44,9 @@ export const POST = async (req: NextRequest) => {
|
||||||
refresh: true,
|
refresh: true,
|
||||||
body: augmentedTicket,
|
body: augmentedTicket,
|
||||||
});
|
});
|
||||||
console.log(out);
|
|
||||||
succeeded.push(id);
|
succeeded.push(id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.error(e);
|
||||||
failed.push(id);
|
failed.push(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -52,4 +55,3 @@ export const POST = async (req: NextRequest) => {
|
||||||
|
|
||||||
return NextResponse.json(results);
|
return NextResponse.json(results);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
2
apps/leafcutter/next-env.d.ts
vendored
2
apps/leafcutter/next-env.d.ts
vendored
|
|
@ -3,4 +3,4 @@
|
||||||
/// <reference types="next/navigation-types/compat/navigation" />
|
/// <reference types="next/navigation-types/compat/navigation" />
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
|
||||||
|
|
|
||||||
|
|
@ -24,17 +24,17 @@ const withAuthInfo =
|
||||||
const requestSignature = req.query.signature;
|
const requestSignature = req.query.signature;
|
||||||
const url = new URL(req.headers.referer as string);
|
const url = new URL(req.headers.referer as string);
|
||||||
const referrerSignature = url.searchParams.get("signature");
|
const referrerSignature = url.searchParams.get("signature");
|
||||||
|
|
||||||
console.log({ requestSignature, referrerSignature });
|
|
||||||
const isAppPath = !!req.url?.startsWith("/app");
|
const isAppPath = !!req.url?.startsWith("/app");
|
||||||
const isResourcePath = !!req.url?.match(/\/(api|app|bootstrap|3961|ui|translations|internal|login|node_modules)/);
|
const isResourcePath = !!req.url?.match(
|
||||||
|
/\/(api|app|bootstrap|3961|ui|translations|internal|login|node_modules)/,
|
||||||
|
);
|
||||||
|
|
||||||
if (requestSignature && isAppPath) {
|
if (requestSignature && isAppPath) {
|
||||||
console.log("Has Signature");
|
console.info("Has Signature");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (referrerSignature && isResourcePath) {
|
if (referrerSignature && isResourcePath) {
|
||||||
console.log("Has Signature");
|
console.info("Has Signature");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!email) {
|
if (!email) {
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,6 @@ export const ZammadWrapper: FC<ZammadWrapperProps> = ({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
redirect: "manual",
|
redirect: "manual",
|
||||||
});
|
});
|
||||||
console.log({ res });
|
|
||||||
if (res.type === "opaqueredirect") {
|
if (res.type === "opaqueredirect") {
|
||||||
setAuthenticated(true);
|
setAuthenticated(true);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -156,14 +156,12 @@ export const getTicketStatesAction = async () => {
|
||||||
const states = await executeREST({
|
const states = await executeREST({
|
||||||
path: "/api/v1/ticket_states",
|
path: "/api/v1/ticket_states",
|
||||||
});
|
});
|
||||||
console.log({ states });
|
|
||||||
const formattedStates =
|
const formattedStates =
|
||||||
states?.map((state: any) => ({
|
states?.map((state: any) => ({
|
||||||
value: `gid://zammad/Ticket::State/${state.id}`,
|
value: `gid://zammad/Ticket::State/${state.id}`,
|
||||||
label: state.name,
|
label: state.name,
|
||||||
disabled: ["new", "merged", "removed"].includes(state.name),
|
disabled: ["new", "merged", "removed"].includes(state.name),
|
||||||
})) ?? [];
|
})) ?? [];
|
||||||
console.log({ formattedStates });
|
|
||||||
return formattedStates;
|
return formattedStates;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e.message);
|
console.error(e.message);
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ const getUserRoles = async (email: string) => {
|
||||||
});
|
});
|
||||||
return roles.filter((role: string) => role !== null);
|
return roles.filter((role: string) => role !== null);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log({ e });
|
console.error({ e });
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -13,21 +13,18 @@ export const fetchLeafcutter = async (url: string, options: any) => {
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
return json;
|
return json;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log({ error });
|
console.error({ error });
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const data = await fetchData(url, options);
|
const data = await fetchData(url, options);
|
||||||
console.log({ data });
|
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
const csrfURL = `${process.env.NEXT_PUBLIC_LEAFCUTTER_URL}/api/auth/csrf`;
|
const csrfURL = `${process.env.NEXT_PUBLIC_LEAFCUTTER_URL}/api/auth/csrf`;
|
||||||
const csrfData = await fetchData(csrfURL, {});
|
const csrfData = await fetchData(csrfURL, {});
|
||||||
console.log({ csrfData });
|
|
||||||
const authURL = `${process.env.NEXT_PUBLIC_LEAFCUTTER_URL}/api/auth/callback/credentials`;
|
const authURL = `${process.env.NEXT_PUBLIC_LEAFCUTTER_URL}/api/auth/callback/credentials`;
|
||||||
const authData = await fetchData(authURL, { method: "POST" });
|
const authData = await fetchData(authURL, { method: "POST" });
|
||||||
console.log({ authData });
|
|
||||||
if (!authData) {
|
if (!authData) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -37,5 +34,3 @@ export const fetchLeafcutter = async (url: string, options: any) => {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ const rewriteURL = (
|
||||||
path = path.slice(1);
|
path = path.slice(1);
|
||||||
}
|
}
|
||||||
const destinationURL = `${destinationBaseURL}/${path}`;
|
const destinationURL = `${destinationBaseURL}/${path}`;
|
||||||
console.log(`Rewriting ${request.url} to ${destinationURL}`);
|
console.info(`Rewriting ${request.url} to ${destinationURL}`);
|
||||||
const requestHeaders = new Headers(request.headers);
|
const requestHeaders = new Headers(request.headers);
|
||||||
|
|
||||||
requestHeaders.delete("x-forwarded-user");
|
requestHeaders.delete("x-forwarded-user");
|
||||||
|
|
|
||||||
2
apps/link/next-env.d.ts
vendored
2
apps/link/next-env.d.ts
vendored
|
|
@ -2,4 +2,4 @@
|
||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,9 @@ const finalCommand = command === "up" ? ["up", "-d", "--remove-orphans"] : [comm
|
||||||
const dockerCompose = spawn('docker', ['compose', '--env-file', '.env', ...finalFiles, ...finalCommand]);
|
const dockerCompose = spawn('docker', ['compose', '--env-file', '.env', ...finalFiles, ...finalCommand]);
|
||||||
|
|
||||||
dockerCompose.stdout.on('data', (data) => {
|
dockerCompose.stdout.on('data', (data) => {
|
||||||
console.log(`${data}`);
|
console.info(`${data}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
dockerCompose.stderr.on('data', (data) => {
|
dockerCompose.stderr.on('data', (data) => {
|
||||||
console.log(`${data}`);
|
console.info(`${data}`);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
15
package-lock.json
generated
15
package-lock.json
generated
|
|
@ -17279,6 +17279,21 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@link-stack/zammad-addon-common": "*"
|
"@link-stack/zammad-addon-common": "*"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-win32-ia32-msvc": {
|
||||||
|
"version": "14.2.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.13.tgz",
|
||||||
|
"integrity": "sha512-V26ezyjPqQpDBV4lcWIh8B/QICQ4v+M5Bo9ykLN+sqeKKBxJVDpEc6biDVyluTXTC40f5IqCU0ttth7Es2ZuMw==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ export const QRCode: FC<QRCodeProps> = ({
|
||||||
if (!verified && getValue && refreshInterval) {
|
if (!verified && getValue && refreshInterval) {
|
||||||
const interval = setInterval(async () => {
|
const interval = setInterval(async () => {
|
||||||
const { qr, kind } = await getValue(token);
|
const { qr, kind } = await getValue(token);
|
||||||
console.log({ kind });
|
|
||||||
setValue(qr);
|
setValue(qr);
|
||||||
setKind(kind);
|
setKind(kind);
|
||||||
}, refreshInterval * 1000);
|
}, refreshInterval * 1000);
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,6 @@ export class Service {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(response);
|
|
||||||
return NextResponse.json(response);
|
return NextResponse.json(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ export const Home: FC<HomeProps> = ({
|
||||||
visualizations = [],
|
visualizations = [],
|
||||||
showWelcome = true,
|
showWelcome = true,
|
||||||
}) => {
|
}) => {
|
||||||
console.log("Home", visualizations);
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const pathname = usePathname() ?? "";
|
const pathname = usePathname() ?? "";
|
||||||
const cookieName = "homeIntroComplete";
|
const cookieName = "homeIntroComplete";
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ export const VisualizationDetail: FC<VisualizationDetailProps> = ({
|
||||||
typography: { h4, p },
|
typography: { h4, p },
|
||||||
} = useLeafcutterContext();
|
} = useLeafcutterContext();
|
||||||
const finalURL = `${url}&_g=(filters%3A!()%2CrefreshInterval%3A(pause%3A!t%2Cvalue%3A0)%2Ctime%3A(from%3Anow-3y%2Cto%3Anow))`;
|
const finalURL = `${url}&_g=(filters%3A!()%2CrefreshInterval%3A(pause%3A!t%2Cvalue%3A0)%2Ctime%3A(from%3Anow-3y%2Cto%3Anow))`;
|
||||||
console.log({ finalURL });
|
|
||||||
return (
|
return (
|
||||||
<Box key={id}>
|
<Box key={id}>
|
||||||
{!editing ? (
|
{!editing ? (
|
||||||
|
|
|
||||||
|
|
@ -284,7 +284,7 @@ export const updateUserVisualization = async (
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log({ e });
|
console.error({ e });
|
||||||
}
|
}
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
|
|
@ -620,7 +620,6 @@ export const performZammadQuery = async (searchQuery: any, limit: number) => {
|
||||||
hits: { hits },
|
hits: { hits },
|
||||||
} = dataResponse.body;
|
} = dataResponse.body;
|
||||||
const results = hits.map((hit: any) => {
|
const results = hits.map((hit: any) => {
|
||||||
console.log(hit);
|
|
||||||
return {
|
return {
|
||||||
...hit._source,
|
...hit._source,
|
||||||
id: hit._id,
|
id: hit._id,
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,6 @@ export const MultiValueField: FC<MultiValueFieldProps> = ({
|
||||||
newFields[index][position] = value;
|
newFields[index][position] = value;
|
||||||
setFields(newFields);
|
setFields(newFields);
|
||||||
// formState.values[name] = Object.fromEntries(newFields);
|
// formState.values[name] = Object.fromEntries(newFields);
|
||||||
// console.log(formState.values);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import path from "path";
|
||||||
import os from "os";
|
import os from "os";
|
||||||
|
|
||||||
const packageFile = async (actualPath: string): Promise<any> => {
|
const packageFile = async (actualPath: string): Promise<any> => {
|
||||||
console.log(`Packaging: ${actualPath}`);
|
console.info(`Packaging: ${actualPath}`);
|
||||||
const packagePath = actualPath.slice(4);
|
const packagePath = actualPath.slice(4);
|
||||||
const data = await fs.readFile(actualPath, "utf-8");
|
const data = await fs.readFile(actualPath, "utf-8");
|
||||||
const content = Buffer.from(data, "utf-8").toString("base64");
|
const content = Buffer.from(data, "utf-8").toString("base64");
|
||||||
|
|
@ -74,7 +74,7 @@ export const createZPM = async ({
|
||||||
|
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
await fs.unlink(file);
|
await fs.unlink(file);
|
||||||
console.log(`${file} was deleted`);
|
console.info(`${file} was deleted`);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|
@ -89,7 +89,7 @@ export const createZPM = async ({
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
const packageJSON = JSON.parse(await fs.readFile("./package.json", "utf-8"));
|
const packageJSON = JSON.parse(await fs.readFile("./package.json", "utf-8"));
|
||||||
const { name: fullName, displayName, version } = packageJSON;
|
const { name: fullName, displayName, version } = packageJSON;
|
||||||
console.log(`Building addon ${displayName} v${version}`);
|
console.info(`Building addon ${displayName} v${version}`);
|
||||||
const name = fullName.split("/").pop();
|
const name = fullName.split("/").pop();
|
||||||
await createZPM({ name, displayName, version });
|
await createZPM({ name, displayName, version });
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -53,4 +53,4 @@ if (!re.test(name))
|
||||||
throw new Error("Name must be only alphanumeric and start with a letter");
|
throw new Error("Name must be only alphanumeric and start with a letter");
|
||||||
|
|
||||||
const res = migration(name, ups, downs);
|
const res = migration(name, ups, downs);
|
||||||
console.log(res);
|
console.info(res);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue