From ccada53316db704ca44dcf1db7d9ac1c4b408c91 Mon Sep 17 00:00:00 2001 From: adriano Date: Mon, 26 Dec 2022 09:05:35 -0300 Subject: [PATCH 1/3] =?UTF-8?q?Altera=C3=A7=C3=A3=20para=20o=20auto=20rest?= =?UTF-8?q?ore=20funcionar=20quando=20o=20status=20for=20PAIRING?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/helpers/TicketCache.ts | 2 +- backend/src/helpers/WhatsCache.ts | 64 +++++++++---------- .../WbotServices/StartWhatsAppSession.ts | 1 + 3 files changed, 34 insertions(+), 33 deletions(-) diff --git a/backend/src/helpers/TicketCache.ts b/backend/src/helpers/TicketCache.ts index b09b31d..1a68fe1 100644 --- a/backend/src/helpers/TicketCache.ts +++ b/backend/src/helpers/TicketCache.ts @@ -53,7 +53,7 @@ const redisConn = async () => { return await conn(); } catch (e) { - console.error(e); + console.error(`There whas an error on redis connection: ${e}`); return Promise.resolve([]); } diff --git a/backend/src/helpers/WhatsCache.ts b/backend/src/helpers/WhatsCache.ts index a4764bf..65b0136 100644 --- a/backend/src/helpers/WhatsCache.ts +++ b/backend/src/helpers/WhatsCache.ts @@ -8,11 +8,11 @@ import { redisConn } from './TicketCache' import Whatsapp from "../models/Whatsapp"; import { response } from 'express' -const deleteWhatsappCache = async (hash:any) => { +const deleteWhatsappCache = async (hash: any) => { const redis: any = await redisConn(); - if(!redis) return + if (!redis) return if (redis.status !== 'connect') return @@ -39,7 +39,7 @@ const updateWhatsappCache = async (hash: any, json_object: any) => { const redis: any = await redisConn(); - if(!redis) return + if (!redis) return if (redis.status !== 'connect') return @@ -57,18 +57,18 @@ const updateWhatsappCache = async (hash: any, json_object: any) => { } -const updateWhatsappCacheById = async (hash:any, update_fields: object | any) => { +const updateWhatsappCacheById = async (hash: any, update_fields: object | any) => { const redis: any = await redisConn(); - if(!redis) return + if (!redis) return if (redis.status !== 'connect') return const whatsapp_cache: any = await redis.hgetall(hash) try { - if (whatsapp_cache && Object.keys(whatsapp_cache).length > 0) { + if (whatsapp_cache && Object.keys(whatsapp_cache).length > 0) { // update_fields.escaped_name = escapeCharCache(update_fields.name) @@ -109,15 +109,15 @@ async function searchWhatsappCache(id: string, status: string) { const redis: any = await redisConn(); - if(!redis) return + if (!redis) return - if (redis.status !== 'connect') return null + if (redis.status !== 'connect') return null - const number_cache: any = await redis.hgetall(`whatsapp:${id}`) + const number_cache: any = await redis.hgetall(`whatsapp:${id}`) - if(Object.entries(number_cache).length == 0){ - redis.quit() + if (Object.entries(number_cache).length == 0) { + await redis.quit() return [] } @@ -126,9 +126,9 @@ async function searchWhatsappCache(id: string, status: string) { // @x:foo @y:bar const response: any = await redis.call('FT.SEARCH', 'idx_whatsapp', `(@status:*${status}*) (@number:*${number_cache.number}*)`, 'SORTBY', 'status', 'ASC') - - - await redis.quit() + + + await redis.quit() if (response.length === 1) { @@ -153,30 +153,30 @@ async function searchWhatsappCache(id: string, status: string) { return results } - -const insertOrUpeateWhatsCache = async (hash:any, whatsapp: any) => { + +const insertOrUpeateWhatsCache = async (hash: any, whatsapp: any) => { const redis: any = await redisConn(); - if(!redis) return + if (!redis) return - if (redis.status !== 'connect') return + if (redis.status !== 'connect') return - if(Array.isArray(whatsapp)){ + if (Array.isArray(whatsapp)) { const pipeline = redis.pipeline() - for (let i = 0; i < whatsapp.length; i++) { - + for (let i = 0; i < whatsapp.length; i++) { + pipeline.hmset(hash, whatsapp[i]); } - + await pipeline.exec(() => { console.log(`${whatsapp.length} WHATSAPP INSERTED IN CACHE!`) }); } - else{ - - await redis.hmset(hash,JSON.parse(JSON.stringify(whatsapp))); + else { + + await redis.hmset(hash, JSON.parse(JSON.stringify(whatsapp))); console.log(`WHATSAPP ID ${whatsapp.id} INSERTED OR UPADTED IN CACHE!`) @@ -195,11 +195,11 @@ const loadWhatsappCache = async () => { const redis: any = await redisConn(); - if(!redis) return + if (!redis) return - if (redis.status !== 'connect') return + if (redis.status !== 'connect') return - let whatsapps:any = await Whatsapp.findAll({raw: true}) + let whatsapps: any = await Whatsapp.findAll({ raw: true }) const pipeline = redis.pipeline() @@ -222,7 +222,7 @@ const createWhatsappIndexCache = async (hashIndex: string) => { const redis: any = await redisConn(); - if(!redis) return + if (!redis) return if (redis.status !== 'connect') return @@ -235,7 +235,7 @@ const createWhatsappIndexCache = async (hashIndex: string) => { await redis.call('FT.DROPINDEX', hashIndex) } - const response = await redis.call('FT.CREATE', hashIndex, 'ON', 'HASH', 'PREFIX', '1', 'whatsapp:', 'SCHEMA','id', 'NUMERIC', 'status', 'TEXT', 'SORTABLE', 'number', 'TEXT', 'SORTABLE') + const response = await redis.call('FT.CREATE', hashIndex, 'ON', 'HASH', 'PREFIX', '1', 'whatsapp:', 'SCHEMA', 'id', 'NUMERIC', 'status', 'TEXT', 'SORTABLE', 'number', 'TEXT', 'SORTABLE') console.log('Whatsapp index created: ', response) @@ -248,8 +248,8 @@ const createWhatsappIndexCache = async (hashIndex: string) => { export { loadWhatsappCache, - searchWhatsappCache, - updateWhatsappCacheById, + searchWhatsappCache, + updateWhatsappCacheById, insertOrUpeateWhatsCache, deleteWhatsappCache } \ No newline at end of file diff --git a/backend/src/services/WbotServices/StartWhatsAppSession.ts b/backend/src/services/WbotServices/StartWhatsAppSession.ts index 60f9162..889876a 100644 --- a/backend/src/services/WbotServices/StartWhatsAppSession.ts +++ b/backend/src/services/WbotServices/StartWhatsAppSession.ts @@ -69,6 +69,7 @@ export const StartWhatsAppSession = async (whatsapp: Whatsapp, backupSession: bo let whatsappStatus = ["CONFLICT", "DEPRECATED_VERSION", "OPENING", + "PAIRING", "PROXYBLOCK", "SMB_TOS_BLOCK", "TIMEOUT", From c5e9c8307f1b6bd15d197a4ea365ebde72edf34a Mon Sep 17 00:00:00 2001 From: adriano Date: Mon, 26 Dec 2022 10:51:54 -0300 Subject: [PATCH 2/3] =?UTF-8?q?Atualiza=C3=A7=C3=A3o=20do=20pino-pretty=20?= =?UTF-8?q?e=20typescript?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/package.json b/backend/package.json index 43e4787..73d49cb 100644 --- a/backend/package.json +++ b/backend/package.json @@ -35,7 +35,7 @@ "mysql2": "^2.2.5", "pg": "^8.4.1", "pino": "^6.9.0", - "pino-pretty": "^4.3.0", + "pino-pretty": "~4.7.1", "qrcode-terminal": "^0.12.0", "reflect-metadata": "^0.1.13", "sequelize": "^5.22.3", @@ -78,6 +78,6 @@ "supertest": "^5.0.0", "ts-jest": "^26.4.1", "ts-node-dev": "^1.0.0-pre.63", - "typescript": "4.1" + "typescript": "4.1.6" } } From 1602b715243a8e269962df6e8fffad0396920428 Mon Sep 17 00:00:00 2001 From: adriano Date: Tue, 27 Dec 2022 11:25:50 -0300 Subject: [PATCH 3/3] =?UTF-8?q?Corre=C3=A7=C3=A3o=20no=20online/offiline?= =?UTF-8?q?=20para=20n=C3=A3o=20sobrecarregar=20o=20servidor=20com=20emitt?= =?UTF-8?q?ers=20e=20evitar=20o=20memery=20leak(vazamento=20de=20mem=C3=B3?= =?UTF-8?q?ria)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/controllers/ReportController.ts | 9 +- backend/src/helpers/WhoIsOnlineMonitor.ts | 46 ++---- backend/src/libs/socket.ts | 2 + .../components/NotificationsPopOver/index.js | 146 +++++++++++------- frontend/src/pages/Dashboard/index.js | 93 ++++++++++- frontend/src/pages/Report/index.js | 2 +- 6 files changed, 203 insertions(+), 95 deletions(-) diff --git a/backend/src/controllers/ReportController.ts b/backend/src/controllers/ReportController.ts index 6baa17f..a731ecf 100644 --- a/backend/src/controllers/ReportController.ts +++ b/backend/src/controllers/ReportController.ts @@ -27,6 +27,7 @@ import ShowQueuesByUser from "../services/UserServices/ShowQueuesByUser"; // import { filter } from "bluebird"; import { getIO } from "../libs/socket"; +import { Json } from "sequelize/types/lib/utils"; type IndexQuery = { userId: string; @@ -87,6 +88,10 @@ export const reportUserService = async (req: Request, res: Response): Promise e.userId == user.id) @@ -169,7 +176,7 @@ export const reportUserService = async (req: Request, res: Response): Promise onlineTime: ', onlineTime) + const io = getIO(); io.emit("onlineStatus", { action: "update", @@ -113,12 +117,14 @@ const monitor = async () => { } + // console.log('el: ', el) + if (el.status == 'waiting...') { emitterOnline(el, el.status, false) } - else { - emitterOnline(el, el.status) - } + // else { + // emitterOnline(el, el.status) + // } @@ -147,39 +153,15 @@ const monitor = async () => { } count++ - } - - - if (countTest > 5) { - countTest = 0 - } - - if (countTest == 0) { - try { - console.log(' Carregando usuarios no listUserId...') - listUserId = await ListUserParamiterService({ profile: 'user' }) - - } catch (error) { - console.log('There was an erro on ListUserParamiterService: ', error) - return - } - - } - - countTest = 1 - - listUserId.forEach((u) => { - - const io = getIO(); - io.emit("isOnline", { action: "online", userId: u.id }); - }); + } + } catch (error) { console.log('>>> There was an error no WhoIsOnlineMonitor.ts: ', error) listUserId = [] count = 0 - countTest = 0 + // countTest = 0 uuid = 0 } @@ -211,7 +193,7 @@ const startWhoIsOnlineMonitor = async (mileseconds?: number) => { listUserId = [] count = 0 - countTest = 0 + // countTest = 0 uuid = 0 clearInterval(_fifo); diff --git a/backend/src/libs/socket.ts b/backend/src/libs/socket.ts index 1323a26..ea923b4 100644 --- a/backend/src/libs/socket.ts +++ b/backend/src/libs/socket.ts @@ -47,6 +47,8 @@ export const initIO = (httpServer: Server): SocketIO => { socket.on("online", (userId: any) => { + + // console.log('userId: ', userId) obj.uuid = uuidv4() diff --git a/frontend/src/components/NotificationsPopOver/index.js b/frontend/src/components/NotificationsPopOver/index.js index 4f458ca..c87d9e0 100644 --- a/frontend/src/components/NotificationsPopOver/index.js +++ b/frontend/src/components/NotificationsPopOver/index.js @@ -40,6 +40,29 @@ const useStyles = makeStyles(theme => ({ }, })); + +let _fifo + +// const onlineEmitter = async (socket, user) => { + +// try { +// clearInterval(_fifo); + +// socket.emit("online", user.id) + +// } catch (error) { +// console.log('error on onlineEmitter: ', error) +// } +// finally { +// _fifo = setInterval(onlineEmitter, 3000); +// } +// } + +// _fifo = setInterval(onlineEmitter, 3000); + + + + const NotificationsPopOver = () => { const classes = useStyles(); @@ -57,20 +80,20 @@ const NotificationsPopOver = () => { const [play] = useSound(alertSound); const soundAlertRef = useRef(); - const historyRef = useRef(history); + const historyRef = useRef(history); - const { handleLogout } = useContext(AuthContext); + const { handleLogout } = useContext(AuthContext); // const [lastRef] = useState(+history.location.pathname.split("/")[2]) - - + + useEffect(() => { soundAlertRef.current = play; if (!("Notification" in window)) { - + } else { Notification.requestPermission(); } @@ -82,53 +105,70 @@ const NotificationsPopOver = () => { useEffect(() => { - - ticketIdRef.current = ticketIdUrl; + + ticketIdRef.current = ticketIdUrl; }, [ticketIdUrl]); useEffect(() => { - const socket = openSocket(process.env.REACT_APP_BACKEND_URL); + const socket = openSocket(process.env.REACT_APP_BACKEND_URL); - socket.on("onlineStatus", (data) => { - - if(data.action === "logout"){ + socket.on("onlineStatus", (data) => { - - - if(`${user.id}` === data.userOnlineTime['userId']){ + if (data.action === "logout") { - socket.emit("online", {logoutUserId: user.id}) + + + if (`${user.id}` === data.userOnlineTime['userId']) { + + socket.emit("online", { logoutUserId: user.id }) handleLogout(); - } - - } - + } + + } + }); + + // socket.on("isOnline", (data) => { + + // if (data.action === "online") { + + // if (data.userId === user.id) { + + // socket.emit("online", user.id) + // } + + // } + + // }); + + + if(user.profile === 'user'){ + + if(_fifo){ + clearInterval(_fifo); + } - socket.on("isOnline", (data) => { - - - - if(data.action === "online"){ - - if(data.userId === user.id){ - + _fifo = setInterval(()=>{ + console.log('user.id: ', user.id) socket.emit("online", user.id) - } + }, 3000); + + } - } - }); + + + return () => { - socket.disconnect(); + socket.disconnect(); }; - }, [user.id, handleLogout]); + }, [user.id, handleLogout]); @@ -136,13 +176,13 @@ const NotificationsPopOver = () => { useEffect(() => { const socket = openSocket(process.env.REACT_APP_BACKEND_URL); - socket.on("connect", () => socket.emit("joinNotification")); - + socket.on("connect", () => socket.emit("joinNotification")); + socket.on("ticket", data => { if (data.action === "updateUnread" || data.action === "delete") { - + setNotifications(prevState => { const ticketIndex = prevState.findIndex(t => t.id === data.ticketId); @@ -169,7 +209,7 @@ const NotificationsPopOver = () => { socket.on("appMessage", data => { - + if ( data.action === "create" && @@ -177,12 +217,12 @@ const NotificationsPopOver = () => { (data.ticket.userId === user?.id || !data.ticket.userId) ) { - - + + setNotifications(prevState => { - + // prevState.forEach((e)=>{ // @@ -190,23 +230,23 @@ const NotificationsPopOver = () => { const ticketIndex = prevState.findIndex(t => t.id === data.ticket.id); if (ticketIndex !== -1) { - + prevState[ticketIndex] = data.ticket; return [...prevState]; } - + return [data.ticket, ...prevState]; - }); - - + }); + + const shouldNotNotificate = (data.message.ticketId === ticketIdRef.current && document.visibilityState === "visible") || - (data.ticket.userId && data.ticket.userId !== user?.id) || + (data.ticket.userId && data.ticket.userId !== user?.id) || data.ticket.isGroup || !data.ticket.userId; - if (shouldNotNotificate) return; - - + if (shouldNotNotificate) return; + + handleNotifications(data); } @@ -219,7 +259,7 @@ const NotificationsPopOver = () => { const handleNotifications = data => { const { message, contact, ticket } = data; - + const options = { body: `${message.body} - ${format(new Date(), "HH:mm")}`, icon: contact.profilePicUrl, @@ -293,13 +333,13 @@ const NotificationsPopOver = () => { onClose={handleClickAway} > - {notifications.length === 0 ? ( - + {notifications.length === 0 ? ( + {i18n.t("notifications.noTickets")} - ) : ( + ) : ( - notifications.map(ticket => ( + notifications.map(ticket => ( diff --git a/frontend/src/pages/Dashboard/index.js b/frontend/src/pages/Dashboard/index.js index 8584ce9..f940a6d 100644 --- a/frontend/src/pages/Dashboard/index.js +++ b/frontend/src/pages/Dashboard/index.js @@ -1,5 +1,7 @@ import React, { useContext, useReducer, useEffect, useState } from "react"; +import { addHours, addMinutes, addSeconds, intervalToDuration, add } from "date-fns"; + import Paper from "@material-ui/core/Paper"; import Container from "@material-ui/core/Container"; import Grid from "@material-ui/core/Grid"; @@ -104,6 +106,36 @@ const useStyles = makeStyles((theme) => ({ }, })); +var _fifo + +const sumOnlineTimeNow = (oldOnlineTimeSum) => { + + let onlineTime = new Date() + + onlineTime.setUTCHours(new Date(oldOnlineTimeSum.onlineTime).getHours()) + onlineTime.setUTCMinutes(new Date(oldOnlineTimeSum.onlineTime).getMinutes()) + onlineTime.setUTCSeconds(new Date(oldOnlineTimeSum.onlineTime).getSeconds()) + + + let newtTime = intervalToDuration({ start: new Date(oldOnlineTimeSum.updatedAt), end: new Date() }) + + + if (newtTime.hours && +newtTime.hours > 0) { + onlineTime = addHours(onlineTime, newtTime.hours) + } + if (newtTime.minutes && +newtTime.minutes > 0) { + onlineTime = addMinutes(onlineTime, newtTime.minutes) + } + if (newtTime.seconds && +newtTime.seconds > 0) { + onlineTime = addSeconds(onlineTime, newtTime.seconds) + } + + const isoDate = new Date(onlineTime); + const newOnlinetime = isoDate.toJSON().slice(0, 19).replace('T', ' '); + + return newOnlinetime +} + const reducer = (state, action) => { if (action.type === "DELETE_USER_STATUS") { const userId = action.payload; @@ -138,6 +170,8 @@ const reducer = (state, action) => { let onlineUser = action.payload; let index = -1; + // console.log('UPDATE_STATUS_ONLINE: ', onlineUser) + let onlySumOpenClosed = false; if (onlineUser.sumOpen || onlineUser.sumClosed) { @@ -157,18 +191,27 @@ const reducer = (state, action) => { if (!("statusOnline" in state[index])) { state[index].statusOnline = onlineUser; } else if ("statusOnline" in state[index]) { + state[index].statusOnline["status"] = onlineUser.status; + } } + if ("onlineTime" in onlineUser) { + if ("sumOnlineTime" in state[index]) { - state[index].sumOnlineTime["sum"] = onlineUser.onlineTime.split(" ")[1]; - } else if (!("sumOnlineTime" in state[index])) { + + // console.log(' ffffffffffffffffffffffffffff ') + state[index].sumOnlineTime.sum = onlineUser.onlineTime.split(" ")[1] + + } else if (!("sumOnlineTime" in state[index])) { + state[index].sumOnlineTime = { userId: onlineUser.userId, sum: onlineUser.onlineTime.split(" ")[1], }; + } } @@ -235,13 +278,16 @@ const Dashboard = () => { let date = new Date().toLocaleDateString("pt-BR").split("/"); let dateToday = `${date[2]}-${date[1]}-${date[0]}`; - const dataQuery = await api.get("/reports/user/services", { + const { data } = await api.get("/reports/user/services", { params: { userId: null, startDate: dateToday, endDate: dateToday }, }); + + // console.log('data.data: ', data.usersProfile) + dispatch({ type: "RESET" }); - dispatch({ type: "LOAD_QUERY", payload: dataQuery.data }); + dispatch({ type: "LOAD_QUERY", payload: data.usersProfile }); } catch (err) { - + } }; @@ -250,6 +296,37 @@ const Dashboard = () => { return () => clearTimeout(delayDebounceFn); }, []); + + useEffect(() => { + + + if (!usersOnlineInfo || usersOnlineInfo.length == 0) return + + if (_fifo) { + clearInterval(_fifo); + } + + _fifo = setInterval(() => { + + usersOnlineInfo.map((e) => { + + if (e.statusOnline && e.statusOnline.status === 'online') { + + let onlineTimeCurrent = sumOnlineTimeNow({onlineTime: e.statusOnline.onlineTime, updatedAt: e.statusOnline.updatedAt}) + + dispatch({ type: "UPDATE_STATUS_ONLINE", payload: { userId: e.id, status: e.statusOnline.status, onlineTime: onlineTimeCurrent } }); + + } + + }) + + + }, 3000); + + + }, [usersOnlineInfo]) + + useEffect(() => { const socket = openSocket(process.env.REACT_APP_BACKEND_URL); @@ -280,7 +357,7 @@ const Dashboard = () => { }, []); useEffect(() => { - if (ticketStatusChange === "") return + if (ticketStatusChange === "") return const delayDebounceFn = setTimeout(() => { const fetchQueries = async () => { try { @@ -372,10 +449,10 @@ const Dashboard = () => { style={{ overflow: "hidden" }} variant="outlined" > - + Em Atendimento - Hoje/Todo Periodo + Hoje/Todo Periodo diff --git a/frontend/src/pages/Report/index.js b/frontend/src/pages/Report/index.js index d0cbe32..d62f124 100644 --- a/frontend/src/pages/Report/index.js +++ b/frontend/src/pages/Report/index.js @@ -728,7 +728,7 @@ const Report = () => { { title: 'Tempo online', field: 'sumOnlineTime.sum' }, { title: `${i18n.t("reports.dateStart")}`, field: 'startDate' }, - { title: `${i18n.t("reports.dateStart")}`, field: 'endDate' }, + { title: `${i18n.t("reports.dateEnd")}`, field: 'endDate' }, { title: 'Em atendimento', field: 'sumOpen.count' }, { title: 'Finalizado', field: 'sumClosed.count' },