import Redis from 'ioredis' import { List } from 'whatsapp-web.js' const unflatten = require('flat').unflatten var flatten = require('flat') import ListTicketServiceCache from "../services/TicketServices/ListTicketServiceCache" import { escapeCharCache } from './ContactsCache' const redisConn = async () => { if(!process.env.CACHE){ return null } try { const redis = new Redis(); const conn = () => new Promise((resolve, reject) => { redis.on('error', (err) => { if (err.code === 'ECONNREFUSED') { console.error(`Redis connection error: ${err}.`) redis.quit() } else { console.error(`Redis encountered an error: ${err.message}.`) } reject(err) }) redis.on('connect', () => { resolve(redis); }) }); return await conn(); } catch (e) { console.error(e); return Promise.resolve([]); } } const flushCache = async () => { const redis: any = await redisConn(); if(!redis) return false if (redis.status === 'connect') { console.log('TICKETS CACHE REMOVED') await redis.call('FLUSHALL') redis.quit() } } const cacheSize = async () => { const redis: any = await redisConn(); if(!redis) return null if (redis.status !== 'connect') { return -1 } const size = await redis.call('dbsize') redis.quit() return size } const loadTicketsCache = async () => { const redis: any = await redisConn(); if(!redis) return if (redis.status !== 'connect') return await createTicketIndexCache('idx_ticket') let tickets = await ListTicketServiceCache({}) const pipeline = redis.pipeline() for (let i = 0; i < tickets.length; i++) { tickets[i].createdAt = new Date(tickets[i].createdAt).toISOString() tickets[i].updatedAt = new Date(tickets[i].updatedAt).toISOString() tickets[i].escaped_name = escapeCharCache(tickets[i]['contact.name']) // tickets[i]['contact_name'] = tickets[i]['contact.name'] // delete tickets[i]['contact.name'] tickets[i]['contact_number'] = tickets[i]['contact.number'] delete tickets[i]['contact.number'] pipeline.hmset(`ticket:${tickets[i].id}`, tickets[i]); } await pipeline.exec(() => { console.log(`${tickets.length} TICKETS INSERTED IN CACHE!`) }); redis.quit() } const createTicketIndexCache = async (hashIndex: string) => { const redis: any = await redisConn(); if(!redis) return if (redis.status !== 'connect') return try { const lst_index_redis: any = await redis.call('FT._LIST') if (lst_index_redis.includes(hashIndex)) { console.log('entrou...') await redis.call('FT.DROPINDEX', hashIndex) } const response = await redis.call('FT.CREATE', hashIndex, 'ON', 'HASH', 'PREFIX', '1', 'ticket:', 'SCHEMA', 'escaped_name', 'TEXT', 'SORTABLE', 'contact_number', 'TEXT', 'SORTABLE', 'status', 'TAG', 'SORTABLE') console.log('Ticket index created: ', response) } catch (error) { console.log('There was an error on createTicketIndexCache: ', error) } redis.quit() } const updateTicketCache = async (hash: any, json_object: any) => { const redis: any = await redisConn(); if(!redis) return if (redis.status !== 'connect') return const pipeline = redis.pipeline() let entries = Object.entries(json_object) entries.forEach((e: any) => { pipeline.hset(hash, e[0], e[1]) }) await pipeline.exec(() => { console.log("updateTicketCache Key/value inserted/updated") }); redis.quit() } const updateTicketCacheByTicketId = async (ticketId: string | number, update_fields: any) => { const redis: any = await redisConn(); if(!redis) return if (redis.status !== 'connect') return const ticket_cache = await redis.hgetall(`ticket:${ticketId}`) try { if (ticket_cache && Object.keys(ticket_cache).length > 0) { if (update_fields.escaped_name) { update_fields.escaped_name = escapeCharCache(update_fields['contact.name']) } await updateTicketCache(`ticket:${ticketId}`, update_fields) console.log(`updateTicketCacheByTicketId TICKET ${ticket_cache['contact_number']} CACHE WAS UPDATED!`) } else { console.log('TICKET CACHE NOT FOUND!') } } catch (error) { console.log(`There was an error on updateTicketCacheByTicketId: ${error}`) } redis.quit() } const createOrUpdateTicketCache = async (hash: any, ticket: any) => { const redis: any = await redisConn(); if(!redis) return if (redis.status !== 'connect') return if (redis.status !== 'connect') return ticket.escaped_name = escapeCharCache(ticket['contact.name']) ticket['contact_number'] = ticket['contact.number'] delete ticket['contact.number'] await redis.hmset(hash, ticket); console.log('CREATED/UPDATED TICKET CACHE') redis.quit() } const deleteTicketsByIdCache = async (ticketId: string | number) => { const redis: any = await redisConn(); if(!redis) return if (redis.status !== 'connect') return const ticket_cache = await redis.hgetall(`ticket:${ticketId}`) try { if (ticket_cache && Object.keys(ticket_cache).length > 0) { await redis.del(`ticket:${ticketId}`) console.log(`TICKET ${ticket_cache['id']} CACHE WAS DELETED!`) } else { console.log('TICKET CACHE NOT FOUND!') } } catch (error) { console.log(`There was an error on deleteTicketsByIdCache: ${error}`) } redis.quit() } const deleteTicketsFieldsCache = async (tickets: any, del_fields: any) => { const redis: any = await redisConn(); if(!redis) return if (redis.status !== 'connect') return const pipeline = redis.pipeline() if (tickets && tickets.length > 0) { try { for (let i = 0; i < tickets.length; i++) { pipeline.hdel(`ticket:${tickets[i]['id']}`, del_fields) } await pipeline.exec(() => { console.log(`Tickets cache contact updated!`) }) } catch (error) { console.log('There was an error on deleteTicketsFieldsByContactsCache function: ', error) } } redis.quit() } const updateTicketsByContactsCache = async (oldNumber: string, newName: string, newNumber: string) => { const redis: any = await redisConn(); if(!redis) return if (redis.status !== 'connect') return const pipeline = redis.pipeline() const tickets = await searchTicketCache(oldNumber) if (tickets && tickets.length > 0) { try { for (let i = 0; i < tickets.length; i++) { tickets[i]['contact.name'] = newName tickets[i].escaped_name = escapeCharCache(newName) tickets[i]['contact_number'] = newNumber pipeline.hmset(`ticket:${tickets[i]['id']}`, tickets[i]) } await pipeline.exec(() => { console.log(`updateTicketsByContactsCache Tickets cache contact updated!`) }) } catch (error) { console.log('There was an error on updateTicketsByContactsCache function: ', error) } } redis.quit() } const deleteTicketsByContactsCache = async (number: string) => { const redis: any = await redisConn(); if(!redis) return if (redis.status !== 'connect') return const pipeline = redis.pipeline() const tickets = await searchTicketCache(number) if (tickets && tickets.length > 0) { try { for (let i = 0; i < tickets.length; i++) { pipeline.del(`ticket:${tickets[i]['id']}`) } await pipeline.exec(() => { console.log(`Tickets cache number ${tickets[0]['contact_number']} deleted!`) }) } catch (error) { console.log('There was an error on deleteTicketsByContactsCache function: ', error) } } redis.quit() } const deleteTicketCache = async (hash: any) => { const redis: any = await redisConn(); if(!redis) return if (redis.status !== 'connect') return await redis.del(hash) redis.quit() } async function searchTicketCache(search: string, offset?: number, limit?: number) { const redis:any = await redisConn(); if(!redis) return if(redis.status!=='connect') return null search = escapeCharCache(search) let response: any = undefined if (offset != undefined && limit != undefined) { response = await redis.call('FT.SEARCH', 'idx_ticket', `(@escaped_name:*${search}*)|(@contact_number:*${search}*)`, 'LIMIT', offset, limit, 'SORTBY', 'status', 'DESC') } else { response = await redis.call('FT.SEARCH', 'idx_ticket', `(@escaped_name:*${search}*)|(@contact_number:*${search}*)`) } redis.quit() // console.log('response: ', response) if (response.length === 1) { return [] } const results: any = [] for (let n = 2; n < response.length; n += 2) { const result: any = {} const fieldNamesAndValues = response[n] for (let m = 0; m < fieldNamesAndValues.length; m += 2) { const k = fieldNamesAndValues[m] const v = fieldNamesAndValues[m + 1] result[k] = v } results.push(result) } return results } export { loadTicketsCache, updateTicketCache, createOrUpdateTicketCache, searchTicketCache, updateTicketCacheByTicketId, deleteTicketsByContactsCache, updateTicketsByContactsCache, deleteTicketsByIdCache, flushCache, deleteTicketsFieldsCache, cacheSize, redisConn }