Skip to content

Commit 1972922

Browse files
committed
Merge branch 'master-COLLAUDO' into features-2604/tts-coll1
2 parents 82cd4d1 + da1f705 commit 1972922

File tree

103 files changed

+5423
-2291
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+5423
-2291
lines changed

.vscode/launch.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
// Usare IntelliSense per informazioni sui possibili attributi.
3+
// Al passaggio del mouse vengono visualizzate le descrizioni degli attributi esistenti.
4+
// Per altre informazioni, visitare: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "Attach",
9+
"port": 9229,
10+
"request": "attach",
11+
"skipFiles": [
12+
"<node_internals>/**"
13+
],
14+
"type": "node"
15+
},
16+
{
17+
"type": "node",
18+
"request": "launch",
19+
"name": "Avvia programma",
20+
"skipFiles": [
21+
"<node_internals>/**"
22+
],
23+
"program": "${workspaceFolder}/bin/www"
24+
}
25+
]
26+
}

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
- Added migration script to add the contact field in request object improving the search by phone number
5959

6060
# 2.15.2
61-
- Updated GitHub actions
61+
- Updated GitHub actionsm
6262

6363
# 2.15.1
6464
- Updated whatsapp-connector to 1.0.24

Dockerfile-en

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ WORKDIR /usr/src/app
1111
ARG NPM_TOKEN
1212
ARG VOICE_TOKEN
1313
ARG VOICE_TWILIO_TOKEN
14+
ARG VOICE_ENGHOUSE_TOKEN
1415

1516
COPY .npmrc_ .npmrc
1617

1718
# Set environment variable based on build argument
1819
ENV VOICE_TOKEN=${VOICE_TOKEN}
1920
ENV VOICE_TWILIO_TOKEN=${VOICE_TWILIO_TOKEN}
21+
ENV VOICE_ENGHOUSE_TOKEN=${VOICE_ENGHOUSE_TOKEN}
2022

2123
# Install app dependencies
2224
# A wildcard is used to ensure both package.json AND package-lock.json are copied

app.js

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ winston.info("DB AutoIndex: " + autoIndex);
6969
let useUnifiedTopology = process.env.MONGOOSE_UNIFIED_TOPOLOGY === 'true';
7070
winston.info("DB useUnifiedTopology: ", useUnifiedTopology, typeof useUnifiedTopology);
7171

