@itleanchatbot/shared-models-js-postgres 3.1.7 → 3.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itleanchatbot/shared-models-js-postgres",
3
- "version": "3.1.7",
3
+ "version": "3.1.9",
4
4
  "description": "Shared Models JS Postgres",
5
5
  "main": "index.js",
6
6
  "license": "ISC",
@@ -0,0 +1,40 @@
1
+ 'use strict'
2
+
3
+ module.exports = {
4
+ async up(queryInterface) {
5
+ await queryInterface.sequelize.query(`
6
+ CREATE INDEX IF NOT EXISTS idx_botiterables_ent_createdat
7
+ ON "BotIterables"("EnterpriseId", "createdAt");
8
+ `)
9
+
10
+ await queryInterface.sequelize.query(`
11
+ CREATE INDEX IF NOT EXISTS idx_botiterables_session
12
+ ON "BotIterables"("SessionId");
13
+ `)
14
+
15
+ await queryInterface.sequelize.query(`
16
+ CREATE INDEX IF NOT EXISTS idx_sessions_id_channel
17
+ ON "Sessions"(id, "ChannelId");
18
+ `)
19
+
20
+ await queryInterface.sequelize.query(`
21
+ CREATE INDEX IF NOT EXISTS idx_channels_id
22
+ ON "Channels"(id);
23
+ `)
24
+ },
25
+
26
+ async down(queryInterface) {
27
+ await queryInterface.sequelize.query(`
28
+ DROP INDEX IF EXISTS idx_botiterables_ent_createdat;
29
+ `)
30
+ await queryInterface.sequelize.query(`
31
+ DROP INDEX IF EXISTS idx_botiterables_session;
32
+ `)
33
+ await queryInterface.sequelize.query(`
34
+ DROP INDEX IF EXISTS idx_sessions_id_channel;
35
+ `)
36
+ await queryInterface.sequelize.query(`
37
+ DROP INDEX IF EXISTS idx_channels_id;
38
+ `)
39
+ },
40
+ }
@@ -0,0 +1,71 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ up: async (queryInterface, Sequelize) => {
5
+ await queryInterface.createTable('EnterpriseSSOConfigs', {
6
+ id: {
7
+ type: Sequelize.UUID,
8
+ allowNull: false,
9
+ primaryKey: true,
10
+ defaultValue: Sequelize.UUIDV4,
11
+ },
12
+
13
+ EnterpriseId: {
14
+ type: Sequelize.UUID,
15
+ allowNull: false,
16
+ references: {
17
+ model: 'Enterprises',
18
+ key: 'id',
19
+ },
20
+ onDelete: 'CASCADE',
21
+ },
22
+
23
+ provider: {
24
+ type: Sequelize.ENUM('azure_saml'),
25
+ allowNull: false,
26
+ },
27
+
28
+ emailDomain: {
29
+ type: Sequelize.STRING(255),
30
+ allowNull: false,
31
+ },
32
+
33
+ samlIssuer: {
34
+ type: Sequelize.STRING,
35
+ allowNull: false,
36
+ },
37
+
38
+ samlIdpUrl: {
39
+ type: Sequelize.STRING,
40
+ allowNull: false,
41
+ },
42
+
43
+ samlCertificate: {
44
+ type: Sequelize.TEXT,
45
+ allowNull: false,
46
+ },
47
+
48
+ isActive: {
49
+ type: Sequelize.BOOLEAN,
50
+ defaultValue: true,
51
+ },
52
+
53
+ createdAt: {
54
+ allowNull: false,
55
+ type: Sequelize.DATE,
56
+ },
57
+
58
+ updatedAt: {
59
+ allowNull: false,
60
+ type: Sequelize.DATE,
61
+ },
62
+ });
63
+ },
64
+
65
+ down: async (queryInterface, Sequelize) => {
66
+ await queryInterface.dropTable('EnterpriseSSOConfigs');
67
+ await queryInterface.sequelize.query(
68
+ 'DROP TYPE IF EXISTS "enum_EnterpriseSSOConfigs_provider";'
69
+ );
70
+ },
71
+ };
@@ -0,0 +1,57 @@
1
+ module.exports.model = (sequelize, DataTypes) => {
2
+ const EnterpriseSSOConfigs = sequelize.define('EnterpriseSSOConfigs', {
3
+ id: {
4
+ type: DataTypes.UUID,
5
+ primaryKey: true,
6
+ defaultValue: DataTypes.UUIDV4,
7
+ allowNull: false,
8
+ },
9
+
10
+ EnterpriseId: {
11
+ type: DataTypes.UUID,
12
+ allowNull: false,
13
+ },
14
+
15
+ provider: {
16
+ type: DataTypes.ENUM('azure_saml'),
17
+ allowNull: false,
18
+ },
19
+
20
+ emailDomain: {
21
+ type: DataTypes.STRING(255),
22
+ allowNull: false,
23
+ comment: 'Domínio usado para discovery (ex: empresa.com.br)',
24
+ },
25
+
26
+ samlIssuer: {
27
+ type: DataTypes.STRING,
28
+ allowNull: false,
29
+ },
30
+
31
+ samlIdpUrl: {
32
+ type: DataTypes.STRING,
33
+ allowNull: false,
34
+ },
35
+
36
+ samlCertificate: {
37
+ type: DataTypes.TEXT,
38
+ allowNull: false,
39
+ },
40
+
41
+ isActive: {
42
+ type: DataTypes.BOOLEAN,
43
+ defaultValue: true,
44
+ },
45
+ })
46
+
47
+ EnterpriseSSOConfigs.associate = (models) => {
48
+ EnterpriseSSOConfigs.belongsTo(models.Enterprises, {
49
+ foreignKey: 'EnterpriseId',
50
+ onDelete: 'CASCADE',
51
+ })
52
+ }
53
+
54
+ EnterpriseSSOConfigs._repository = EnterpriseSSOConfigs
55
+
56
+ return EnterpriseSSOConfigs
57
+ }
@@ -74,6 +74,7 @@ exports.model = (sequelize, DataTypes) => {
74
74
  Enterprises.hasMany(models.BotIterables)
75
75
  Enterprises.hasMany(models.BlacklistContacts)
76
76
  Enterprises.hasMany(models.WhitelistContacts)
77
+ Enterprises.hasOne(models.EnterpriseSSOConfigs)
77
78
  }
