From 78b8547e9da3f3f2b122b417ceb486ce93cc8456 Mon Sep 17 00:00:00 2001 From: gsd Date: Sun, 15 Feb 2026 21:24:03 +0300 Subject: [PATCH] multidevices support backend --- dbService.py | 83 ++++++++++++++++++++++++--------- service.py | 5 +- webExtensions/messageList.py | 25 +++++----- webExtensions/nodeList.py | 34 ++++++++++---- webExtensions/packetEndpoint.py | 10 ++-- 5 files changed, 108 insertions(+), 49 deletions(-) diff --git a/dbService.py b/dbService.py index 7833a09..b1f2347 100644 --- a/dbService.py +++ b/dbService.py @@ -3,16 +3,56 @@ from pymongo.asynchronous.database import AsyncDatabase from typing import List from entities.PacketGroup import PacketGroup from entities.PacketSignal import PacketSignal +from pymongo import DESCENDING -class NodeDbService: +class MultiDeviceDbSupport: def __init__(self, dbStore, core): self.dbStore:AsyncDatabase = dbStore self.core = core - #self.core.defaultDeviceUUID - async def listOfNodes(self): + #return {"$or": [{"device_uuid": value}]} + def deviceHash2Match(self, devices_hashes = []): + if devices_hashes == None: + devices_hashes = [self.core.defaultDeviceUUID] + elif (type(devices_hashes) == list): + if devices_hashes.__len__() == 0: + devices_hashes = [self.core.defaultDeviceUUID] + else: + devices_hashes = [devices_hashes] + + orPart = [] + for hash in devices_hashes: + uuid = self.core.devicesUuidHashToUuid.get(hash, "") + if uuid: + if uuid == self.core.defaultDeviceUUID: + orPart.append({"device_uuid", None}) + orPart.append({"device_uuid": uuid}) + return orPart#-->{"$or": orPart} + +class MessageDbService(MultiDeviceDbSupport): + def __init__(self, dbStore, core): + super().__init__(dbStore, core) + + async def listOfMessage(self, limit:int, offset:int, after: float, before: float, devices = []): + collection = self.dbStore['packet'] + + payload = { + "to": int(self.core.PUB_CH), + "portnum":self.MESSAGE_PORTNUM, + "ts":{'$gt': after, "$lt": before}, + "$or": self.deviceHash2Match(devices) + } + + c = collection.find(payload).sort("ts", DESCENDING).skip(offset).limit(limit) + return await c.to_list() + +class NodeDbService(MultiDeviceDbSupport): + def __init__(self, dbStore, core): + super().__init__(dbStore, core) + + async def listOfNodes(self, devices = []): pipeline = [ #ai slooop - {"$match": {"device_uuid": self.core.defaultDeviceUUID}}, + {"$match": {"$or": self.deviceHash2Match(devices)}}, # Сортировка для каждого num по убыванию ts {"$sort": {"num": 1, "ts": -1}}, # Группировка по num и взятие первого (последнего) документа @@ -30,9 +70,9 @@ class NodeDbService: c = await collection.aggregate(pipeline) return await c.to_list() - async def listOfDirectNodes(self): + async def listOfDirectNodes(self, devices = []): pipeline = [ #ai slooop - {"$match": {"hops_away": 0, "device_uuid": self.core.defaultDeviceUUID}}, # Фильтруем по списку + {"$match": {"hops_away": 0, "$or": self.deviceHash2Match(devices)}}, # Фильтруем по списку # Сортировка для каждого num по убыванию ts {"$sort": {"num": 1, "ts": -1}}, # Группировка по num и взятие первого (последнего) документа @@ -50,17 +90,17 @@ class NodeDbService: c = await collection.aggregate(pipeline) return await c.to_list() - async def oneNode(self, num: int): + async def oneNode(self, num: int, devices = []): collection = self.dbStore['node_info'] c = await collection.find_one( - {"num":int(num), "device_uuid": self.core.defaultDeviceUUID}, + {"num":int(num), "$or": self.deviceHash2Match(devices)}, sort=[("ts", -1)] ) return c - async def listOfSelectedNodes(self, nums: List[int]): + async def listOfSelectedNodes(self, nums: List[int], devices = []): pipeline = [ - {"$match": {"num": {"$in": nums}, "device_uuid": self.core.defaultDeviceUUID}}, # Фильтруем по списку + {"$match": {"num": {"$in": nums}, "$or": self.deviceHash2Match(devices)}}, # Фильтруем по списку {"$sort": {"num": 1, "ts": -1}}, # Сортируем по num и ts (по убыванию) { "$group": { @@ -75,9 +115,9 @@ class NodeDbService: c = await collection.aggregate(pipeline) return await c.to_list() - async def listOfFindLikeName(self, long_name): + async def listOfFindLikeName(self, long_name, devices = []): pipeline = [ - {"$match": {"long_name": {"$regex":long_name ,'$options' : 'i'}, "device_uuid": self.core.defaultDeviceUUID}}, # Фильтруем по списку + {"$match": {"long_name": {"$regex":long_name ,'$options' : 'i'}, "$or": self.deviceHash2Match(devices)}}, # Фильтруем по списку {"$sort": {"num": 1, "ts": -1}}, # Сортируем по num и ts (по убыванию) { "$group": { @@ -92,17 +132,17 @@ class NodeDbService: c = await collection.aggregate(pipeline) return await c.to_list() -class PacketDbService: +class PacketDbService(MultiDeviceDbSupport): def __init__(self, dbStore, core): - self.dbStore:AsyncDatabase = dbStore - self.core = core + super().__init__(dbStore, core) async def findPacketsSignals(self, after: float = -1, before: float = -1, - nums: List[int] = []): + nums: List[int] = [], + devices = []): pipeline = [] - match = {"device_uuid": self.core.defaultDeviceUUID} + match = {"$or": self.deviceHash2Match(devices)} if after != -1 or before != -1: match["ts"] = {} if after != -1: @@ -127,10 +167,11 @@ class PacketDbService: nums: List[int] = [], portnums: List[int] = [], packetsPerNode = False, - packetsSumNode = False): + packetsSumNode = False, + devices = []): pipeline = [] - match = {} + match = {"$or": self.deviceHash2Match(devices)} if after != -1 or before != -1: match["ts"] = {} if after != -1: @@ -149,7 +190,6 @@ class PacketDbService: match["portnums"] = {"$in":portnums} if match: - match.update({"device_uuid": self.core.defaultDeviceUUID}) pipeline.append({"$match":match}) #групировка по количеству на выхлопе _id номер портнума и count число его юзов @@ -175,8 +215,9 @@ class PacketDbService: l = await c.to_list() return [PacketGroup(p, packetsPerNode, packetsSumNode) for p in l] -class DbService(NodeDbService, PacketDbService): +class DbService(NodeDbService, PacketDbService, MessageDbService): def __init__(self, dbStore, core): NodeDbService.__init__(self, dbStore, core) PacketDbService.__init__(self, dbStore, core) + MessageDbService.__init__(self, dbStore, core) \ No newline at end of file diff --git a/service.py b/service.py index 9aafa81..11bf989 100644 --- a/service.py +++ b/service.py @@ -30,7 +30,7 @@ from dbService import DbService #other from botManager import BotManager -from utils import isInt, generate_random_string +from utils import isInt, generate_random_string, md5hash class MeshArgsParse: def __init__(self, args): @@ -41,6 +41,7 @@ class MeshMultiListener(MeshArgsParse): self.PUB_CH = PUB_CH super().__init__(args) self.devices:List[MeshtDevice] = [] + self.devicesUuidHashToUuid = {} self.defaultDeviceUUID = "" self.readConfig(args.change_workdir, args.mesh_config) @@ -91,6 +92,8 @@ class MeshMultiListener(MeshArgsParse): #set default mesh self.defaultDeviceUUID = self.json_config[0]["uuid"] + for device in self.devices: + self.devicesUuidHashToUuid[md5hash(device.device_uuid)] = device.device_uuid async def meshListener(self, device: MeshtDevice, queue: asyncio.Queue): run = not self.args.disable_mesh or self.args.enable_mesh diff --git a/webExtensions/messageList.py b/webExtensions/messageList.py index 84b25f9..cec289e 100644 --- a/webExtensions/messageList.py +++ b/webExtensions/messageList.py @@ -6,7 +6,6 @@ from fastapi import Query from pymongo.asynchronous.database import AsyncDatabase from extra.MessageDTO import MessageDTO from typing import List, Annotated -from pymongo import DESCENDING class WebExtension: MESSAGE_PORTNUM = 1 @@ -15,19 +14,19 @@ class WebExtension: def __init__(self, core): self.core = core self.app = core.app - self.dbStore = core.dbStore + self.dbService = core.dbService @self.app.get(f"{self.core.context}/messages") @self.core.authManager.authRequest() - async def listOfMessages(request: Request, limit: int = Query(10), offset: int = Query(0), after: float = Query(0), before: float = Query(0)): - collection = self.dbStore['packet'] - - payload = { - "to": int(self.core.PUB_CH), - "portnum":self.MESSAGE_PORTNUM, - "ts":{'$gt': after, "$lt": before} - } - - c = collection.find(payload).sort("ts", DESCENDING).skip(offset).limit(limit) - l = await c.to_list() + async def listOfMessages( + request: Request, + limit: int = Query(10), + offset: int = Query(0), + after: float = Query(0), + before: float = Query(0), + devices: List[str] = Query([]) + ): + l = await self.dbService.listOfMessage( + limit, offset, after, before, devices + ) return [MessageDTO(msg) for msg in l] \ No newline at end of file diff --git a/webExtensions/nodeList.py b/webExtensions/nodeList.py index 68026e7..91c1c9b 100644 --- a/webExtensions/nodeList.py +++ b/webExtensions/nodeList.py @@ -17,25 +17,35 @@ class WebExtension: @self.app.get(f"{self.core.context}/nodes/list") @self.core.authManager.authRequest() - async def listOfNodes(request: Request, p: bool = Query(False), m:bool = Query(False)): - l = await self.dbService.listOfNodes() + async def listOfNodes(request: Request, + p: bool = Query(False), + m:bool = Query(False), + devices: List[str] = Query([])): + l = await self.dbService.listOfNodes(devices) return [NodeDTO(node, p, m) for node in l] @self.app.get(f"{self.core.context}/nodes/direct") @self.core.authManager.authRequest() - async def listOfDirectNodes(request: Request, p: bool = Query(False), m:bool = Query(False)): - l = await self.dbService.listOfDirectNodes() + async def listOfDirectNodes(request: Request, + p: bool = Query(False), + m:bool = Query(False), + devices: List[str] = Query([])): + l = await self.dbService.listOfDirectNodes(devices) return [NodeDTO(node, p, m) for node in l] @self.app.get(self.core.context + "/nodes/search") - async def listOfFindLikeName(name=str): - l = await self.dbService.listOfFindLikeName(name) + async def listOfFindLikeName(name=str, devices: List[str] = Query([])): + l = await self.dbService.listOfFindLikeName(name, devices) return [NodeShortDTO(node) for node in l] @self.app.get(self.core.context + "/nodes/{num}") @self.core.authManager.authRequest() - async def oneNode(request: Request, num: int, p: bool = Query(False), m:bool = Query(False)): - c = await self.dbService.oneNode(num) + async def oneNode(request: Request, + num: int, + p: bool = Query(False), + m:bool = Query(False), + devices: List[str] = Query([])): + c = await self.dbService.oneNode(num, devices) if c: return NodeDTO(c, p, m) else: @@ -43,9 +53,13 @@ class WebExtension: @self.app.get(self.core.context + "/nodes") @self.core.authManager.authRequest() - async def listOfSelectedNodes(request: Request, nums: List[int] = Query(None), p: bool = Query(False), m:bool = Query(False)): + async def listOfSelectedNodes(request: Request, + nums: List[int] = Query(None), + p: bool = Query(False), + m:bool = Query(False), + devices: List[str] = Query([])): if type(nums) != list: nums = [nums] - l = await self.dbService.listOfSelectedNodes(nums) + l = await self.dbService.listOfSelectedNodes(nums, devices) return [NodeDTO(node, p, m) for node in l] \ No newline at end of file diff --git a/webExtensions/packetEndpoint.py b/webExtensions/packetEndpoint.py index bcb8178..2160886 100644 --- a/webExtensions/packetEndpoint.py +++ b/webExtensions/packetEndpoint.py @@ -24,8 +24,9 @@ class WebExtension: nums: List[int] = Query([]), portnums: List[int] = Query([]), packetsPerNode:bool = Query(False), - packetsSumNode:bool = Query(False)): - gl = await self.core.dbService.findPacketsAndGroupCount(after, before, nums, portnums, packetsPerNode, packetsSumNode) + packetsSumNode:bool = Query(False), + devices: List[str] = Query([])): + gl = await self.core.dbService.findPacketsAndGroupCount(after, before, nums, portnums, packetsPerNode, packetsSumNode, devices) return gl @self.app.get(f"{self.core.context}/packet/signal") @@ -33,6 +34,7 @@ class WebExtension: async def findPacketsAndGroupCount(request: Request, after: float = Query(-1), before: float = Query(-1), - nums: List[int] = Query([])): - gl = await self.core.dbService.findPacketsSignals(after, before, nums) + nums: List[int] = Query([]), + devices: List[str] = Query([])): + gl = await self.core.dbService.findPacketsSignals(after, before, nums, devices) return gl \ No newline at end of file