From 643e5c15e36f7a7ce55ddebffc93c3b574c2745a Mon Sep 17 00:00:00 2001 From: Red-Asuka Date: Wed, 28 Aug 2024 17:02:08 +0800 Subject: [PATCH 1/3] fix(desktop): correct handling of '#' wildcard in topic filtering --- src/database/services/MessageService.ts | 59 +++++++++++++++++++++---- 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/src/database/services/MessageService.ts b/src/database/services/MessageService.ts index 52c3952ea..5784b2cd7 100644 --- a/src/database/services/MessageService.ts +++ b/src/database/services/MessageService.ts @@ -61,18 +61,36 @@ export default class MessageService { msgType !== 'all' && query.andWhere('msg.out = :out', { out: msgType === 'publish' }) if (topic && topic !== '#') { + // Escape special characters for SQL LIKE topic = topic.replace(/[\\%_]/g, '\\$&') - if (topic.startsWith('$share/')) topic = topic.split('/').slice(2).join('/') - if (topic.includes('#') && topic.endsWith('/#')) topic = topic.replace('#', '%') + + // Remove $share prefix if present + if (topic.startsWith('$share/')) { + topic = topic.split('/').slice(2).join('/') + } + /* + Handle `+` wildcard Known Issue: '+' wildcard handling in MQTT topics is incorrect. '+' is replaced with '%' for SQL LIKE, causing multi-level match. - Incorrect: 'testtopic/+/test' matches 'testtopic/1/2/test' - Incorrect: 'testtopic/+/hello/+' can not matches 'testtopic/hello/hello/hello' TODO: FIX this issue. */ - if (topic.includes('+')) topic = topic.replace('+', '%') - query.andWhere('msg.topic LIKE :topic ESCAPE "\\"', { topic }) + if (topic.includes('+')) { + topic = topic.replace('+', '%') + } + + // Handle '#' wildcard + if (topic.endsWith('/#')) { + const baseTopic = topic.slice(0, -2) // Remove '/#' + query.andWhere('(msg.topic LIKE :baseTopic OR msg.topic LIKE :topic ESCAPE "\\")', { + baseTopic, + topic: baseTopic + '/%', + }) + } else { + query.andWhere('msg.topic LIKE :topic ESCAPE "\\"', { topic }) + } } if (options.searchParams) { @@ -125,11 +143,36 @@ export default class MessageService { msgType !== 'all' && query.andWhere('msg.out = :out', { out: msgType === 'publish' }) if (topic && topic !== '#') { + // Escape special characters for SQL LIKE topic = topic.replace(/[\\%_]/g, '\\$&') - if (topic.startsWith('$share/')) topic = topic.split('/').slice(2).join('/') - if (topic.includes('#') && topic.endsWith('/#')) topic = topic.replace('#', '%') - if (topic.includes('+')) topic = topic.replace('+', '%') - query.andWhere('msg.topic LIKE :topic ESCAPE "\\"', { topic }) + + // Remove $share prefix if present + if (topic.startsWith('$share/')) { + topic = topic.split('/').slice(2).join('/') + } + + /* + Handle `+` wildcard + Known Issue: '+' wildcard handling in MQTT topics is incorrect. + '+' is replaced with '%' for SQL LIKE, causing multi-level match. + - Incorrect: 'testtopic/+/test' matches 'testtopic/1/2/test' + - Incorrect: 'testtopic/+/hello/+' can not matches 'testtopic/hello/hello/hello' + TODO: FIX this issue. + */ + if (topic.includes('+')) { + topic = topic.replace('+', '%') + } + + // Handle '#' wildcard + if (topic.endsWith('/#')) { + const baseTopic = topic.slice(0, -2) // Remove '/#' + query.andWhere('(msg.topic LIKE :baseTopic OR msg.topic LIKE :topic ESCAPE "\\")', { + baseTopic, + topic: baseTopic + '/%', + }) + } else { + query.andWhere('msg.topic LIKE :topic ESCAPE "\\"', { topic }) + } } if (options.searchParams) { From 869e0775c01ca9e9e842b62de3c53fdcdcebb8fe Mon Sep 17 00:00:00 2001 From: Red-Asuka Date: Wed, 28 Aug 2024 17:31:37 +0800 Subject: [PATCH 2/3] improve(desktop): merge duplicate topic processing logic --- src/database/services/MessageService.ts | 86 +++++++++---------------- 1 file changed, 30 insertions(+), 56 deletions(-) diff --git a/src/database/services/MessageService.ts b/src/database/services/MessageService.ts index 5784b2cd7..2351348a9 100644 --- a/src/database/services/MessageService.ts +++ b/src/database/services/MessageService.ts @@ -36,30 +36,7 @@ export default class MessageService { } as MessageModel } - public async get( - connectionId: string, - options: { - page?: number - limit?: number - msgType?: MessageType - topic?: string - searchParams?: { topic?: string; payload?: string } - } = {}, - ): Promise { - const defaultOpts = { page: 1, limit: 20, msgType: 'all' } - const { page, limit, msgType } = { ...defaultOpts, ...options } - let { topic } = { ...defaultOpts, ...options } - - const total = await this.messageRepository.count({ connectionId }) - const publishedTotal = await this.messageRepository.count({ connectionId, out: true }) - const receivedTotal = await this.messageRepository.count({ connectionId, out: false }) - - let query = this.messageRepository - .createQueryBuilder('msg') - .where('msg.connectionId = :connection', { connection: connectionId }) - - msgType !== 'all' && query.andWhere('msg.out = :out', { out: msgType === 'publish' }) - + public handleTopicQuery(query: $TSFixed, topic?: string) { if (topic && topic !== '#') { // Escape special characters for SQL LIKE topic = topic.replace(/[\\%_]/g, '\\$&') @@ -92,6 +69,34 @@ export default class MessageService { query.andWhere('msg.topic LIKE :topic ESCAPE "\\"', { topic }) } } + return query + } + + public async get( + connectionId: string, + options: { + page?: number + limit?: number + msgType?: MessageType + topic?: string + searchParams?: { topic?: string; payload?: string } + } = {}, + ): Promise { + const defaultOpts = { page: 1, limit: 20, msgType: 'all' } + const { page, limit, msgType } = { ...defaultOpts, ...options } + let { topic } = { ...defaultOpts, ...options } + + const total = await this.messageRepository.count({ connectionId }) + const publishedTotal = await this.messageRepository.count({ connectionId, out: true }) + const receivedTotal = await this.messageRepository.count({ connectionId, out: false }) + + let query = this.messageRepository + .createQueryBuilder('msg') + .where('msg.connectionId = :connection', { connection: connectionId }) + + msgType !== 'all' && query.andWhere('msg.out = :out', { out: msgType === 'publish' }) + + query = this.handleTopicQuery(query, topic) if (options.searchParams) { const { topic, payload } = options.searchParams @@ -142,38 +147,7 @@ export default class MessageService { msgType !== 'all' && query.andWhere('msg.out = :out', { out: msgType === 'publish' }) - if (topic && topic !== '#') { - // Escape special characters for SQL LIKE - topic = topic.replace(/[\\%_]/g, '\\$&') - - // Remove $share prefix if present - if (topic.startsWith('$share/')) { - topic = topic.split('/').slice(2).join('/') - } - - /* - Handle `+` wildcard - Known Issue: '+' wildcard handling in MQTT topics is incorrect. - '+' is replaced with '%' for SQL LIKE, causing multi-level match. - - Incorrect: 'testtopic/+/test' matches 'testtopic/1/2/test' - - Incorrect: 'testtopic/+/hello/+' can not matches 'testtopic/hello/hello/hello' - TODO: FIX this issue. - */ - if (topic.includes('+')) { - topic = topic.replace('+', '%') - } - - // Handle '#' wildcard - if (topic.endsWith('/#')) { - const baseTopic = topic.slice(0, -2) // Remove '/#' - query.andWhere('(msg.topic LIKE :baseTopic OR msg.topic LIKE :topic ESCAPE "\\")', { - baseTopic, - topic: baseTopic + '/%', - }) - } else { - query.andWhere('msg.topic LIKE :topic ESCAPE "\\"', { topic }) - } - } + query = this.handleTopicQuery(query, topic) if (options.searchParams) { const { topic, payload } = options.searchParams From 58426231df15a88e63212b3e8f53448390728687 Mon Sep 17 00:00:00 2001 From: Red-Asuka Date: Wed, 28 Aug 2024 17:54:24 +0800 Subject: [PATCH 3/3] fix(desktop): fix the incorrect topic query --- src/database/services/MessageService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/database/services/MessageService.ts b/src/database/services/MessageService.ts index 2351348a9..33f465df7 100644 --- a/src/database/services/MessageService.ts +++ b/src/database/services/MessageService.ts @@ -61,7 +61,7 @@ export default class MessageService { // Handle '#' wildcard if (topic.endsWith('/#')) { const baseTopic = topic.slice(0, -2) // Remove '/#' - query.andWhere('(msg.topic LIKE :baseTopic OR msg.topic LIKE :topic ESCAPE "\\")', { + query.andWhere('(msg.topic = :baseTopic OR msg.topic LIKE :topic ESCAPE "\\")', { baseTopic, topic: baseTopic + '/%', })