const path = require('path') const { StatusCodes } = require("http-status-codes") const { createCRMContact, sendMessageSocket, mustContainProperties, journaling, findProperty, journalingRequest, createContact, lookupContactByPhone, templateValidator } = require('../utils') const fs = require("fs") const { URL } = require('url') const { oauth2 } = require('../utils') const { exchangeForTokens } = oauth2 const Company = require('../models/Company') const CRM = require('../models/CRM') const CustomError = require('../errors') const contactCreate = async (req, res) => { const { companyId, crmFirstName, crmLastName, crmPhone, crmEmail } = req.body mustContainProperties(req, ['companyId', 'crmPhone',]) await createCRMContact(companyId, crmFirstName, crmPhone, crmEmail, crmLastName) res.status(StatusCodes.OK).send() } const deleteCrm = async (req, res) => { const { companyId, crmBaseURL } = req.body mustContainProperties(req, ['companyId', 'crmBaseURL',]) const crm = await CRM.findOne({ companyId, crmBaseURL: new URL(crmBaseURL.trim()).hostname }) if (crm) await crm.deleteOne() res.status(StatusCodes.OK).send() } const deleteCompany = async (req, res) => { const { companyId } = req.body mustContainProperties(req, ['companyId',]) const company = await Company.findOne({ companyId, }) if (company) await company.deleteOne() res.status(StatusCodes.OK).send() } const callJournaling = async (req, res) => { const { companyId, operation, crmPhone, crmAgent, crmCallDuration } = req.body mustContainProperties(req, ['companyId', 'operation', 'crmPhone', 'crmAgent',]) if (operation == 'inboundAnsweredCall' && !crmCallDuration) throw new CustomError.BadRequestError(`The crmCallDuration property must be provided when operation is inboundAnsweredCall`) if (operation == 'outboundAsweredCall' && !crmCallDuration) throw new CustomError.BadRequestError(`The crmCallDuration property must be provided when operation is outboundAsweredCall`) await journaling(companyId, operation, crmPhone, crmAgent, crmCallDuration,) res.status(StatusCodes.OK).send() } const install = async (req, res) => { const { authUrl, companyId } = req.query // Store the authUrl in the session req.session.authUrl = authUrl res.redirect(authUrl) } const oauthCallBack = async (req, res) => { const { code } = req.query // Retrieve the stored authUrl from the session const storedAuthUrl = req.session.authUrl const parsedUrl = new URL(storedAuthUrl) const clientId = parsedUrl.searchParams.get('client_id') if (code) { let crmOauth = await CRM.findOne({ 'crm.authentication.crmClientId': clientId }) crmOauth = crmOauth.toObject() const authCodeProof = { grant_type: 'authorization_code', client_id: crmOauth.crm.authentication.crmClientId, client_secret: crmOauth.crm.authentication.crmClientSecret, redirect_uri: process.env.URL_OAUTH_CALLBACK, code } // Exchange the authorization code for an access token and refresh token await exchangeForTokens(crmOauth, authCodeProof) const url = `${process.env.URL_OAUTH_FRONTEND_SUCCESS_REDIRECT}?clientId=${encodeURIComponent(clientId)}` return res.redirect(url) // return res.redirect(process.env.URL_OAUTH_FRONTEND_SUCCESS_REDIRECT) } res.status(StatusCodes.OK).send() } const testTemplate = async (req, res) => { const { clientId, companyId } = req.body let crmOauth = await CRM.findOne({ 'crm.authentication.crmClientId': clientId, testing: true }) if (!crmOauth) return res.status(StatusCodes.OK).send() crmOauth = crmOauth.toObject() const { crmPhoneTest } = crmOauth.crm.authentication await templateValidator(crmPhoneTest, crmOauth.crm, companyId) crmOauth = await CRM.findOne({ 'crm.authentication.crmClientId': clientId }) crmOauth.testing = false await crmOauth.save() res.status(StatusCodes.OK).send() } const uploadCrmConfig = async (req, res) => { const { companyId, } = req.body mustContainProperties(req, ['companyId',]) if (!req?.file) throw new CustomError.BadRequestError(`The crm property file must be provided`) const file = req.file let newCrm = fs.readFileSync(file.path, "utf8") newCrm = JSON.parse(newCrm) const index = newCrm.crmRest.findIndex(rest => rest?.createContactRecord) const crmBaseURL = new URL(newCrm.crmRest[index].createContactRecord.request.url.trim()).hostname let company = await Company.findOne({ companyId }) if (!company) { company = await Company.create({ companyId }) } let crm = await CRM.findOne({ companyId, crmBaseURL }) if (crm) { crm.testing = true crm.crm = newCrm await crm.save() } else { crm = await CRM.create({ crmBaseURL, companyId: company.companyId, company, testing: true, crm: newCrm }) } fs.unlinkSync(file.path) const { type, crmClientId, crmClientSecret, crmScopes, crmPhoneTest } = crm.crm.authentication if (type == 'oauth2') { const { request, body, response } = findProperty(crm.crm.crmRest, 'authorizationEndpoint') let { url } = request url = url.replace('crmClientId', crmClientId) .replace('crmClientSecret', crmClientSecret) .replace('crmScopes', crmScopes) .replace('crmRedirectURI', process.env.URL_OAUTH_CALLBACK) const authUrl = `http://localhost:6001/api/v1/crm/install?authUrl=${encodeURIComponent(url)}&companyId=${encodeURIComponent(companyId)}` console.log('--------> authUrl: ', authUrl) return res.status(StatusCodes.OK).json({ url: authUrl }) } else { await templateValidator(crmPhoneTest, newCrm, companyId) crm.testing = false await crm.save() } res.status(StatusCodes.OK).send() } module.exports = { contactCreate, uploadCrmConfig, callJournaling, oauthCallBack, install, deleteCrm, deleteCompany, testTemplate }