n8n-nodes-atendix 1.0.0

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.
@@ -0,0 +1,16 @@
1
+ import { IAuthenticateGeneric, ICredentialType, INodeProperties, ICredentialDataDecryptedObject, ICredentialTestRequest, Icon } from 'n8n-workflow';
2
+ export declare class TrayApiAuto implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ icon: Icon;
6
+ documentationUrl: string;
7
+ properties: INodeProperties[];
8
+ authenticate: IAuthenticateGeneric;
9
+ preAuthentication(credentials: ICredentialDataDecryptedObject): Promise<ICredentialDataDecryptedObject>;
10
+ /**
11
+ * Renomeado para evitar conflito com a propriedade 'authenticate' da interface
12
+ */
13
+ private _authenticateFlow;
14
+ private _refreshTokenFlow;
15
+ test: ICredentialTestRequest;
16
+ }
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ /* Arquivo: n8n-nodes-atendix/credentials/TrayApiAuto.credentials.ts
3
+ *
4
+ * Credenciais Tray API - Autenticação Automática
5
+ * Ajustado para evitar conflitos de nomes e erros de tipagem (unknown).
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.TrayApiAuto = void 0;
9
+ const CONSUMER_KEY = process.env.TRAY_CONSUMER_KEY;
10
+ const CONSUMER_SECRET = process.env.TRAY_CONSUMER_SECRET;
11
+ class TrayApiAuto {
12
+ constructor() {
13
+ this.name = 'trayApiAuto';
14
+ this.displayName = 'Atendix - Tray API';
15
+ this.icon = {
16
+ light: 'file:tray.svg',
17
+ dark: 'file:tray.svg',
18
+ };
19
+ this.documentationUrl = 'https://developers.tray.com.br/docs/';
20
+ this.properties = [
21
+ {
22
+ displayName: 'API Address',
23
+ name: 'apiAddress',
24
+ type: 'string',
25
+ default: 'https://api.tray.com.br',
26
+ required: true,
27
+ description: 'Endereço base da API Tray (geralmente https://api.tray.com.br)',
28
+ },
29
+ {
30
+ displayName: 'Authorization Code',
31
+ name: 'authCode',
32
+ type: 'string',
33
+ typeOptions: {
34
+ password: true,
35
+ },
36
+ default: '',
37
+ required: true,
38
+ placeholder: 'a9777f8cdfe4cf41b0a72c295adef1f9c43a093052c9e6dd98d1e629a09f13f3',
39
+ description: 'Código de autorização único da loja (fornecido pela Tray)',
40
+ },
41
+ {
42
+ displayName: 'Access Token',
43
+ name: 'accessToken',
44
+ type: 'hidden',
45
+ default: '',
46
+ },
47
+ {
48
+ displayName: 'Refresh Token',
49
+ name: 'refreshToken',
50
+ type: 'hidden',
51
+ default: '',
52
+ },
53
+ {
54
+ displayName: 'API Host',
55
+ name: 'apiHost',
56
+ type: 'hidden',
57
+ default: '',
58
+ },
59
+ {
60
+ displayName: 'Token Expiration',
61
+ name: 'tokenExpiration',
62
+ type: 'hidden',
63
+ default: '',
64
+ },
65
+ {
66
+ displayName: 'Store ID',
67
+ name: 'storeId',
68
+ type: 'hidden',
69
+ default: '',
70
+ },
71
+ ];
72
+ // O segredo para o n8n injetar o token em todas as chamadas
73
+ this.authenticate = {
74
+ type: 'generic',
75
+ properties: {
76
+ qs: {
77
+ access_token: '={{$credentials.accessToken}}',
78
+ },
79
+ },
80
+ };
81
+ this.test = {
82
+ request: {
83
+ // Usamos o apiAddress direto para o teste não quebrar se o apiHost estiver vazio
84
+ baseURL: '={{$credentials.apiAddress}}',
85
+ url: '/auth',
86
+ method: 'POST',
87
+ body: {
88
+ consumer_key: process.env.TRAY_CONSUMER_KEY,
89
+ consumer_secret: process.env.TRAY_CONSUMER_SECRET,
90
+ code: '={{$credentials.authCode}}',
91
+ },
92
+ },
93
+ };
94
+ }
95
+ async preAuthentication(credentials) {
96
+ const now = new Date();
97
+ const tokenExpiration = credentials.tokenExpiration
98
+ ? new Date(credentials.tokenExpiration)
99
+ : null;
100
+ if (credentials.accessToken && tokenExpiration && tokenExpiration > now) {
101
+ return credentials;
102
+ }
103
+ if (credentials.refreshToken && tokenExpiration && tokenExpiration <= now) {
104
+ return await this._refreshTokenFlow(credentials);
105
+ }
106
+ return await this._authenticateFlow(credentials);
107
+ }
108
+ /**
109
+ * Renomeado para evitar conflito com a propriedade 'authenticate' da interface
110
+ */
111
+ async _authenticateFlow(credentials) {
112
+ const apiAddress = credentials.apiAddress.replace(/\/$/, '');
113
+ const authCode = credentials.authCode;
114
+ // Pega as chaves direto do processo para garantir que não estão vazias
115
+ const consumerKey = process.env.TRAY_CONSUMER_KEY;
116
+ const consumerSecret = process.env.TRAY_CONSUMER_SECRET;
117
+ console.log(` Tentando autenticar na URL: ${apiAddress}/auth`);
118
+ console.log(` Usando Key: ${consumerKey ? 'Preenchida' : 'VAZIA!'}`);
119
+ try {
120
+ const response = await fetch(`${apiAddress}/auth`, {
121
+ method: 'POST',
122
+ headers: { 'Content-Type': 'application/json' },
123
+ body: JSON.stringify({
124
+ consumer_key: consumerKey,
125
+ consumer_secret: consumerSecret,
126
+ code: authCode,
127
+ }),
128
+ });
129
+ const data = (await response.json());
130
+ if (!response.ok) {
131
+ console.error(' Erro retornado pela Tray:', data);
132
+ throw new Error(data.message || 'Erro desconhecido na Tray');
133
+ }
134
+ console.log(' Autenticação com a Tray teve SUCESSO!');
135
+ return {
136
+ ...credentials,
137
+ accessToken: data.access_token,
138
+ refreshToken: data.refresh_token,
139
+ apiHost: data.api_host, // Geralmente vem algo como "https://1225878.commercesuite.com.br"
140
+ storeId: data.store_id,
141
+ tokenExpiration: data.date_expiration_access_token,
142
+ };
143
+ }
144
+ catch (error) {
145
+ console.error(' Erro Crítico no Flow de Autenticação:', error.message);
146
+ throw new Error(`Falha ao autenticar na Tray: ${error.message}`);
147
+ }
148
+ }
149
+ async _refreshTokenFlow(credentials) {
150
+ const apiAddress = credentials.apiAddress;
151
+ const refreshToken = credentials.refreshToken;
152
+ try {
153
+ const response = await fetch(`${apiAddress}/auth?refresh_token=${refreshToken}`);
154
+ const data = (await response.json());
155
+ if (!response.ok) {
156
+ return await this._authenticateFlow(credentials);
157
+ }
158
+ return {
159
+ ...credentials,
160
+ accessToken: data.access_token,
161
+ refreshToken: data.refresh_token,
162
+ tokenExpiration: data.date_expiration_access_token,
163
+ };
164
+ }
165
+ catch (error) {
166
+ return await this._authenticateFlow(credentials);
167
+ }
168
+ }
169
+ }
170
+ exports.TrayApiAuto = TrayApiAuto;
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ viewBox="0 0 24 24"
5
+ fill="none"
6
+ stroke="#7D7D87"
7
+ stroke-width="1.3"
8
+ stroke-linecap="round"
9
+ stroke-linejoin="round"
10
+ >
11
+ <path d="M3 21 L3 6 C3 4.343 4.343 3 6 3 H18 C19.657 3 22 4.343 22 6 V14 C22 15.657 19.657 17 18 17 H8.5 L3 21 Z" />
12
+
13
+ <path d="M6.8 14V9.5" />
14
+ <circle cx="6.8" cy="8" r="1.5" />
15
+
16
+ <path d="M10.6 14V11.5L12.5 9" />
17
+ <circle cx="13.5" cy="7.8" r="1.5" />
18
+
19
+ <path d="M14 14V12.5C14 11.8 14.5 11.5 15.2 11.5H17" />
20
+ <circle cx="18.5" cy="11.5" r="1.5" />
21
+ </svg>
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ viewBox="0 0 24 24"
5
+ fill="none"
6
+ stroke="#7D7D87"
7
+ stroke-width="1.3"
8
+ stroke-linecap="round"
9
+ stroke-linejoin="round"
10
+ >
11
+ <path d="M3 21 L3 6 C3 4.343 4.343 3 6 3 H18 C19.657 3 22 4.343 22 6 V14 C22 15.657 19.657 17 18 17 H8.5 L3 21 Z" />
12
+
13
+ <path d="M6.8 14V9.5" />
14
+ <circle cx="6.8" cy="8" r="1.5" />
15
+
16
+ <path d="M10.6 14V11.5L12.5 9" />
17
+ <circle cx="13.5" cy="7.8" r="1.5" />
18
+
19
+ <path d="M14 14V12.5C14 11.8 14.5 11.5 15.2 11.5H17" />
20
+ <circle cx="18.5" cy="11.5" r="1.5" />
21
+ </svg>
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Arquivo: n8n-nodes-atendix/nodes/Atendix/Atendix.node.ts
3
+ *
4
+ * Node oficial Atendix - Tray Commerce
5
+ * Versão 1.2.0 - Licenciamento SaaS + Full Operations
6
+ */
7
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
8
+ export declare class Atendix implements INodeType {
9
+ description: INodeTypeDescription;
10
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
11
+ }
@@ -0,0 +1,325 @@
1
+ "use strict";
2
+ /**
3
+ * Arquivo: n8n-nodes-atendix/nodes/Atendix/Atendix.node.ts
4
+ *
5
+ * Node oficial Atendix - Tray Commerce
6
+ * Versão 1.2.0 - Licenciamento SaaS + Full Operations
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.Atendix = void 0;
10
+ const n8n_workflow_1 = require("n8n-workflow");
11
+ class Atendix {
12
+ constructor() {
13
+ this.description = {
14
+ displayName: 'Atendix - Tray API',
15
+ name: 'atendix',
16
+ icon: {
17
+ light: 'file:tray.svg',
18
+ dark: 'file:tray.svg',
19
+ },
20
+ group: ['transform'],
21
+ version: 1,
22
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
23
+ description: 'Integração com a plataforma Tray Commerce',
24
+ defaults: {
25
+ name: 'Atendix - Tray API',
26
+ },
27
+ inputs: ['main'],
28
+ outputs: ['main'],
29
+ credentials: [
30
+ {
31
+ name: 'trayApiAuto',
32
+ required: true,
33
+ displayName: 'Atendix - Tray API',
34
+ },
35
+ ],
36
+ properties: [
37
+ // ==================== RESOURCE SELECTOR ====================
38
+ {
39
+ displayName: 'Resource',
40
+ name: 'resource',
41
+ type: 'options',
42
+ noDataExpression: true,
43
+ options: [
44
+ { name: 'Pedido', value: 'order' },
45
+ { name: 'Cliente', value: 'customer' },
46
+ { name: 'Produto', value: 'product' },
47
+ ],
48
+ default: 'order',
49
+ },
50
+ // ==================== PEDIDOS - OPERATIONS ====================
51
+ {
52
+ displayName: 'Operation',
53
+ name: 'operation',
54
+ type: 'options',
55
+ noDataExpression: true,
56
+ displayOptions: { show: { resource: ['order'] } },
57
+ options: [
58
+ {
59
+ name: 'Buscar Pedido',
60
+ value: 'get',
61
+ description: 'Busca um pedido específico por ID',
62
+ action: 'Buscar um pedido',
63
+ },
64
+ {
65
+ name: 'Listar Pedidos',
66
+ value: 'list',
67
+ description: 'Lista pedidos com filtros opcionais',
68
+ action: 'Listar pedidos',
69
+ },
70
+ ],
71
+ default: 'get',
72
+ },
73
+ {
74
+ displayName: 'ID do Pedido',
75
+ name: 'orderId',
76
+ type: 'string',
77
+ required: true,
78
+ displayOptions: { show: { resource: ['order'], operation: ['get'] } },
79
+ default: '',
80
+ },
81
+ {
82
+ displayName: 'Filtros',
83
+ name: 'filters',
84
+ type: 'collection',
85
+ placeholder: 'Adicionar Filtro',
86
+ default: {},
87
+ displayOptions: { show: { resource: ['order'], operation: ['list'] } },
88
+ options: [
89
+ {
90
+ displayName: 'Status',
91
+ name: 'status',
92
+ type: 'options',
93
+ options: [
94
+ { name: 'Pendente', value: 'pending' },
95
+ { name: 'Aprovado', value: 'approved' },
96
+ { name: 'Enviado', value: 'shipped' },
97
+ { name: 'Entregue', value: 'delivered' },
98
+ { name: 'Cancelado', value: 'cancelled' },
99
+ ],
100
+ default: '',
101
+ },
102
+ { displayName: 'Limite', name: 'limit', type: 'number', default: 50 },
103
+ ],
104
+ },
105
+ // ==================== CLIENTES - OPERATIONS ====================
106
+ {
107
+ displayName: 'Operation',
108
+ name: 'operation',
109
+ type: 'options',
110
+ noDataExpression: true,
111
+ displayOptions: { show: { resource: ['customer'] } },
112
+ options: [
113
+ { name: 'Buscar Cliente', value: 'get', action: 'Buscar um cliente' },
114
+ { name: 'Criar/Atualizar Cliente', value: 'upsert', action: 'Criar ou atualizar cliente' },
115
+ ],
116
+ default: 'get',
117
+ },
118
+ {
119
+ displayName: 'Buscar Por',
120
+ name: 'searchType',
121
+ type: 'options',
122
+ displayOptions: { show: { resource: ['customer'], operation: ['get'] } },
123
+ options: [
124
+ { name: 'E-mail', value: 'email' },
125
+ { name: 'CPF/CNPJ', value: 'cpf_cnpj' },
126
+ ],
127
+ default: 'email',
128
+ },
129
+ {
130
+ displayName: 'Valor',
131
+ name: 'searchValue',
132
+ type: 'string',
133
+ displayOptions: { show: { resource: ['customer'], operation: ['get'] } },
134
+ default: '',
135
+ },
136
+ // ==================== PRODUTOS - OPERATIONS ====================
137
+ {
138
+ displayName: 'Operation',
139
+ name: 'operation',
140
+ type: 'options',
141
+ noDataExpression: true,
142
+ displayOptions: { show: { resource: ['product'] } },
143
+ options: [
144
+ { name: 'Buscar Produto', value: 'get', action: 'Buscar um produto' },
145
+ { name: 'Atualizar Estoque', value: 'updateStock', action: 'Atualizar estoque do produto' },
146
+ ],
147
+ default: 'get',
148
+ },
149
+ {
150
+ displayName: 'Buscar Por',
151
+ name: 'searchType',
152
+ type: 'options',
153
+ displayOptions: { show: { resource: ['product'], operation: ['get'] } },
154
+ options: [
155
+ { name: 'ID do Produto', value: 'id' },
156
+ { name: 'SKU', value: 'sku' },
157
+ ],
158
+ default: 'id',
159
+ },
160
+ {
161
+ displayName: 'Valor',
162
+ name: 'searchValue',
163
+ type: 'string',
164
+ displayOptions: { show: { resource: ['product'], operation: ['get'] } },
165
+ default: '',
166
+ },
167
+ ],
168
+ };
169
+ }
170
+ async execute() {
171
+ var _a, _b;
172
+ const items = this.getInputData();
173
+ const returnData = [];
174
+ const resource = this.getNodeParameter('resource', 0);
175
+ const operation = this.getNodeParameter('operation', 0);
176
+ const atendixAuthToken = process.env.ATENDIX_AUTH_TOKEN;
177
+ if (!atendixAuthToken) {
178
+ console.error('❌ ATENDIX_AUTH_TOKEN não configurado nas variáveis de ambiente');
179
+ throw new Error('Configuração de validação Atendix incompleta. Entre em contato com o suporte.');
180
+ }
181
+ // 1. Get Credentials
182
+ const credentials = await this.getCredentials('trayApiAuto');
183
+ let baseUrl = (credentials.apiHost || credentials.apiAddress || '').trim().replace(/\/$/, '');
184
+ let licenseCheck;
185
+ // ==========================================
186
+ // 1.1 VALIDAÇÃO DE LICENÇA (SaaS GATEKEEPER)
187
+ // ==========================================
188
+ // Pegamos a URL base da loja para validar no seu servidor
189
+ const storeBaseUrl = baseUrl.replace('/web_api', '');
190
+ try {
191
+ licenseCheck = await this.helpers.httpRequest({
192
+ method: 'POST',
193
+ url: 'https://n8n.mariapinho.com.br/webhook/valida-atendix',
194
+ headers: {
195
+ 'Content-Type': 'application/json',
196
+ 'Authorization': atendixAuthToken,
197
+ },
198
+ body: { url: storeBaseUrl },
199
+ json: true,
200
+ });
201
+ console.error('LICENSE CHECK RESPONSE: ' + licenseCheck.autorizado);
202
+ if (!licenseCheck || licenseCheck.autorizado !== true) {
203
+ console.error('Resposta inválida do servidor de licenças:', licenseCheck);
204
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Sua licença Atendix para a loja ${storeBaseUrl} não está ativa ou não pode ser validada. Para liberar seu acesso e automatizar sua operação, acesse https://atendix.com.br e assine um plano agora mesmo!`);
205
+ }
206
+ }
207
+ catch (error) {
208
+ console.error('ATENDIX_AUTH_TOKEN:', atendixAuthToken);
209
+ console.error('STORE BASE URL:', storeBaseUrl);
210
+ console.error('LICENSECHECK:', licenseCheck);
211
+ console.error('Erro HTTP licença:', (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status);
212
+ console.error('Body licença:', (_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.data);
213
+ // Se for erro de acesso bloqueado, repassa. Se for erro de rede, avisa o usuário.
214
+ if (error instanceof n8n_workflow_1.NodeOperationError)
215
+ throw error;
216
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), "Erro crítico: Não foi possível validar a licença do Atendix no servidor MariaPinho.");
217
+ }
218
+ // 2. TOKEN MANAGER (Fallback)
219
+ let accessToken = credentials.accessToken;
220
+ if (!accessToken) {
221
+ try {
222
+ const authResponse = await this.helpers.httpRequest({
223
+ method: 'POST',
224
+ url: `${baseUrl}/auth`,
225
+ body: {
226
+ consumer_key: process.env.TRAY_CONSUMER_KEY,
227
+ consumer_secret: process.env.TRAY_CONSUMER_SECRET,
228
+ code: credentials.authCode,
229
+ },
230
+ json: true,
231
+ });
232
+ accessToken = authResponse.access_token;
233
+ }
234
+ catch (authError) {
235
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Falha na Autenticação Tray: ${authError.message}`);
236
+ }
237
+ }
238
+ for (let i = 0; i < items.length; i++) {
239
+ try {
240
+ let responseData = {};
241
+ const qs = { access_token: accessToken };
242
+ // ==================== PEDIDOS ====================
243
+ if (resource === 'order') {
244
+ if (operation === 'get') {
245
+ const orderId = this.getNodeParameter('orderId', i);
246
+ responseData = await this.helpers.httpRequest({
247
+ method: 'GET',
248
+ url: `${baseUrl}/orders/${orderId}`,
249
+ qs,
250
+ json: true,
251
+ });
252
+ }
253
+ if (operation === 'list') {
254
+ const filters = this.getNodeParameter('filters', i, {});
255
+ if (filters.status)
256
+ qs.status = filters.status;
257
+ if (filters.limit)
258
+ qs.limit = filters.limit;
259
+ responseData = await this.helpers.httpRequest({
260
+ method: 'GET',
261
+ url: `${baseUrl}/orders`,
262
+ qs,
263
+ json: true,
264
+ });
265
+ }
266
+ }
267
+ // ==================== CLIENTES ====================
268
+ if (resource === 'customer') {
269
+ if (operation === 'get') {
270
+ const searchType = this.getNodeParameter('searchType', i);
271
+ const searchValue = this.getNodeParameter('searchValue', i);
272
+ if (searchType === 'email')
273
+ qs.email = searchValue;
274
+ else
275
+ qs.cpf_cnpj = searchValue;
276
+ responseData = await this.helpers.httpRequest({
277
+ method: 'GET',
278
+ url: `${baseUrl}/customers`,
279
+ qs,
280
+ json: true,
281
+ });
282
+ }
283
+ // Nota da Gerente: Se for adicionar UPSERT aqui, o código deve ser incluído abaixo
284
+ }
285
+ // ==================== PRODUTOS ====================
286
+ if (resource === 'product') {
287
+ if (operation === 'get') {
288
+ const searchType = this.getNodeParameter('searchType', i);
289
+ const searchValue = this.getNodeParameter('searchValue', i);
290
+ if (searchType === 'id') {
291
+ responseData = await this.helpers.httpRequest({
292
+ method: 'GET',
293
+ url: `${baseUrl}/products/${searchValue}`,
294
+ qs,
295
+ json: true,
296
+ });
297
+ }
298
+ else {
299
+ qs.sku = searchValue;
300
+ responseData = await this.helpers.httpRequest({
301
+ method: 'GET',
302
+ url: `${baseUrl}/products`,
303
+ qs,
304
+ json: true,
305
+ });
306
+ }
307
+ }
308
+ // Nota da Gerente: Se for adicionar UPDATE_STOCK aqui, o código deve ser incluído abaixo
309
+ }
310
+ const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(responseData), { itemData: { item: i } });
311
+ returnData.push(...executionData);
312
+ }
313
+ catch (error) {
314
+ const errorMessage = error instanceof Error ? error.message : String(error);
315
+ if (this.continueOnFail()) {
316
+ returnData.push({ json: { error: errorMessage }, pairedItem: { item: i } });
317
+ continue;
318
+ }
319
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), errorMessage, { itemIndex: i });
320
+ }
321
+ }
322
+ return [returnData];
323
+ }
324
+ }
325
+ exports.Atendix = Atendix;
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ viewBox="0 0 24 24"
5
+ fill="none"
6
+ stroke="#7D7D87"
7
+ stroke-width="1.3"
8
+ stroke-linecap="round"
9
+ stroke-linejoin="round"
10
+ >
11
+ <path d="M3 21 L3 6 C3 4.343 4.343 3 6 3 H18 C19.657 3 22 4.343 22 6 V14 C22 15.657 19.657 17 18 17 H8.5 L3 21 Z" />
12
+
13
+ <path d="M6.8 14V9.5" />
14
+ <circle cx="6.8" cy="8" r="1.5" />
15
+
16
+ <path d="M10.6 14V11.5L12.5 9" />
17
+ <circle cx="13.5" cy="7.8" r="1.5" />
18
+
19
+ <path d="M14 14V12.5C14 11.8 14.5 11.5 15.2 11.5H17" />
20
+ <circle cx="18.5" cy="11.5" r="1.5" />
21
+ </svg>
package/icons/tray.svg ADDED
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ viewBox="0 0 24 24"
5
+ fill="none"
6
+ stroke="#7D7D87"
7
+ stroke-width="1.3"
8
+ stroke-linecap="round"
9
+ stroke-linejoin="round"
10
+ >
11
+ <path d="M3 21 L3 6 C3 4.343 4.343 3 6 3 H18 C19.657 3 22 4.343 22 6 V14 C22 15.657 19.657 17 18 17 H8.5 L3 21 Z" />
12
+
13
+ <path d="M6.8 14V9.5" />
14
+ <circle cx="6.8" cy="8" r="1.5" />
15
+
16
+ <path d="M10.6 14V11.5L12.5 9" />
17
+ <circle cx="13.5" cy="7.8" r="1.5" />
18
+
19
+ <path d="M14 14V12.5C14 11.8 14.5 11.5 15.2 11.5H17" />
20
+ <circle cx="18.5" cy="11.5" r="1.5" />
21
+ </svg>
package/index.js ADDED
@@ -0,0 +1,2 @@
1
+ // Este arquivo é necessário para o n8n
2
+ module.exports = {};
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "n8n-nodes-atendix",
3
+ "version": "1.0.0",
4
+ "description": "Conector Atendix para integração com Tray Commerce",
5
+ "keywords": ["n8n-community-node-package"],
6
+ "license": "MIT",
7
+ "homepage": "https://atendix.com.br",
8
+ "author": {
9
+ "name": "Atendix",
10
+ "email": "contato@atendix.com.br"
11
+ },
12
+ "main": "index.js",
13
+ "scripts": {
14
+ "build": "tsc && copy nodes\\Atendix\\tray.svg dist\\nodes\\Atendix\\tray.svg && copy nodes\\Atendix\\tray.svg dist\\credentials\\tray.svg",
15
+ "dev": "tsc --watch"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "icons"
20
+ ],
21
+ "n8n": {
22
+ "n8nNodesApiVersion": 1,
23
+ "credentials": [
24
+ "dist/credentials/TrayApiAuto.credentials.js"
25
+ ],
26
+ "nodes": [
27
+ "dist/nodes/Atendix/Atendix.node.js"
28
+ ]
29
+ },
30
+ "dependencies": {
31
+ "n8n-workflow": "^1.0.0"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^18.0.0",
35
+ "typescript": "^5.0.0"
36
+ }
37
+ }