78
79
 
79
80
  return Enterprises
@@ -1,236 +0,0 @@
1
- 'use strict';
2
-
3
- module.exports = {
4
- async up(queryInterface) {
5
- await queryInterface.sequelize.query(`
6
- DROP VIEW IF EXISTS channel_message_type_daily;
7
-
8
- CREATE VIEW channel_message_type_daily AS
9
- WITH base AS (
10
- SELECT
11
- ((bi."createdAt" AT TIME ZONE 'UTC') AT TIME ZONE 'America/Sao_Paulo')::date AS day,
12
- bi."EnterpriseId" AS "EnterpriseId",
13
- s."ChannelId" AS "SessionChannelId",
14
-
15
- LOWER(
16
- COALESCE(
17
- NULLIF(bi.context::jsonb ->> 'channelType', ''),
18
- NULLIF(bi.context::jsonb #>> '{inbound_message,channel}', ''),
19
- NULLIF(bi.context::jsonb #>> '{inboundMessage,channel}', ''),
20
- CASE
21
- WHEN (bi.payload::jsonb #>> '{channelId}') = 'msteams'
22
- OR (bi.context::jsonb #>> '{parsedMessage,channel,id}') = 'msteams'
23
- THEN 'microsoft_teams' ELSE NULL END,
24
- CASE
25
- WHEN (bi.context::jsonb ->> 'channel') ILIKE 'whats%'
26
- OR (bi.context::jsonb ->> 'channelType') ILIKE 'whats%'
27
- THEN 'whatsapp_gupshup' ELSE NULL END
28
- )
29
- ) AS inferred_channel_type_raw,
30
-
31
- COALESCE(
32
- bi.context::jsonb #>> '{inbound_id}',
33
- bi.payload::jsonb #>> '{inboundId}',
34
- bi.context::jsonb #>> '{inbound_message,payload,id}',
35
- bi.context::jsonb #>> '{inboundMessage,payload,id}',
36
- bi.context::jsonb #>> '{inbound_message,id}',
37
- bi.context::jsonb #>> '{inboundMessage,id}',
38
- bi.payload::jsonb #>> '{transcription,jobId}',
39
- bi.id::text
40
- ) AS inbound_id,
41
-
42
- NULLIF(
43
- COALESCE(
44
- bi.payload::jsonb #>> '{transcription,jobId}',
45
- bi.payload::jsonb ->> 'jobId',
46
- bi.context::jsonb #>> '{inbound_message,payload,transcription,jobId}',
47
- bi.context::jsonb #>> '{inboundMessage,payload,transcription,jobId}'
48
- ),
49
- ''
50
- ) AS audio_job_id,
51
-
52
- /*
53
- Flags explícitas de áudio (sem regex):
54
- - contentType/mimetype começando com audio/
55
- - type = 'audio'
56
- - fallback por extensão usando ILIKE ANY
57
- */
58
- (
59
- COALESCE(bi.payload::jsonb ->> 'mimetype', '') ILIKE 'audio/%'
60
- OR COALESCE(bi.payload::jsonb ->> 'contentType', '') ILIKE 'audio/%'
61
- OR COALESCE(bi.payload::jsonb #>> '{file,contentType}', '') ILIKE 'audio/%'
62
- OR COALESCE(bi.context::jsonb #>> '{inbound_message,payload,payload,contentType}', '') ILIKE 'audio/%'
63
- OR COALESCE(bi.context::jsonb #>> '{inboundMessage,payload,payload,contentType}', '') ILIKE 'audio/%'
64
- OR COALESCE(bi.context::jsonb #>> '{inbound_message,payload,file,contentType}', '') ILIKE 'audio/%'
65
- OR COALESCE(bi.context::jsonb #>> '{inboundMessage,payload,file,contentType}', '') ILIKE 'audio/%'
66
- OR COALESCE(bi.context::jsonb #>> '{inbound_message,payload,payload,type}', '') = 'audio'
67
- OR COALESCE(bi.context::jsonb #>> '{inboundMessage,payload,payload,type}', '') = 'audio'
68
- OR LOWER(COALESCE(bi.payload::jsonb #>> '{file,name}', '')) ILIKE ANY (ARRAY['%.ogg','%.oga','%.mp3','%.m4a','%.wav','%.opus','%.ogg%','%.oga%','%.mp3%','%.m4a%','%.wav%','%.opus%'])
69
- OR LOWER(COALESCE(bi.context::jsonb #>> '{inbound_message,payload,file,name}', '')) ILIKE ANY (ARRAY['%.ogg','%.oga','%.mp3','%.m4a','%.wav','%.opus','%.ogg%','%.oga%','%.mp3%','%.m4a%','%.wav%','%.opus%'])
70
- OR LOWER(COALESCE(bi.context::jsonb #>> '{inboundMessage,payload,file,name}', '')) ILIKE ANY (ARRAY['%.ogg','%.oga','%.mp3','%.m4a','%.wav','%.opus','%.ogg%','%.oga%','%.mp3%','%.m4a%','%.wav%','%.opus%'])
71
- OR LOWER(COALESCE(bi.payload::jsonb #>> '{file,url}', '')) ILIKE ANY (ARRAY['%.ogg%','%.oga%','%.mp3%','%.m4a%','%.wav%','%.opus%'])
72
- OR LOWER(COALESCE(bi.context::jsonb #>> '{inbound_message,payload,file,url}', '')) ILIKE ANY (ARRAY['%.ogg%','%.oga%','%.mp3%','%.m4a%','%.wav%','%.opus%'])
73
- OR LOWER(COALESCE(bi.context::jsonb #>> '{inboundMessage,payload,file,url}', '')) ILIKE ANY (ARRAY['%.ogg%','%.oga%','%.mp3%','%.m4a%','%.wav%','%.opus%'])
74
- ) AS has_explicit_audio,
75
-
76
- (
77
- COALESCE(
78
- bi.payload::jsonb #>> '{transcription,jobId}',
79
- bi.payload::jsonb ->> 'jobId',
80
- bi.context::jsonb #>> '{inbound_message,payload,transcription,jobId}',
81
- bi.context::jsonb #>> '{inboundMessage,payload,transcription,jobId}'
82
- ) IS NOT NULL
83
- AND COALESCE(
84
- NULLIF(bi.payload::jsonb #>> '{transcription,jobId}', ''),
85
- NULLIF(bi.payload::jsonb ->> 'jobId', ''),
86
- NULLIF(bi.context::jsonb #>> '{inbound_message,payload,transcription,jobId}', ''),
87
- NULLIF(bi.context::jsonb #>> '{inboundMessage,payload,transcription,jobId}', '')
88
- ) IS NOT NULL
89
- ) AS has_stt_job
90
-
91
- FROM "BotIterables" bi
92
- LEFT JOIN "Sessions" s ON s.id = bi."SessionId"
93
- WHERE bi.action = 'client'
94
- AND (
95
- (bi.context::jsonb ? 'inbound_message') OR
96
- (bi.context::jsonb ? 'inboundMessage') OR
97
- (bi.context::jsonb ? 'inbound_id') OR
98
- (bi.payload::jsonb ? 'inboundId')
99
- )
100
- ),
101
-
102
- norm AS (
103
- SELECT
104
- b.*,
105
- CASE
106
- WHEN inferred_channel_type_raw IN ('microsoft_teams','msteams','teams') THEN 'microsoft_teams'
107
- WHEN inferred_channel_type_raw IN ('webchat','webchat-transcript') THEN 'webchat'
108
- WHEN inferred_channel_type_raw LIKE 'whatsapp%' THEN 'whatsapp_gupshup'
109
- ELSE NULL
110
- END AS "channelFamily"
111
- FROM base b
112
- ),
113
-
114
- typed AS (
115
- SELECT
116
- n.*,
117
- CASE
118
- WHEN n."channelFamily" = 'microsoft_teams' THEN FALSE
119
- ELSE (n.has_explicit_audio OR (n.has_stt_job AND n."channelFamily" IN ('webchat','whatsapp_gupshup')))
120
- END AS has_audio_flag
121
- FROM norm n
122
- ),
123
-
124
- consolidated AS (
125
- SELECT
126
- t.day,
127
- t."EnterpriseId",
128
- t."SessionChannelId",
129
- t."channelFamily",
130
- FIRST_VALUE(t.inferred_channel_type_raw) OVER w AS rawChannelTypeAny,
131
- BOOL_OR(t.has_audio_flag) OVER w AS any_audio_flag,
132
- FIRST_VALUE(t.audio_job_id) OVER w AS audio_job_id_any,
133
- t.inbound_id
134
- FROM typed t
135
- WINDOW w AS (PARTITION BY t.inbound_id, t."SessionChannelId")
136
- ),
137
-
138
- msgs_final AS (
139
- SELECT
140
- c.day,
141
- c."EnterpriseId",
142
- c."SessionChannelId",
143
- c."channelFamily",
144
- c.rawChannelTypeAny AS "rawChannelName_from_infer",
145
- c.inbound_id,
146
- CASE WHEN c.any_audio_flag THEN 'audio' ELSE 'text' END AS "messageType"
147
- FROM consolidated c
148
- )
149
-
150
- SELECT
151
- m.day,
152
- m."EnterpriseId",
153
- COALESCE(ch2.id, m."SessionChannelId") AS "ChannelId",
154
- CASE m."channelFamily"
155
- WHEN 'microsoft_teams' THEN 'Microsoft Teams'
156
- WHEN 'webchat' THEN 'WebChat'
157
- WHEN 'whatsapp_gupshup' THEN 'WhatsApp'
158
- ELSE COALESCE(m."rawChannelName_from_infer", ch.name)
159
- END AS "channelName",
160
- COALESCE(m."rawChannelName_from_infer", ch.name) AS "rawChannelName",
161
- m."channelFamily",
162
- m."messageType",
163
- COUNT(DISTINCT m.inbound_id) AS total
164
- FROM msgs_final m
165
- LEFT JOIN "Channels" ch ON ch.id = m."SessionChannelId"
166
- LEFT JOIN "Channels" ch2 ON ch2."EnterpriseId" = m."EnterpriseId"
167
- AND (
168
- (m."channelFamily" = 'microsoft_teams' AND ch2.name ILIKE '%teams%')
169
- OR (m."channelFamily" = 'webchat' AND ch2.name ILIKE '%webchat%')
170
- OR (m."channelFamily" = 'whatsapp_gupshup' AND (ch2.name ILIKE '%whatsapp%' OR ch2.name ILIKE '%gupshup%' OR ch2.name ILIKE '%whats%'))
171
- )
172
- GROUP BY
173
- m.day,
174
- m."EnterpriseId",
175
- COALESCE(ch2.id, m."SessionChannelId"),
176
- CASE m."channelFamily"
177
- WHEN 'microsoft_teams' THEN 'Microsoft Teams'
178
- WHEN 'webchat' THEN 'WebChat'
179
- WHEN 'whatsapp_gupshup' THEN 'WhatsApp'
180
- ELSE COALESCE(m."rawChannelName_from_infer", ch.name)
181
- END,
182
- COALESCE(m."rawChannelName_from_infer", ch.name),
183
- m."channelFamily",
184
- m."messageType";
185
-
186
-
187
-
188
- `);
189
- },
190
-
191
-
192
- async down(queryInterface) {
193
- await queryInterface.sequelize.query(`
194
- DROP VIEW IF EXISTS channel_message_type_daily;
195
-
196
- CREATE VIEW channel_message_type_daily AS
197
- WITH base AS (
198
- SELECT
199
- ((bi."createdAt" AT TIME ZONE 'UTC') AT TIME ZONE 'America/Sao_Paulo')::date AS day,
200
- bi."EnterpriseId" AS "EnterpriseId",
201
- s."ChannelId" AS "ChannelId",
202
- ch.name AS "channelName",
203
- COALESCE(
204
- bi.context::jsonb #>> '{inbound_id}',
205
- bi.payload::jsonb #>> '{inboundId}',
206
- bi.context::jsonb #>> '{inbound_message,payload,id}',
207
- bi.context::jsonb #>> '{inboundMessage,payload,id}',
208
- bi.context::jsonb #>> '{inbound_message,id}',
209
- bi.context::jsonb #>> '{inboundMessage,id}',
210
- bi.payload::jsonb #>> '{transcription,jobId}',
211
- bi.id::text
212
- ) AS inbound_id,
213
- CASE WHEN
214
- (bi.context::jsonb #>> '{inbound_message,payload,payload,type}') = 'audio' OR
215
- (bi.context::jsonb #>> '{inboundMessage,payload,payload,type}') = 'audio' OR
216
- (bi.context::jsonb #>> '{inbound_message,payload,payload,contentType}') ILIKE 'audio/%' OR
217
- (bi.context::jsonb #>> '{inboundMessage,payload,payload,contentType}') ILIKE 'audio/%' OR
218
- ((bi.payload::jsonb ->> 'mimetype') ILIKE 'audio/%')
219
- THEN 'audio' ELSE 'text' END AS "messageType"
220
- FROM "BotIterables" bi
221
- LEFT JOIN "Sessions" s ON s.id = bi."SessionId"
222
- LEFT JOIN "Channels" ch ON ch.id = s."ChannelId"
223
- WHERE bi.action = 'client'
224
- AND (
225
- (bi.context::jsonb ? 'inbound_message') OR
226
- (bi.context::jsonb ? 'inboundMessage')
227
- )
228
- )
229
- SELECT
230
- day, "EnterpriseId", "ChannelId", "channelName", "messageType",
231
- COUNT(DISTINCT inbound_id) AS total
232
- FROM base
233
- GROUP BY day, "EnterpriseId", "ChannelId", "channelName", "messageType";
234
- `);
235
- }
236
- };