@alvarobr/n8n-nodes-glpi-connect 1.1.0 → 1.2.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.
package/README.md
CHANGED
|
@@ -38,6 +38,8 @@ Creado por **Álvaro Blanca Rodríguez**.
|
|
|
38
38
|
### Otros
|
|
39
39
|
- **Usuario → Identificar por correo**: usuario de GLPI asociado a un correo.
|
|
40
40
|
- **Base de conocimiento → Buscar preguntas frecuentes**: artículos FAQ públicos.
|
|
41
|
+
- **Base de conocimiento → Índice de preguntas frecuentes**: títulos e ID de todas las FAQ públicas. Páselo al prompt del agente para que sepa qué artículos existen y no busque a ciegas.
|
|
42
|
+
- **Base de conocimiento → Leer artículo**: contenido completo de una FAQ pública por su ID.
|
|
41
43
|
- **Petición API → Petición personalizada**: cualquier ruta de `apirest.php` con la sesión ya gestionada. No comprueba permisos por usuario, así que **no debe conectarse a un agente de IA**.
|
|
42
44
|
|
|
43
45
|
## Seguridad
|
|
@@ -112,7 +112,11 @@ const properties = [
|
|
|
112
112
|
type: 'options',
|
|
113
113
|
noDataExpression: true,
|
|
114
114
|
displayOptions: show(['conocimiento']),
|
|
115
|
-
options: [
|
|
115
|
+
options: [
|
|
116
|
+
{ name: 'Buscar preguntas frecuentes', value: 'buscar', action: 'Buscar en la base de conocimiento' },
|
|
117
|
+
{ name: 'Índice de preguntas frecuentes', value: 'indice', action: 'Listar los títulos de la base de conocimiento', description: 'Títulos e ID de las preguntas frecuentes públicas, para que la IA sepa qué hay antes de consultar' },
|
|
118
|
+
{ name: 'Leer artículo', value: 'leer', action: 'Leer un artículo de la base de conocimiento', description: 'Contenido completo de una pregunta frecuente por su ID' },
|
|
119
|
+
],
|
|
116
120
|
default: 'buscar',
|
|
117
121
|
},
|
|
118
122
|
{
|
|
@@ -350,7 +354,15 @@ const properties = [
|
|
|
350
354
|
required: true,
|
|
351
355
|
default: '',
|
|
352
356
|
placeholder: 'vpn',
|
|
353
|
-
displayOptions: show(['conocimiento']),
|
|
357
|
+
displayOptions: show(['conocimiento'], ['buscar']),
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
displayName: 'ID del artículo',
|
|
361
|
+
name: 'articuloId',
|
|
362
|
+
type: 'number',
|
|
363
|
+
required: true,
|
|
364
|
+
default: 0,
|
|
365
|
+
displayOptions: show(['conocimiento'], ['leer']),
|
|
354
366
|
},
|
|
355
367
|
// --- Petición API ---------------------------------------------------
|
|
356
368
|
{
|
|
@@ -462,6 +474,10 @@ async function runOperation(ctx, client, users, i) {
|
|
|
462
474
|
const resource = ctx.getNodeParameter('resource', i);
|
|
463
475
|
const operation = ctx.getNodeParameter('operation', i);
|
|
464
476
|
if (resource === 'conocimiento') {
|
|
477
|
+
if (operation === 'indice')
|
|
478
|
+
return ops.faqIndex(client);
|
|
479
|
+
if (operation === 'leer')
|
|
480
|
+
return ops.readFaq(client, Number(ctx.getNodeParameter('articuloId', i)));
|
|
465
481
|
return ops.searchFaq(client, ctx.getNodeParameter('busqueda', i));
|
|
466
482
|
}
|
|
467
483
|
if (resource === 'api') {
|
|
@@ -21,6 +21,8 @@ exports.linkAsset = linkAsset;
|
|
|
21
21
|
exports.createTicket = createTicket;
|
|
22
22
|
exports.myAssets = myAssets;
|
|
23
23
|
exports.computerSheet = computerSheet;
|
|
24
|
+
exports.faqIndex = faqIndex;
|
|
25
|
+
exports.readFaq = readFaq;
|
|
24
26
|
exports.searchFaq = searchFaq;
|
|
25
27
|
exports.apiRequest = apiRequest;
|
|
26
28
|
const GlpiClient_1 = require("./GlpiClient");
|
|
@@ -707,6 +709,37 @@ async function computerSheet(client, user, computerId, search) {
|
|
|
707
709
|
// ----------------------------------------------------------------------
|
|
708
710
|
// Base de conocimiento
|
|
709
711
|
// ----------------------------------------------------------------------
|
|
712
|
+
const FAQ_INDEX_LIMIT = 200;
|
|
713
|
+
/** Solo las preguntas frecuentes se consideran públicas para el usuario. */
|
|
714
|
+
function isPublicFaq(article) {
|
|
715
|
+
return !!article && typeof article === 'object' && (article.is_faq === undefined || Number(article.is_faq) === 1);
|
|
716
|
+
}
|
|
717
|
+
/** Títulos de todas las preguntas frecuentes, para que la IA sepa qué existe antes de consultar. */
|
|
718
|
+
async function faqIndex(client) {
|
|
719
|
+
const response = await client.raw('GET', 'KnowbaseItem', { qs: { range: `0-${FAQ_INDEX_LIMIT - 1}`, sort: 'name', order: 'ASC' } });
|
|
720
|
+
if (response.statusCode >= 300 || !Array.isArray(response.body)) {
|
|
721
|
+
return { ok: false, error: 'No se ha podido consultar la base de conocimiento.' };
|
|
722
|
+
}
|
|
723
|
+
const articles = response.body.filter(isPublicFaq).map((a) => ({ id: Number(a.id), titulo: (0, utils_1.plainText)(a.name, 200) }));
|
|
724
|
+
return (0, utils_1.compact)({
|
|
725
|
+
ok: true,
|
|
726
|
+
total: articles.length,
|
|
727
|
+
articulos: articles,
|
|
728
|
+
aviso: response.body.length >= FAQ_INDEX_LIMIT ? `Solo se muestran los ${FAQ_INDEX_LIMIT} primeros artículos.` : undefined,
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
/** Contenido completo de una pregunta frecuente. */
|
|
732
|
+
async function readFaq(client, articleId) {
|
|
733
|
+
const id = Number(articleId);
|
|
734
|
+
if (!Number.isSafeInteger(id) || id <= 0)
|
|
735
|
+
return { ok: false, error: 'Falta el ID del artículo. Use el índice de la base de conocimiento.' };
|
|
736
|
+
const response = await client.raw('GET', `KnowbaseItem/${id}`);
|
|
737
|
+
if (response.statusCode >= 300 || !response.body || Array.isArray(response.body) || !isPublicFaq(response.body)) {
|
|
738
|
+
return { ok: false, error: `No existe una pregunta frecuente pública con el ID ${id}.` };
|
|
739
|
+
}
|
|
740
|
+
const a = response.body;
|
|
741
|
+
return { ok: true, articulo: { id, titulo: (0, utils_1.plainText)(a.name, 200), contenido: (0, utils_1.plainText)(a.answer, 8000), enlace: `${client.front}/helpdesk.faq.php?id=${id}` } };
|
|
742
|
+
}
|
|
710
743
|
async function searchFaq(client, text) {
|
|
711
744
|
const term = String(text ?? '').trim();
|
|
712
745
|
if (term.length < 2)
|
|
@@ -720,10 +753,9 @@ async function searchFaq(client, text) {
|
|
|
720
753
|
if (r.statusCode >= 300 || !Array.isArray(r.body))
|
|
721
754
|
continue;
|
|
722
755
|
for (const a of r.body) {
|
|
723
|
-
if (!a ||
|
|
756
|
+
if (!a || seen.has(a.id))
|
|
724
757
|
continue;
|
|
725
|
-
|
|
726
|
-
if (a.is_faq !== undefined && Number(a.is_faq) !== 1)
|
|
758
|
+
if (!isPublicFaq(a))
|
|
727
759
|
continue;
|
|
728
760
|
seen.add(a.id);
|
|
729
761
|
articles.push({ id: a.id, titulo: (0, utils_1.plainText)(a.name, 200), contenido: (0, utils_1.plainText)(a.answer, 1500), enlace: `${client.front}/helpdesk.faq.php?id=${a.id}` });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alvarobr/n8n-nodes-glpi-connect",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Nodo de n8n para GLPI: autoservicio IT seguro para flujos y agentes de IA. Tickets, inventario, software y base de conocimiento con sesión automática, permisos por usuario y confirmación de cambios.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"n8n-community-node-package",
|