tychat-contracts 1.0.75 → 1.0.76
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/dist/connections/index.d.ts +1 -0
- package/dist/connections/index.d.ts.map +1 -1
- package/dist/connections/index.js +1 -0
- package/dist/connections/whatsapp-official.dto.d.ts +31 -0
- package/dist/connections/whatsapp-official.dto.d.ts.map +1 -0
- package/dist/connections/whatsapp-official.dto.js +149 -0
- package/dist/conversations/followup-config-response.dto.d.ts +7 -0
- package/dist/conversations/followup-config-response.dto.d.ts.map +1 -1
- package/dist/conversations/followup-config-response.dto.js +73 -0
- package/dist/tenants/index.d.ts +2 -0
- package/dist/tenants/index.d.ts.map +1 -1
- package/dist/tenants/index.js +2 -0
- package/dist/tenants/tenant-slug.util.d.ts +46 -0
- package/dist/tenants/tenant-slug.util.d.ts.map +1 -0
- package/dist/tenants/tenant-slug.util.js +59 -0
- package/dist/tenants/tenant-slug.util.spec.d.ts +2 -0
- package/dist/tenants/tenant-slug.util.spec.d.ts.map +1 -0
- package/dist/tenants/tenant-slug.util.spec.js +102 -0
- package/dist/tenants/whatsapp-provider-kind.dto.d.ts +9 -0
- package/dist/tenants/whatsapp-provider-kind.dto.d.ts.map +1 -0
- package/dist/tenants/whatsapp-provider-kind.dto.js +13 -0
- package/package.json +2 -1
- package/src/connections/index.ts +1 -0
- package/src/connections/whatsapp-official.dto.ts +113 -0
- package/src/conversations/followup-config-response.dto.ts +60 -0
- package/src/tenants/index.ts +2 -0
- package/src/tenants/tenant-slug.util.ts +56 -0
- package/src/tenants/whatsapp-provider-kind.dto.ts +12 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/connections/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/connections/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payload to register a WhatsApp Official (Meta Cloud API) instance for a tenant.
|
|
3
|
+
* Requires the access token, phone number ID, and WhatsApp Business Account ID
|
|
4
|
+
* obtained from the Meta developer portal.
|
|
5
|
+
*/
|
|
6
|
+
export declare class CreateWhatsAppOfficialInstanceDto {
|
|
7
|
+
accessToken: string;
|
|
8
|
+
phoneNumberId: string;
|
|
9
|
+
wabaId: string;
|
|
10
|
+
webhookVerifyToken?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Payload to send a pre-approved template message via WhatsApp Official (Meta Cloud API).
|
|
14
|
+
* Templates must be created and approved in the Meta Business Manager before use.
|
|
15
|
+
*/
|
|
16
|
+
export declare class SendWhatsAppOfficialTemplateDto {
|
|
17
|
+
to: string;
|
|
18
|
+
templateName: string;
|
|
19
|
+
languageCode: string;
|
|
20
|
+
bodyParameters?: string[];
|
|
21
|
+
headerParameters?: string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Payload to send a free-form text message via WhatsApp Official (Meta Cloud API).
|
|
25
|
+
* Only works within the 24-hour customer service window.
|
|
26
|
+
*/
|
|
27
|
+
export declare class SendWhatsAppOfficialTextDto {
|
|
28
|
+
to: string;
|
|
29
|
+
text: string;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=whatsapp-official.dto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whatsapp-official.dto.d.ts","sourceRoot":"","sources":["../../src/connections/whatsapp-official.dto.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,qBAAa,iCAAiC;IAO5C,WAAW,EAAE,MAAM,CAAC;IAQpB,aAAa,EAAE,MAAM,CAAC;IAQtB,MAAM,EAAE,MAAM,CAAC;IAQf,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,qBAAa,+BAA+B;IAO1C,EAAE,EAAE,MAAM,CAAC;IAQX,YAAY,EAAE,MAAM,CAAC;IAQrB,YAAY,EAAE,MAAM,CAAC;IAWrB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAS1B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED;;;GAGG;AACH,qBAAa,2BAA2B;IAOtC,EAAE,EAAE,MAAM,CAAC;IAQX,IAAI,EAAE,MAAM,CAAC;CACd"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.SendWhatsAppOfficialTextDto = exports.SendWhatsAppOfficialTemplateDto = exports.CreateWhatsAppOfficialInstanceDto = void 0;
|
|
13
|
+
const swagger_1 = require("@nestjs/swagger");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
/**
|
|
16
|
+
* Payload to register a WhatsApp Official (Meta Cloud API) instance for a tenant.
|
|
17
|
+
* Requires the access token, phone number ID, and WhatsApp Business Account ID
|
|
18
|
+
* obtained from the Meta developer portal.
|
|
19
|
+
*/
|
|
20
|
+
class CreateWhatsAppOfficialInstanceDto {
|
|
21
|
+
accessToken;
|
|
22
|
+
phoneNumberId;
|
|
23
|
+
wabaId;
|
|
24
|
+
webhookVerifyToken;
|
|
25
|
+
}
|
|
26
|
+
exports.CreateWhatsAppOfficialInstanceDto = CreateWhatsAppOfficialInstanceDto;
|
|
27
|
+
__decorate([
|
|
28
|
+
(0, swagger_1.ApiProperty)({
|
|
29
|
+
description: 'Meta Cloud API permanent access token',
|
|
30
|
+
example: 'EAABs...',
|
|
31
|
+
}),
|
|
32
|
+
(0, class_validator_1.IsString)(),
|
|
33
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
34
|
+
__metadata("design:type", String)
|
|
35
|
+
], CreateWhatsAppOfficialInstanceDto.prototype, "accessToken", void 0);
|
|
36
|
+
__decorate([
|
|
37
|
+
(0, swagger_1.ApiProperty)({
|
|
38
|
+
description: 'Phone Number ID from Meta Business Manager',
|
|
39
|
+
example: '106540352242922',
|
|
40
|
+
}),
|
|
41
|
+
(0, class_validator_1.IsString)(),
|
|
42
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
43
|
+
__metadata("design:type", String)
|
|
44
|
+
], CreateWhatsAppOfficialInstanceDto.prototype, "phoneNumberId", void 0);
|
|
45
|
+
__decorate([
|
|
46
|
+
(0, swagger_1.ApiProperty)({
|
|
47
|
+
description: 'WhatsApp Business Account ID (WABA ID)',
|
|
48
|
+
example: '102291529438901',
|
|
49
|
+
}),
|
|
50
|
+
(0, class_validator_1.IsString)(),
|
|
51
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
52
|
+
__metadata("design:type", String)
|
|
53
|
+
], CreateWhatsAppOfficialInstanceDto.prototype, "wabaId", void 0);
|
|
54
|
+
__decorate([
|
|
55
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
56
|
+
description: 'Webhook verify token used to validate Meta webhook registration',
|
|
57
|
+
example: 'my-verify-token',
|
|
58
|
+
}),
|
|
59
|
+
(0, class_validator_1.IsOptional)(),
|
|
60
|
+
(0, class_validator_1.IsString)(),
|
|
61
|
+
__metadata("design:type", String)
|
|
62
|
+
], CreateWhatsAppOfficialInstanceDto.prototype, "webhookVerifyToken", void 0);
|
|
63
|
+
/**
|
|
64
|
+
* Payload to send a pre-approved template message via WhatsApp Official (Meta Cloud API).
|
|
65
|
+
* Templates must be created and approved in the Meta Business Manager before use.
|
|
66
|
+
*/
|
|
67
|
+
class SendWhatsAppOfficialTemplateDto {
|
|
68
|
+
to;
|
|
69
|
+
templateName;
|
|
70
|
+
languageCode;
|
|
71
|
+
bodyParameters;
|
|
72
|
+
headerParameters;
|
|
73
|
+
}
|
|
74
|
+
exports.SendWhatsAppOfficialTemplateDto = SendWhatsAppOfficialTemplateDto;
|
|
75
|
+
__decorate([
|
|
76
|
+
(0, swagger_1.ApiProperty)({
|
|
77
|
+
description: 'Recipient phone number with country code (digits only)',
|
|
78
|
+
example: '5511999999999',
|
|
79
|
+
}),
|
|
80
|
+
(0, class_validator_1.IsString)(),
|
|
81
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
82
|
+
__metadata("design:type", String)
|
|
83
|
+
], SendWhatsAppOfficialTemplateDto.prototype, "to", void 0);
|
|
84
|
+
__decorate([
|
|
85
|
+
(0, swagger_1.ApiProperty)({
|
|
86
|
+
description: 'Approved template name in Meta Business Manager',
|
|
87
|
+
example: 'appointment_confirmation',
|
|
88
|
+
}),
|
|
89
|
+
(0, class_validator_1.IsString)(),
|
|
90
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
91
|
+
__metadata("design:type", String)
|
|
92
|
+
], SendWhatsAppOfficialTemplateDto.prototype, "templateName", void 0);
|
|
93
|
+
__decorate([
|
|
94
|
+
(0, swagger_1.ApiProperty)({
|
|
95
|
+
description: 'Template language code (BCP 47)',
|
|
96
|
+
example: 'pt_BR',
|
|
97
|
+
}),
|
|
98
|
+
(0, class_validator_1.IsString)(),
|
|
99
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
100
|
+
__metadata("design:type", String)
|
|
101
|
+
], SendWhatsAppOfficialTemplateDto.prototype, "languageCode", void 0);
|
|
102
|
+
__decorate([
|
|
103
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
104
|
+
description: 'Template body variable components (positional parameters). ' +
|
|
105
|
+
'Each element replaces {{1}}, {{2}}, etc. in the template body.',
|
|
106
|
+
example: ['João', '15/04/2026 às 10:00'],
|
|
107
|
+
type: [String],
|
|
108
|
+
}),
|
|
109
|
+
(0, class_validator_1.IsOptional)(),
|
|
110
|
+
(0, class_validator_1.IsString)({ each: true }),
|
|
111
|
+
__metadata("design:type", Array)
|
|
112
|
+
], SendWhatsAppOfficialTemplateDto.prototype, "bodyParameters", void 0);
|
|
113
|
+
__decorate([
|
|
114
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
115
|
+
description: 'Template header variable components (positional parameters)',
|
|
116
|
+
example: ['Clínica Saúde'],
|
|
117
|
+
type: [String],
|
|
118
|
+
}),
|
|
119
|
+
(0, class_validator_1.IsOptional)(),
|
|
120
|
+
(0, class_validator_1.IsString)({ each: true }),
|
|
121
|
+
__metadata("design:type", Array)
|
|
122
|
+
], SendWhatsAppOfficialTemplateDto.prototype, "headerParameters", void 0);
|
|
123
|
+
/**
|
|
124
|
+
* Payload to send a free-form text message via WhatsApp Official (Meta Cloud API).
|
|
125
|
+
* Only works within the 24-hour customer service window.
|
|
126
|
+
*/
|
|
127
|
+
class SendWhatsAppOfficialTextDto {
|
|
128
|
+
to;
|
|
129
|
+
text;
|
|
130
|
+
}
|
|
131
|
+
exports.SendWhatsAppOfficialTextDto = SendWhatsAppOfficialTextDto;
|
|
132
|
+
__decorate([
|
|
133
|
+
(0, swagger_1.ApiProperty)({
|
|
134
|
+
description: 'Recipient phone number with country code (digits only)',
|
|
135
|
+
example: '5511999999999',
|
|
136
|
+
}),
|
|
137
|
+
(0, class_validator_1.IsString)(),
|
|
138
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
139
|
+
__metadata("design:type", String)
|
|
140
|
+
], SendWhatsAppOfficialTextDto.prototype, "to", void 0);
|
|
141
|
+
__decorate([
|
|
142
|
+
(0, swagger_1.ApiProperty)({
|
|
143
|
+
description: 'Plain text message body',
|
|
144
|
+
example: 'Olá! Tudo bem?',
|
|
145
|
+
}),
|
|
146
|
+
(0, class_validator_1.IsString)(),
|
|
147
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
148
|
+
__metadata("design:type", String)
|
|
149
|
+
], SendWhatsAppOfficialTextDto.prototype, "text", void 0);
|
|
@@ -25,6 +25,13 @@ export declare class FollowupConfigResponseDto {
|
|
|
25
25
|
wellnessCheckEnabled: boolean;
|
|
26
26
|
returnSuggestionDelayMinutes: number;
|
|
27
27
|
returnSuggestionEnabled: boolean;
|
|
28
|
+
templateAppointmentConfirmation?: string;
|
|
29
|
+
templateSatisfactionBooking?: string;
|
|
30
|
+
templateSatisfactionFinished?: string;
|
|
31
|
+
templateNoShowReschedule?: string;
|
|
32
|
+
templateWellnessCheck?: string;
|
|
33
|
+
templateReturnSuggestion?: string;
|
|
34
|
+
templateAbandonment?: string;
|
|
28
35
|
enabled: boolean;
|
|
29
36
|
}
|
|
30
37
|
//# sourceMappingURL=followup-config-response.dto.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"followup-config-response.dto.d.ts","sourceRoot":"","sources":["../../src/conversations/followup-config-response.dto.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,qBAAa,yBAAyB;IAIpC,EAAE,EAAE,MAAM,CAAC;IAMX,MAAM,CAAC,EAAE,MAAM,CAAC;IAYhB,2BAA2B,EAAE,MAAM,CAAC;IAKpC,qBAAqB,EAAE,MAAM,CAAC;IAK9B,qBAAqB,EAAE,MAAM,CAAC;IAK9B,qBAAqB,EAAE,MAAM,CAAC;IAK9B,qBAAqB,EAAE,MAAM,CAAC;IAK9B,qBAAqB,EAAE,MAAM,CAAC;IAS9B,yBAAyB,EAAE,MAAM,CAAC;IAIlC,mBAAmB,EAAE,OAAO,CAAC;IAS7B,+BAA+B,EAAE,MAAM,CAAC;IAIxC,0BAA0B,EAAE,OAAO,CAAC;IAKpC,gCAAgC,EAAE,MAAM,CAAC;IAIzC,2BAA2B,EAAE,OAAO,CAAC;IASrC,kBAAkB,EAAE,MAAM,CAAC;IAI3B,aAAa,EAAE,OAAO,CAAC;IASvB,yBAAyB,EAAE,MAAM,CAAC;IAIlC,oBAAoB,EAAE,OAAO,CAAC;IAS9B,4BAA4B,EAAE,MAAM,CAAC;IAIrC,uBAAuB,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"followup-config-response.dto.d.ts","sourceRoot":"","sources":["../../src/conversations/followup-config-response.dto.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,qBAAa,yBAAyB;IAIpC,EAAE,EAAE,MAAM,CAAC;IAMX,MAAM,CAAC,EAAE,MAAM,CAAC;IAYhB,2BAA2B,EAAE,MAAM,CAAC;IAKpC,qBAAqB,EAAE,MAAM,CAAC;IAK9B,qBAAqB,EAAE,MAAM,CAAC;IAK9B,qBAAqB,EAAE,MAAM,CAAC;IAK9B,qBAAqB,EAAE,MAAM,CAAC;IAK9B,qBAAqB,EAAE,MAAM,CAAC;IAS9B,yBAAyB,EAAE,MAAM,CAAC;IAIlC,mBAAmB,EAAE,OAAO,CAAC;IAS7B,+BAA+B,EAAE,MAAM,CAAC;IAIxC,0BAA0B,EAAE,OAAO,CAAC;IAKpC,gCAAgC,EAAE,MAAM,CAAC;IAIzC,2BAA2B,EAAE,OAAO,CAAC;IASrC,kBAAkB,EAAE,MAAM,CAAC;IAI3B,aAAa,EAAE,OAAO,CAAC;IASvB,yBAAyB,EAAE,MAAM,CAAC;IAIlC,oBAAoB,EAAE,OAAO,CAAC;IAS9B,4BAA4B,EAAE,MAAM,CAAC;IAIrC,uBAAuB,EAAE,OAAO,CAAC;IAYjC,+BAA+B,CAAC,EAAE,MAAM,CAAC;IAQzC,2BAA2B,CAAC,EAAE,MAAM,CAAC;IAQrC,4BAA4B,CAAC,EAAE,MAAM,CAAC;IAQtC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAQlC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAQ/B,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAQlC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAQ7B,OAAO,EAAE,OAAO,CAAC;CAClB"}
|
|
@@ -58,6 +58,16 @@ class FollowupConfigResponseDto {
|
|
|
58
58
|
returnSuggestionDelayMinutes;
|
|
59
59
|
returnSuggestionEnabled;
|
|
60
60
|
// ---------------------------------------------------------------------------
|
|
61
|
+
// WhatsApp Official template IDs (per follow-up type)
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
templateAppointmentConfirmation;
|
|
64
|
+
templateSatisfactionBooking;
|
|
65
|
+
templateSatisfactionFinished;
|
|
66
|
+
templateNoShowReschedule;
|
|
67
|
+
templateWellnessCheck;
|
|
68
|
+
templateReturnSuggestion;
|
|
69
|
+
templateAbandonment;
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
61
71
|
// Global setting
|
|
62
72
|
// ---------------------------------------------------------------------------
|
|
63
73
|
enabled;
|
|
@@ -181,6 +191,69 @@ __decorate([
|
|
|
181
191
|
(0, class_validator_1.IsBoolean)(),
|
|
182
192
|
__metadata("design:type", Boolean)
|
|
183
193
|
], FollowupConfigResponseDto.prototype, "returnSuggestionEnabled", void 0);
|
|
194
|
+
__decorate([
|
|
195
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
196
|
+
description: 'WhatsApp Official template name for appointment confirmation follow-ups',
|
|
197
|
+
example: 'appointment_confirmation',
|
|
198
|
+
}),
|
|
199
|
+
(0, class_validator_1.IsOptional)(),
|
|
200
|
+
(0, class_validator_1.IsString)(),
|
|
201
|
+
__metadata("design:type", String)
|
|
202
|
+
], FollowupConfigResponseDto.prototype, "templateAppointmentConfirmation", void 0);
|
|
203
|
+
__decorate([
|
|
204
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
205
|
+
description: 'WhatsApp Official template name for satisfaction booking follow-ups',
|
|
206
|
+
example: 'satisfaction_booking',
|
|
207
|
+
}),
|
|
208
|
+
(0, class_validator_1.IsOptional)(),
|
|
209
|
+
(0, class_validator_1.IsString)(),
|
|
210
|
+
__metadata("design:type", String)
|
|
211
|
+
], FollowupConfigResponseDto.prototype, "templateSatisfactionBooking", void 0);
|
|
212
|
+
__decorate([
|
|
213
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
214
|
+
description: 'WhatsApp Official template name for satisfaction finished follow-ups',
|
|
215
|
+
example: 'satisfaction_finished',
|
|
216
|
+
}),
|
|
217
|
+
(0, class_validator_1.IsOptional)(),
|
|
218
|
+
(0, class_validator_1.IsString)(),
|
|
219
|
+
__metadata("design:type", String)
|
|
220
|
+
], FollowupConfigResponseDto.prototype, "templateSatisfactionFinished", void 0);
|
|
221
|
+
__decorate([
|
|
222
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
223
|
+
description: 'WhatsApp Official template name for no-show reschedule follow-ups',
|
|
224
|
+
example: 'no_show_reschedule',
|
|
225
|
+
}),
|
|
226
|
+
(0, class_validator_1.IsOptional)(),
|
|
227
|
+
(0, class_validator_1.IsString)(),
|
|
228
|
+
__metadata("design:type", String)
|
|
229
|
+
], FollowupConfigResponseDto.prototype, "templateNoShowReschedule", void 0);
|
|
230
|
+
__decorate([
|
|
231
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
232
|
+
description: 'WhatsApp Official template name for wellness check follow-ups',
|
|
233
|
+
example: 'wellness_check',
|
|
234
|
+
}),
|
|
235
|
+
(0, class_validator_1.IsOptional)(),
|
|
236
|
+
(0, class_validator_1.IsString)(),
|
|
237
|
+
__metadata("design:type", String)
|
|
238
|
+
], FollowupConfigResponseDto.prototype, "templateWellnessCheck", void 0);
|
|
239
|
+
__decorate([
|
|
240
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
241
|
+
description: 'WhatsApp Official template name for return suggestion follow-ups',
|
|
242
|
+
example: 'return_suggestion',
|
|
243
|
+
}),
|
|
244
|
+
(0, class_validator_1.IsOptional)(),
|
|
245
|
+
(0, class_validator_1.IsString)(),
|
|
246
|
+
__metadata("design:type", String)
|
|
247
|
+
], FollowupConfigResponseDto.prototype, "templateReturnSuggestion", void 0);
|
|
248
|
+
__decorate([
|
|
249
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
250
|
+
description: 'WhatsApp Official template name for abandonment follow-ups',
|
|
251
|
+
example: 'abandonment_followup',
|
|
252
|
+
}),
|
|
253
|
+
(0, class_validator_1.IsOptional)(),
|
|
254
|
+
(0, class_validator_1.IsString)(),
|
|
255
|
+
__metadata("design:type", String)
|
|
256
|
+
], FollowupConfigResponseDto.prototype, "templateAbandonment", void 0);
|
|
184
257
|
__decorate([
|
|
185
258
|
(0, swagger_1.ApiProperty)({ description: 'Whether abandonment follow-up is enabled for this tenant' }),
|
|
186
259
|
(0, class_validator_1.IsBoolean)(),
|
package/dist/tenants/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export * from './update-tenant-environment-url.dto';
|
|
2
2
|
export * from './get-tenant-by-address.query.dto';
|
|
3
3
|
export * from './tenant-public-by-address-response.dto';
|
|
4
|
+
export * from './whatsapp-provider-kind.dto';
|
|
5
|
+
export * from './tenant-slug.util';
|
|
4
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tenants/index.ts"],"names":[],"mappings":"AAAA,cAAc,qCAAqC,CAAC;AACpD,cAAc,mCAAmC,CAAC;AAClD,cAAc,yCAAyC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tenants/index.ts"],"names":[],"mappings":"AAAA,cAAc,qCAAqC,CAAC;AACpD,cAAc,mCAAmC,CAAC;AAClD,cAAc,yCAAyC,CAAC;AACxD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,oBAAoB,CAAC"}
|
package/dist/tenants/index.js
CHANGED
|
@@ -17,3 +17,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./update-tenant-environment-url.dto"), exports);
|
|
18
18
|
__exportStar(require("./get-tenant-by-address.query.dto"), exports);
|
|
19
19
|
__exportStar(require("./tenant-public-by-address-response.dto"), exports);
|
|
20
|
+
__exportStar(require("./whatsapp-provider-kind.dto"), exports);
|
|
21
|
+
__exportStar(require("./tenant-slug.util"), exports);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for tenant slug <-> domain transformations.
|
|
3
|
+
*
|
|
4
|
+
* Convention:
|
|
5
|
+
* - Slugs stored in DB use underscores for word separation (e.g. "clinica_sampaio")
|
|
6
|
+
* or are single words (e.g. "homolog"). Hyphens are NOT allowed in slugs.
|
|
7
|
+
* - DNS hostnames use hyphens (underscores are invalid in DNS labels).
|
|
8
|
+
* - Conversion is bijective: slug "clinica_sampaio" <-> domain "clinica-sampaio"
|
|
9
|
+
*
|
|
10
|
+
* Examples:
|
|
11
|
+
* - Slug "homolog" -> domain segment "homolog" -> slug "homolog"
|
|
12
|
+
* - Slug "clinica_sampaio" -> domain segment "clinica-sampaio" -> slug "clinica_sampaio"
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Converts a tenant slug to a DNS-safe domain segment.
|
|
16
|
+
* Replaces underscores with hyphens and lowercases the result.
|
|
17
|
+
*
|
|
18
|
+
* @example slugToDomainSegment("clinica_sampaio") // "clinica-sampaio"
|
|
19
|
+
* @example slugToDomainSegment("homolog") // "homolog"
|
|
20
|
+
*/
|
|
21
|
+
export declare function slugToDomainSegment(slug: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Converts a domain segment back to the tenant slug.
|
|
24
|
+
* Replaces hyphens with underscores (the stored DB format).
|
|
25
|
+
*
|
|
26
|
+
* @example domainSegmentToSlug("clinica-sampaio") // "clinica_sampaio"
|
|
27
|
+
* @example domainSegmentToSlug("homolog") // "homolog"
|
|
28
|
+
*/
|
|
29
|
+
export declare function domainSegmentToSlug(domainSegment: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Regex pattern that validates an acceptable tenant slug.
|
|
32
|
+
* Allows lowercase alphanumeric characters and underscores only (no hyphens).
|
|
33
|
+
* Must start and end with an alphanumeric character.
|
|
34
|
+
* Single-character slugs (e.g. "a") are allowed.
|
|
35
|
+
* Max 63 chars (DNS label limit after _ -> - conversion).
|
|
36
|
+
*/
|
|
37
|
+
export declare const TENANT_SLUG_REGEX: RegExp;
|
|
38
|
+
/**
|
|
39
|
+
* Validates whether a slug is acceptable.
|
|
40
|
+
* Rules:
|
|
41
|
+
* - Only lowercase letters, digits, and underscores (no hyphens)
|
|
42
|
+
* - Must start and end with a letter or digit
|
|
43
|
+
* - Length between 1 and 63 characters
|
|
44
|
+
*/
|
|
45
|
+
export declare function isValidTenantSlug(slug: string): boolean;
|
|
46
|
+
//# sourceMappingURL=tenant-slug.util.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-slug.util.d.ts","sourceRoot":"","sources":["../../src/tenants/tenant-slug.util.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,QAAoC,CAAC;AAEnE;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGvD"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Utility functions for tenant slug <-> domain transformations.
|
|
4
|
+
*
|
|
5
|
+
* Convention:
|
|
6
|
+
* - Slugs stored in DB use underscores for word separation (e.g. "clinica_sampaio")
|
|
7
|
+
* or are single words (e.g. "homolog"). Hyphens are NOT allowed in slugs.
|
|
8
|
+
* - DNS hostnames use hyphens (underscores are invalid in DNS labels).
|
|
9
|
+
* - Conversion is bijective: slug "clinica_sampaio" <-> domain "clinica-sampaio"
|
|
10
|
+
*
|
|
11
|
+
* Examples:
|
|
12
|
+
* - Slug "homolog" -> domain segment "homolog" -> slug "homolog"
|
|
13
|
+
* - Slug "clinica_sampaio" -> domain segment "clinica-sampaio" -> slug "clinica_sampaio"
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.TENANT_SLUG_REGEX = void 0;
|
|
17
|
+
exports.slugToDomainSegment = slugToDomainSegment;
|
|
18
|
+
exports.domainSegmentToSlug = domainSegmentToSlug;
|
|
19
|
+
exports.isValidTenantSlug = isValidTenantSlug;
|
|
20
|
+
/**
|
|
21
|
+
* Converts a tenant slug to a DNS-safe domain segment.
|
|
22
|
+
* Replaces underscores with hyphens and lowercases the result.
|
|
23
|
+
*
|
|
24
|
+
* @example slugToDomainSegment("clinica_sampaio") // "clinica-sampaio"
|
|
25
|
+
* @example slugToDomainSegment("homolog") // "homolog"
|
|
26
|
+
*/
|
|
27
|
+
function slugToDomainSegment(slug) {
|
|
28
|
+
return (slug || 'default').toLowerCase().replace(/_/g, '-');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Converts a domain segment back to the tenant slug.
|
|
32
|
+
* Replaces hyphens with underscores (the stored DB format).
|
|
33
|
+
*
|
|
34
|
+
* @example domainSegmentToSlug("clinica-sampaio") // "clinica_sampaio"
|
|
35
|
+
* @example domainSegmentToSlug("homolog") // "homolog"
|
|
36
|
+
*/
|
|
37
|
+
function domainSegmentToSlug(domainSegment) {
|
|
38
|
+
return (domainSegment || 'default').toLowerCase().replace(/-/g, '_');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Regex pattern that validates an acceptable tenant slug.
|
|
42
|
+
* Allows lowercase alphanumeric characters and underscores only (no hyphens).
|
|
43
|
+
* Must start and end with an alphanumeric character.
|
|
44
|
+
* Single-character slugs (e.g. "a") are allowed.
|
|
45
|
+
* Max 63 chars (DNS label limit after _ -> - conversion).
|
|
46
|
+
*/
|
|
47
|
+
exports.TENANT_SLUG_REGEX = /^[a-z0-9]([a-z0-9_]*[a-z0-9])?$/;
|
|
48
|
+
/**
|
|
49
|
+
* Validates whether a slug is acceptable.
|
|
50
|
+
* Rules:
|
|
51
|
+
* - Only lowercase letters, digits, and underscores (no hyphens)
|
|
52
|
+
* - Must start and end with a letter or digit
|
|
53
|
+
* - Length between 1 and 63 characters
|
|
54
|
+
*/
|
|
55
|
+
function isValidTenantSlug(slug) {
|
|
56
|
+
if (!slug || slug.length > 63)
|
|
57
|
+
return false;
|
|
58
|
+
return exports.TENANT_SLUG_REGEX.test(slug.toLowerCase());
|
|
59
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-slug.util.spec.d.ts","sourceRoot":"","sources":["../../src/tenants/tenant-slug.util.spec.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tenant_slug_util_1 = require("./tenant-slug.util");
|
|
4
|
+
describe('slugToDomainSegment', () => {
|
|
5
|
+
it('converts underscores to hyphens', () => {
|
|
6
|
+
expect((0, tenant_slug_util_1.slugToDomainSegment)('clinica_sampaio')).toBe('clinica-sampaio');
|
|
7
|
+
});
|
|
8
|
+
it('keeps single-word slugs unchanged', () => {
|
|
9
|
+
expect((0, tenant_slug_util_1.slugToDomainSegment)('homolog')).toBe('homolog');
|
|
10
|
+
});
|
|
11
|
+
it('handles multiple underscores', () => {
|
|
12
|
+
expect((0, tenant_slug_util_1.slugToDomainSegment)('my_big_clinic')).toBe('my-big-clinic');
|
|
13
|
+
});
|
|
14
|
+
it('lowercases the result', () => {
|
|
15
|
+
expect((0, tenant_slug_util_1.slugToDomainSegment)('Homolog')).toBe('homolog');
|
|
16
|
+
expect((0, tenant_slug_util_1.slugToDomainSegment)('Clinica_Sampaio')).toBe('clinica-sampaio');
|
|
17
|
+
});
|
|
18
|
+
it('returns default for empty string', () => {
|
|
19
|
+
expect((0, tenant_slug_util_1.slugToDomainSegment)('')).toBe('default');
|
|
20
|
+
});
|
|
21
|
+
it('keeps numeric slugs unchanged', () => {
|
|
22
|
+
expect((0, tenant_slug_util_1.slugToDomainSegment)('tenant01')).toBe('tenant01');
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe('domainSegmentToSlug', () => {
|
|
26
|
+
it('converts hyphens to underscores', () => {
|
|
27
|
+
expect((0, tenant_slug_util_1.domainSegmentToSlug)('clinica-sampaio')).toBe('clinica_sampaio');
|
|
28
|
+
});
|
|
29
|
+
it('keeps single-word segments unchanged', () => {
|
|
30
|
+
expect((0, tenant_slug_util_1.domainSegmentToSlug)('homolog')).toBe('homolog');
|
|
31
|
+
});
|
|
32
|
+
it('handles multiple hyphens', () => {
|
|
33
|
+
expect((0, tenant_slug_util_1.domainSegmentToSlug)('my-big-clinic')).toBe('my_big_clinic');
|
|
34
|
+
});
|
|
35
|
+
it('lowercases the result', () => {
|
|
36
|
+
expect((0, tenant_slug_util_1.domainSegmentToSlug)('Homolog')).toBe('homolog');
|
|
37
|
+
});
|
|
38
|
+
it('returns default for empty string', () => {
|
|
39
|
+
expect((0, tenant_slug_util_1.domainSegmentToSlug)('')).toBe('default');
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
describe('round-trip slug <-> domain', () => {
|
|
43
|
+
const cases = ['homolog', 'clinica_sampaio', 'my_big_clinic', 'tenant01', 'a'];
|
|
44
|
+
it.each(cases)('slug "%s" survives round-trip', (slug) => {
|
|
45
|
+
const domain = (0, tenant_slug_util_1.slugToDomainSegment)(slug);
|
|
46
|
+
const back = (0, tenant_slug_util_1.domainSegmentToSlug)(domain);
|
|
47
|
+
expect(back).toBe(slug);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe('isValidTenantSlug', () => {
|
|
51
|
+
it('accepts single-word slugs', () => {
|
|
52
|
+
expect((0, tenant_slug_util_1.isValidTenantSlug)('homolog')).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
it('accepts underscore slugs', () => {
|
|
55
|
+
expect((0, tenant_slug_util_1.isValidTenantSlug)('clinica_sampaio')).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
it('accepts single char slug', () => {
|
|
58
|
+
expect((0, tenant_slug_util_1.isValidTenantSlug)('a')).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
it('accepts numeric slugs', () => {
|
|
61
|
+
expect((0, tenant_slug_util_1.isValidTenantSlug)('tenant01')).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
it('rejects hyphens', () => {
|
|
64
|
+
expect((0, tenant_slug_util_1.isValidTenantSlug)('clinica-sampaio')).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
it('rejects slugs starting with underscore', () => {
|
|
67
|
+
expect((0, tenant_slug_util_1.isValidTenantSlug)('_invalid')).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
it('rejects slugs ending with underscore', () => {
|
|
70
|
+
expect((0, tenant_slug_util_1.isValidTenantSlug)('invalid_')).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
it('rejects empty string', () => {
|
|
73
|
+
expect((0, tenant_slug_util_1.isValidTenantSlug)('')).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
it('rejects special characters', () => {
|
|
76
|
+
expect((0, tenant_slug_util_1.isValidTenantSlug)('my.clinic')).toBe(false);
|
|
77
|
+
expect((0, tenant_slug_util_1.isValidTenantSlug)('my clinic')).toBe(false);
|
|
78
|
+
expect((0, tenant_slug_util_1.isValidTenantSlug)('my@clinic')).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
it('rejects strings longer than 63 chars', () => {
|
|
81
|
+
const longSlug = 'a'.repeat(64);
|
|
82
|
+
expect((0, tenant_slug_util_1.isValidTenantSlug)(longSlug)).toBe(false);
|
|
83
|
+
});
|
|
84
|
+
it('accepts strings exactly 63 chars', () => {
|
|
85
|
+
const slug = 'a'.repeat(63);
|
|
86
|
+
expect((0, tenant_slug_util_1.isValidTenantSlug)(slug)).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe('TENANT_SLUG_REGEX', () => {
|
|
90
|
+
it('matches valid slugs', () => {
|
|
91
|
+
expect(tenant_slug_util_1.TENANT_SLUG_REGEX.test('homolog')).toBe(true);
|
|
92
|
+
expect(tenant_slug_util_1.TENANT_SLUG_REGEX.test('clinica_sampaio')).toBe(true);
|
|
93
|
+
expect(tenant_slug_util_1.TENANT_SLUG_REGEX.test('a1b2')).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
it('does not match hyphens', () => {
|
|
96
|
+
expect(tenant_slug_util_1.TENANT_SLUG_REGEX.test('clinica-sampaio')).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
it('does not match leading/trailing underscores', () => {
|
|
99
|
+
expect(tenant_slug_util_1.TENANT_SLUG_REGEX.test('_test')).toBe(false);
|
|
100
|
+
expect(tenant_slug_util_1.TENANT_SLUG_REGEX.test('test_')).toBe(false);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported WhatsApp provider types for tenant instance provisioning.
|
|
3
|
+
*
|
|
4
|
+
* - `evolution_api`: Evolution API v2 (Baileys-based unofficial).
|
|
5
|
+
* - `whatsapp_official`: Meta Cloud API (official WhatsApp Business).
|
|
6
|
+
*/
|
|
7
|
+
export declare const WHATSAPP_PROVIDER_KINDS: readonly ["evolution_api", "whatsapp_official"];
|
|
8
|
+
export type WhatsAppProviderKindDto = (typeof WHATSAPP_PROVIDER_KINDS)[number];
|
|
9
|
+
//# sourceMappingURL=whatsapp-provider-kind.dto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whatsapp-provider-kind.dto.d.ts","sourceRoot":"","sources":["../../src/tenants/whatsapp-provider-kind.dto.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,iDAG1B,CAAC;AAEX,MAAM,MAAM,uBAAuB,GAAG,CAAC,OAAO,uBAAuB,CAAC,CAAC,MAAM,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WHATSAPP_PROVIDER_KINDS = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Supported WhatsApp provider types for tenant instance provisioning.
|
|
6
|
+
*
|
|
7
|
+
* - `evolution_api`: Evolution API v2 (Baileys-based unofficial).
|
|
8
|
+
* - `whatsapp_official`: Meta Cloud API (official WhatsApp Business).
|
|
9
|
+
*/
|
|
10
|
+
exports.WHATSAPP_PROVIDER_KINDS = [
|
|
11
|
+
'evolution_api',
|
|
12
|
+
'whatsapp_official',
|
|
13
|
+
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tychat-contracts",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.76",
|
|
4
4
|
"description": "DTOs compartilhados com class-validator (API e microserviços)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"class-transformer": "^0.5.1",
|
|
14
14
|
"class-validator": "^0.14.1",
|
|
15
15
|
"ioredis": "^5.10.0",
|
|
16
|
+
"jest": "^30.3.0",
|
|
16
17
|
"reflect-metadata": "*"
|
|
17
18
|
},
|
|
18
19
|
"devDependencies": {
|
package/src/connections/index.ts
CHANGED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
2
|
+
import { IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Payload to register a WhatsApp Official (Meta Cloud API) instance for a tenant.
|
|
6
|
+
* Requires the access token, phone number ID, and WhatsApp Business Account ID
|
|
7
|
+
* obtained from the Meta developer portal.
|
|
8
|
+
*/
|
|
9
|
+
export class CreateWhatsAppOfficialInstanceDto {
|
|
10
|
+
@ApiProperty({
|
|
11
|
+
description: 'Meta Cloud API permanent access token',
|
|
12
|
+
example: 'EAABs...',
|
|
13
|
+
})
|
|
14
|
+
@IsString()
|
|
15
|
+
@IsNotEmpty()
|
|
16
|
+
accessToken: string;
|
|
17
|
+
|
|
18
|
+
@ApiProperty({
|
|
19
|
+
description: 'Phone Number ID from Meta Business Manager',
|
|
20
|
+
example: '106540352242922',
|
|
21
|
+
})
|
|
22
|
+
@IsString()
|
|
23
|
+
@IsNotEmpty()
|
|
24
|
+
phoneNumberId: string;
|
|
25
|
+
|
|
26
|
+
@ApiProperty({
|
|
27
|
+
description: 'WhatsApp Business Account ID (WABA ID)',
|
|
28
|
+
example: '102291529438901',
|
|
29
|
+
})
|
|
30
|
+
@IsString()
|
|
31
|
+
@IsNotEmpty()
|
|
32
|
+
wabaId: string;
|
|
33
|
+
|
|
34
|
+
@ApiPropertyOptional({
|
|
35
|
+
description: 'Webhook verify token used to validate Meta webhook registration',
|
|
36
|
+
example: 'my-verify-token',
|
|
37
|
+
})
|
|
38
|
+
@IsOptional()
|
|
39
|
+
@IsString()
|
|
40
|
+
webhookVerifyToken?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Payload to send a pre-approved template message via WhatsApp Official (Meta Cloud API).
|
|
45
|
+
* Templates must be created and approved in the Meta Business Manager before use.
|
|
46
|
+
*/
|
|
47
|
+
export class SendWhatsAppOfficialTemplateDto {
|
|
48
|
+
@ApiProperty({
|
|
49
|
+
description: 'Recipient phone number with country code (digits only)',
|
|
50
|
+
example: '5511999999999',
|
|
51
|
+
})
|
|
52
|
+
@IsString()
|
|
53
|
+
@IsNotEmpty()
|
|
54
|
+
to: string;
|
|
55
|
+
|
|
56
|
+
@ApiProperty({
|
|
57
|
+
description: 'Approved template name in Meta Business Manager',
|
|
58
|
+
example: 'appointment_confirmation',
|
|
59
|
+
})
|
|
60
|
+
@IsString()
|
|
61
|
+
@IsNotEmpty()
|
|
62
|
+
templateName: string;
|
|
63
|
+
|
|
64
|
+
@ApiProperty({
|
|
65
|
+
description: 'Template language code (BCP 47)',
|
|
66
|
+
example: 'pt_BR',
|
|
67
|
+
})
|
|
68
|
+
@IsString()
|
|
69
|
+
@IsNotEmpty()
|
|
70
|
+
languageCode: string;
|
|
71
|
+
|
|
72
|
+
@ApiPropertyOptional({
|
|
73
|
+
description:
|
|
74
|
+
'Template body variable components (positional parameters). ' +
|
|
75
|
+
'Each element replaces {{1}}, {{2}}, etc. in the template body.',
|
|
76
|
+
example: ['João', '15/04/2026 às 10:00'],
|
|
77
|
+
type: [String],
|
|
78
|
+
})
|
|
79
|
+
@IsOptional()
|
|
80
|
+
@IsString({ each: true })
|
|
81
|
+
bodyParameters?: string[];
|
|
82
|
+
|
|
83
|
+
@ApiPropertyOptional({
|
|
84
|
+
description: 'Template header variable components (positional parameters)',
|
|
85
|
+
example: ['Clínica Saúde'],
|
|
86
|
+
type: [String],
|
|
87
|
+
})
|
|
88
|
+
@IsOptional()
|
|
89
|
+
@IsString({ each: true })
|
|
90
|
+
headerParameters?: string[];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Payload to send a free-form text message via WhatsApp Official (Meta Cloud API).
|
|
95
|
+
* Only works within the 24-hour customer service window.
|
|
96
|
+
*/
|
|
97
|
+
export class SendWhatsAppOfficialTextDto {
|
|
98
|
+
@ApiProperty({
|
|
99
|
+
description: 'Recipient phone number with country code (digits only)',
|
|
100
|
+
example: '5511999999999',
|
|
101
|
+
})
|
|
102
|
+
@IsString()
|
|
103
|
+
@IsNotEmpty()
|
|
104
|
+
to: string;
|
|
105
|
+
|
|
106
|
+
@ApiProperty({
|
|
107
|
+
description: 'Plain text message body',
|
|
108
|
+
example: 'Olá! Tudo bem?',
|
|
109
|
+
})
|
|
110
|
+
@IsString()
|
|
111
|
+
@IsNotEmpty()
|
|
112
|
+
text: string;
|
|
113
|
+
}
|
|
@@ -130,6 +130,66 @@ export class FollowupConfigResponseDto {
|
|
|
130
130
|
@IsBoolean()
|
|
131
131
|
returnSuggestionEnabled: boolean;
|
|
132
132
|
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
// WhatsApp Official template IDs (per follow-up type)
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
@ApiPropertyOptional({
|
|
138
|
+
description: 'WhatsApp Official template name for appointment confirmation follow-ups',
|
|
139
|
+
example: 'appointment_confirmation',
|
|
140
|
+
})
|
|
141
|
+
@IsOptional()
|
|
142
|
+
@IsString()
|
|
143
|
+
templateAppointmentConfirmation?: string;
|
|
144
|
+
|
|
145
|
+
@ApiPropertyOptional({
|
|
146
|
+
description: 'WhatsApp Official template name for satisfaction booking follow-ups',
|
|
147
|
+
example: 'satisfaction_booking',
|
|
148
|
+
})
|
|
149
|
+
@IsOptional()
|
|
150
|
+
@IsString()
|
|
151
|
+
templateSatisfactionBooking?: string;
|
|
152
|
+
|
|
153
|
+
@ApiPropertyOptional({
|
|
154
|
+
description: 'WhatsApp Official template name for satisfaction finished follow-ups',
|
|
155
|
+
example: 'satisfaction_finished',
|
|
156
|
+
})
|
|
157
|
+
@IsOptional()
|
|
158
|
+
@IsString()
|
|
159
|
+
templateSatisfactionFinished?: string;
|
|
160
|
+
|
|
161
|
+
@ApiPropertyOptional({
|
|
162
|
+
description: 'WhatsApp Official template name for no-show reschedule follow-ups',
|
|
163
|
+
example: 'no_show_reschedule',
|
|
164
|
+
})
|
|
165
|
+
@IsOptional()
|
|
166
|
+
@IsString()
|
|
167
|
+
templateNoShowReschedule?: string;
|
|
168
|
+
|
|
169
|
+
@ApiPropertyOptional({
|
|
170
|
+
description: 'WhatsApp Official template name for wellness check follow-ups',
|
|
171
|
+
example: 'wellness_check',
|
|
172
|
+
})
|
|
173
|
+
@IsOptional()
|
|
174
|
+
@IsString()
|
|
175
|
+
templateWellnessCheck?: string;
|
|
176
|
+
|
|
177
|
+
@ApiPropertyOptional({
|
|
178
|
+
description: 'WhatsApp Official template name for return suggestion follow-ups',
|
|
179
|
+
example: 'return_suggestion',
|
|
180
|
+
})
|
|
181
|
+
@IsOptional()
|
|
182
|
+
@IsString()
|
|
183
|
+
templateReturnSuggestion?: string;
|
|
184
|
+
|
|
185
|
+
@ApiPropertyOptional({
|
|
186
|
+
description: 'WhatsApp Official template name for abandonment follow-ups',
|
|
187
|
+
example: 'abandonment_followup',
|
|
188
|
+
})
|
|
189
|
+
@IsOptional()
|
|
190
|
+
@IsString()
|
|
191
|
+
templateAbandonment?: string;
|
|
192
|
+
|
|
133
193
|
// ---------------------------------------------------------------------------
|
|
134
194
|
// Global setting
|
|
135
195
|
// ---------------------------------------------------------------------------
|
package/src/tenants/index.ts
CHANGED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for tenant slug <-> domain transformations.
|
|
3
|
+
*
|
|
4
|
+
* Convention:
|
|
5
|
+
* - Slugs stored in DB use underscores for word separation (e.g. "clinica_sampaio")
|
|
6
|
+
* or are single words (e.g. "homolog"). Hyphens are NOT allowed in slugs.
|
|
7
|
+
* - DNS hostnames use hyphens (underscores are invalid in DNS labels).
|
|
8
|
+
* - Conversion is bijective: slug "clinica_sampaio" <-> domain "clinica-sampaio"
|
|
9
|
+
*
|
|
10
|
+
* Examples:
|
|
11
|
+
* - Slug "homolog" -> domain segment "homolog" -> slug "homolog"
|
|
12
|
+
* - Slug "clinica_sampaio" -> domain segment "clinica-sampaio" -> slug "clinica_sampaio"
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Converts a tenant slug to a DNS-safe domain segment.
|
|
17
|
+
* Replaces underscores with hyphens and lowercases the result.
|
|
18
|
+
*
|
|
19
|
+
* @example slugToDomainSegment("clinica_sampaio") // "clinica-sampaio"
|
|
20
|
+
* @example slugToDomainSegment("homolog") // "homolog"
|
|
21
|
+
*/
|
|
22
|
+
export function slugToDomainSegment(slug: string): string {
|
|
23
|
+
return (slug || 'default').toLowerCase().replace(/_/g, '-');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Converts a domain segment back to the tenant slug.
|
|
28
|
+
* Replaces hyphens with underscores (the stored DB format).
|
|
29
|
+
*
|
|
30
|
+
* @example domainSegmentToSlug("clinica-sampaio") // "clinica_sampaio"
|
|
31
|
+
* @example domainSegmentToSlug("homolog") // "homolog"
|
|
32
|
+
*/
|
|
33
|
+
export function domainSegmentToSlug(domainSegment: string): string {
|
|
34
|
+
return (domainSegment || 'default').toLowerCase().replace(/-/g, '_');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Regex pattern that validates an acceptable tenant slug.
|
|
39
|
+
* Allows lowercase alphanumeric characters and underscores only (no hyphens).
|
|
40
|
+
* Must start and end with an alphanumeric character.
|
|
41
|
+
* Single-character slugs (e.g. "a") are allowed.
|
|
42
|
+
* Max 63 chars (DNS label limit after _ -> - conversion).
|
|
43
|
+
*/
|
|
44
|
+
export const TENANT_SLUG_REGEX = /^[a-z0-9]([a-z0-9_]*[a-z0-9])?$/;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Validates whether a slug is acceptable.
|
|
48
|
+
* Rules:
|
|
49
|
+
* - Only lowercase letters, digits, and underscores (no hyphens)
|
|
50
|
+
* - Must start and end with a letter or digit
|
|
51
|
+
* - Length between 1 and 63 characters
|
|
52
|
+
*/
|
|
53
|
+
export function isValidTenantSlug(slug: string): boolean {
|
|
54
|
+
if (!slug || slug.length > 63) return false;
|
|
55
|
+
return TENANT_SLUG_REGEX.test(slug.toLowerCase());
|
|
56
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported WhatsApp provider types for tenant instance provisioning.
|
|
3
|
+
*
|
|
4
|
+
* - `evolution_api`: Evolution API v2 (Baileys-based unofficial).
|
|
5
|
+
* - `whatsapp_official`: Meta Cloud API (official WhatsApp Business).
|
|
6
|
+
*/
|
|
7
|
+
export const WHATSAPP_PROVIDER_KINDS = [
|
|
8
|
+
'evolution_api',
|
|
9
|
+
'whatsapp_official',
|
|
10
|
+
] as const;
|
|
11
|
+
|
|
12
|
+
export type WhatsAppProviderKindDto = (typeof WHATSAPP_PROVIDER_KINDS)[number];
|