72+
7273
var connection = mongoose.connect(databaseUri, { "useNewUrlParser": true, "autoIndex": autoIndex, "useUnifiedTopology": useUnifiedTopology }, function(err) {
7374
if (err) {
7475
winston.error('Failed to connect to MongoDB on ' + databaseUri + " ", err);
@@ -77,11 +78,13 @@ var connection = mongoose.connect(databaseUri, { "useNewUrlParser": true, "autoI
7778
winston.info("Mongoose connection done on host: "+mongoose.connection.host + " on port: " + mongoose.connection.port + " with name: "+ mongoose.connection.name)// , mongoose.connection.db);
7879
});
7980
if (process.env.MONGOOSE_DEBUG==="true") {
81+
winston.info("Mongoose log enabled");
8082
mongoose.set('debug', true);
8183
}
8284
mongoose.set('useFindAndModify', false); // https://mongoosejs.com/docs/deprecations.html#-findandmodify-
8385
mongoose.set('useCreateIndex', true);
8486
//mongoose.set('useUnifiedTopology', false);
87+
//mongoose.set('useUnifiedTopology', useUnifiedTopology);
8588

8689
// CONNECT REDIS - CHECK IT
8790
const { TdCache } = require('./utils/TdCache');
@@ -93,6 +96,9 @@ let tdCache = new TdCache({
9396

9497
tdCache.connect();
9598

99+
var cacheManager = require('./utils/cacheManager');
100+
cacheManager.setClient(tdCache);
101+
96102
// ROUTES DECLARATION
97103
var troubleshooting = require('./routes/troubleshooting');
98104
var auth = require('./routes/auth');
@@ -149,8 +155,11 @@ var property = require('./routes/property');
149155
var segment = require('./routes/segment');
150156
var webhook = require('./routes/webhook');
151157
var webhooks = require('./routes/webhooks');
158+
var roles = require('./routes/roles');
152159
var copilot = require('./routes/copilot');
153160
var mcp = require('./routes/mcp');
161+
var scheduler = require('./routes/scheduledJobs');
162+
var voice = require('./routes/voice');
154163

155164
var bootDataLoader = require('./services/bootDataLoader');
156165
var settingDataLoader = require('./services/settingDataLoader');
@@ -265,6 +274,7 @@ app.set('chatbot_service', new ChatbotService())
265274
app.set('redis_client', tdCache);
266275
app.set('quote_manager', qm);
267276
app.set('rate_manager', rm);
277+
app.set('trust proxy', true);
268278

269279
// TODO DELETE IT IN THE NEXT RELEASE
270280
if (process.env.ENABLE_ALTERNATIVE_CORS_MIDDLEWARE === "true") {
@@ -347,32 +357,38 @@ if (process.env.DISABLE_SESSION_STRATEGY==true || process.env.DISABLE_SESSION_S
347357
// redisClient.connect().catch(console.error)
348358

349359
let cacheClient = undefined;
350-
if (pubModulesManager.cache) {
360+
if (pubModulesManager.cache && pubModulesManager.cache._cache && pubModulesManager.cache._cache._cache) {
351361
cacheClient = pubModulesManager.cache._cache._cache; //_cache._cache to jump directly to redis modules without cacheoose wrapper (don't support await)
352362
}
363+
364+
if (cacheClient) {
353365
// winston.info("Express Session cacheClient",cacheClient);
354366

355367

356-
let redisStore = new RedisStore({
357-
client: cacheClient,
358-
prefix: "sessions:",
359-
})
360-
361-
app.use(
362-
session({
363-
store: redisStore,
364-
resave: false, // required: force lightweight session keep alive (touch)
365-
saveUninitialized: false, // recommended: only save session when data exists
366-
secret: sessionSecret,
367-
cookie: {
368-
secure: true, // ✅ Use HTTPS
369-
httpOnly: true, // ✅ Only accessible by the server (not client-side JS)
370-
sameSite: 'None' // ✅ Allows cross-origin (e.g., Keycloak on a different domain)
371-
}
368+
let redisStore = new RedisStore({
369+
client: cacheClient,
370+
prefix: "sessions:",
372371
})
373-
)
374-
winston.info("Express Session with Redis enabled with Secret: " + sessionSecret);
375372

373+
app.use(
374+
session({
375+
store: redisStore,
376+
resave: false, // required: force lightweight session keep alive (touch)
377+
saveUninitialized: false, // recommended: only save session when data exists
378+
secret: sessionSecret,
379+
cookie: {
380+
secure: true, // ✅ Use HTTPS
381+
httpOnly: true, // ✅ Only accessible by the server (not client-side JS)
382+
sameSite: 'None' // ✅ Allows cross-origin (e.g., Keycloak on a different domain)
383+
}
384+
})
385+
)
386+
winston.info("Express Session with Redis enabled with Secret: " + sessionSecret);
387+
} else {
388+
winston.warn("ENABLE_REDIS_SESSION is true but Redis cache is not available (pubmodules cache not initialized). Using default in-memory session store.");
389+
app.use(session({ secret: sessionSecret}));
390+
winston.info("Express Session enabled with Secret: " + sessionSecret);
391+
}
376392

377393
} else {
378394
app.use(session({ secret: sessionSecret}));
@@ -647,7 +663,10 @@ app.use('/:projectid/kb', [passport.authenticate(['basic', 'jwt'], { session: fa
647663
app.use('/:projectid/logs', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRole('agent')], logs);
648664

649665
app.use('/:projectid/webhooks', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRole('admin')], webhooks);
666+
app.use('/:projectid/scheduler', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRoleOrTypes('admin', ['bot','subscription'])], scheduler);
650667
app.use('/:projectid/copilot', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRole('agent')], copilot);
668+
app.use('/:projectid/voice', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRole('admin')], voice);
669+
app.use('/:projectid/roles', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken, roleChecker.hasRole('agent')], roles);
651670

652671
app.use('/:projectid/files', filesp);
653672

channels/chat21/chat21Handler.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,9 +527,15 @@ class Chat21Handler {
527527
requestEvent.on('request.create', function(request) {
528528

529529
winston.debug("chat21Handler requestEvent request.create called" , request);
530+
console.log('[WELCOME_MSG_FLOW] chat21Handler: received request.create', { request_id: request.request_id, channelOutbound: request.channelOutbound?.name, first_text: request.first_text });
530531
// setImmediate(() => {
531532
// perche nn c'è setImmedite? per performace
532-
if (request.channelOutbound.name === ChannelConstants.CHAT21) {
533+
if (request.channelOutbound && request.channelOutbound.name === ChannelConstants.CHAT21) {
534+
console.log('[WELCOME_MSG_FLOW] chat21Handler: channelOutbound is CHAT21, creating group');
535+
} else {
536+
console.log('[WELCOME_MSG_FLOW] chat21Handler: channelOutbound is NOT CHAT21, skipping group creation', { channelOutbound: request.channelOutbound, channelOutboundName: request.channelOutbound?.name, expected: ChannelConstants.CHAT21 });
537+
}
538+
if (request.channelOutbound && request.channelOutbound.name === ChannelConstants.CHAT21) {
533539

534540
chat21.auth.setAdminToken(adminToken);
535541

@@ -619,6 +625,7 @@ class Chat21Handler {
619625
// performance console log
620626
// console.log("************* after request.support_group.created: "+new Date().toISOString());
621627

628+
console.log('[WELCOME_MSG_FLOW] chat21Handler: emitting request.support_group.created', { request_id: request.request_id, id_project: request.id_project });
622629
requestEvent.emit('request.support_group.created', request);
623630

624631
chat21Event.emit('group.create', data);

channels/chat21/chat21WebHook.js

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ var MessageConstants = require("../../models/messageConstants");
1818
var ProjectUserUtil = require("../../utils/project_userUtil");
1919
var RequestUtil = require("../../utils/requestUtil");
2020
const authEvent = require('../../event/authEvent');
21+
const requestEvent = require('../../event/requestEvent');
2122

2223
var syncJoinAndLeaveGroupEvent = false;
2324
if (process.env.SYNC_JOIN_LEAVE_GROUP_EVENT === true || process.env.SYNC_JOIN_LEAVE_GROUP_EVENT ==="true") {
@@ -74,6 +75,7 @@ router.post('/', function (req, res) {
7475

7576
winston.debug("Chat21 message", message);
7677

78+
winston.info('chat21 webhook message received');
7779
// requestcachefarequi nocachepopulatereqired
7880
let q = Request.findOne({request_id: message.recipient})
7981

@@ -97,6 +99,15 @@ router.post('/', function (req, res) {
9799

98100
winston.debug("request not exists with request_id: " + message.recipient);
99101

102+
// After close or retention delete, Chat21 may still POST new-message (e.g. system line).
103+
// Sender "system" is not a project_user (no uuid_user/id_user row); creating a request here is wrong.
104+
// if (message.sender === "system") {
105+
// winston.verbose("Chat21 webhook: no request for recipient, sender is system — ignore (no new request)", {
106+
// recipient: message.recipient
107+
// });
108+
// return res.status(200).send({ success: true, msg: "Ignored: system message without existing request" });
109+
// }
110+
100111
var departmentid = "default";
101112

102113

@@ -208,7 +219,14 @@ router.post('/', function (req, res) {
208219
winston.debug("project_user_id: " + project_user_id);
209220
} else {
210221
// error->utente bloccato oppure non autenticator request.requester sarà nulll...⁄
211-
return winston.error("project_user not found with query: ", queryProjectUser);
222+
223+
winston.warn("project_user not found with query: ", queryProjectUser);
224+
return res.status(404).send({
225+
success: false,
226+
msg: "Project user not found for sender; cannot create request",
227+
query: queryProjectUser
228+
});
229+
//return winston.error("project_user not found with query: ", queryProjectUser);
212230
}
213231

214232

@@ -307,6 +325,45 @@ router.post('/', function (req, res) {
307325
// TODO se stato = 50 e scrive visitatotre sposto a stato 100 poi queuue lo smista
308326

309327
// TOOD update also request attributes and sourcePage
328+
if (message.sender !== "system") {
329+
const rid = request.request_id;
330+
const idProj = request.id_project;
331+
Request.findOneAndUpdate(
332+
{ request_id: rid, id_project: idProj },
333+
{ $set: { "attributes.last_message": savedMessage } },
334+
{ new: true }
335+
)
336+
.then(() => {
337+
// ConciergeBot runs on message.create inside setImmediate and sets preflight:false (request.update).
338+
// Emitting request.update here with findOneAndUpdate's doc can race: cache/WS may apply this emit
339+
// AFTER Concierge's and overwrite preflight:false with a stale true. Defer + full reload matches
340+
// changeFirstTextAndPreflightByRequestId populates and picks up the committed preflight + last_message.
341+
342+
setImmediate(() => {
343+
Request.findOne({ request_id: rid, id_project: idProj })
344+
.populate('lead')
345+
.populate('department')
346+
.populate('participatingBots')
347+
.populate('participatingAgents')
348+
.populate({ path: 'requester', populate: { path: 'id_user' } })
349+
.exec((err, freshRequest) => {
350+
if (err) {
351+
winston.error("Create message - Error reloading request after last_message update", err);
352+
return;
353+
}
354+
355+
if (freshRequest) {
356+
requestEvent.emit('request.update', freshRequest);
357+
}
358+
});
359+
});
360+
})
361+
.catch((err) => {
362+
winston.error("Create message - Error updating request attributes with last_message", err);
363+
});
364+
365+
}
366+
310367

311368
// return requestService.incrementMessagesCountByRequestId(request.request_id, request.id_project).then(function(savedRequest) {
312369
// winston.debug("savedRequest.participants.indexOf(message.sender)", savedRequest.participants.indexOf(message.sender));
@@ -315,7 +372,7 @@ router.post('/', function (req, res) {
315372
// TODO it doesn't work for internal requests bacause participanets == message.sender⁄
316373
if (request.participants && request.participants.indexOf(message.sender) > -1) { //update waiitng time if write an agent (member of participants)
317374
winston.debug("updateWaitingTimeByRequestId*******");
318-
//leave this parameter to true because it is used by websocket to notify request.update
375+
//leave this parameter to true because it is used by websocket to notify request.update
319376
return requestService.updateWaitingTimeByRequestId(request.request_id, request.id_project, true).then(function(upRequest) {
320377
return res.json(upRequest);
321378
});
@@ -398,7 +455,7 @@ router.post('/', function (req, res) {
398455
var query = {request_id: recipient_id, id_project: projectId};
399456
winston.debug('query:'+ projectId);
400457

401-
winston.debug('conversation-archived Request.findOne(query);:');
458+
winston.info('conversation-archived Request.findOne(query);:');
402459
let q = Request.findOne(query);
403460
// if (cacheEnabler.request) {
404461
// q.cache(cacheUtil.defaultTTL, projectId+":requests:request_id:"+recipient_id+":simple"); //request_cache NOT IMPORTANT HERE
@@ -485,7 +542,7 @@ router.post('/', function (req, res) {
485542
winston.debug("id_project: " + id_project);
486543

487544
// requestcachefarequi populaterequired
488-
winston.debug('join-member Request.findOne(query);:');
545+
winston.info('join-member Request.findOne(query);:');
489546
return Request.findOne({request_id: request_id, id_project: id_project})
490547
.populate('lead') //TODO posso prenderlo da snapshot senza populate cache_attention
491548
.exec(function(err, request) {
@@ -656,7 +713,7 @@ else if (req.body.event_type == "typing-start") {
656713

657714

658715
// requestcachefarequi nocachepopulatereqired
659-
winston.debug('typing-start Request.findOne(query);:');
716+
winston.info('typing-start Request.findOne(query);:');
660717
return Request.findOne({request_id: recipient_id})
661718
//TOD errore cache sistemare e riabbilitare->
662719
// .cache(cacheUtil.defaultTTL, req.projectid+":requests:request_id:"+recipient_id) cache_attention

event/authEvent.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const EventEmitter = require('events');
2+
var RoleConstants = require("../models/roleConstants");
23

34
class AuthEvent extends EventEmitter {
45
constructor() {
@@ -9,6 +10,23 @@ class AuthEvent extends EventEmitter {
910

1011
const authEvent = new AuthEvent();
1112

13+
var projectuserUpdateKey = 'project_user.update';
14+
if (process.env.QUEUE_ENABLED === "true") {
15+
projectuserUpdateKey = 'project_user.update.queue';
16+
}
17+
18+
authEvent.on(projectuserUpdateKey, function(event) {
19+
if (event.updatedProject_userPopulated) {
20+
var pu = event.updatedProject_userPopulated;
21+
console.log("AuthEvent pu.roleType: ", pu.roleType)
22+
if (pu.roleType === RoleConstants.TYPE_AGENTS) {
23+
console.log("Emit event!!!");
24+
authEvent.emit("project_user.update.agent", event);
25+
} else {
26+
authEvent.emit("project_user.update.user", event);
27+
}
28+
}
29+
});
1230

1331
//listen for sigin and signup event
1432

event/botEvent.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ class BotEvent extends EventEmitter {
8383
}
8484

8585
}
86-
86+
// winston.info("main_flow_cache_3 botevent find faqkb"); it is cached
87+
8788
// qui potresti leggere anche +secret ed evitare prossima query in botNotification
8889
// let qbot = Faq_kb.findById(botId); //TODO add cache_bot_here
8990
let qbot = Faq_kb.findById(botId).select('+secret')

event/kbEvent.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const EventEmitter = require('events');
2+
3+
class KbEvent extends EventEmitter {
4+
constructor() {
5+
super();
6+
this.queueEnabled = false;
7+
}
8+
}
9+
10+
const kbEvent = new KbEvent();
11+
12+
module.exports = kbEvent;

0 commit comments

Comments
 (0)