@merkaly/nest 0.2.0 → 0.2.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 +44 -24
- package/dist/events/aliexpress.d.ts +10 -0
- package/dist/events/aliexpress.d.ts.map +1 -0
- package/dist/events/aliexpress.js +12 -0
- package/dist/events/aliexpress.js.map +1 -0
- package/dist/events/index.d.ts +2 -2
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +2 -2
- package/dist/events/index.js.map +1 -1
- package/dist/events/merkaly-event.d.ts +96 -0
- package/dist/events/merkaly-event.d.ts.map +1 -0
- package/dist/events/merkaly-event.js +49 -0
- package/dist/events/merkaly-event.js.map +1 -0
- package/dist/http/request-logger.d.ts +1 -0
- package/dist/http/request-logger.d.ts.map +1 -1
- package/dist/http/request-logger.js +16 -5
- package/dist/http/request-logger.js.map +1 -1
- package/dist/logger/request-context.d.ts +7 -0
- package/dist/logger/request-context.d.ts.map +1 -1
- package/dist/logger/request-context.js +8 -0
- package/dist/logger/request-context.js.map +1 -1
- package/dist/rabbit/events.consumer.d.ts +19 -0
- package/dist/rabbit/events.consumer.d.ts.map +1 -0
- package/dist/rabbit/events.consumer.js +96 -0
- package/dist/rabbit/events.consumer.js.map +1 -0
- package/dist/rabbit/events.publisher.d.ts +8 -8
- package/dist/rabbit/events.publisher.d.ts.map +1 -1
- package/dist/rabbit/events.publisher.js +36 -41
- package/dist/rabbit/events.publisher.js.map +1 -1
- package/dist/rabbit/index.d.ts +1 -0
- package/dist/rabbit/index.d.ts.map +1 -1
- package/dist/rabbit/index.js +1 -0
- package/dist/rabbit/index.js.map +1 -1
- package/dist/rabbit/rabbit.consumer.d.ts +7 -10
- package/dist/rabbit/rabbit.consumer.d.ts.map +1 -1
- package/dist/rabbit/rabbit.consumer.js +38 -13
- package/dist/rabbit/rabbit.consumer.js.map +1 -1
- package/dist/rabbit/rabbit.module.d.ts +4 -2
- package/dist/rabbit/rabbit.module.d.ts.map +1 -1
- package/dist/rabbit/rabbit.module.js +35 -15
- package/dist/rabbit/rabbit.module.js.map +1 -1
- package/dist/rabbit/rabbit.options.d.ts +25 -5
- package/dist/rabbit/rabbit.options.d.ts.map +1 -1
- package/dist/rabbit/rabbit.options.js +6 -2
- package/dist/rabbit/rabbit.options.js.map +1 -1
- package/package.json +1 -1
- package/dist/events/webhook-event.d.ts +0 -12
- package/dist/events/webhook-event.d.ts.map +0 -1
- package/dist/events/webhook-event.js +0 -11
- package/dist/events/webhook-event.js.map +0 -1
- package/dist/events/webhook-patterns.d.ts +0 -52
- package/dist/events/webhook-patterns.d.ts.map +0 -1
- package/dist/events/webhook-patterns.js +0 -49
- package/dist/events/webhook-patterns.js.map +0 -1
package/README.md
CHANGED
|
@@ -39,10 +39,11 @@ strict-peer-dependencies=false
|
|
|
39
39
|
import {
|
|
40
40
|
env, loadEnv, // config
|
|
41
41
|
LoggerModule, LoggerService, // logger
|
|
42
|
-
requestLogger, requestOrigin, requestContext, bodyParsers, bodyParserReason, // uma linha por request
|
|
42
|
+
requestLogger, requestOrigin, requestContext, noteRequest, bodyParsers, bodyParserReason, // uma linha por request
|
|
43
43
|
AbstractExceptionFilter, ExceptionsFilter, rememberError, // filtros (motivo do erro para o log)
|
|
44
|
-
RabbitModule, EventsPublisher,
|
|
45
|
-
|
|
44
|
+
RabbitModule, EventsPublisher, EventsConsumer, EventRejectedException, // eventos
|
|
45
|
+
rabbitConsumerOptions, assertConsumerTopology, OnMerkalyEvent,
|
|
46
|
+
MerkalyEvent, MerkalyEventType, MerkalyProducer, EventDataMap, createMerkalyEvent, AliexpressOrderStatus,
|
|
46
47
|
SignatureGuard, safeCompare, hmacDigest, verifyHmac,
|
|
47
48
|
} from '@merkaly/nest';
|
|
48
49
|
```
|
|
@@ -54,9 +55,8 @@ import {
|
|
|
54
55
|
imports: [
|
|
55
56
|
LoggerModule.forRoot({ service: 'webhooks.merkaly.io' }), // global
|
|
56
57
|
RabbitModule.forRoot({
|
|
57
|
-
url: env.string('RABBITMQ_URL'), // sem url:
|
|
58
|
-
|
|
59
|
-
routes: { [WebhookPatterns.asaas.PAYMENT_CONFIRMED]: 'payments.merkaly.io-queue' }, // opcional
|
|
58
|
+
url: env.string('RABBITMQ_URL'), // sem url: emit rejeita com 503
|
|
59
|
+
producer: MerkalyProducer.WEBHOOKS, // vai em todo evento
|
|
60
60
|
timeout: env.number('RABBITMQ_PUBLISH_TIMEOUT', 5000),
|
|
61
61
|
required: env.isProduction, // produção não sobe sem url
|
|
62
62
|
}),
|
|
@@ -68,7 +68,7 @@ export class AppModule {}
|
|
|
68
68
|
|
|
69
69
|
`RabbitModule.forRootAsync({ imports, inject, useFactory })` aceita a config injetada.
|
|
70
70
|
|
|
71
|
-
**
|
|
71
|
+
**Exchange e filas**: o produtor publica no exchange topic `merkaly.events` com o tipo do evento como routing key e nunca conhece uma fila. Cada consumidor declara a sua fila, com o nome do repositório (`api.merkaly.io`, `payments.merkaly.io`), e o Nest a liga a cada tipo que ele escuta. Um consumidor novo não toca no produtor.
|
|
72
72
|
|
|
73
73
|
**Health check**: o pacote não registra controllers. O Swagger servido em `/` responde 200 e funciona como health check.
|
|
74
74
|
|
|
@@ -89,16 +89,17 @@ Error: boom
|
|
|
89
89
|
|
|
90
90
|
- **ORIGIN** (24): quem fez o request, nas linhas de request e nos logs internos emitidos durante ele: `x-merkaly-host`, senão o host do `Origin`, `cf-connecting-ip`, o primeiro `x-forwarded-for` ou o socket (`requestOrigin`). Fora de um request, o `service`.
|
|
91
91
|
- **SCOPE** (22): contexto de negócio definido pelo app (`requestLogger({ scope })` ou `LoggerModule({ scope })`, ex.: id da organização). Nas linhas de request é resolvido ao terminar a resposta, então pega o que os guards definiram.
|
|
92
|
-
- **KIND** (16): método HTTP nas linhas de request; nos logs internos, o `group` de quem loga.
|
|
92
|
+
- **KIND** (16): método HTTP nas linhas de request; nos logs internos, o `group` de quem loga. Com `requestLogger({ kind })` a linha de request usa esse valor (ex.: o evento do webhook) e o método passa a abrir a mensagem: `PAYMENT_RECEIVED POST /asaas 200 44ms`.
|
|
93
93
|
- **MESSAGE**: `/path STATUS Nms "motivo"` (motivo cortado em 200, sem query, body nem headers) ou o texto. Nível: `ERROR` em 5xx com o stack embaixo, `WARN` em 4xx, `LOG` no resto.
|
|
94
94
|
|
|
95
95
|
```ts
|
|
96
96
|
// main.ts
|
|
97
|
-
app.use(requestLogger(app)); // antes do Swagger; opcional { scope: (req) => 'stripe' }
|
|
97
|
+
app.use(requestLogger(app)); // antes do Swagger; opcional { scope: (req) => 'stripe', kind: (req) => req.body?.event }
|
|
98
98
|
bodyParsers(app, { raw: ['/webhook/stripe'] }); // json + urlencoded com motivo seguro ("Invalid JSON body")
|
|
99
99
|
SwaggerModule.setup('/', app, document);
|
|
100
100
|
```
|
|
101
101
|
|
|
102
|
+
- `noteRequest(texto, nível?)` soma um resultado ao fim da linha do request (`-> payment.received`, `"discarded"`) em vez de uma linha separada; o nível só sobe (`warn`/`error`). Fora de um request devolve `false`. O `EventsPublisher` usa isso: dentro de um request o `emit` vira `-> <tipo>` na linha do request, fora dele continua com linha própria.
|
|
102
103
|
- O ORIGIN do request fica num `AsyncLocalStorage` do pacote (`requestContext`). O `requestLogger` cria o contexto e o `LoggerModule` registra um middleware que volta a entrar nele depois dos body parsers (que podem perder o contexto assíncrono), igual ao `nestjs-cls`.
|
|
103
104
|
- `bodyParsers(app, { raw?, rawBody? })`: `raw` mantém o body como Buffer nesses paths; `rawBody: true` preenche `req.rawBody` (igual ao `NestFactory.create(..., { rawBody: true })`, que deixa de registrar os próprios parsers).
|
|
104
105
|
- SCOPE com CLS, sem o pacote depender de `nestjs-cls`:
|
|
@@ -114,32 +115,51 @@ LoggerModule.forRootAsync({
|
|
|
114
115
|
|
|
115
116
|
### Eventos
|
|
116
117
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
- Asaas: `asaas.<evento em minúsculas>` (ex.: `asaas.payment_received`).
|
|
120
|
-
- Stripe: tipo pontuado original (ex.: `stripe.invoice.paid`, `stripe.customer.subscription.updated`).
|
|
121
|
-
- Auth0: nome legível do código de log (ex.: `auth0.success_signup`, `auth0.success_login`, `auth0.user_deleted`) e `auth0.user_login` da Action.
|
|
122
|
-
- AliExpress: tópico do `message_type` (53 `aliexpress.dropshipper_order_status_update`, 18 `aliexpress.waybill_changed`, 15 `aliexpress.product_deleted`); status do pedido DS em `AliexpressOrderStatus`.
|
|
118
|
+
Contrato completo em [`docs/events.md`](docs/events.md). Eventos de negócio normalizados (`payment.received`, nunca `asaas.payment_received`): se o provedor muda, o consumidor não muda.
|
|
123
119
|
|
|
124
120
|
```ts
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
);
|
|
121
|
+
// produtor: data tipado pelo tipo; resolve com o evento ou ServiceUnavailableException (o provedor reintenta)
|
|
122
|
+
await this.events.emit(MerkalyEventType.PAYMENT_RECEIVED, { ...paymentRef, receivedAt }, {
|
|
123
|
+
id: `asaas:${body.id}`, source: 'asaas', organization, occurredAt, raw: { type: body.event, payload },
|
|
124
|
+
});
|
|
129
125
|
```
|
|
130
126
|
|
|
131
|
-
Consumidor
|
|
127
|
+
Consumidor:
|
|
132
128
|
|
|
133
129
|
```ts
|
|
134
|
-
|
|
130
|
+
// main.ts
|
|
131
|
+
const consumer = { url, queue: 'api.merkaly.io' };
|
|
132
|
+
await assertConsumerTopology(consumer); // cria .retry e .dlq
|
|
133
|
+
app.connectMicroservice(rabbitConsumerOptions(consumer));
|
|
135
134
|
await app.startAllMicroservices();
|
|
136
135
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
136
|
+
// módulo: `around` roda cada mensagem dentro do contexto do app (ex.: CLS com a organização)
|
|
137
|
+
RabbitModule.forConsumerAsync({
|
|
138
|
+
inject: [ClsService],
|
|
139
|
+
useFactory: (cls: ClsService) => ({
|
|
140
|
+
queue: 'api.merkaly.io',
|
|
141
|
+
around: (event, next) => cls.run(() => { cls.set('identity', event.organization); return next(); }),
|
|
142
|
+
}),
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// handler
|
|
146
|
+
@OnMerkalyEvent(MerkalyEventType.PAYMENT_RECEIVED)
|
|
147
|
+
received(@Payload() event: MerkalyEvent<MerkalyEventType.PAYMENT_RECEIVED>, @Ctx() context: RmqContext) {
|
|
148
|
+
return this.consumer.handle(event, context, (e) => this.payments.refresh(e.data.saleId));
|
|
140
149
|
}
|
|
141
150
|
```
|
|
142
151
|
|
|
152
|
+
`@OnMerkalyEvent(...types)` aceita vários tipos no mesmo handler. `EventsConsumer.handle` fecha a mensagem e loga uma linha (`ORIGIN` = provedor, `SCOPE` = organização, `KIND` = `EVENT`):
|
|
153
|
+
|
|
154
|
+
| Resultado | O que acontece |
|
|
155
|
+
| --- | --- |
|
|
156
|
+
| Sucesso | `ack`. |
|
|
157
|
+
| `EventRejectedException` (venda inexistente, valor diferente...) | `ack` + `WARN`: reintentar não resolve. |
|
|
158
|
+
| Outro erro (Mongo, rede, provedor fora) | Vai para `<fila>.retry` (30 s) e volta; depois de 5 tentativas, `<fila>.dlq`. |
|
|
159
|
+
| Malformado ou `version` maior que a do handler | `<fila>.dlq` direto. |
|
|
160
|
+
|
|
161
|
+
A fila principal descarta para a `.dlq` o que ficar 7 dias sem consumo ou passar de 10.000 mensagens.
|
|
162
|
+
|
|
143
163
|
### Env
|
|
144
164
|
|
|
145
165
|
`env.string`, `env.number`, `env.boolean`, `env.list`, `env.required`, `env.isProduction`. Carrega o `.env` (dotenv) no primeiro acesso. Valor obrigatório ausente ou inválido: **erro em produção**, aviso no log fora dela.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const AliexpressOrderStatus: {
|
|
2
|
+
readonly CREATED: "OrderCreated";
|
|
3
|
+
readonly PAYMENT_AUTHORIZED: "PaymentAuthorized";
|
|
4
|
+
readonly PAYMENT_FAILED: "paymentFailedEvent";
|
|
5
|
+
readonly SHIPPED: "OrderShipped";
|
|
6
|
+
readonly CONFIRMED: "OrderConfirmed";
|
|
7
|
+
readonly CLOSED: "OrderClosed";
|
|
8
|
+
};
|
|
9
|
+
export type AliexpressOrderStatus = (typeof AliexpressOrderStatus)[keyof typeof AliexpressOrderStatus];
|
|
10
|
+
//# sourceMappingURL=aliexpress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aliexpress.d.ts","sourceRoot":"","sources":["../../src/events/aliexpress.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,qBAAqB;;;;;;;CAOxB,CAAC;AAEX,MAAM,MAAM,qBAAqB,GAC/B,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,OAAO,qBAAqB,CAAC,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AliexpressOrderStatus = void 0;
|
|
4
|
+
exports.AliexpressOrderStatus = {
|
|
5
|
+
CREATED: 'OrderCreated',
|
|
6
|
+
PAYMENT_AUTHORIZED: 'PaymentAuthorized',
|
|
7
|
+
PAYMENT_FAILED: 'paymentFailedEvent',
|
|
8
|
+
SHIPPED: 'OrderShipped',
|
|
9
|
+
CONFIRMED: 'OrderConfirmed',
|
|
10
|
+
CLOSED: 'OrderClosed',
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=aliexpress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aliexpress.js","sourceRoot":"","sources":["../../src/events/aliexpress.ts"],"names":[],"mappings":";;;AACa,QAAA,qBAAqB,GAAG;IACnC,OAAO,EAAE,cAAc;IACvB,kBAAkB,EAAE,mBAAmB;IACvC,cAAc,EAAE,oBAAoB;IACpC,OAAO,EAAE,cAAc;IACvB,SAAS,EAAE,gBAAgB;IAC3B,MAAM,EAAE,aAAa;CACb,CAAC"}
|
package/dist/events/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './
|
|
1
|
+
export * from './aliexpress';
|
|
2
|
+
export * from './merkaly-event';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/events/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/events/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC"}
|
package/dist/events/index.js
CHANGED
|
@@ -14,6 +14,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./
|
|
18
|
-
__exportStar(require("./
|
|
17
|
+
__exportStar(require("./aliexpress"), exports);
|
|
18
|
+
__exportStar(require("./merkaly-event"), exports);
|
|
19
19
|
//# sourceMappingURL=index.js.map
|
package/dist/events/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/events/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,kDAAgC
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/events/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B;AAC7B,kDAAgC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
export declare enum MerkalyEventType {
|
|
2
|
+
PAYMENT_CONFIRMED = "payment.confirmed",
|
|
3
|
+
PAYMENT_RECEIVED = "payment.received",
|
|
4
|
+
PAYMENT_FAILED = "payment.failed",
|
|
5
|
+
PAYMENT_OVERDUE = "payment.overdue",
|
|
6
|
+
PAYMENT_CANCELLED = "payment.cancelled",
|
|
7
|
+
PAYMENT_REFUNDED = "payment.refunded",
|
|
8
|
+
PAYMENT_CHARGEBACK_OPENED = "payment.chargeback.opened",
|
|
9
|
+
PAYMENT_CHARGEBACK_UPDATED = "payment.chargeback.updated",
|
|
10
|
+
PAYMENT_ACCOUNT_APPROVED = "payment.account.approved",
|
|
11
|
+
PAYMENT_ACCOUNT_REJECTED = "payment.account.rejected",
|
|
12
|
+
PAYMENT_ACCOUNT_STATUS_CHANGED = "payment.account.status_changed"
|
|
13
|
+
}
|
|
14
|
+
export declare enum MerkalyProducer {
|
|
15
|
+
WEBHOOKS = "webhooks.merkaly.io",
|
|
16
|
+
API = "api.merkaly.io",
|
|
17
|
+
PAYMENTS = "payments.merkaly.io"
|
|
18
|
+
}
|
|
19
|
+
export type MerkalyEventSource = 'asaas' | 'stripe' | 'auth0' | 'aliexpress';
|
|
20
|
+
export type PaymentProvider = 'asaas';
|
|
21
|
+
export type PaymentMethod = 'PIX' | 'CARD' | 'BOLETO';
|
|
22
|
+
export interface PaymentRef {
|
|
23
|
+
provider: PaymentProvider;
|
|
24
|
+
paymentId: string;
|
|
25
|
+
saleId: string | null;
|
|
26
|
+
method: PaymentMethod;
|
|
27
|
+
amount: number;
|
|
28
|
+
netAmount: number | null;
|
|
29
|
+
}
|
|
30
|
+
export type PaymentAccountSection = 'general' | 'commercialInfo' | 'documentation' | 'bankAccountInfo';
|
|
31
|
+
export type PaymentAccountStatus = 'PENDING' | 'AWAITING_APPROVAL' | 'APPROVED' | 'REJECTED' | 'EXPIRING_SOON' | 'EXPIRED';
|
|
32
|
+
export interface PaymentAccountRef {
|
|
33
|
+
provider: PaymentProvider;
|
|
34
|
+
accountId: string;
|
|
35
|
+
}
|
|
36
|
+
export interface EventDataMap {
|
|
37
|
+
[MerkalyEventType.PAYMENT_CONFIRMED]: PaymentRef & {
|
|
38
|
+
confirmedAt: string;
|
|
39
|
+
};
|
|
40
|
+
[MerkalyEventType.PAYMENT_RECEIVED]: PaymentRef & {
|
|
41
|
+
receivedAt: string;
|
|
42
|
+
};
|
|
43
|
+
[MerkalyEventType.PAYMENT_FAILED]: PaymentRef & {
|
|
44
|
+
reason: 'card_refused' | 'risk_rejected';
|
|
45
|
+
};
|
|
46
|
+
[MerkalyEventType.PAYMENT_OVERDUE]: PaymentRef & {
|
|
47
|
+
dueDate: string;
|
|
48
|
+
};
|
|
49
|
+
[MerkalyEventType.PAYMENT_CANCELLED]: PaymentRef;
|
|
50
|
+
[MerkalyEventType.PAYMENT_REFUNDED]: PaymentRef & {
|
|
51
|
+
refundedAmount: number;
|
|
52
|
+
partial: boolean;
|
|
53
|
+
};
|
|
54
|
+
[MerkalyEventType.PAYMENT_CHARGEBACK_OPENED]: PaymentRef & {
|
|
55
|
+
reason: string | null;
|
|
56
|
+
};
|
|
57
|
+
[MerkalyEventType.PAYMENT_CHARGEBACK_UPDATED]: PaymentRef & {
|
|
58
|
+
stage: 'disputed' | 'awaiting_reversal';
|
|
59
|
+
};
|
|
60
|
+
[MerkalyEventType.PAYMENT_ACCOUNT_APPROVED]: PaymentAccountRef;
|
|
61
|
+
[MerkalyEventType.PAYMENT_ACCOUNT_REJECTED]: PaymentAccountRef;
|
|
62
|
+
[MerkalyEventType.PAYMENT_ACCOUNT_STATUS_CHANGED]: PaymentAccountRef & {
|
|
63
|
+
section: PaymentAccountSection;
|
|
64
|
+
status: PaymentAccountStatus;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export interface MerkalyRawEvent {
|
|
68
|
+
type: string;
|
|
69
|
+
payload?: unknown;
|
|
70
|
+
truncated?: boolean;
|
|
71
|
+
}
|
|
72
|
+
export interface MerkalyEvent<K extends MerkalyEventType = MerkalyEventType> {
|
|
73
|
+
id: string;
|
|
74
|
+
type: K;
|
|
75
|
+
version: number;
|
|
76
|
+
producer: MerkalyProducer;
|
|
77
|
+
source?: MerkalyEventSource;
|
|
78
|
+
organization?: string;
|
|
79
|
+
occurredAt: string;
|
|
80
|
+
publishedAt: string;
|
|
81
|
+
correlationId?: string;
|
|
82
|
+
data: EventDataMap[K];
|
|
83
|
+
raw?: MerkalyRawEvent;
|
|
84
|
+
}
|
|
85
|
+
export interface MerkalyEventMeta {
|
|
86
|
+
id?: string;
|
|
87
|
+
version?: number;
|
|
88
|
+
source?: MerkalyEventSource;
|
|
89
|
+
organization?: string;
|
|
90
|
+
occurredAt?: string;
|
|
91
|
+
correlationId?: string;
|
|
92
|
+
raw?: MerkalyRawEvent;
|
|
93
|
+
}
|
|
94
|
+
export declare const RAW_PAYLOAD_LIMIT: number;
|
|
95
|
+
export declare function createMerkalyEvent<K extends MerkalyEventType>(producer: MerkalyProducer, type: K, data: EventDataMap[K], meta?: MerkalyEventMeta): MerkalyEvent<K>;
|
|
96
|
+
//# sourceMappingURL=merkaly-event.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merkaly-event.d.ts","sourceRoot":"","sources":["../../src/events/merkaly-event.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RAW_PAYLOAD_LIMIT = exports.MerkalyProducer = exports.MerkalyEventType = void 0;
|
|
4
|
+
exports.createMerkalyEvent = createMerkalyEvent;
|
|
5
|
+
const crypto_1 = require("crypto");
|
|
6
|
+
var MerkalyEventType;
|
|
7
|
+
(function (MerkalyEventType) {
|
|
8
|
+
MerkalyEventType["PAYMENT_CONFIRMED"] = "payment.confirmed";
|
|
9
|
+
MerkalyEventType["PAYMENT_RECEIVED"] = "payment.received";
|
|
10
|
+
MerkalyEventType["PAYMENT_FAILED"] = "payment.failed";
|
|
11
|
+
MerkalyEventType["PAYMENT_OVERDUE"] = "payment.overdue";
|
|
12
|
+
MerkalyEventType["PAYMENT_CANCELLED"] = "payment.cancelled";
|
|
13
|
+
MerkalyEventType["PAYMENT_REFUNDED"] = "payment.refunded";
|
|
14
|
+
MerkalyEventType["PAYMENT_CHARGEBACK_OPENED"] = "payment.chargeback.opened";
|
|
15
|
+
MerkalyEventType["PAYMENT_CHARGEBACK_UPDATED"] = "payment.chargeback.updated";
|
|
16
|
+
MerkalyEventType["PAYMENT_ACCOUNT_APPROVED"] = "payment.account.approved";
|
|
17
|
+
MerkalyEventType["PAYMENT_ACCOUNT_REJECTED"] = "payment.account.rejected";
|
|
18
|
+
MerkalyEventType["PAYMENT_ACCOUNT_STATUS_CHANGED"] = "payment.account.status_changed";
|
|
19
|
+
})(MerkalyEventType || (exports.MerkalyEventType = MerkalyEventType = {}));
|
|
20
|
+
var MerkalyProducer;
|
|
21
|
+
(function (MerkalyProducer) {
|
|
22
|
+
MerkalyProducer["WEBHOOKS"] = "webhooks.merkaly.io";
|
|
23
|
+
MerkalyProducer["API"] = "api.merkaly.io";
|
|
24
|
+
MerkalyProducer["PAYMENTS"] = "payments.merkaly.io";
|
|
25
|
+
})(MerkalyProducer || (exports.MerkalyProducer = MerkalyProducer = {}));
|
|
26
|
+
exports.RAW_PAYLOAD_LIMIT = 16 * 1024;
|
|
27
|
+
function capRaw(raw) {
|
|
28
|
+
if (!raw?.payload)
|
|
29
|
+
return raw;
|
|
30
|
+
const size = Buffer.byteLength(JSON.stringify(raw.payload) ?? '');
|
|
31
|
+
return size > exports.RAW_PAYLOAD_LIMIT ? { type: raw.type, truncated: true } : raw;
|
|
32
|
+
}
|
|
33
|
+
function createMerkalyEvent(producer, type, data, meta = {}) {
|
|
34
|
+
const publishedAt = new Date().toISOString();
|
|
35
|
+
return {
|
|
36
|
+
id: meta.id ?? (0, crypto_1.randomUUID)(),
|
|
37
|
+
type,
|
|
38
|
+
version: meta.version ?? 1,
|
|
39
|
+
producer,
|
|
40
|
+
source: meta.source,
|
|
41
|
+
organization: meta.organization || undefined,
|
|
42
|
+
occurredAt: meta.occurredAt ?? publishedAt,
|
|
43
|
+
publishedAt,
|
|
44
|
+
correlationId: meta.correlationId,
|
|
45
|
+
data,
|
|
46
|
+
raw: capRaw(meta.raw),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=merkaly-event.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merkaly-event.js","sourceRoot":"","sources":["../../src/events/merkaly-event.ts"],"names":[],"mappings":";;;AA6GA,gDAqBC;AAlID,mCAAoC;AAGpC,IAAY,gBAYX;AAZD,WAAY,gBAAgB;IAC1B,2DAAuC,CAAA;IACvC,yDAAqC,CAAA;IACrC,qDAAiC,CAAA;IACjC,uDAAmC,CAAA;IACnC,2DAAuC,CAAA;IACvC,yDAAqC,CAAA;IACrC,2EAAuD,CAAA;IACvD,6EAAyD,CAAA;IACzD,yEAAqD,CAAA;IACrD,yEAAqD,CAAA;IACrD,qFAAiE,CAAA;AACnE,CAAC,EAZW,gBAAgB,gCAAhB,gBAAgB,QAY3B;AAED,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,mDAAgC,CAAA;IAChC,yCAAsB,CAAA;IACtB,mDAAgC,CAAA;AAClC,CAAC,EAJW,eAAe,+BAAf,eAAe,QAI1B;AA6EY,QAAA,iBAAiB,GAAG,EAAE,GAAG,IAAI,CAAC;AAG3C,SAAS,MAAM,CAAC,GAAqB;IACnC,IAAI,CAAC,GAAG,EAAE,OAAO;QAAE,OAAO,GAAG,CAAC;IAE9B,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAElE,OAAO,IAAI,GAAG,yBAAiB,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;AAC9E,CAAC;AAED,SAAgB,kBAAkB,CAChC,QAAyB,EACzB,IAAO,EACP,IAAqB,EACrB,OAAyB,EAAE;IAE3B,MAAM,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAE7C,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE,IAAI,IAAA,mBAAU,GAAE;QAC3B,IAAI;QACJ,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,CAAC;QAC1B,QAAQ;QACR,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,SAAS;QAC5C,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,WAAW;QAC1C,WAAW;QACX,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,IAAI;QACJ,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;KACtB,CAAC;AACJ,CAAC"}
|
|
@@ -6,6 +6,7 @@ export interface RequestLogLocals {
|
|
|
6
6
|
}
|
|
7
7
|
export interface RequestLoggerOptions {
|
|
8
8
|
scope?: (request: Request) => string | undefined;
|
|
9
|
+
kind?: (request: Request) => string | undefined;
|
|
9
10
|
}
|
|
10
11
|
export declare function requestLogger(app: INestApplicationContext, options?: RequestLoggerOptions): RequestHandler;
|
|
11
12
|
//# sourceMappingURL=request-logger.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-logger.d.ts","sourceRoot":"","sources":["../../src/http/request-logger.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,KAAK,EAAgB,OAAO,EAAE,cAAc,EAAY,MAAM,SAAS,CAAC;AAE/E,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"request-logger.d.ts","sourceRoot":"","sources":["../../src/http/request-logger.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,KAAK,EAAgB,OAAO,EAAE,cAAc,EAAY,MAAM,SAAS,CAAC;AAE/E,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;IACjD,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;CACjD;AAaD,wBAAgB,aAAa,CAC3B,GAAG,EAAE,uBAAuB,EAC5B,OAAO,GAAE,oBAAyB,GACjC,cAAc,CAuChB"}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.requestLogger = requestLogger;
|
|
4
4
|
const logger_1 = require("../logger");
|
|
5
|
+
const LEVELS = ['log', 'warn', 'error'];
|
|
6
|
+
const higher = (a, b) => LEVELS.indexOf(a) >= LEVELS.indexOf(b) ? a : b;
|
|
5
7
|
const MAX_REASON = 200;
|
|
6
8
|
const capped = (reason) => reason.length > MAX_REASON ? `${reason.slice(0, MAX_REASON)}…` : reason;
|
|
7
9
|
function requestLogger(app, options = {}) {
|
|
@@ -15,12 +17,21 @@ function requestLogger(app, options = {}) {
|
|
|
15
17
|
const ms = Number(process.hrtime.bigint() - started) / 1e6;
|
|
16
18
|
const path = request.originalUrl.split('?')[0];
|
|
17
19
|
const { error, trace } = response.locals;
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
20
|
+
const kind = options.kind?.(request);
|
|
21
|
+
const target = kind ? `${request.method} ${path}` : path;
|
|
22
|
+
const notes = context.notes ?? [];
|
|
23
|
+
const line = [
|
|
24
|
+
`${target} ${response.statusCode} ${ms.toFixed(0)}ms`,
|
|
25
|
+
error ? JSON.stringify(capped(error)) : '',
|
|
26
|
+
...notes.map((note) => note.text),
|
|
27
|
+
]
|
|
28
|
+
.filter(Boolean)
|
|
29
|
+
.join(' ');
|
|
30
|
+
const status = response.statusCode >= 500 ? 'error' : response.statusCode >= 400 ? 'warn' : 'log';
|
|
31
|
+
const tier = notes.reduce((level, note) => higher(level, note.level), status);
|
|
21
32
|
logger_1.requestContext.run(context, () => logger.request(tier, {
|
|
22
|
-
method: request.method,
|
|
23
|
-
message,
|
|
33
|
+
method: kind || request.method,
|
|
34
|
+
message: line,
|
|
24
35
|
trace: tier === 'error' ? trace : undefined,
|
|
25
36
|
}));
|
|
26
37
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-logger.js","sourceRoot":"","sources":["../../src/http/request-logger.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"request-logger.js","sourceRoot":"","sources":["../../src/http/request-logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
2
2
|
import { NestMiddleware } from '@nestjs/common';
|
|
3
3
|
import type { NextFunction, Request, Response } from 'express';
|
|
4
|
+
export type RequestNoteLevel = 'log' | 'warn' | 'error';
|
|
5
|
+
export interface RequestNote {
|
|
6
|
+
text: string;
|
|
7
|
+
level: RequestNoteLevel;
|
|
8
|
+
}
|
|
4
9
|
export interface RequestContext {
|
|
5
10
|
origin: string;
|
|
6
11
|
scope?: () => string | undefined;
|
|
12
|
+
notes?: RequestNote[];
|
|
7
13
|
}
|
|
8
14
|
export declare const requestContext: AsyncLocalStorage<RequestContext>;
|
|
15
|
+
export declare function noteRequest(text: string, level?: RequestNoteLevel): boolean;
|
|
9
16
|
export declare function requestOrigin(request: Request): string;
|
|
10
17
|
export declare function contextOf(request: Request): RequestContext;
|
|
11
18
|
export declare class RequestContextMiddleware implements NestMiddleware {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/logger/request-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAc,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE/D,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"request-context.d.ts","sourceRoot":"","sources":["../../src/logger/request-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAc,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE/D,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,gBAAgB,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IACjC,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;CACvB;AAED,eAAO,MAAM,cAAc,mCAA0C,CAAC;AAGtE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,gBAAwB,WAQxE;AASD,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,UAS7C;AAGD,wBAAgB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAI1D;AAGD,qBACa,wBAAyB,YAAW,cAAc;IAC7D,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY;CAGtD"}
|
|
@@ -7,11 +7,19 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
};
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.RequestContextMiddleware = exports.requestContext = void 0;
|
|
10
|
+
exports.noteRequest = noteRequest;
|
|
10
11
|
exports.requestOrigin = requestOrigin;
|
|
11
12
|
exports.contextOf = contextOf;
|
|
12
13
|
const async_hooks_1 = require("async_hooks");
|
|
13
14
|
const common_1 = require("@nestjs/common");
|
|
14
15
|
exports.requestContext = new async_hooks_1.AsyncLocalStorage();
|
|
16
|
+
function noteRequest(text, level = 'log') {
|
|
17
|
+
const context = exports.requestContext.getStore();
|
|
18
|
+
if (!context)
|
|
19
|
+
return false;
|
|
20
|
+
(context.notes ??= []).push({ level, text });
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
15
23
|
const CONTEXT = Symbol('merkaly.request.context');
|
|
16
24
|
const hostOf = (origin) => origin?.split('/').pop() || undefined;
|
|
17
25
|
function requestOrigin(request) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"request-context.js","sourceRoot":"","sources":["../../src/logger/request-context.ts"],"names":[],"mappings":";;;;;;;;;
|
|
1
|
+
{"version":3,"file":"request-context.js","sourceRoot":"","sources":["../../src/logger/request-context.ts"],"names":[],"mappings":";;;;;;;;;AAoBA,kCAQC;AASD,sCASC;AAGD,8BAIC;AArDD,6CAAgD;AAChD,2CAA4D;AAgB/C,QAAA,cAAc,GAAG,IAAI,+BAAiB,EAAkB,CAAC;AAGtE,SAAgB,WAAW,CAAC,IAAY,EAAE,QAA0B,KAAK;IACvE,MAAM,OAAO,GAAG,sBAAc,CAAC,QAAQ,EAAE,CAAC;IAE1C,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAE3B,CAAC,OAAO,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAE7C,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,OAAO,GAAG,MAAM,CAAC,yBAAyB,CAAC,CAAC;AAIlD,MAAM,MAAM,GAAG,CAAC,MAAe,EAAE,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,SAAS,CAAC;AAG1E,SAAgB,aAAa,CAAC,OAAgB;IAC5C,OAAO,CACL,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC;QAChC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAChC,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC;QAClC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;QACvD,OAAO,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;QACtD,SAAS,CACV,CAAC;AACJ,CAAC;AAGD,SAAgB,SAAS,CAAC,OAAgB;IACxC,MAAM,GAAG,GAAG,OAAyB,CAAC;IAEtC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAC/D,CAAC;AAIM,IAAM,wBAAwB,GAA9B,MAAM,wBAAwB;IACnC,GAAG,CAAC,OAAgB,EAAE,CAAW,EAAE,IAAkB;QACnD,sBAAc,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;IAC/C,CAAC;CACF,CAAA;AAJY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,mBAAU,GAAE;GACA,wBAAwB,CAIpC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { RmqContext } from '@nestjs/microservices';
|
|
2
|
+
import { MerkalyEvent, MerkalyEventType } from '../events';
|
|
3
|
+
import { LoggerService } from '../logger';
|
|
4
|
+
import { EventsConsumerOptions } from './rabbit.options';
|
|
5
|
+
export declare class EventRejectedException extends Error {
|
|
6
|
+
}
|
|
7
|
+
export interface HandleOptions {
|
|
8
|
+
version?: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class EventsConsumer {
|
|
11
|
+
private readonly options;
|
|
12
|
+
private readonly logger;
|
|
13
|
+
constructor(options: EventsConsumerOptions, logger?: LoggerService);
|
|
14
|
+
handle<K extends MerkalyEventType>(event: MerkalyEvent<K>, context: RmqContext, handler: (event: MerkalyEvent<K>) => Promise<unknown>, { version }?: HandleOptions): Promise<void>;
|
|
15
|
+
private ack;
|
|
16
|
+
private deadLetter;
|
|
17
|
+
private retry;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=events.consumer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.consumer.d.ts","sourceRoot":"","sources":["../../src/rabbit/events.consumer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAkB,MAAM,WAAW,CAAC;AAG1D,OAAO,EAAqC,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAM5F,qBAAa,sBAAuB,SAAQ,KAAK;CAAG;AAEpD,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBACa,cAAc;IAIG,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;gBAGM,OAAO,EAAE,qBAAqB,EAC7D,MAAM,CAAC,EAAE,aAAa;IAM7B,MAAM,CAAC,CAAC,SAAS,gBAAgB,EACtC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,EACtB,OAAO,EAAE,UAAU,EACnB,OAAO,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,EACrD,EAAE,OAAW,EAAE,GAAE,aAAkB,GAClC,OAAO,CAAC,IAAI,CAAC;IAoChB,OAAO,CAAC,GAAG;IAKX,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,KAAK;CAiBd"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.EventsConsumer = exports.EventRejectedException = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const logger_1 = require("../logger");
|
|
18
|
+
const events_publisher_1 = require("./events.publisher");
|
|
19
|
+
const rabbit_consumer_1 = require("./rabbit.consumer");
|
|
20
|
+
const rabbit_options_1 = require("./rabbit.options");
|
|
21
|
+
const GROUP = 'EVENT';
|
|
22
|
+
const RETRIES_HEADER = 'x-merkaly-retries';
|
|
23
|
+
class EventRejectedException extends Error {
|
|
24
|
+
}
|
|
25
|
+
exports.EventRejectedException = EventRejectedException;
|
|
26
|
+
let EventsConsumer = class EventsConsumer {
|
|
27
|
+
options;
|
|
28
|
+
logger;
|
|
29
|
+
constructor(options, logger) {
|
|
30
|
+
this.options = options;
|
|
31
|
+
this.logger = logger ?? new logger_1.LoggerService();
|
|
32
|
+
}
|
|
33
|
+
handle(event, context, handler, { version = 1 } = {}) {
|
|
34
|
+
const started = Date.now();
|
|
35
|
+
const scope = {
|
|
36
|
+
origin: event?.source ?? event?.producer ?? 'unknown',
|
|
37
|
+
scope: () => event?.organization,
|
|
38
|
+
};
|
|
39
|
+
const line = (text) => `${event?.type} (${event?.id}) ${text} ${Date.now() - started}ms`;
|
|
40
|
+
return logger_1.requestContext.run(scope, async () => {
|
|
41
|
+
if (!event?.id || !event?.type)
|
|
42
|
+
return this.deadLetter(context, line('malformed'));
|
|
43
|
+
if (event.version > version)
|
|
44
|
+
return this.deadLetter(context, line(`unknown version ${event.version}`));
|
|
45
|
+
const next = () => handler(event);
|
|
46
|
+
const run = this.options.around ? () => this.options.around(event, next) : next;
|
|
47
|
+
return Promise.resolve()
|
|
48
|
+
.then(run)
|
|
49
|
+
.then(() => {
|
|
50
|
+
this.ack(context);
|
|
51
|
+
this.logger.log({ group: GROUP, message: line('ok') });
|
|
52
|
+
})
|
|
53
|
+
.catch((error) => {
|
|
54
|
+
if (error instanceof EventRejectedException) {
|
|
55
|
+
this.ack(context);
|
|
56
|
+
return this.logger.warn({ group: GROUP, message: line(`rejected "${error.message}"`) });
|
|
57
|
+
}
|
|
58
|
+
return this.retry(context, line(`failed "${(0, events_publisher_1.describeError)(error)}"`), error);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
ack(context) {
|
|
63
|
+
context.getChannelRef().ack(context.getMessage());
|
|
64
|
+
}
|
|
65
|
+
deadLetter(context, message, trace) {
|
|
66
|
+
context.getChannelRef().nack(context.getMessage(), false, false);
|
|
67
|
+
this.logger.error({
|
|
68
|
+
group: GROUP,
|
|
69
|
+
message: `${message} -> ${(0, rabbit_consumer_1.deadLetterQueue)(this.options.queue)}`,
|
|
70
|
+
trace,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
retry(context, message, trace) {
|
|
74
|
+
const msg = context.getMessage();
|
|
75
|
+
const attempt = Number(msg.properties.headers?.[RETRIES_HEADER] ?? 0) + 1;
|
|
76
|
+
const max = this.options.retries ?? rabbit_options_1.DEFAULT_RETRIES;
|
|
77
|
+
if (attempt > max)
|
|
78
|
+
return this.deadLetter(context, `${message}, ${max} retries`, trace);
|
|
79
|
+
const channel = context.getChannelRef();
|
|
80
|
+
channel.sendToQueue((0, rabbit_consumer_1.retryQueue)(this.options.queue), msg.content, {
|
|
81
|
+
...msg.properties,
|
|
82
|
+
headers: { ...msg.properties.headers, [RETRIES_HEADER]: attempt },
|
|
83
|
+
persistent: true,
|
|
84
|
+
});
|
|
85
|
+
channel.ack(msg);
|
|
86
|
+
this.logger.error({ group: GROUP, message: `${message}, retry ${attempt}/${max}`, trace });
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
exports.EventsConsumer = EventsConsumer;
|
|
90
|
+
exports.EventsConsumer = EventsConsumer = __decorate([
|
|
91
|
+
(0, common_1.Injectable)(),
|
|
92
|
+
__param(0, (0, common_1.Inject)(rabbit_options_1.CONSUMER_OPTIONS)),
|
|
93
|
+
__param(1, (0, common_1.Optional)()),
|
|
94
|
+
__metadata("design:paramtypes", [Object, logger_1.LoggerService])
|
|
95
|
+
], EventsConsumer);
|
|
96
|
+
//# sourceMappingURL=events.consumer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.consumer.js","sourceRoot":"","sources":["../../src/rabbit/events.consumer.ts"],"names":[],"mappings":"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"}
|