@modernlock/common 1.0.63 → 1.0.65

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.
@@ -8,8 +8,11 @@ export declare abstract class Consumer {
8
8
  abstract exchangeType: ExchangeTypes;
9
9
  abstract routingKey: RoutingKeys;
10
10
  abstract queue: Queues;
11
- abstract onMessage(msg: amqp.Message, data: any, channel: amqp.Channel): void;
11
+ abstract onMessage(msg: amqp.Message, data: any, channel?: amqp.Channel): Promise<void>;
12
12
  private channel;
13
+ private dlx;
14
+ private dlxType;
15
+ private dlRoutingKey;
13
16
  constructor(channel: amqp.Channel);
14
17
  listen(): Promise<void>;
15
18
  parseMessage(msg: amqp.Message): any;
@@ -12,21 +12,51 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.Consumer = void 0;
13
13
  class Consumer {
14
14
  constructor(channel) {
15
+ // We define standard names for our Dead letter queu
16
+ this.dlx = 'dead_letter_exchange';
17
+ this.dlxType = 'direct';
18
+ this.dlRoutingKey = 'dead_letter';
15
19
  this.channel = channel;
16
20
  }
17
21
  ;
18
22
  listen() {
19
23
  return __awaiter(this, void 0, void 0, function* () {
20
24
  try {
25
+ // Assert the Dead Letter Exchange
26
+ yield this.channel.assertExchange(this.dlx, this.dlxType);
27
+ // Assert the Dead Letter Queue
28
+ const dlQueueName = `${this.queue}.dlq`; // e.g., "update-calendar.dlq"
29
+ yield this.channel.assertQueue(dlQueueName, { durable: true });
30
+ // Bind them so failed messages go into the DLQ
31
+ yield this.channel.bindQueue(dlQueueName, this.dlx, this.dlRoutingKey);
21
32
  yield this.channel.assertExchange(this.exchange, this.exchangeType);
22
- const q = yield this.channel.assertQueue(this.queue, { durable: true });
33
+ // Assert the Main Queue with special arguments linking to DLQ
34
+ const q = yield this.channel.assertQueue(this.queue, {
35
+ durable: true,
36
+ arguments: {
37
+ // If message is Nack'ed (rejected), send to this exchange:
38
+ 'x-dead-letter-exchange': this.dlx,
39
+ // With this routing key:
40
+ 'x-dead-letter-routing-key': this.dlRoutingKey
41
+ // Optional: Time to Live (TTL) before moving to DLQ
42
+ // 'x-message-ttl': 60000
43
+ }
44
+ });
23
45
  yield this.channel.bindQueue(q.queue, this.exchange, this.routingKey);
24
- this.channel.consume(q.queue, (msg) => {
46
+ yield this.channel.prefetch(1);
47
+ this.channel.consume(q.queue, (msg) => __awaiter(this, void 0, void 0, function* () {
25
48
  if (!msg)
26
- throw new Error('Message is required');
27
- const data = this.parseMessage(msg);
28
- this.onMessage(msg, data, this.channel);
29
- });
49
+ return;
50
+ try {
51
+ const data = this.parseMessage(msg);
52
+ yield this.onMessage(data, msg);
53
+ this.channel.ack(msg);
54
+ }
55
+ catch (error) {
56
+ console.error(`Error processing message in ${this.queue}:`, error);
57
+ this.channel.nack(msg, false, false);
58
+ }
59
+ }));
30
60
  }
31
61
  catch (error) {
32
62
  console.error('Failed to publish message:', error);
@@ -10,5 +10,6 @@ export declare class RabbitmqWrapper {
10
10
  connect(url: string): Promise<void>;
11
11
  connectWithRetries(url: string): Promise<void>;
12
12
  private reconnect;
13
+ close(): Promise<void>;
13
14
  }
14
15
  export declare const rabbitmqWrapper: RabbitmqWrapper;
@@ -115,6 +115,19 @@ class RabbitmqWrapper {
115
115
  }
116
116
  });
117
117
  }
118
+ close() {
119
+ return __awaiter(this, void 0, void 0, function* () {
120
+ try {
121
+ if (this.channel)
122
+ yield this.channel.close();
123
+ if (this.connection)
124
+ yield this.connection.close();
125
+ }
126
+ catch (err) {
127
+ console.error("Error closing RabbitMQ connection", err);
128
+ }
129
+ });
130
+ }
118
131
  }
119
132
  exports.RabbitmqWrapper = RabbitmqWrapper;
120
133
  ;
package/build/index.d.ts CHANGED
@@ -38,6 +38,7 @@ export * from "./config/elasticsearchConnection";
38
38
  export * from "./interfaces/reservation";
39
39
  export * from "./things/events";
40
40
  export * from "./mqtt/topics";
41
+ export * from "./mqtt/mqtt-wrapper";
41
42
  export * from "./things/commands";
42
43
  export * from "./@types/notification";
43
44
  export * from "./@types/express";
package/build/index.js CHANGED
@@ -54,6 +54,7 @@ __exportStar(require("./config/elasticsearchConnection"), exports);
54
54
  __exportStar(require("./interfaces/reservation"), exports);
55
55
  __exportStar(require("./things/events"), exports);
56
56
  __exportStar(require("./mqtt/topics"), exports);
57
+ __exportStar(require("./mqtt/mqtt-wrapper"), exports);
57
58
  __exportStar(require("./things/commands"), exports);
58
59
  __exportStar(require("./@types/notification"), exports);
59
60
  __exportStar(require("./@types/express"), exports);
@@ -0,0 +1,15 @@
1
+ import * as mqtt from 'mqtt';
2
+ type MqttMessageHandler = (message: string, topic: string) => void;
3
+ declare class MqttWrapper {
4
+ private client?;
5
+ private handlers;
6
+ constructor();
7
+ connect(options: mqtt.IClientOptions): Promise<void>;
8
+ private routeMessage;
9
+ private topicMatches;
10
+ publish(topic: string, message: string, options?: mqtt.IClientPublishOptions): Promise<void>;
11
+ subscribe(topic: string, callback: MqttMessageHandler): Promise<void>;
12
+ disconnect(): Promise<void>;
13
+ }
14
+ export declare const mqttWrapper: MqttWrapper;
15
+ export {};
@@ -0,0 +1,140 @@
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ Object.defineProperty(exports, "__esModule", { value: true });
35
+ exports.mqttWrapper = void 0;
36
+ const mqtt = __importStar(require("mqtt"));
37
+ class MqttWrapper {
38
+ constructor() {
39
+ this.handlers = new Map();
40
+ }
41
+ // 1. Connect Method (Async & Explicit)
42
+ connect(options) {
43
+ return new Promise((resolve, reject) => {
44
+ this.client = mqtt.connect(options);
45
+ this.client.on('connect', () => {
46
+ console.log('✅ Connected to MQTT broker');
47
+ resolve();
48
+ });
49
+ this.client.on('error', (err) => {
50
+ console.error('❌ MQTT error:', err);
51
+ // Only reject if we are still trying to connect initially
52
+ // If already connected, this is just a runtime error log
53
+ });
54
+ this.client.on('message', (topic, message) => {
55
+ this.routeMessage(topic, message.toString('utf8'));
56
+ });
57
+ });
58
+ }
59
+ routeMessage(topic, message) {
60
+ // Optimization: If no handlers, don't loop
61
+ if (this.handlers.size === 0)
62
+ return;
63
+ for (const [filter, handler] of this.handlers.entries()) {
64
+ if (this.topicMatches(filter, topic)) {
65
+ handler(message, topic);
66
+ }
67
+ }
68
+ }
69
+ topicMatches(filter, topic) {
70
+ const filterParts = filter.split('/');
71
+ const topicParts = topic.split('/');
72
+ for (let i = 0; i < filterParts.length; i++) {
73
+ const filterPart = filterParts[i];
74
+ const topicPart = topicParts[i];
75
+ if (filterPart === '#')
76
+ return true;
77
+ if (filterPart === '+') {
78
+ if (!topicPart)
79
+ return false;
80
+ continue;
81
+ }
82
+ if (filterPart !== topicPart)
83
+ return false;
84
+ }
85
+ return filterParts.length === topicParts.length;
86
+ }
87
+ publish(topic, message, options = { qos: 0 }) {
88
+ return __awaiter(this, void 0, void 0, function* () {
89
+ if (!this.client)
90
+ throw new Error("Cannot publish before connecting");
91
+ return new Promise((resolve, reject) => {
92
+ this.client.publish(topic, message, options, (err) => {
93
+ if (err)
94
+ reject(err);
95
+ else
96
+ resolve();
97
+ });
98
+ });
99
+ });
100
+ }
101
+ subscribe(topic, callback) {
102
+ return __awaiter(this, void 0, void 0, function* () {
103
+ if (!this.client)
104
+ throw new Error("Cannot subscribe before connecting");
105
+ return new Promise((resolve, reject) => {
106
+ if (this.handlers.has(topic)) {
107
+ this.handlers.set(topic, callback);
108
+ return resolve();
109
+ }
110
+ this.client.subscribe(topic, { qos: 1 }, (err) => {
111
+ if (err) {
112
+ reject(err);
113
+ }
114
+ else {
115
+ console.log(`Subscribed to MQTT topic: ${topic}`);
116
+ this.handlers.set(topic, callback);
117
+ resolve();
118
+ }
119
+ });
120
+ });
121
+ });
122
+ }
123
+ // 2. Graceful Shutdown
124
+ disconnect() {
125
+ return __awaiter(this, void 0, void 0, function* () {
126
+ return new Promise((resolve) => {
127
+ if (this.client) {
128
+ this.client.end(false, () => {
129
+ console.log("MQTT Disconnected");
130
+ resolve();
131
+ });
132
+ }
133
+ else {
134
+ resolve();
135
+ }
136
+ });
137
+ });
138
+ }
139
+ }
140
+ exports.mqttWrapper = new MqttWrapper();
@@ -3,7 +3,9 @@ export declare enum MqttTopics {
3
3
  connectionJoiningReply = "m@dernl0ck/connection-joining-reply/#",
4
4
  database = "m@dernl0ck/database/#",
5
5
  databaseInquiry = "m@dernl0ck/database-inquiry/#",
6
- thingsNotification = "m@dernl0ck/devices/#"
6
+ thingsNotification = "m@dernl0ck/devices/#",
7
+ factoryDevicesDetected = "factory/devices/detected",
8
+ factoryDevicesRegistered = "factory/devices/registered"
7
9
  }
8
10
  export declare const mqttCallTopics: {
9
11
  getConnectionJoiningTopic: (coordinatorId: string) => string;
@@ -8,6 +8,8 @@ var MqttTopics;
8
8
  MqttTopics["database"] = "m@dernl0ck/database/#";
9
9
  MqttTopics["databaseInquiry"] = "m@dernl0ck/database-inquiry/#";
10
10
  MqttTopics["thingsNotification"] = "m@dernl0ck/devices/#";
11
+ MqttTopics["factoryDevicesDetected"] = "factory/devices/detected";
12
+ MqttTopics["factoryDevicesRegistered"] = "factory/devices/registered";
11
13
  })(MqttTopics = exports.MqttTopics || (exports.MqttTopics = {}));
12
14
  exports.mqttCallTopics = {
13
15
  getConnectionJoiningTopic: (coordinatorId) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modernlock/common",
3
- "version": "1.0.63",
3
+ "version": "1.0.65",
4
4
  "main": "./build/index.js",
5
5
  "types": "./build/index.d.ts",
6
6
  "files": [
@@ -27,6 +27,7 @@
27
27
  "cookie-session": "^2.0.0",
28
28
  "express": "4.18.2",
29
29
  "jsonwebtoken": "9.0.0",
30
+ "mqtt": "^5.14.1",
30
31
  "winston": "^3.12.0",
31
32
  "winston-elasticsearch": "^0.18.0"
32
33
  },