@hostwebhook/platform-contracts 0.1.0 → 0.3.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 +64 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/operadores.d.ts +60 -0
- package/dist/operadores.js +123 -0
- package/dist/servicios/almacenes-vectoriales.d.ts +105 -0
- package/dist/servicios/almacenes-vectoriales.js +26 -0
- package/dist/servicios/aprobaciones-pendientes.d.ts +207 -0
- package/dist/servicios/aprobaciones-pendientes.js +51 -0
- package/dist/servicios/avisos-en-vivo.d.ts +106 -0
- package/dist/servicios/avisos-en-vivo.js +60 -0
- package/dist/servicios/canal-de-stream.d.ts +78 -0
- package/dist/servicios/canal-de-stream.js +5 -0
- package/dist/servicios/conversaciones-del-chat.d.ts +139 -0
- package/dist/servicios/conversaciones-del-chat.js +51 -0
- package/dist/servicios/corridas-programadas.d.ts +29 -0
- package/dist/servicios/corridas-programadas.js +5 -0
- package/dist/servicios/credencial/permiso-sobre-credencial.d.ts +61 -0
- package/dist/servicios/credencial/permiso-sobre-credencial.js +42 -0
- package/dist/servicios/credenciales-del-gateway.d.ts +722 -0
- package/dist/servicios/credenciales-del-gateway.js +114 -0
- package/dist/servicios/cuentas-de-atlassian.d.ts +22 -0
- package/dist/servicios/cuentas-de-atlassian.js +5 -0
- package/dist/servicios/descarga-de-drive.d.ts +74 -0
- package/dist/servicios/descarga-de-drive.js +41 -0
- package/dist/servicios/ejecucion-de-ia.d.ts +81 -0
- package/dist/servicios/ejecucion-de-ia.js +39 -0
- package/dist/servicios/enlaces-de-flujo.d.ts +59 -0
- package/dist/servicios/enlaces-de-flujo.js +27 -0
- package/dist/servicios/ficheros-del-gateway.d.ts +210 -0
- package/dist/servicios/ficheros-del-gateway.js +8 -0
- package/dist/servicios/formas-compartidas.d.ts +57 -0
- package/dist/servicios/formas-compartidas.js +22 -0
- package/dist/servicios/historial-de-corridas.d.ts +75 -0
- package/dist/servicios/historial-de-corridas.js +7 -0
- package/dist/servicios/index.d.ts +57 -0
- package/dist/servicios/index.js +44 -0
- package/dist/servicios/limites-del-plan.d.ts +82 -0
- package/dist/servicios/limites-del-plan.js +41 -0
- package/dist/servicios/motor-de-ejecucion.d.ts +197 -0
- package/dist/servicios/motor-de-ejecucion.js +11 -0
- package/dist/servicios/plantillas-de-origen.d.ts +25 -0
- package/dist/servicios/plantillas-de-origen.js +5 -0
- package/dist/servicios/posts-sociales.d.ts +69 -0
- package/dist/servicios/posts-sociales.js +23 -0
- package/dist/servicios/registro-de-entregas.d.ts +93 -0
- package/dist/servicios/registro-de-entregas.js +46 -0
- package/dist/servicios/registro-de-eventos.d.ts +135 -0
- package/dist/servicios/registro-de-eventos.js +62 -0
- package/dist/servicios/restauracion-de-nodos.d.ts +53 -0
- package/dist/servicios/restauracion-de-nodos.js +4 -0
- package/dist/servicios/salud-del-webhook.d.ts +26 -0
- package/dist/servicios/salud-del-webhook.js +6 -0
- package/dist/servicios/secretos-de-firma.d.ts +26 -0
- package/dist/servicios/secretos-de-firma.js +5 -0
- package/dist/servicios/telemetria.d.ts +114 -0
- package/dist/servicios/telemetria.js +60 -0
- package/dist/servicios/trazas-de-llm.d.ts +79 -0
- package/dist/servicios/trazas-de-llm.js +23 -0
- package/dist/servicios/tuneles.d.ts +106 -0
- package/dist/servicios/tuneles.js +87 -0
- package/dist/servicios/workspace/acceso-al-recurso.d.ts +39 -0
- package/dist/servicios/workspace/acceso-al-recurso.js +5 -0
- package/dist/servicios/zona-horaria-del-usuario.d.ts +40 -0
- package/dist/servicios/zona-horaria-del-usuario.js +7 -0
- package/package.json +9 -3
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Lo que un nodo apunta en el registro. Una sola operación.
|
|
4
|
+
*
|
|
5
|
+
* ## Por qué
|
|
6
|
+
*
|
|
7
|
+
* Costura de la Fase 2. Después del #459 quedaban 16 ficheros de `nodes/`
|
|
8
|
+
* importando `TelemetryService`, más tres del motor en `common/` —que viaja
|
|
9
|
+
* con ellos—. Los 19 lo usan para **lo mismo**: `emit`.
|
|
10
|
+
*
|
|
11
|
+
* `TelemetryService` arrastra la colección de logs, su conexión de Mongo
|
|
12
|
+
* propia y el gateway de sockets que empuja la fila al panel en vivo. Nada de
|
|
13
|
+
* eso puede vivir en `hw-nodes`: el registro es del gateway, y lo que el nodo
|
|
14
|
+
* necesita es poder DECIR lo que ha pasado.
|
|
15
|
+
*
|
|
16
|
+
* ## Tres métodos, y medidos
|
|
17
|
+
*
|
|
18
|
+
* En `nodes/` el inventario dio **uno solo** —`emit`— en 15 de los 16
|
|
19
|
+
* ficheros. El que sobraba (`ai-nodes.controller`) no lo llamaba: se lo
|
|
20
|
+
* pasaba al probador, y para eso ya existe la bandera `conTelemetria` desde
|
|
21
|
+
* el #458.
|
|
22
|
+
*
|
|
23
|
+
* ⚠️ Pero el inventario iba apuntado sólo a `nodes/`, y el motor vive en
|
|
24
|
+
* `common/`, que viaja con ellos. Al compilar salieron dos métodos más:
|
|
25
|
+
* `hasRecentSuccess` y `emitAndWait`. Los dos son de la recuperación de
|
|
26
|
+
* caídas, no del registro: sin ellos un nodo caro se re-ejecuta. Lo cazó tsc,
|
|
27
|
+
* no yo — la lección de siempre, con el barrido mal apuntado.
|
|
28
|
+
*
|
|
29
|
+
* ⚠️ Y ahí apareció una regresión MÍA. El #459 le quitó a
|
|
30
|
+
* `code-nodes.controller` el parámetro `telemetry` y su `super()`, pero dejó
|
|
31
|
+
* en pie un `this.telemetry` en la ruta `:id/test` escrita a mano. Desde
|
|
32
|
+
* entonces valía `undefined`, así que un paso de Run Pipeline sobre un Code
|
|
33
|
+
* node **no anotaba nada** — justo el comportamiento que el #459 decía haber
|
|
34
|
+
* conservado. No lo vio tsc porque la clase base del factory está tipada como
|
|
35
|
+
* `=> any`: dentro de una subclase, `this.loquesea` compila.
|
|
36
|
+
*
|
|
37
|
+
* ## Los campos son los que se usan
|
|
38
|
+
*
|
|
39
|
+
* El esquema `TelemetryLog` tiene ~20 campos. Se contaron los que aparecen de
|
|
40
|
+
* verdad en las 67 llamadas de `nodes/` y las 53 de `common/`: son quince.
|
|
41
|
+
* Fuera quedan `timestamp` —lo pone el gateway si falta—, `correlationId` y
|
|
42
|
+
* los dos de voz, que nadie de este lado escribe.
|
|
43
|
+
*
|
|
44
|
+
* ## Cómo viaja
|
|
45
|
+
*
|
|
46
|
+
* Bien: sólo datos planos. `TelemetryLog` es una clase con decoradores pero
|
|
47
|
+
* sin métodos, así que no hay documento vivo que aplanar; aun así el contrato
|
|
48
|
+
* lo declara aparte para no arrastrar `@nestjs/mongoose` a `hw-nodes`.
|
|
49
|
+
*
|
|
50
|
+
* ⚠️ `emit` devuelve `void` y no espera: por dentro hace `create(...).catch()`.
|
|
51
|
+
* Eso es deliberado y hay que conservarlo cuando esto sea una llamada de red —
|
|
52
|
+
* un nodo no puede quedarse esperando a que se escriba una fila de log, y
|
|
53
|
+
* menos aún fallar porque no se pudo escribir.
|
|
54
|
+
*/
|
|
55
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
|
+
exports.TELEMETRIA = void 0;
|
|
57
|
+
/**
|
|
58
|
+
* ⚠️ Cadena y no `Symbol`, igual que el resto de tokens de la Fase 2.
|
|
59
|
+
*/
|
|
60
|
+
exports.TELEMETRIA = 'TELEMETRIA';
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lo que el AI Node apunta y consulta sobre sus llamadas al modelo.
|
|
3
|
+
*
|
|
4
|
+
* Costura de la Fase 2, del lote de las pequeñas. `LlmTracesClientService` es
|
|
5
|
+
* un CLIENTE HTTP contra `hw-llm-traces`, que ya es un servicio aparte — el
|
|
6
|
+
* coste de los logs no vive en esta api. Aun así el nodo no debe conocer al
|
|
7
|
+
* cliente: arrastra su configuración, su reintento y su autenticación.
|
|
8
|
+
*
|
|
9
|
+
* ## Cómo viaja
|
|
10
|
+
*
|
|
11
|
+
* ⚠️ `emitTrace` devuelve `Promise<void>` y su llamante NO la espera: apuntar
|
|
12
|
+
* una traza no puede retrasar una respuesta del modelo ni tumbarla. Eso hay
|
|
13
|
+
* que conservarlo.
|
|
14
|
+
*
|
|
15
|
+
* Las dos consultas devuelven `unknown` a propósito — es lo que devuelve el
|
|
16
|
+
* cliente real, que reenvía lo que conteste el servicio de trazas sin darle
|
|
17
|
+
* forma. Tipar aquí una forma que allí no se garantiza sería inventarla.
|
|
18
|
+
*/
|
|
19
|
+
/** Lo que se apunta de una llamada al modelo. */
|
|
20
|
+
export interface TrazaDeLlm {
|
|
21
|
+
traceId?: string;
|
|
22
|
+
spanId?: string;
|
|
23
|
+
startTime: string | Date;
|
|
24
|
+
endTime?: string | Date;
|
|
25
|
+
provider: 'openai' | 'anthropic' | 'google' | 'ollama' | 'mistral' | 'cohere' | 'custom';
|
|
26
|
+
model: string;
|
|
27
|
+
operation?: 'chat' | 'completion' | 'embedding' | 'image' | 'audio';
|
|
28
|
+
inputTokens?: number;
|
|
29
|
+
outputTokens?: number;
|
|
30
|
+
cacheReadInputTokens?: number;
|
|
31
|
+
cacheCreationInputTokens?: number;
|
|
32
|
+
costUsd?: number;
|
|
33
|
+
status?: 'success' | 'error' | 'timeout';
|
|
34
|
+
error?: {
|
|
35
|
+
message?: string;
|
|
36
|
+
code?: string;
|
|
37
|
+
httpStatus?: number;
|
|
38
|
+
};
|
|
39
|
+
input?: {
|
|
40
|
+
messages?: unknown;
|
|
41
|
+
system?: unknown;
|
|
42
|
+
truncated?: boolean;
|
|
43
|
+
};
|
|
44
|
+
output?: {
|
|
45
|
+
content?: string;
|
|
46
|
+
toolCalls?: unknown[];
|
|
47
|
+
stopReason?: string;
|
|
48
|
+
truncated?: boolean;
|
|
49
|
+
};
|
|
50
|
+
metadata?: Record<string, unknown>;
|
|
51
|
+
aiNodeId?: string;
|
|
52
|
+
aiNodeName?: string;
|
|
53
|
+
eventId?: string;
|
|
54
|
+
workspaceId?: string;
|
|
55
|
+
invokedFrom?: string;
|
|
56
|
+
tags?: string[];
|
|
57
|
+
}
|
|
58
|
+
/** Los filtros con que la pantalla pide trazas. */
|
|
59
|
+
export interface FiltrosDeTrazas {
|
|
60
|
+
limit?: number;
|
|
61
|
+
offset?: number;
|
|
62
|
+
aiNodeId?: string;
|
|
63
|
+
status?: 'success' | 'error' | 'timeout';
|
|
64
|
+
model?: string;
|
|
65
|
+
provider?: string;
|
|
66
|
+
startAfter?: string;
|
|
67
|
+
startBefore?: string;
|
|
68
|
+
search?: string;
|
|
69
|
+
sortBy?: 'startTime' | 'costUsd' | 'latencyMs';
|
|
70
|
+
sortOrder?: 'asc' | 'desc';
|
|
71
|
+
}
|
|
72
|
+
export interface TrazasDeLlm {
|
|
73
|
+
queryTraces(orgId: string, params: FiltrosDeTrazas): Promise<unknown>;
|
|
74
|
+
getTrace(orgId: string, traceId: string): Promise<unknown>;
|
|
75
|
+
/** ⚠️ No se espera. Ver la cabecera. */
|
|
76
|
+
emitTrace(orgId: string, traza: TrazaDeLlm): Promise<void>;
|
|
77
|
+
}
|
|
78
|
+
/** ⚠️ Cadena y no `Symbol`, igual que el resto de tokens de la Fase 2. */
|
|
79
|
+
export declare const TRAZAS_DE_LLM = "TRAZAS_DE_LLM";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Lo que el AI Node apunta y consulta sobre sus llamadas al modelo.
|
|
4
|
+
*
|
|
5
|
+
* Costura de la Fase 2, del lote de las pequeñas. `LlmTracesClientService` es
|
|
6
|
+
* un CLIENTE HTTP contra `hw-llm-traces`, que ya es un servicio aparte — el
|
|
7
|
+
* coste de los logs no vive en esta api. Aun así el nodo no debe conocer al
|
|
8
|
+
* cliente: arrastra su configuración, su reintento y su autenticación.
|
|
9
|
+
*
|
|
10
|
+
* ## Cómo viaja
|
|
11
|
+
*
|
|
12
|
+
* ⚠️ `emitTrace` devuelve `Promise<void>` y su llamante NO la espera: apuntar
|
|
13
|
+
* una traza no puede retrasar una respuesta del modelo ni tumbarla. Eso hay
|
|
14
|
+
* que conservarlo.
|
|
15
|
+
*
|
|
16
|
+
* Las dos consultas devuelven `unknown` a propósito — es lo que devuelve el
|
|
17
|
+
* cliente real, que reenvía lo que conteste el servicio de trazas sin darle
|
|
18
|
+
* forma. Tipar aquí una forma que allí no se garantiza sería inventarla.
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.TRAZAS_DE_LLM = void 0;
|
|
22
|
+
/** ⚠️ Cadena y no `Symbol`, igual que el resto de tokens de la Fase 2. */
|
|
23
|
+
exports.TRAZAS_DE_LLM = 'TRAZAS_DE_LLM';
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lo que un nodo necesita de un túnel. Dos cosas, y por dos caminos distintos.
|
|
3
|
+
*
|
|
4
|
+
* ## Por qué
|
|
5
|
+
*
|
|
6
|
+
* Costura de la Fase 2. `nodes/` importaba `tunnels/` así:
|
|
7
|
+
*
|
|
8
|
+
* 5 `TcpTunnelPool` el pool de reenviadores TCP
|
|
9
|
+
* 1 `TunnelsService` para comprobar que un túnel es de la organización
|
|
10
|
+
* 1 `tunnels.module` cableado
|
|
11
|
+
*
|
|
12
|
+
* ## ⚠️ Ésta NO es como las anteriores: el pool no puede mudarse
|
|
13
|
+
*
|
|
14
|
+
* Las once costuras anteriores acababan igual — el nodo pide por contrato y
|
|
15
|
+
* el día de la Fase 4 el contrato se convierte en una llamada HTTP. Aquí eso
|
|
16
|
+
* **no funciona**, y conviene decirlo antes de que alguien lo intente.
|
|
17
|
+
*
|
|
18
|
+
* `TcpTunnelPool.getOrCreate()` devuelve un reenviador que abre un **listener
|
|
19
|
+
* TCP en 127.0.0.1**, y lo que el nodo hace con él es apuntar ahí el driver de
|
|
20
|
+
* Mongo o de Postgres. Un `host:port` local sólo sirve dentro del proceso que
|
|
21
|
+
* lo abrió: pedirlo por red devolvería una dirección de otra máquina.
|
|
22
|
+
*
|
|
23
|
+
* Y el otro extremo tampoco se mueve: el pool se enchufa al servidor de
|
|
24
|
+
* sockets en el arranque —`webhook.gateway.ts` llama a `setGateway(this)`—
|
|
25
|
+
* porque los bytes del túnel viajan por la conexión que el cliente del
|
|
26
|
+
* usuario mantiene ABIERTA contra el gateway.
|
|
27
|
+
*
|
|
28
|
+
* O sea que el día de la mudanza hay dos salidas, y ninguna es «que el
|
|
29
|
+
* contrato hable HTTP»:
|
|
30
|
+
*
|
|
31
|
+
* a) el pool se va con los nodos, y `hw-nodes` mantiene su propio enlace de
|
|
32
|
+
* sockets con los clientes de túnel;
|
|
33
|
+
* b) los nodos de base de datos por túnel se quedan en el gateway.
|
|
34
|
+
*
|
|
35
|
+
* Lo que este contrato SÍ consigue hoy es que `nodes/` deje de importar la
|
|
36
|
+
* clase —con su reenviador, sus sockets y su barrido— y que la decisión de
|
|
37
|
+
* arriba se pueda tomar cambiando un registro, no cinco ficheros.
|
|
38
|
+
*
|
|
39
|
+
* ## Por qué un registro y no un token
|
|
40
|
+
*
|
|
41
|
+
* ⚠️ Dos de los cuatro llamantes del pool no tienen DI: uno es un método
|
|
42
|
+
* `static` (`ExternalMongoBackend.resolveCollection`) y otro una función
|
|
43
|
+
* suelta (`resolvePool` en el backend de Postgres). Un token de Nest no llega
|
|
44
|
+
* ahí.
|
|
45
|
+
*
|
|
46
|
+
* El registro de módulo es el patrón que ya usa `probador-de-nodos.ts` desde
|
|
47
|
+
* el #458, por el mismo motivo. Lo enchufa un provider con `onModuleInit`
|
|
48
|
+
* —ver `tunnels/monta-el-tunel-local.ts`— para que se pueda PROBAR que el
|
|
49
|
+
* registro ocurrió: un registro no lo garantiza el compilador.
|
|
50
|
+
*
|
|
51
|
+
* El otro contrato, `TunelesDeLaOrganizacion`, sí va por token: su único
|
|
52
|
+
* llamante es un servicio con constructor.
|
|
53
|
+
*/
|
|
54
|
+
/** Una dirección a la que conectar un driver. Siempre en `127.0.0.1`. */
|
|
55
|
+
export interface DireccionLocal {
|
|
56
|
+
host: string;
|
|
57
|
+
port: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Un túnel, visto desde el nodo: «dame una dirección local que salga ahí».
|
|
61
|
+
*
|
|
62
|
+
* Los cuatro llamantes hacían exactamente los mismos dos pasos —pedir el
|
|
63
|
+
* reenviador al pool y sacarle `getLocalAddress()`—, así que el contrato es
|
|
64
|
+
* uno solo.
|
|
65
|
+
*/
|
|
66
|
+
export interface TunelLocal {
|
|
67
|
+
/**
|
|
68
|
+
* ⚠️ Lanza si el túnel no está enchufado al gateway. Es lo que hace hoy
|
|
69
|
+
* `getOrCreate`, y hay que conservarlo: devolver una dirección que no
|
|
70
|
+
* escucha convertiría un error claro en un timeout del driver.
|
|
71
|
+
*/
|
|
72
|
+
direccionLocalPara(tunnelId: string, host: string, port: number): Promise<DireccionLocal>;
|
|
73
|
+
}
|
|
74
|
+
/** Lo llama `tunnels/monta-el-tunel-local.ts` al arrancar. */
|
|
75
|
+
export declare function registrarTunelLocal(t: TunelLocal): void;
|
|
76
|
+
/** Para los tests, que montan el suyo. */
|
|
77
|
+
export declare function olvidarTunelLocal(): void;
|
|
78
|
+
export declare function hayTunelLocal(): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* ⚠️ Lanza con un mensaje que dice QUÉ falta. Si nadie registró, el nodo no
|
|
81
|
+
* puede abrir el túnel, y un `undefined` silencioso acabaría en un error del
|
|
82
|
+
* driver a diez fotogramas de distancia de la causa.
|
|
83
|
+
*/
|
|
84
|
+
export declare function obtenerTunelLocal(): TunelLocal;
|
|
85
|
+
/** Lo que el nodo pregunta sobre a quién pertenece un túnel. */
|
|
86
|
+
export interface TunelesDeLaOrganizacion {
|
|
87
|
+
/**
|
|
88
|
+
* ⚠️ Devuelve `unknown` y LANZA si el túnel no existe o no es de esa
|
|
89
|
+
* organización.
|
|
90
|
+
*
|
|
91
|
+
* `unknown` porque el servicio real devuelve el documento de Mongoose y su
|
|
92
|
+
* único llamante lo DESCARTA: sólo le importa si lanzó. Así el documento no
|
|
93
|
+
* cruza y el llamante no puede usarlo sin un cast.
|
|
94
|
+
*
|
|
95
|
+
* ⚠️ Y una nota para la Fase 4: ese llamante envuelve la llamada en un
|
|
96
|
+
* `catch` que lo convierte todo en «Tunnel not found». Hoy da igual —lo
|
|
97
|
+
* único que puede fallar es la consulta—, pero en cuanto esto sea una
|
|
98
|
+
* llamada de red, un corte de red se leerá como «el túnel no existe». Quien
|
|
99
|
+
* lo implemente por HTTP tiene que distinguir las dos cosas ahí.
|
|
100
|
+
*/
|
|
101
|
+
asegurarQueEsDeLaOrganizacion(tunnelId: string, orgId: string): Promise<unknown>;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* ⚠️ Cadena y no `Symbol`, igual que el resto de tokens de la Fase 2.
|
|
105
|
+
*/
|
|
106
|
+
export declare const TUNELES_DE_LA_ORGANIZACION = "TUNELES_DE_LA_ORGANIZACION";
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Lo que un nodo necesita de un túnel. Dos cosas, y por dos caminos distintos.
|
|
4
|
+
*
|
|
5
|
+
* ## Por qué
|
|
6
|
+
*
|
|
7
|
+
* Costura de la Fase 2. `nodes/` importaba `tunnels/` así:
|
|
8
|
+
*
|
|
9
|
+
* 5 `TcpTunnelPool` el pool de reenviadores TCP
|
|
10
|
+
* 1 `TunnelsService` para comprobar que un túnel es de la organización
|
|
11
|
+
* 1 `tunnels.module` cableado
|
|
12
|
+
*
|
|
13
|
+
* ## ⚠️ Ésta NO es como las anteriores: el pool no puede mudarse
|
|
14
|
+
*
|
|
15
|
+
* Las once costuras anteriores acababan igual — el nodo pide por contrato y
|
|
16
|
+
* el día de la Fase 4 el contrato se convierte en una llamada HTTP. Aquí eso
|
|
17
|
+
* **no funciona**, y conviene decirlo antes de que alguien lo intente.
|
|
18
|
+
*
|
|
19
|
+
* `TcpTunnelPool.getOrCreate()` devuelve un reenviador que abre un **listener
|
|
20
|
+
* TCP en 127.0.0.1**, y lo que el nodo hace con él es apuntar ahí el driver de
|
|
21
|
+
* Mongo o de Postgres. Un `host:port` local sólo sirve dentro del proceso que
|
|
22
|
+
* lo abrió: pedirlo por red devolvería una dirección de otra máquina.
|
|
23
|
+
*
|
|
24
|
+
* Y el otro extremo tampoco se mueve: el pool se enchufa al servidor de
|
|
25
|
+
* sockets en el arranque —`webhook.gateway.ts` llama a `setGateway(this)`—
|
|
26
|
+
* porque los bytes del túnel viajan por la conexión que el cliente del
|
|
27
|
+
* usuario mantiene ABIERTA contra el gateway.
|
|
28
|
+
*
|
|
29
|
+
* O sea que el día de la mudanza hay dos salidas, y ninguna es «que el
|
|
30
|
+
* contrato hable HTTP»:
|
|
31
|
+
*
|
|
32
|
+
* a) el pool se va con los nodos, y `hw-nodes` mantiene su propio enlace de
|
|
33
|
+
* sockets con los clientes de túnel;
|
|
34
|
+
* b) los nodos de base de datos por túnel se quedan en el gateway.
|
|
35
|
+
*
|
|
36
|
+
* Lo que este contrato SÍ consigue hoy es que `nodes/` deje de importar la
|
|
37
|
+
* clase —con su reenviador, sus sockets y su barrido— y que la decisión de
|
|
38
|
+
* arriba se pueda tomar cambiando un registro, no cinco ficheros.
|
|
39
|
+
*
|
|
40
|
+
* ## Por qué un registro y no un token
|
|
41
|
+
*
|
|
42
|
+
* ⚠️ Dos de los cuatro llamantes del pool no tienen DI: uno es un método
|
|
43
|
+
* `static` (`ExternalMongoBackend.resolveCollection`) y otro una función
|
|
44
|
+
* suelta (`resolvePool` en el backend de Postgres). Un token de Nest no llega
|
|
45
|
+
* ahí.
|
|
46
|
+
*
|
|
47
|
+
* El registro de módulo es el patrón que ya usa `probador-de-nodos.ts` desde
|
|
48
|
+
* el #458, por el mismo motivo. Lo enchufa un provider con `onModuleInit`
|
|
49
|
+
* —ver `tunnels/monta-el-tunel-local.ts`— para que se pueda PROBAR que el
|
|
50
|
+
* registro ocurrió: un registro no lo garantiza el compilador.
|
|
51
|
+
*
|
|
52
|
+
* El otro contrato, `TunelesDeLaOrganizacion`, sí va por token: su único
|
|
53
|
+
* llamante es un servicio con constructor.
|
|
54
|
+
*/
|
|
55
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
56
|
+
exports.TUNELES_DE_LA_ORGANIZACION = void 0;
|
|
57
|
+
exports.registrarTunelLocal = registrarTunelLocal;
|
|
58
|
+
exports.olvidarTunelLocal = olvidarTunelLocal;
|
|
59
|
+
exports.hayTunelLocal = hayTunelLocal;
|
|
60
|
+
exports.obtenerTunelLocal = obtenerTunelLocal;
|
|
61
|
+
let tunelLocal = null;
|
|
62
|
+
/** Lo llama `tunnels/monta-el-tunel-local.ts` al arrancar. */
|
|
63
|
+
function registrarTunelLocal(t) {
|
|
64
|
+
tunelLocal = t;
|
|
65
|
+
}
|
|
66
|
+
/** Para los tests, que montan el suyo. */
|
|
67
|
+
function olvidarTunelLocal() {
|
|
68
|
+
tunelLocal = null;
|
|
69
|
+
}
|
|
70
|
+
function hayTunelLocal() {
|
|
71
|
+
return tunelLocal !== null;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* ⚠️ Lanza con un mensaje que dice QUÉ falta. Si nadie registró, el nodo no
|
|
75
|
+
* puede abrir el túnel, y un `undefined` silencioso acabaría en un error del
|
|
76
|
+
* driver a diez fotogramas de distancia de la causa.
|
|
77
|
+
*/
|
|
78
|
+
function obtenerTunelLocal() {
|
|
79
|
+
if (!tunelLocal) {
|
|
80
|
+
throw new Error('Nadie registró el túnel local — `MontaElTunelLocal` tiene que estar en los providers de `TunnelsModule`');
|
|
81
|
+
}
|
|
82
|
+
return tunelLocal;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* ⚠️ Cadena y no `Symbol`, igual que el resto de tokens de la Fase 2.
|
|
86
|
+
*/
|
|
87
|
+
exports.TUNELES_DE_LA_ORGANIZACION = 'TUNELES_DE_LA_ORGANIZACION';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* La pregunta que hace el guardia de workspace: ¿puede este usuario tocar
|
|
3
|
+
* este recurso?
|
|
4
|
+
*
|
|
5
|
+
* Costura de la Fase 2, del lote de las pequeñas. El guardia resolvía el
|
|
6
|
+
* workspace del recurso con la CONEXIÓN de Mongoose —`connection.model(...)`—
|
|
7
|
+
* y luego comprobaba el acceso con ella otra vez. Eso es la base de datos del
|
|
8
|
+
* gateway, y no puede viajar.
|
|
9
|
+
*
|
|
10
|
+
* Mismo reparto que el guardia de credenciales del #457: el guardia se muda,
|
|
11
|
+
* la consulta se queda y se pregunta por contrato.
|
|
12
|
+
*
|
|
13
|
+
* ## ⚠️ Lo que hay que conservar
|
|
14
|
+
*
|
|
15
|
+
* - **Si el recurso NO existe, se deja pasar.** El manejador contestará 404
|
|
16
|
+
* como hasta ahora; un 403 aquí diría «existe pero no es tuyo» sobre algo
|
|
17
|
+
* que no existe, que es filtrar información.
|
|
18
|
+
* - **Si el modelo no está registrado, LANZA.** Un nombre mal escrito tiene
|
|
19
|
+
* que reventar la ruta la primera vez, no dejar pasar todo en silencio.
|
|
20
|
+
* - **Deniega lanzando**, no devolviendo `false`. Es lo que hace hoy
|
|
21
|
+
* `asegurarAccesoAlWorkspace`, y el guardia cuenta con ello.
|
|
22
|
+
*/
|
|
23
|
+
export interface AccesoAlRecurso {
|
|
24
|
+
/**
|
|
25
|
+
* Lanza si el usuario no puede con ese recurso. Vuelve sin más si puede —
|
|
26
|
+
* o si el recurso no existe. Ver la cabecera.
|
|
27
|
+
*/
|
|
28
|
+
asegurarAcceso(params: {
|
|
29
|
+
/** Nombre del MODELO de Mongoose, no de la colección. */
|
|
30
|
+
modelo: string;
|
|
31
|
+
/** Campo contra el que se compara el id. Casi siempre `_id`. */
|
|
32
|
+
campo: string;
|
|
33
|
+
id: string;
|
|
34
|
+
orgId: string;
|
|
35
|
+
userId: string;
|
|
36
|
+
}): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
/** ⚠️ Cadena y no `Symbol`, igual que el resto de tokens de la Fase 2. */
|
|
39
|
+
export declare const ACCESO_AL_RECURSO = "ACCESO_AL_RECURSO";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* En qué zona horaria vive un usuario. Una pregunta, un campo.
|
|
3
|
+
*
|
|
4
|
+
* ## Por qué
|
|
5
|
+
*
|
|
6
|
+
* Último trozo de la costura `nodes/` → `auth/`. `ScheduledWorkflowsService`
|
|
7
|
+
* inyectaba el modelo de `User` para leer **un campo**: la zona horaria con la
|
|
8
|
+
* que programar un cron cuando el DTO no la trae.
|
|
9
|
+
*
|
|
10
|
+
* Un servicio de nodo consultando la colección de usuarios es justo lo que no
|
|
11
|
+
* puede viajar a `hw-nodes`: allí no hay colección de usuarios, y no debe
|
|
12
|
+
* haberla — la identidad vive en el gateway.
|
|
13
|
+
*
|
|
14
|
+
* ## Lo que se conserva
|
|
15
|
+
*
|
|
16
|
+
* ⚠️ La caída a `'UTC'`, y en los DOS casos que ya tenía: si el usuario no
|
|
17
|
+
* existe, y si la consulta falla. Eso no es un detalle: sin zona horaria un
|
|
18
|
+
* cron no se puede programar, así que el original prefería una por defecto a
|
|
19
|
+
* reventar la creación del nodo. Quien implemente esto por HTTP tiene que
|
|
20
|
+
* seguir cayendo a `'UTC'` cuando el gateway no conteste — está escrito en el
|
|
21
|
+
* contrato para que no se pierda por el camino.
|
|
22
|
+
*
|
|
23
|
+
* ## Y esto viaja
|
|
24
|
+
*
|
|
25
|
+
* Una cadena. Como los topes del plan y a diferencia de la costura del motor,
|
|
26
|
+
* aquí no hay nada que aplanar.
|
|
27
|
+
*/
|
|
28
|
+
export interface ZonaHorariaDelUsuario {
|
|
29
|
+
/**
|
|
30
|
+
* ⚠️ Nunca lanza y nunca devuelve vacío: si no se sabe, `'UTC'`.
|
|
31
|
+
*
|
|
32
|
+
* Devolver `null` obligaría a cada llamante a decidir el respaldo, y ése es
|
|
33
|
+
* el tipo de decisión que se toma distinta en cada sitio.
|
|
34
|
+
*/
|
|
35
|
+
zonaDe(userId: string): Promise<string>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* ⚠️ Cadena y no `Symbol`, igual que el resto de tokens de la Fase 2.
|
|
39
|
+
*/
|
|
40
|
+
export declare const ZONA_HORARIA_DEL_USUARIO = "ZONA_HORARIA_DEL_USUARIO";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ZONA_HORARIA_DEL_USUARIO = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* ⚠️ Cadena y no `Symbol`, igual que el resto de tokens de la Fase 2.
|
|
6
|
+
*/
|
|
7
|
+
exports.ZONA_HORARIA_DEL_USUARIO = 'ZONA_HORARIA_DEL_USUARIO';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hostwebhook/platform-contracts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Contratos compartidos entre los servicios de HostWebhook: addons del plan, identidad interna, y las formas que cruzan una frontera de red",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"dist"
|
|
9
9
|
],
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "tsc",
|
|
11
|
+
"build": "npm --prefix ../node-types run build && tsc",
|
|
12
12
|
"test": "vitest run",
|
|
13
13
|
"prepublishOnly": "npm run build"
|
|
14
14
|
},
|
|
@@ -20,6 +20,12 @@
|
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"typescript": "^5.0.0",
|
|
23
|
-
"vitest": "^3.0.0"
|
|
23
|
+
"vitest": "^3.0.0",
|
|
24
|
+
"@nestjs/common": "^11.0.0",
|
|
25
|
+
"@hostwebhook/node-types": "*"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@nestjs/common": ">=11.0.0 <12",
|
|
29
|
+
"@hostwebhook/node-types": ">=1.67.0 <2"
|
|
24
30
|
}
|
|
25
31
|
}
|