@justbrunasso/n8n-nodes-glpi 1.0.2

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/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # n8n-nodes-glpi
2
+
3
+ GLPI REST API node for n8n — GLPI 10.x compatible — **Criteria Builder** + **automatic session** (no cache).
4
+
5
+ **Features**
6
+ - Automatic `initSession` on each execution (no cache) — você não precisa chamar Get Session Token manualmente.
7
+ - Criteria Builder: adicione múltiplos critérios e envie `criteria[n][...]` corretamente.
8
+ - Operações suportadas (v1):
9
+ - initSession
10
+ - killSession (force logout)
11
+ - search (generic) — Criteria Builder
12
+ - getTickets (convenience)
13
+ - listSearchOptions
14
+ - getItem (GET /:itemtype/:id)
15
+ - getTicketFollowup
16
+ - getTicketSolution
17
+ - getTicketTasks
18
+ - getUserById
19
+ - getUserByEmail (helper)
20
+ - createTicket (POST /Ticket/)
21
+ - updateTicket (PUT /Ticket/:id)
22
+ - addFollowup (POST /Ticket/:id/ITILFollowup)
23
+ - addSolution (POST /ITILSolution/)
24
+
25
+ > Implementação obtida e revisada com base na documentação oficial do GLPI (initSession é GET; listSearchOptions/search endpoints e uso de headers). :contentReference[oaicite:1]{index=1}
26
+
27
+ ---
28
+
29
+ ## Instalação (local / desenvolvimento)
30
+
31
+ 1. Coloque a pasta `n8n-nodes-glpi` no seu workspace.
32
+ 2. `npm install`
33
+ 3. `npm run build`
34
+ 4. No n8n (instância), instale o pacote (opções):
35
+ - Link local: `npm link` no pacote e `npm link n8n-nodes-glpi` na instância do n8n.
36
+ - Ou publique no npm e `npm install n8n-nodes-glpi` no n8n.
37
+ 5. Reinicie o n8n.
38
+
39
+ ---
40
+
41
+ ## Credenciais
42
+
43
+ Crie credenciais do tipo **GLPI API** no n8n com:
44
+
45
+ - Base URL: `https://seu-glpi.exemplo` (sem `/apirest.php`)
46
+ - App Token (obrigatório)
47
+ - User Token (opcional) — se presente, usará `Authorization: user_token <token>`
48
+ - Username/Password (opcional) — se não usar userToken, o node usa Basic Auth
49
+
50
+ ---
51
+
52
+ ## Como usar (exemplos)
53
+
54
+ ### 1) Init Session (operação manual — raramente precisa)
55
+ - Operation: `Init Session`
56
+ - Executar → retorna objeto com `session_token`.
57
+
58
+ ### 2) Search com Criteria Builder
59
+ - Operation: `Search`
60
+ - Entity: `Ticket`
61
+ - Criteria:
62
+ - Field: `19`
63
+ - Search type: `morethan`
64
+ - Value: `={{ (() => { const d = new Date(); d.setDate(d.getDate() - 1); return d.toISOString().split('T')[0] + ' 00:00:00'; })() }}`
65
+ - Glue: `AND`
66
+ - Range: `0-500`
67
+
68
+ > O node monta parâmetros do tipo `criteria[n][...]` e envia para `/apirest.php/search/Ticket`. Exemplo:
69
+ >
70
+ > `criteria[0][field]=...&criteria[0][searchtype]=...&criteria[0][value]=...&criteria[0][glue]=AND&range=0-50`
71
+ >
72
+ > (Nota: o operador lógico é enviado em `criteria[n][glue]` — ajuste apenas se sua versão da API exigir outra chave.)
73
+
74
+ ---
75
+
76
+ ## Headers gerados (exemplo)
77
+ - Sempre: `App-Token: <appToken>`
78
+ - Se `userToken` presente: `Authorization: user_token <userToken>`
79
+ - Se usar username/password: Basic Auth via `auth` (não envia header Authorization direto, usa credenciais HTTP)
80
+
81
+ ---
82
+
83
+ ## Testar localmente (recomendações rápidas)
84
+ 1. Build:
85
+ ```bash
86
+ npm install
87
+ npm run build
88
+ ```
89
+ 2. Gerar tarball e instalar na instância do n8n:
90
+ ```bash
91
+ npm pack
92
+ # copia o .tgz para o host do n8n e, no diretório do n8n:
93
+ npm install /caminho/para/n8n-nodes-glpi-1.0.0.tgz
94
+ # reinicie o n8n
95
+ ```
96
+
97
+ ---
98
+
99
+ ## Publicação
100
+ - Adicione em package.json: `"files": ["dist"]`, `repository`, `bugs` e `homepage`.
101
+ - Mantenha `n8n-core` e `n8n-workflow` como peerDependencies e instale-os como devDependencies para desenvolvimento.
102
+ - Use `npm publish --access public` após verificar o tarball (`npm pack`) localmente.
103
+
104
+ # Troubleshooting rápido
105
+ - Erro TS no VS Code: instale devDependencies (`n8n-core`, `n8n-workflow`, `@types/node`, `typescript`) e reinicie o TS Server (Ctrl+Shift+P → TypeScript: Restart TS Server).
106
+ - Se o node não aparecer no n8n, verifique se `dist/index.js` existe e se instalou o pacote na instância do n8n; reinicie o serviço n8n.
107
+
108
+ # Licença / Contribuição
109
+ - Inclua LICENSE e um link para issues/PRs no package.json.
@@ -0,0 +1,2 @@
1
+ export * from './nodes/Glpi/Glpi.node';
2
+ export * from './nodes/Glpi/GlpiApi.credentials';
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./nodes/Glpi/Glpi.node"), exports);
18
+ __exportStar(require("./nodes/Glpi/GlpiApi.credentials"), exports);
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yDAAuC;AACvC,mEAAiD"}
@@ -0,0 +1,3 @@
1
+ import { INodeProperties } from 'n8n-workflow';
2
+ export declare const glpiOperations: INodeProperties[];
3
+ export declare const glpiFields: INodeProperties[];
@@ -0,0 +1,132 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.glpiFields = exports.glpiOperations = void 0;
4
+ exports.glpiOperations = [
5
+ {
6
+ displayName: 'Operation',
7
+ name: 'operation',
8
+ type: 'options',
9
+ options: [
10
+ { name: 'Init Session', value: 'initSession', description: 'GET /apirest.php/initSession' },
11
+ { name: 'Kill Session (force logout)', value: 'killSession', description: 'GET /apirest.php/killSession' },
12
+ { name: 'Search (generic) - Criteria Builder', value: 'search', description: 'GET /apirest.php/search/:entity' },
13
+ { name: 'Get Tickets (convenience)', value: 'getTickets', description: 'Search Ticket (with criteria builder or searchText)' },
14
+ { name: 'List Search Options', value: 'listSearchOptions', description: 'GET /apirest.php/listSearchOptions/:entity' },
15
+ { name: 'Get Item', value: 'getItem', description: 'GET /apirest.php/:itemtype/:id' },
16
+ { name: 'Get Ticket Followup', value: 'getTicketFollowup', description: 'GET /apirest.php/Ticket/:id/ITILFollowup' },
17
+ { name: 'Get Ticket Solution', value: 'getTicketSolution', description: 'GET /apirest.php/Ticket/:id/ITILSolution' },
18
+ { name: 'Get Ticket Tasks', value: 'getTicketTasks', description: 'GET /apirest.php/Ticket/:id/TicketTask' },
19
+ { name: 'Get User by ID', value: 'getUserById', description: 'GET /apirest.php/User/:id' },
20
+ { name: 'Get User by Email (helper)', value: 'getUserByEmail', description: 'Search User by email' },
21
+ { name: 'Create Ticket', value: 'createTicket', description: 'POST /apirest.php/Ticket' },
22
+ { name: 'Update Ticket', value: 'updateTicket', description: 'PUT /apirest.php/Ticket/:id' },
23
+ { name: 'Add Followup', value: 'addFollowup', description: 'POST /apirest.php/Ticket/:id/ITILFollowup' },
24
+ { name: 'Add Solution', value: 'addSolution', description: 'POST /apirest.php/ITILSolution' },
25
+ ],
26
+ default: 'initSession',
27
+ }
28
+ ];
29
+ exports.glpiFields = [
30
+ // entity for search and listSearchOptions
31
+ {
32
+ displayName: 'Entity',
33
+ name: 'entity',
34
+ type: 'string',
35
+ default: 'Ticket',
36
+ description: 'Entity name (Ticket, User, ITILCategory, ...).',
37
+ displayOptions: { show: { operation: ['search', 'listSearchOptions', 'getTickets'] } },
38
+ },
39
+ // Criteria builder as fixedCollection multiple
40
+ {
41
+ displayName: 'Criteria',
42
+ name: 'criteria',
43
+ type: 'fixedCollection',
44
+ typeOptions: { multipleValues: true },
45
+ placeholder: 'Add Criterion',
46
+ description: 'One or more criteria for the search',
47
+ displayOptions: {
48
+ show: {
49
+ operation: ['search', 'getTickets'],
50
+ },
51
+ },
52
+ options: [
53
+ {
54
+ displayName: 'Criterion',
55
+ name: 'criterion',
56
+ values: [
57
+ { displayName: 'Field', name: 'field', type: 'string', default: '' },
58
+ { displayName: 'Search Type', name: 'searchtype', type: 'options', options: [{ name: 'contains', value: 'contains' }], default: 'contains' },
59
+ { displayName: 'Value', name: 'value', type: 'string', default: '' },
60
+ { displayName: 'Glue (AND/OR)', name: 'glue', type: 'options', options: [{ name: 'AND', value: 'AND' }, { name: 'OR', value: 'OR' }], default: 'AND' },
61
+ ],
62
+ },
63
+ ],
64
+ default: [], // <-- obrigatório para fixedCollection com multipleValues: true
65
+ },
66
+ // range and searchText
67
+ {
68
+ displayName: 'Range',
69
+ name: 'range',
70
+ type: 'string',
71
+ default: '0-50',
72
+ displayOptions: { show: { operation: ['search', 'getTickets'] } },
73
+ description: 'Range parameter for GLPI (ex: 0-500).'
74
+ },
75
+ {
76
+ displayName: 'Search Text (convenience)',
77
+ name: 'searchText',
78
+ type: 'string',
79
+ default: '',
80
+ displayOptions: { show: { operation: ['getTickets'] } },
81
+ description: 'searchText param for Ticket search.',
82
+ },
83
+ // generic itemId & ticketInput
84
+ {
85
+ displayName: 'Item ID',
86
+ name: 'itemId',
87
+ type: 'number',
88
+ default: 0,
89
+ displayOptions: { show: { operation: ['getTicketFollowup', 'getTicketSolution', 'getTicketTasks', 'getItem', 'updateTicket', 'addFollowup'] } },
90
+ description: 'ID of the item.',
91
+ },
92
+ {
93
+ displayName: 'Ticket Input (JSON)',
94
+ name: 'ticketInput',
95
+ type: 'json',
96
+ default: { input: {} },
97
+ displayOptions: { show: { operation: ['createTicket', 'updateTicket'] } },
98
+ description: 'Provide {"input": { ... }} body for Ticket creation or update.',
99
+ },
100
+ {
101
+ displayName: 'Followup Input (JSON)',
102
+ name: 'followupInput',
103
+ type: 'json',
104
+ default: { input: {} },
105
+ displayOptions: { show: { operation: ['addFollowup'] } },
106
+ description: 'Provide {"input": {...}} for ITILFollowup.',
107
+ },
108
+ {
109
+ displayName: 'Solution Input (JSON)',
110
+ name: 'solutionInput',
111
+ type: 'json',
112
+ default: { input: {} },
113
+ displayOptions: { show: { operation: ['addSolution'] } },
114
+ description: 'Provide {"input": {...}} for ITILSolution.',
115
+ },
116
+ // user helpers
117
+ {
118
+ displayName: 'User ID',
119
+ name: 'userId',
120
+ type: 'number',
121
+ default: 0,
122
+ displayOptions: { show: { operation: ['getUserById'] } },
123
+ },
124
+ {
125
+ displayName: 'User Email (helper)',
126
+ name: 'userEmail',
127
+ type: 'string',
128
+ default: '',
129
+ displayOptions: { show: { operation: ['getUserByEmail'] } },
130
+ }
131
+ ];
132
+ //# sourceMappingURL=Glpi.descriptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Glpi.descriptions.js","sourceRoot":"","sources":["../../../nodes/Glpi/Glpi.descriptions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class Glpi implements INodeType {
3
+ description: INodeTypeDescription;
4
+ private buildBaseAuth;
5
+ private getSessionToken;
6
+ private buildCriteriaQuery;
7
+ execute(this: any): Promise<INodeExecutionData[][]>;
8
+ }
@@ -0,0 +1,253 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Glpi = void 0;
4
+ const Glpi_descriptions_1 = require("./Glpi.descriptions");
5
+ class Glpi {
6
+ constructor() {
7
+ this.description = {
8
+ displayName: 'GLPI',
9
+ name: 'glpi',
10
+ icon: 'file:glpi.svg',
11
+ group: ['transform'],
12
+ version: 1,
13
+ description: 'GLPI REST API node (GLPI 10.x) with Criteria Builder and automatic session (no cache).',
14
+ defaults: { name: 'GLPI' },
15
+ inputs: ['main'],
16
+ outputs: ['main'],
17
+ credentials: [{ name: 'glpiApi', required: true }],
18
+ properties: [
19
+ ...Glpi_descriptions_1.glpiOperations,
20
+ ...Glpi_descriptions_1.glpiFields,
21
+ ],
22
+ };
23
+ }
24
+ // build base headers & possible basic auth from credentials
25
+ async buildBaseAuth() {
26
+ const credentials = await this.getCredentials('glpiApi');
27
+ if (!credentials)
28
+ throw new Error('Credenciais GLPI não configuradas.');
29
+ const baseUrl = credentials.baseUrl.replace(/\/+$/, '');
30
+ const appToken = credentials.appToken;
31
+ const userToken = credentials.userToken;
32
+ const username = credentials.username;
33
+ const password = credentials.password;
34
+ const headers = {
35
+ 'App-Token': appToken,
36
+ 'Content-Type': 'application/json',
37
+ };
38
+ let auth;
39
+ if (userToken && userToken.toString().trim() !== '') {
40
+ headers['Authorization'] = `user_token ${userToken}`;
41
+ }
42
+ else if (username && username.toString().trim() !== '') {
43
+ auth = { user: username, pass: password };
44
+ }
45
+ return { baseUrl, headers, auth };
46
+ }
47
+ // request initSession and return session token
48
+ async getSessionToken(baseUrl, headers, auth) {
49
+ // initSession is GET
50
+ const endpoint = `${baseUrl}/apirest.php/initSession`;
51
+ const response = await this.helpers.request({
52
+ method: 'GET',
53
+ uri: endpoint,
54
+ headers,
55
+ auth,
56
+ json: true,
57
+ resolveWithFullResponse: false,
58
+ });
59
+ // response usually contains session_token
60
+ // support different shapes: { session_token: '...' } or { session: { session_token: '...' } }
61
+ if (!response) {
62
+ throw new Error('No response from GLPI initSession');
63
+ }
64
+ const token = response.session_token || response.session?.session_token;
65
+ if (!token) {
66
+ throw new Error('No session token returned by GLPI initSession');
67
+ }
68
+ return token;
69
+ }
70
+ // monta query string para criteria[] usando índice sequencial
71
+ buildCriteriaQuery(criteriaInput) {
72
+ if (!criteriaInput || !Array.isArray(criteriaInput) || criteriaInput.length === 0)
73
+ return '';
74
+ const parts = [];
75
+ // Because fixedCollection returns array of {criterion: [ ... ]}, flatten
76
+ let flat = [];
77
+ for (const c of criteriaInput) {
78
+ if (c && c.criterion && Array.isArray(c.criterion))
79
+ flat = flat.concat(c.criterion);
80
+ else if (Array.isArray(c))
81
+ flat = flat.concat(c);
82
+ else
83
+ flat.push(c);
84
+ }
85
+ for (let i = 0; i < flat.length; i++) {
86
+ const item = flat[i];
87
+ parts.push(`criteria[${i}][field]=${encodeURIComponent(String(item.field))}`);
88
+ parts.push(`criteria[${i}][searchtype]=${encodeURIComponent(String(item.searchtype))}`);
89
+ parts.push(`criteria[${i}][value]=${encodeURIComponent(String(item.value))}`);
90
+ // send glue (logical operator) — use "glue" key (change to "email" only if required for compatibility)
91
+ parts.push(`criteria[${i}][glue]=${encodeURIComponent(String(item.glue || 'AND'))}`);
92
+ }
93
+ return parts.join('&');
94
+ }
95
+ async execute() {
96
+ const items = this.getInputData();
97
+ const returnData = [];
98
+ const operation = this.getNodeParameter('operation', 0);
99
+ // build base auth & headers
100
+ const { baseUrl, headers: baseHeaders, auth } = await this.buildBaseAuth();
101
+ try {
102
+ // If operation is initSession itself, just call it and return
103
+ if (operation === 'initSession') {
104
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/initSession`, headers: baseHeaders, auth, json: true });
105
+ returnData.push({ json: response });
106
+ return [returnData];
107
+ }
108
+ // For all other operations, per your option C (no cache) -> request NEW session token now
109
+ const sessionToken = await this.getSessionToken(baseUrl, baseHeaders, auth);
110
+ // add Session-Token header for subsequent requests
111
+ const headers = { ...baseHeaders, 'Session-Token': sessionToken };
112
+ // handle operations
113
+ // killSession (force logout)
114
+ if (operation === 'killSession') {
115
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/killSession`, headers, json: true });
116
+ returnData.push({ json: response });
117
+ return [returnData];
118
+ }
119
+ // listSearchOptions
120
+ if (operation === 'listSearchOptions') {
121
+ const entity = this.getNodeParameter('entity', 0);
122
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/listSearchOptions/${entity}`, headers, json: true });
123
+ returnData.push({ json: response });
124
+ return [returnData];
125
+ }
126
+ // generic search (build criteria)
127
+ if (operation === 'search') {
128
+ const entity = this.getNodeParameter('entity', 0);
129
+ const criteriaInput = this.getNodeParameter('criteria', 0, []);
130
+ const range = this.getNodeParameter('range', 0, '0-50');
131
+ const qs = this.buildCriteriaQuery(criteriaInput);
132
+ const endpoint = `${baseUrl}/apirest.php/search/${entity}?${qs}&range=${encodeURIComponent(range)}`;
133
+ const response = await this.helpers.request({ method: 'GET', uri: endpoint, headers, json: true });
134
+ returnData.push({ json: response });
135
+ return [returnData];
136
+ }
137
+ // getTickets convenience
138
+ if (operation === 'getTickets') {
139
+ const criteriaInput = this.getNodeParameter('criteria', 0, []);
140
+ const range = this.getNodeParameter('range', 0, '0-50');
141
+ const searchText = this.getNodeParameter('searchText', 0, '');
142
+ const qs = this.buildCriteriaQuery(criteriaInput);
143
+ let endpoint = `${baseUrl}/apirest.php/search/Ticket?${qs}&range=${encodeURIComponent(range)}`;
144
+ if (searchText && searchText.trim() !== '')
145
+ endpoint += `&searchText=${encodeURIComponent(searchText)}`;
146
+ const response = await this.helpers.request({ method: 'GET', uri: endpoint, headers, json: true });
147
+ returnData.push({ json: response });
148
+ return [returnData];
149
+ }
150
+ // getItem generic
151
+ if (operation === 'getItem') {
152
+ const entity = this.getNodeParameter('entity', 0);
153
+ const itemId = this.getNodeParameter('itemId', 0);
154
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/${entity}/${itemId}`, headers, json: true });
155
+ returnData.push({ json: response });
156
+ return [returnData];
157
+ }
158
+ // getTicketFollowup
159
+ if (operation === 'getTicketFollowup') {
160
+ const itemId = this.getNodeParameter('itemId', 0);
161
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/Ticket/${itemId}/ITILFollowup`, headers, json: true });
162
+ returnData.push({ json: response });
163
+ return [returnData];
164
+ }
165
+ // getTicketSolution
166
+ if (operation === 'getTicketSolution') {
167
+ const itemId = this.getNodeParameter('itemId', 0);
168
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/Ticket/${itemId}/ITILSolution`, headers, json: true });
169
+ returnData.push({ json: response });
170
+ return [returnData];
171
+ }
172
+ // getTicketTasks
173
+ if (operation === 'getTicketTasks') {
174
+ const itemId = this.getNodeParameter('itemId', 0);
175
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/Ticket/${itemId}/TicketTask`, headers, json: true });
176
+ returnData.push({ json: response });
177
+ return [returnData];
178
+ }
179
+ // getUserById
180
+ if (operation === 'getUserById') {
181
+ const userId = this.getNodeParameter('userId', 0);
182
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/User/${userId}`, headers, json: true });
183
+ returnData.push({ json: response });
184
+ return [returnData];
185
+ }
186
+ // getUserByEmail helper
187
+ if (operation === 'getUserByEmail') {
188
+ const email = this.getNodeParameter('userEmail', 0);
189
+ if (!email)
190
+ throw new Error('E-mail obrigatório.');
191
+ const qs = `criteria[0][glue]=AND&criteria[0][field]=1&criteria[0][searchtype]=contains&criteria[0][value]=${encodeURIComponent(email)}&range=0-50`;
192
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/search/User?${qs}`, headers, json: true });
193
+ returnData.push({ json: response });
194
+ return [returnData];
195
+ }
196
+ // createTicket (POST /Ticket/)
197
+ if (operation === 'createTicket') {
198
+ const ticketInput = this.getNodeParameter('ticketInput', 0);
199
+ if (!ticketInput || typeof ticketInput !== 'object')
200
+ throw new Error('ticketInput inválido.');
201
+ const response = await this.helpers.request({ method: 'POST', uri: `${baseUrl}/apirest.php/Ticket/`, headers, body: ticketInput, json: true });
202
+ returnData.push({ json: response });
203
+ return [returnData];
204
+ }
205
+ // updateTicket (PUT /Ticket/:id)
206
+ if (operation === 'updateTicket') {
207
+ const itemId = this.getNodeParameter('itemId', 0);
208
+ const ticketInput = this.getNodeParameter('ticketInput', 0);
209
+ if (!itemId)
210
+ throw new Error('itemId obrigatório para updateTicket.');
211
+ if (!ticketInput || typeof ticketInput !== 'object')
212
+ throw new Error('ticketInput inválido.');
213
+ const response = await this.helpers.request({ method: 'PUT', uri: `${baseUrl}/apirest.php/Ticket/${itemId}`, headers, body: ticketInput, json: true });
214
+ returnData.push({ json: response });
215
+ return [returnData];
216
+ }
217
+ // addFollowup (POST /Ticket/:id/ITILFollowup)
218
+ if (operation === 'addFollowup') {
219
+ const itemId = this.getNodeParameter('itemId', 0);
220
+ const followupInput = this.getNodeParameter('followupInput', 0);
221
+ if (!itemId)
222
+ throw new Error('itemId obrigatório para addFollowup.');
223
+ if (!followupInput || typeof followupInput !== 'object')
224
+ throw new Error('followupInput inválido.');
225
+ const response = await this.helpers.request({ method: 'POST', uri: `${baseUrl}/apirest.php/Ticket/${itemId}/ITILFollowup`, headers, body: followupInput, json: true });
226
+ returnData.push({ json: response });
227
+ return [returnData];
228
+ }
229
+ // addSolution (POST /ITILSolution/)
230
+ if (operation === 'addSolution') {
231
+ const solutionInput = this.getNodeParameter('solutionInput', 0);
232
+ if (!solutionInput || typeof solutionInput !== 'object')
233
+ throw new Error('solutionInput inválido.');
234
+ const response = await this.helpers.request({ method: 'POST', uri: `${baseUrl}/apirest.php/ITILSolution/`, headers, body: solutionInput, json: true });
235
+ returnData.push({ json: response });
236
+ return [returnData];
237
+ }
238
+ throw new Error(`Operação desconhecida: ${operation}`);
239
+ }
240
+ catch (err) {
241
+ returnData.push({
242
+ json: {
243
+ error: true,
244
+ message: err.message || String(err),
245
+ stack: err.stack,
246
+ },
247
+ });
248
+ return [returnData];
249
+ }
250
+ }
251
+ }
252
+ exports.Glpi = Glpi;
253
+ //# sourceMappingURL=Glpi.node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Glpi.node.js","sourceRoot":"","sources":["../../../nodes/Glpi/Glpi.node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ export declare class GlpiApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ documentationUrl: string;
6
+ properties: INodeProperties[];
7
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GlpiApi = void 0;
4
+ class GlpiApi {
5
+ constructor() {
6
+ this.name = 'glpiApi';
7
+ this.displayName = 'GLPI API';
8
+ this.documentationUrl = 'https://your-glpi-docs-or-repo.example.com';
9
+ this.properties = [
10
+ {
11
+ displayName: 'Base URL',
12
+ name: 'baseUrl',
13
+ type: 'string',
14
+ default: '',
15
+ placeholder: 'https://atendimento.centrium.com.br',
16
+ required: true,
17
+ description: 'Base URL sem o sufixo /apirest.php',
18
+ },
19
+ {
20
+ displayName: 'App Token',
21
+ name: 'appToken',
22
+ type: 'string',
23
+ typeOptions: { password: true },
24
+ default: '',
25
+ required: true,
26
+ },
27
+ {
28
+ displayName: 'User Token (optional)',
29
+ name: 'userToken',
30
+ type: 'string',
31
+ typeOptions: { password: true },
32
+ default: '',
33
+ required: false,
34
+ description: 'Se presente, envia header "Authorization: user_token <token>" e pode ser usado para initSession.',
35
+ },
36
+ {
37
+ displayName: 'Username (for Basic Auth, optional)',
38
+ name: 'username',
39
+ type: 'string',
40
+ default: '',
41
+ placeholder: 'usuário',
42
+ required: false,
43
+ },
44
+ {
45
+ displayName: 'Password (for Basic Auth, optional)',
46
+ name: 'password',
47
+ type: 'string',
48
+ typeOptions: { password: true },
49
+ default: '',
50
+ placeholder: 'senha',
51
+ required: false,
52
+ }
53
+ ];
54
+ }
55
+ }
56
+ exports.GlpiApi = GlpiApi;
57
+ //# sourceMappingURL=GlpiApi.credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GlpiApi.credentials.js","sourceRoot":"","sources":["../../../nodes/Glpi/GlpiApi.credentials.ts"],"names":[],"mappings":";;;AAEA,MAAa,OAAO;IAApB;QACC,SAAI,GAAG,SAAS,CAAC;QACjB,gBAAW,GAAG,UAAU,CAAC;QACzB,qBAAgB,GAAG,4CAA4C,CAAC;QAChE,eAAU,GAAsB;YAC/B;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,qCAAqC;gBAClD,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,oCAAoC;aACjD;YACD;gBACC,WAAW,EAAE,WAAW;gBACxB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;aACd;YACD;gBACC,WAAW,EAAE,uBAAuB;gBACpC,IAAI,EAAE,WAAW;gBACjB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,KAAK;gBACf,WAAW,EAAE,kGAAkG;aAC/G;YACD;gBACC,WAAW,EAAE,qCAAqC;gBAClD,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,SAAS;gBACtB,QAAQ,EAAE,KAAK;aACf;YACD;gBACC,WAAW,EAAE,qCAAqC;gBAClD,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,OAAO;gBACpB,QAAQ,EAAE,KAAK;aACf;SACD,CAAC;IACH,CAAC;CAAA;AAjDD,0BAiDC"}
package/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './nodes/Glpi/Glpi.node';
2
+ export * from './nodes/Glpi/GlpiApi.credentials';
@@ -0,0 +1,131 @@
1
+ import { INodeProperties } from 'n8n-workflow';
2
+
3
+ export const glpiOperations: INodeProperties[] = [
4
+ {
5
+ displayName: 'Operation',
6
+ name: 'operation',
7
+ type: 'options',
8
+ options: [
9
+ { name: 'Init Session', value: 'initSession', description: 'GET /apirest.php/initSession' },
10
+ { name: 'Kill Session (force logout)', value: 'killSession', description: 'GET /apirest.php/killSession' },
11
+ { name: 'Search (generic) - Criteria Builder', value: 'search', description: 'GET /apirest.php/search/:entity' },
12
+ { name: 'Get Tickets (convenience)', value: 'getTickets', description: 'Search Ticket (with criteria builder or searchText)' },
13
+ { name: 'List Search Options', value: 'listSearchOptions', description: 'GET /apirest.php/listSearchOptions/:entity' },
14
+ { name: 'Get Item', value: 'getItem', description: 'GET /apirest.php/:itemtype/:id' },
15
+ { name: 'Get Ticket Followup', value: 'getTicketFollowup', description: 'GET /apirest.php/Ticket/:id/ITILFollowup' },
16
+ { name: 'Get Ticket Solution', value: 'getTicketSolution', description: 'GET /apirest.php/Ticket/:id/ITILSolution' },
17
+ { name: 'Get Ticket Tasks', value: 'getTicketTasks', description: 'GET /apirest.php/Ticket/:id/TicketTask' },
18
+ { name: 'Get User by ID', value: 'getUserById', description: 'GET /apirest.php/User/:id' },
19
+ { name: 'Get User by Email (helper)', value: 'getUserByEmail', description: 'Search User by email' },
20
+ { name: 'Create Ticket', value: 'createTicket', description: 'POST /apirest.php/Ticket' },
21
+ { name: 'Update Ticket', value: 'updateTicket', description: 'PUT /apirest.php/Ticket/:id' },
22
+ { name: 'Add Followup', value: 'addFollowup', description: 'POST /apirest.php/Ticket/:id/ITILFollowup' },
23
+ { name: 'Add Solution', value: 'addSolution', description: 'POST /apirest.php/ITILSolution' },
24
+ ],
25
+ default: 'initSession',
26
+ }
27
+ ];
28
+
29
+ export const glpiFields: INodeProperties[] = [
30
+ // entity for search and listSearchOptions
31
+ {
32
+ displayName: 'Entity',
33
+ name: 'entity',
34
+ type: 'string',
35
+ default: 'Ticket',
36
+ description: 'Entity name (Ticket, User, ITILCategory, ...).',
37
+ displayOptions: { show: { operation: ['search', 'listSearchOptions', 'getTickets'] } },
38
+ },
39
+ // Criteria builder as fixedCollection multiple
40
+ {
41
+ displayName: 'Criteria',
42
+ name: 'criteria',
43
+ type: 'fixedCollection',
44
+ typeOptions: { multipleValues: true },
45
+ placeholder: 'Add Criterion',
46
+ description: 'One or more criteria for the search',
47
+ displayOptions: {
48
+ show: {
49
+ operation: ['search', 'getTickets'],
50
+ },
51
+ },
52
+ options: [
53
+ {
54
+ displayName: 'Criterion',
55
+ name: 'criterion',
56
+ values: [
57
+ { displayName: 'Field', name: 'field', type: 'string', default: '' },
58
+ { displayName: 'Search Type', name: 'searchtype', type: 'options', options: [{ name: 'contains', value: 'contains' }], default: 'contains' },
59
+ { displayName: 'Value', name: 'value', type: 'string', default: '' },
60
+ { displayName: 'Glue (AND/OR)', name: 'glue', type: 'options', options: [{ name: 'AND', value: 'AND' }, { name: 'OR', value: 'OR' }], default: 'AND' },
61
+ ],
62
+ },
63
+ ],
64
+ default: [], // <-- obrigatório para fixedCollection com multipleValues: true
65
+ },
66
+ // range and searchText
67
+ {
68
+ displayName: 'Range',
69
+ name: 'range',
70
+ type: 'string',
71
+ default: '0-50',
72
+ displayOptions: { show: { operation: ['search', 'getTickets'] } },
73
+ description: 'Range parameter for GLPI (ex: 0-500).'
74
+ },
75
+ {
76
+ displayName: 'Search Text (convenience)',
77
+ name: 'searchText',
78
+ type: 'string',
79
+ default: '',
80
+ displayOptions: { show: { operation: ['getTickets'] } },
81
+ description: 'searchText param for Ticket search.',
82
+ },
83
+ // generic itemId & ticketInput
84
+ {
85
+ displayName: 'Item ID',
86
+ name: 'itemId',
87
+ type: 'number',
88
+ default: 0,
89
+ displayOptions: { show: { operation: ['getTicketFollowup','getTicketSolution','getTicketTasks','getItem','updateTicket','addFollowup'] } },
90
+ description: 'ID of the item.',
91
+ },
92
+ {
93
+ displayName: 'Ticket Input (JSON)',
94
+ name: 'ticketInput',
95
+ type: 'json',
96
+ default: { input: {} },
97
+ displayOptions: { show: { operation: ['createTicket','updateTicket'] } },
98
+ description: 'Provide {"input": { ... }} body for Ticket creation or update.',
99
+ },
100
+ {
101
+ displayName: 'Followup Input (JSON)',
102
+ name: 'followupInput',
103
+ type: 'json',
104
+ default: { input: {} },
105
+ displayOptions: { show: { operation: ['addFollowup'] } },
106
+ description: 'Provide {"input": {...}} for ITILFollowup.',
107
+ },
108
+ {
109
+ displayName: 'Solution Input (JSON)',
110
+ name: 'solutionInput',
111
+ type: 'json',
112
+ default: { input: {} },
113
+ displayOptions: { show: { operation: ['addSolution'] } },
114
+ description: 'Provide {"input": {...}} for ITILSolution.',
115
+ },
116
+ // user helpers
117
+ {
118
+ displayName: 'User ID',
119
+ name: 'userId',
120
+ type: 'number',
121
+ default: 0,
122
+ displayOptions: { show: { operation: ['getUserById'] } },
123
+ },
124
+ {
125
+ displayName: 'User Email (helper)',
126
+ name: 'userEmail',
127
+ type: 'string',
128
+ default: '',
129
+ displayOptions: { show: { operation: ['getUserByEmail'] } },
130
+ }
131
+ ];
@@ -0,0 +1,265 @@
1
+ import {
2
+ INodeExecutionData,
3
+ INodeType,
4
+ INodeTypeDescription,
5
+ } from 'n8n-workflow';
6
+
7
+ import { glpiOperations, glpiFields } from './Glpi.descriptions';
8
+
9
+ export class Glpi implements INodeType {
10
+ description: INodeTypeDescription = {
11
+ displayName: 'GLPI',
12
+ name: 'glpi',
13
+ icon: 'file:glpi.svg',
14
+ group: ['transform'],
15
+ version: 1,
16
+ description: 'GLPI REST API node (GLPI 10.x) with Criteria Builder and automatic session (no cache).',
17
+ defaults: { name: 'GLPI' },
18
+ inputs: ['main'],
19
+ outputs: ['main'],
20
+ credentials: [{ name: 'glpiApi', required: true }],
21
+ properties: [
22
+ ...glpiOperations,
23
+ ...glpiFields,
24
+ ],
25
+ };
26
+
27
+ // build base headers & possible basic auth from credentials
28
+ private async buildBaseAuth(this: any) {
29
+ const credentials: any = await this.getCredentials('glpiApi');
30
+ if (!credentials) throw new Error('Credenciais GLPI não configuradas.');
31
+ const baseUrl = (credentials.baseUrl as string).replace(/\/+$/, '');
32
+ const appToken = credentials.appToken;
33
+ const userToken = credentials.userToken;
34
+ const username = credentials.username;
35
+ const password = credentials.password;
36
+
37
+ const headers: Record<string, string> = {
38
+ 'App-Token': appToken,
39
+ 'Content-Type': 'application/json',
40
+ };
41
+
42
+ let auth: { user?: string; pass?: string } | undefined;
43
+ if (userToken && userToken.toString().trim() !== '') {
44
+ headers['Authorization'] = `user_token ${userToken}`;
45
+ } else if (username && username.toString().trim() !== '') {
46
+ auth = { user: username, pass: password };
47
+ }
48
+ return { baseUrl, headers, auth };
49
+ }
50
+
51
+ // request initSession and return session token
52
+ private async getSessionToken(this: any, baseUrl: string, headers: Record<string, string>, auth?: { user?: string; pass?: string }) {
53
+ // initSession is GET
54
+ const endpoint = `${baseUrl}/apirest.php/initSession`;
55
+ const response = await this.helpers.request({
56
+ method: 'GET',
57
+ uri: endpoint,
58
+ headers,
59
+ auth,
60
+ json: true,
61
+ resolveWithFullResponse: false,
62
+ });
63
+ // response usually contains session_token
64
+ // support different shapes: { session_token: '...' } or { session: { session_token: '...' } }
65
+ if (!response) {
66
+ throw new Error('No response from GLPI initSession');
67
+ }
68
+ const token = (response as any).session_token || (response as any).session?.session_token;
69
+ if (!token) {
70
+ throw new Error('No session token returned by GLPI initSession');
71
+ }
72
+ return token;
73
+ }
74
+
75
+ // monta query string para criteria[] usando índice sequencial
76
+ private buildCriteriaQuery(criteriaInput: Array<any>): string {
77
+ if (!criteriaInput || !Array.isArray(criteriaInput) || criteriaInput.length === 0) return '';
78
+ const parts: string[] = [];
79
+ // Because fixedCollection returns array of {criterion: [ ... ]}, flatten
80
+ let flat: any[] = [];
81
+ for (const c of criteriaInput) {
82
+ if (c && c.criterion && Array.isArray(c.criterion)) flat = flat.concat(c.criterion);
83
+ else if (Array.isArray(c)) flat = flat.concat(c);
84
+ else flat.push(c);
85
+ }
86
+ for (let i = 0; i < flat.length; i++) {
87
+ const item = flat[i];
88
+ parts.push(`criteria[${i}][field]=${encodeURIComponent(String(item.field))}`);
89
+ parts.push(`criteria[${i}][searchtype]=${encodeURIComponent(String(item.searchtype))}`);
90
+ parts.push(`criteria[${i}][value]=${encodeURIComponent(String(item.value))}`);
91
+ // send glue (logical operator) — use "glue" key (change to "email" only if required for compatibility)
92
+ parts.push(`criteria[${i}][glue]=${encodeURIComponent(String(item.glue || 'AND'))}`);
93
+ }
94
+ return parts.join('&');
95
+ }
96
+
97
+ async execute(this: any): Promise<INodeExecutionData[][]> {
98
+ const items = this.getInputData();
99
+ const returnData: INodeExecutionData[] = [];
100
+
101
+ const operation = this.getNodeParameter('operation', 0) as string;
102
+
103
+ // build base auth & headers
104
+ const { baseUrl, headers: baseHeaders, auth } = await this.buildBaseAuth();
105
+
106
+ try {
107
+ // If operation is initSession itself, just call it and return
108
+ if (operation === 'initSession') {
109
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/initSession`, headers: baseHeaders, auth, json: true });
110
+ returnData.push({ json: response });
111
+ return [returnData];
112
+ }
113
+
114
+ // For all other operations, per your option C (no cache) -> request NEW session token now
115
+ const sessionToken = await this.getSessionToken(baseUrl, baseHeaders, auth);
116
+ // add Session-Token header for subsequent requests
117
+ const headers = { ...baseHeaders, 'Session-Token': sessionToken };
118
+
119
+ // handle operations
120
+
121
+ // killSession (force logout)
122
+ if (operation === 'killSession') {
123
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/killSession`, headers, json: true });
124
+ returnData.push({ json: response });
125
+ return [returnData];
126
+ }
127
+
128
+ // listSearchOptions
129
+ if (operation === 'listSearchOptions') {
130
+ const entity = this.getNodeParameter('entity', 0) as string;
131
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/listSearchOptions/${entity}`, headers, json: true });
132
+ returnData.push({ json: response });
133
+ return [returnData];
134
+ }
135
+
136
+ // generic search (build criteria)
137
+ if (operation === 'search') {
138
+ const entity = this.getNodeParameter('entity', 0) as string;
139
+ const criteriaInput = this.getNodeParameter('criteria', 0, []) as any[];
140
+ const range = this.getNodeParameter('range', 0, '0-50') as string;
141
+ const qs = this.buildCriteriaQuery(criteriaInput);
142
+ const endpoint = `${baseUrl}/apirest.php/search/${entity}?${qs}&range=${encodeURIComponent(range)}`;
143
+ const response = await this.helpers.request({ method: 'GET', uri: endpoint, headers, json: true });
144
+ returnData.push({ json: response });
145
+ return [returnData];
146
+ }
147
+
148
+ // getTickets convenience
149
+ if (operation === 'getTickets') {
150
+ const criteriaInput = this.getNodeParameter('criteria', 0, []) as any[];
151
+ const range = this.getNodeParameter('range', 0, '0-50') as string;
152
+ const searchText = this.getNodeParameter('searchText', 0, '') as string;
153
+ const qs = this.buildCriteriaQuery(criteriaInput);
154
+ let endpoint = `${baseUrl}/apirest.php/search/Ticket?${qs}&range=${encodeURIComponent(range)}`;
155
+ if (searchText && searchText.trim() !== '') endpoint += `&searchText=${encodeURIComponent(searchText)}`;
156
+ const response = await this.helpers.request({ method: 'GET', uri: endpoint, headers, json: true });
157
+ returnData.push({ json: response });
158
+ return [returnData];
159
+ }
160
+
161
+ // getItem generic
162
+ if (operation === 'getItem') {
163
+ const entity = this.getNodeParameter('entity', 0) as string;
164
+ const itemId = this.getNodeParameter('itemId', 0) as number;
165
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/${entity}/${itemId}`, headers, json: true });
166
+ returnData.push({ json: response });
167
+ return [returnData];
168
+ }
169
+
170
+ // getTicketFollowup
171
+ if (operation === 'getTicketFollowup') {
172
+ const itemId = this.getNodeParameter('itemId', 0) as number;
173
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/Ticket/${itemId}/ITILFollowup`, headers, json: true });
174
+ returnData.push({ json: response });
175
+ return [returnData];
176
+ }
177
+
178
+ // getTicketSolution
179
+ if (operation === 'getTicketSolution') {
180
+ const itemId = this.getNodeParameter('itemId', 0) as number;
181
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/Ticket/${itemId}/ITILSolution`, headers, json: true });
182
+ returnData.push({ json: response });
183
+ return [returnData];
184
+ }
185
+
186
+ // getTicketTasks
187
+ if (operation === 'getTicketTasks') {
188
+ const itemId = this.getNodeParameter('itemId', 0) as number;
189
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/Ticket/${itemId}/TicketTask`, headers, json: true });
190
+ returnData.push({ json: response });
191
+ return [returnData];
192
+ }
193
+
194
+ // getUserById
195
+ if (operation === 'getUserById') {
196
+ const userId = this.getNodeParameter('userId', 0) as number;
197
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/User/${userId}`, headers, json: true });
198
+ returnData.push({ json: response });
199
+ return [returnData];
200
+ }
201
+
202
+ // getUserByEmail helper
203
+ if (operation === 'getUserByEmail') {
204
+ const email = this.getNodeParameter('userEmail', 0) as string;
205
+ if (!email) throw new Error('E-mail obrigatório.');
206
+ const qs = `criteria[0][glue]=AND&criteria[0][field]=1&criteria[0][searchtype]=contains&criteria[0][value]=${encodeURIComponent(email)}&range=0-50`;
207
+ const response = await this.helpers.request({ method: 'GET', uri: `${baseUrl}/apirest.php/search/User?${qs}`, headers, json: true });
208
+ returnData.push({ json: response });
209
+ return [returnData];
210
+ }
211
+
212
+ // createTicket (POST /Ticket/)
213
+ if (operation === 'createTicket') {
214
+ const ticketInput = this.getNodeParameter('ticketInput', 0) as any;
215
+ if (!ticketInput || typeof ticketInput !== 'object') throw new Error('ticketInput inválido.');
216
+ const response = await this.helpers.request({ method: 'POST', uri: `${baseUrl}/apirest.php/Ticket/`, headers, body: ticketInput, json: true });
217
+ returnData.push({ json: response });
218
+ return [returnData];
219
+ }
220
+
221
+ // updateTicket (PUT /Ticket/:id)
222
+ if (operation === 'updateTicket') {
223
+ const itemId = this.getNodeParameter('itemId', 0) as number;
224
+ const ticketInput = this.getNodeParameter('ticketInput', 0) as any;
225
+ if (!itemId) throw new Error('itemId obrigatório para updateTicket.');
226
+ if (!ticketInput || typeof ticketInput !== 'object') throw new Error('ticketInput inválido.');
227
+ const response = await this.helpers.request({ method: 'PUT', uri: `${baseUrl}/apirest.php/Ticket/${itemId}`, headers, body: ticketInput, json: true });
228
+ returnData.push({ json: response });
229
+ return [returnData];
230
+ }
231
+
232
+ // addFollowup (POST /Ticket/:id/ITILFollowup)
233
+ if (operation === 'addFollowup') {
234
+ const itemId = this.getNodeParameter('itemId', 0) as number;
235
+ const followupInput = this.getNodeParameter('followupInput', 0) as any;
236
+ if (!itemId) throw new Error('itemId obrigatório para addFollowup.');
237
+ if (!followupInput || typeof followupInput !== 'object') throw new Error('followupInput inválido.');
238
+ const response = await this.helpers.request({ method: 'POST', uri: `${baseUrl}/apirest.php/Ticket/${itemId}/ITILFollowup`, headers, body: followupInput, json: true });
239
+ returnData.push({ json: response });
240
+ return [returnData];
241
+ }
242
+
243
+ // addSolution (POST /ITILSolution/)
244
+ if (operation === 'addSolution') {
245
+ const solutionInput = this.getNodeParameter('solutionInput', 0) as any;
246
+ if (!solutionInput || typeof solutionInput !== 'object') throw new Error('solutionInput inválido.');
247
+ const response = await this.helpers.request({ method: 'POST', uri: `${baseUrl}/apirest.php/ITILSolution/`, headers, body: solutionInput, json: true });
248
+ returnData.push({ json: response });
249
+ return [returnData];
250
+ }
251
+
252
+ throw new Error(`Operação desconhecida: ${operation}`);
253
+
254
+ } catch (err) {
255
+ returnData.push({
256
+ json: {
257
+ error: true,
258
+ message: (err as Error).message || String(err),
259
+ stack: (err as any).stack,
260
+ },
261
+ });
262
+ return [returnData];
263
+ }
264
+ }
265
+ }
@@ -0,0 +1,52 @@
1
+ import { ICredentialType, INodeProperties } from 'n8n-workflow';
2
+
3
+ export class GlpiApi implements ICredentialType {
4
+ name = 'glpiApi';
5
+ displayName = 'GLPI API';
6
+ documentationUrl = 'https://your-glpi-docs-or-repo.example.com';
7
+ properties: INodeProperties[] = [
8
+ {
9
+ displayName: 'Base URL',
10
+ name: 'baseUrl',
11
+ type: 'string',
12
+ default: '',
13
+ placeholder: 'https://atendimento.centrium.com.br',
14
+ required: true,
15
+ description: 'Base URL sem o sufixo /apirest.php',
16
+ },
17
+ {
18
+ displayName: 'App Token',
19
+ name: 'appToken',
20
+ type: 'string',
21
+ typeOptions: { password: true },
22
+ default: '',
23
+ required: true,
24
+ },
25
+ {
26
+ displayName: 'User Token (optional)',
27
+ name: 'userToken',
28
+ type: 'string',
29
+ typeOptions: { password: true },
30
+ default: '',
31
+ required: false,
32
+ description: 'Se presente, envia header "Authorization: user_token <token>" e pode ser usado para initSession.',
33
+ },
34
+ {
35
+ displayName: 'Username (for Basic Auth, optional)',
36
+ name: 'username',
37
+ type: 'string',
38
+ default: '',
39
+ placeholder: 'usuário',
40
+ required: false,
41
+ },
42
+ {
43
+ displayName: 'Password (for Basic Auth, optional)',
44
+ name: 'password',
45
+ type: 'string',
46
+ typeOptions: { password: true },
47
+ default: '',
48
+ placeholder: 'senha',
49
+ required: false,
50
+ }
51
+ ];
52
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@justbrunasso/n8n-nodes-glpi",
3
+ "version": "1.0.2",
4
+ "description": "GLPI REST API node for n8n (GLPI 10.x) with Criteria Builder and automatic session handling (no cache).",
5
+ "keywords": [
6
+ "n8n",
7
+ "n8n-nodes",
8
+ "glpi",
9
+ "glpi-api",
10
+ "glpi-node",
11
+ "itsm",
12
+ "automation",
13
+ "workflow"
14
+ ],
15
+ "main": "dist/index.js",
16
+ "types": "dist/index.d.ts",
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "prepare": "npm run build"
20
+ },
21
+ "author": "Bruno Eduardo Santos",
22
+ "license": "MIT",
23
+ "peerDependencies": {
24
+ "n8n-core": "^1.0.0",
25
+ "n8n-workflow": "^1.0.0"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^20.19.25",
29
+ "n8n-core": "^1.17.0",
30
+ "n8n-workflow": "^1.17.0",
31
+ "typescript": "^5.9.3"
32
+ }
33
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "CommonJS",
5
+ "declaration": true,
6
+ "outDir": "dist",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "moduleResolution": "node",
11
+ "rootDir": ".",
12
+ "sourceMap": true,
13
+ "resolveJsonModule": true,
14
+ "lib": ["ES2020"],
15
+ "types": ["node"]
16
+ },
17
+ "include": ["nodes/**/*", "index.ts"]
18
+ }