From 4c6b969f5dc0aef52000cdd73f052bc512cf367a Mon Sep 17 00:00:00 2001 From: Struchkov Mark Date: Sun, 4 Jun 2023 22:00:31 +0300 Subject: [PATCH] simple chat --- arch.excalidraw | 2048 +++++++++++++++++ pom.xml | 30 +- .../struchkov/example/ChatInputMessage.java | 14 + .../struchkov/example/ChatMessageDecoder.java | 34 + .../struchkov/example/ChatMessageEncoder.java | 18 + .../struchkov/example/ChatOutputMessage.java | 19 + .../dev/struchkov/example/StartWebSocket.java | 65 +- .../example/WebsocketAuthFilter.java | 52 + 8 files changed, 2265 insertions(+), 15 deletions(-) create mode 100644 arch.excalidraw create mode 100644 src/main/java/dev/struchkov/example/ChatInputMessage.java create mode 100644 src/main/java/dev/struchkov/example/ChatMessageDecoder.java create mode 100644 src/main/java/dev/struchkov/example/ChatMessageEncoder.java create mode 100644 src/main/java/dev/struchkov/example/ChatOutputMessage.java create mode 100644 src/main/java/dev/struchkov/example/WebsocketAuthFilter.java diff --git a/arch.excalidraw b/arch.excalidraw new file mode 100644 index 0000000..378e2a3 --- /dev/null +++ b/arch.excalidraw @@ -0,0 +1,2048 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "ZZvXDgbZhHlFusw-j33Kh", + "type": "rectangle", + "x": 1028, + "y": 258, + "width": 289, + "height": 358.1650712617006, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0bfff", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "roundness": { + "type": 3 + }, + "seed": 1197854649, + "version": 161, + "versionNonce": 663996377, + "isDeleted": false, + "boundElements": null, + "updated": 1685897876797, + "link": null, + "locked": false + }, + { + "id": "ZxPooPnv33jv1fc1-BnCv", + "type": "image", + "x": 896.8718204953054, + "y": 220.3531695385629, + "width": 64.0984251968504, + "height": 67.00000000000001, + "angle": 0, + "strokeColor": "transparent", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "roundness": null, + "seed": 1378476951, + "version": 210, + "versionNonce": 231666873, + "isDeleted": false, + "boundElements": null, + "updated": 1685903642509, + "link": null, + "locked": false, + "status": "saved", + "fileId": "ba30281f8050ab64bab2d5d2e5f9ef37c2490946", + "scale": [ + 1, + 1 + ] + }, + { + "type": "rectangle", + "version": 1491, + "versionNonce": 600503511, + "isDeleted": false, + "id": "pLpIJplWqaPfCEUiDgvcg", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1082.8219908748595, + "y": 322.1951488014888, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 170.35601825028087, + "height": 60, + "seed": 331656377, + "groupIds": [ + "27nv-mMLHqXfc4pDtV1XN" + ], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "glH4eqThikWdu1uAVXsln" + }, + { + "id": "25OnlmfhB-r-TsynF0Uev", + "type": "arrow" + } + ], + "updated": 1685897926633, + "link": null, + "locked": false + }, + { + "id": "glH4eqThikWdu1uAVXsln", + "type": "text", + "x": 1093.8720016479492, + "y": 342.1951488014888, + "width": 148.25599670410156, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "27nv-mMLHqXfc4pDtV1XN" + ], + "roundness": null, + "seed": 404703865, + "version": 28, + "versionNonce": 200203447, + "isDeleted": false, + "boundElements": null, + "updated": 1685897639472, + "link": null, + "locked": false, + "text": "websocket-service-1", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "center", + "verticalAlign": "middle", + "baseline": 14, + "containerId": "pLpIJplWqaPfCEUiDgvcg", + "originalText": "websocket-service-1", + "lineHeight": 1.25 + }, + { + "type": "rectangle", + "version": 1551, + "versionNonce": 258801015, + "isDeleted": false, + "id": "bhy9fKY2dOcUCPkA3FJhQ", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1081.82199087486, + "y": 422.35316953856295, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 170.35601825028087, + "height": 60, + "seed": 697709561, + "groupIds": [ + "l6jPvelT5q1Sb_x-2FQ1-" + ], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "SwaS8p1TTDvqsQ-xDCGXg" + }, + { + "id": "CYUb4mHSViTfssWJ2bRgC", + "type": "arrow" + } + ], + "updated": 1685897944220, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 91, + "versionNonce": 1261348727, + "isDeleted": false, + "id": "SwaS8p1TTDvqsQ-xDCGXg", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1089.34400177002, + "y": 442.35316953856295, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 155.31199645996094, + "height": 20, + "seed": 880757977, + "groupIds": [ + "l6jPvelT5q1Sb_x-2FQ1-" + ], + "roundness": null, + "boundElements": [], + "updated": 1685897880783, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "websocket-service-2", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "bhy9fKY2dOcUCPkA3FJhQ", + "originalText": "websocket-service-2", + "lineHeight": 1.25, + "baseline": 14 + }, + { + "type": "rectangle", + "version": 1552, + "versionNonce": 719151511, + "isDeleted": false, + "id": "xr4EQu_CIBo_YOVuYXbp8", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1083.82199087486, + "y": 521.841656030982, + "strokeColor": "#000000", + "backgroundColor": "transparent", + "width": 170.35601825028087, + "height": 60, + "seed": 1591060343, + "groupIds": [ + "FArvYQ_Prgv57z_0lvs9r" + ], + "roundness": null, + "boundElements": [ + { + "type": "text", + "id": "kdX1Vxi3eIBZJdX7WZ-Mq" + }, + { + "id": "hCQIOEoXbzsBWRnd9zOcn", + "type": "arrow" + } + ], + "updated": 1685897949073, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 92, + "versionNonce": 1568244089, + "isDeleted": false, + "id": "kdX1Vxi3eIBZJdX7WZ-Mq", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1091.5920028686528, + "y": 541.841656030982, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "width": 154.8159942626953, + "height": 20, + "seed": 1925161111, + "groupIds": [ + "FArvYQ_Prgv57z_0lvs9r" + ], + "roundness": null, + "boundElements": [], + "updated": 1685897878198, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "websocket-service-3", + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "xr4EQu_CIBo_YOVuYXbp8", + "originalText": "websocket-service-3", + "lineHeight": 1.25, + "baseline": 14 + }, + { + "id": "naDzjWMnKep2vlZMaER-K", + "type": "text", + "x": 1043, + "y": 265.5, + "width": 154.39999389648438, + "height": 25, + "angle": 0, + "strokeColor": "#6741d9", + "backgroundColor": "#d0bfff", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "groupIds": [], + "roundness": null, + "seed": 505314359, + "version": 143, + "versionNonce": 776977271, + "isDeleted": false, + "boundElements": null, + "updated": 1685897740779, + "link": null, + "locked": false, + "text": "Consumer Group", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "baseline": 18, + "containerId": null, + "originalText": "Consumer Group", + "lineHeight": 1.25 + }, + { + "type": "line", + "version": 3895, + "versionNonce": 734706135, + "isDeleted": false, + "id": "G_OrTf2CdAQMOinUnigN2", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 1.5736158627318666, + "x": 947.1116025505845, + "y": 294.33103418319075, + "strokeColor": "#000000", + "backgroundColor": "#eeeeee", + "width": 38.65325679716944, + "height": 168.4697307649053, + "seed": 1258390007, + "groupIds": [ + "1SvI_6_3k4or3EHQ-5PUo" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903629326, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + -38.65325679716944, + -0.148687517943469 + ], + [ + -38.3218808787729, + 151.49906373730906 + ], + [ + -18.587419899461096, + 168.32104324696184 + ], + [ + -0.606069113909371, + 150.570893170753 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 1916, + "versionNonce": 1454879513, + "isDeleted": false, + "id": "3z07QxFW0zcvlEr02rqM-", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 938.3115173892014, + "y": 368.0816787424143, + "strokeColor": "#000000", + "backgroundColor": "#868e96", + "width": 31.446435586986933, + "height": 20.901523054823727, + "seed": 1368140567, + "groupIds": [ + "wJlJmib49tVGBbmjQhJAV", + "1SvI_6_3k4or3EHQ-5PUo" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903629326, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 3240, + "versionNonce": 1478715127, + "isDeleted": false, + "id": "BpsyhRg4deb5H-5AC7RXE", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 954.8664094669934, + "y": 378.3256016149524, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 30.611335400621975, + "height": 9.874004828326399, + "seed": 1059369015, + "groupIds": [ + "wJlJmib49tVGBbmjQhJAV", + "1SvI_6_3k4or3EHQ-5PUo" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903629326, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 14.915663195484907, + -9.874004828326399 + ], + [ + -15.695672205137068, + -9.753102937860293 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "line", + "version": 2083, + "versionNonce": 996403193, + "isDeleted": false, + "id": "eXrgx17zVvas9nSHwkCHc", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 904.0004547453639, + "y": 378.76858099583296, + "strokeColor": "#000000", + "backgroundColor": "#868e96", + "width": 39.513013581967954, + "height": 0.7319961126032674, + "seed": 766480727, + "groupIds": [ + "1SvI_6_3k4or3EHQ-5PUo" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903629326, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 39.513013581967954, + -0.7319961126032674 + ] + ] + }, + { + "type": "rectangle", + "version": 1869, + "versionNonce": 187265047, + "isDeleted": false, + "id": "BYjCPoTJdw0miyu5hTPLz", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 975.108007468579, + "y": 369.0075805014147, + "strokeColor": "#000000", + "backgroundColor": "#868e96", + "width": 31.446435586986933, + "height": 20.901523054823727, + "seed": 109137527, + "groupIds": [ + "HMFPsq56APlni47KzFM2r", + "1SvI_6_3k4or3EHQ-5PUo" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903629326, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 3190, + "versionNonce": 712409305, + "isDeleted": false, + "id": "9qu-BALp3Azjyq9QrHrOH", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 991.6880636916856, + "y": 378.5201654940041, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 30.611335400621975, + "height": 9.874004828326399, + "seed": 341731223, + "groupIds": [ + "HMFPsq56APlni47KzFM2r", + "1SvI_6_3k4or3EHQ-5PUo" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903629326, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 14.915663195484907, + -9.874004828326399 + ], + [ + -15.695672205137068, + -9.753102937860293 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 1959, + "versionNonce": 1053906231, + "isDeleted": false, + "id": "yQ3K-4YLG3ChgKoq0o53c", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 871.2764029718592, + "y": 368.0787757391273, + "strokeColor": "#000000", + "backgroundColor": "#868e96", + "width": 31.446435586986933, + "height": 20.901523054823727, + "seed": 1434148023, + "groupIds": [ + "QwoFhAB9UNFubD2njOP5C", + "1SvI_6_3k4or3EHQ-5PUo" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903629326, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 3316, + "versionNonce": 1367539129, + "isDeleted": false, + "id": "GZXbj4ljCJ-sQ6CBTSWvM", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 888.4913440970645, + "y": 377.14099762603934, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 30.611335400621975, + "height": 9.874004828326399, + "seed": 226274775, + "groupIds": [ + "QwoFhAB9UNFubD2njOP5C", + "1SvI_6_3k4or3EHQ-5PUo" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903629326, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 14.915663195484907, + -9.874004828326399 + ], + [ + -15.695672205137068, + -9.753102937860293 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "text", + "version": 733, + "versionNonce": 26275833, + "isDeleted": false, + "id": "rsKZwGdw-xVqc7VG1kAb9", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1414.2252704689033, + "y": 375.2018138950206, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 106.11199951171875, + "height": 21.599999999999977, + "seed": 1844600471, + "groupIds": [ + "PN9W8XZuOQ-mpWvrLOv8g" + ], + "roundness": null, + "boundElements": [], + "updated": 1685897894636, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "browser tab 1", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "browser tab 1", + "lineHeight": 1.3499999999999985, + "baseline": 14 + }, + { + "type": "rectangle", + "version": 2639, + "versionNonce": 1826406519, + "isDeleted": false, + "id": "Fx-kFIl7C18W1ztf_n4ed", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1408.444082009745, + "y": 303.4768236838817, + "strokeColor": "#000000", + "backgroundColor": "#ffffff", + "width": 119.08867836148006, + "height": 71.27323137166177, + "seed": 1735947191, + "groupIds": [ + "AhwCjuicENXHmt6cLTH4u", + "PN9W8XZuOQ-mpWvrLOv8g" + ], + "roundness": null, + "boundElements": [ + { + "id": "25OnlmfhB-r-TsynF0Uev", + "type": "arrow" + } + ], + "updated": 1685897929699, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1880, + "versionNonce": 901941465, + "isDeleted": false, + "id": "MUGHoaFlu38_K5tpuwVWQ", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1408.609853669996, + "y": 303.4971039600528, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 118.30271761622996, + "height": 11.61061154323735, + "seed": 767485143, + "groupIds": [ + "AhwCjuicENXHmt6cLTH4u", + "PN9W8XZuOQ-mpWvrLOv8g" + ], + "roundness": null, + "boundElements": [], + "updated": 1685897894636, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 2042, + "versionNonce": 1680461111, + "isDeleted": false, + "id": "5Yg3YGtLo6Zx-FvQP8J13", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1412.6234547144934, + "y": 306.6484446634106, + "strokeColor": "#000000", + "backgroundColor": "#fa5252", + "width": 4.657945888720507, + "height": 4.657945888720507, + "seed": 5911031, + "groupIds": [ + "AhwCjuicENXHmt6cLTH4u", + "PN9W8XZuOQ-mpWvrLOv8g" + ], + "roundness": null, + "boundElements": [], + "updated": 1685897894636, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 2086, + "versionNonce": 638453177, + "isDeleted": false, + "id": "OpAz4A3lxIQxc3NP-GAu_", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1422.1803374447945, + "y": 306.6484446634106, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 4.657945888720507, + "height": 4.657945888720507, + "seed": 944691991, + "groupIds": [ + "AhwCjuicENXHmt6cLTH4u", + "PN9W8XZuOQ-mpWvrLOv8g" + ], + "roundness": null, + "boundElements": [], + "updated": 1685897894636, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 2144, + "versionNonce": 235474519, + "isDeleted": false, + "id": "O19FVLtXLxoxhp-c2p-OG", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1432.1569431698865, + "y": 307.068167658216, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 4.657945888720507, + "height": 4.657945888720507, + "seed": 437346359, + "groupIds": [ + "AhwCjuicENXHmt6cLTH4u", + "PN9W8XZuOQ-mpWvrLOv8g" + ], + "roundness": null, + "boundElements": [], + "updated": 1685897894636, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 737, + "versionNonce": 1141124023, + "isDeleted": false, + "id": "sO6Jxmok5ZwCCZdSCeToT", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1417.9331862409022, + "y": 485.49982524642746, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 99.37839337198359, + "height": 18.968023741052583, + "seed": 1017460281, + "groupIds": [ + "mwdhzWoy77yFeiJRO00RW" + ], + "roundness": null, + "boundElements": [], + "updated": 1685897896239, + "link": null, + "locked": false, + "fontSize": 14.050387956335262, + "fontFamily": 1, + "text": "browser tab 2", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "browser tab 2", + "lineHeight": 1.3499999999999985, + "baseline": 12.068023741052603 + }, + { + "type": "rectangle", + "version": 2645, + "versionNonce": 33306263, + "isDeleted": false, + "id": "vUKJdOZKMe54zVB1iiY7n", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1411.028862480024, + "y": 416.28599823769315, + "strokeColor": "#000000", + "backgroundColor": "#ffffff", + "width": 114.54741054605502, + "height": 68.55533378321928, + "seed": 183764761, + "groupIds": [ + "ZkzXp-U7tMPbcLIeV4f5t", + "mwdhzWoy77yFeiJRO00RW" + ], + "roundness": null, + "boundElements": [ + { + "id": "CYUb4mHSViTfssWJ2bRgC", + "type": "arrow" + } + ], + "updated": 1685897944220, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1886, + "versionNonce": 1880333527, + "isDeleted": false, + "id": "zw4eHJR1nbjIvzA7i9_YV", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1411.188312687087, + "y": 416.30550515600413, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 113.79142123289822, + "height": 11.167858317287024, + "seed": 1387123929, + "groupIds": [ + "ZkzXp-U7tMPbcLIeV4f5t", + "mwdhzWoy77yFeiJRO00RW" + ], + "roundness": null, + "boundElements": [], + "updated": 1685897896239, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 2048, + "versionNonce": 35815449, + "isDeleted": false, + "id": "ZPrJ0n6bmmgKqwbaW7aZG", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1415.0488610862944, + "y": 419.33667421661215, + "strokeColor": "#000000", + "backgroundColor": "#fa5252", + "width": 4.480322120941085, + "height": 4.480322120941085, + "seed": 531616185, + "groupIds": [ + "ZkzXp-U7tMPbcLIeV4f5t", + "mwdhzWoy77yFeiJRO00RW" + ], + "roundness": null, + "boundElements": [], + "updated": 1685897896239, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 2092, + "versionNonce": 123202039, + "isDeleted": false, + "id": "-QtVF4i8HBKoNALJOhanQ", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1424.2413064531447, + "y": 419.33667421661215, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 4.480322120941085, + "height": 4.480322120941085, + "seed": 2032087705, + "groupIds": [ + "ZkzXp-U7tMPbcLIeV4f5t", + "mwdhzWoy77yFeiJRO00RW" + ], + "roundness": null, + "boundElements": [], + "updated": 1685897896239, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 2150, + "versionNonce": 703403257, + "isDeleted": false, + "id": "ijCFMNzCDoflg1QDc0MAp", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1433.8374693090245, + "y": 419.74039170565555, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 4.480322120941085, + "height": 4.480322120941085, + "seed": 867137401, + "groupIds": [ + "ZkzXp-U7tMPbcLIeV4f5t", + "mwdhzWoy77yFeiJRO00RW" + ], + "roundness": null, + "boundElements": [], + "updated": 1685897896239, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 736, + "versionNonce": 1268292217, + "isDeleted": false, + "id": "Pxr5hI7icr_zcj34O5y0j", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1420.185489184173, + "y": 587.4740591275263, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 94.03955525348759, + "height": 18.028032219999915, + "seed": 1350401175, + "groupIds": [ + "zn9yyXtxpzOidCUM2Nfmw" + ], + "roundness": null, + "boundElements": [], + "updated": 1685897898021, + "link": null, + "locked": false, + "fontSize": 13.354097940740692, + "fontFamily": 1, + "text": "browser tab 3", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "browser tab 3", + "lineHeight": 1.3499999999999985, + "baseline": 12.478032219999935 + }, + { + "type": "rectangle", + "version": 2641, + "versionNonce": 1980438199, + "isDeleted": false, + "id": "Q3r5TEeI3onw0SPXLjMlV", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1413.4163310742317, + "y": 521.0437596642591, + "strokeColor": "#000000", + "backgroundColor": "#ffffff", + "width": 108.87082577677343, + "height": 65.15796179766599, + "seed": 1190121911, + "groupIds": [ + "mHtE2_beeuBZZqBeFd8uk", + "zn9yyXtxpzOidCUM2Nfmw" + ], + "roundness": null, + "boundElements": [ + { + "id": "hCQIOEoXbzsBWRnd9zOcn", + "type": "arrow" + } + ], + "updated": 1685897949073, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1882, + "versionNonce": 1718674265, + "isDeleted": false, + "id": "rQJ0KNRC5bz4RI1xwjzKJ", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1413.5678794648115, + "y": 521.0622998852448, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 108.15230075373324, + "height": 10.614416784849086, + "seed": 1410469879, + "groupIds": [ + "mHtE2_beeuBZZqBeFd8uk", + "zn9yyXtxpzOidCUM2Nfmw" + ], + "roundness": null, + "boundElements": [], + "updated": 1685897898021, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 2044, + "versionNonce": 537156279, + "isDeleted": false, + "id": "y3AChmmN_x9jBoghgVJAM", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1417.2371120574057, + "y": 523.9432543924156, + "strokeColor": "#000000", + "backgroundColor": "#fa5252", + "width": 4.258292411216796, + "height": 4.258292411216796, + "seed": 1163589911, + "groupIds": [ + "mHtE2_beeuBZZqBeFd8uk", + "zn9yyXtxpzOidCUM2Nfmw" + ], + "roundness": null, + "boundElements": [], + "updated": 1685897898021, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 2088, + "versionNonce": 353177657, + "isDeleted": false, + "id": "joScnoSwDcdZD9Zs93oN0", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1425.9740107199425, + "y": 523.9432543924156, + "strokeColor": "#000000", + "backgroundColor": "#fab005", + "width": 4.258292411216796, + "height": 4.258292411216796, + "seed": 736738871, + "groupIds": [ + "mHtE2_beeuBZZqBeFd8uk", + "zn9yyXtxpzOidCUM2Nfmw" + ], + "roundness": null, + "boundElements": [], + "updated": 1685897898021, + "link": null, + "locked": false + }, + { + "type": "ellipse", + "version": 2146, + "versionNonce": 1513483223, + "isDeleted": false, + "id": "TSu-h_S_JlH52iNk9xijg", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 0, + "opacity": 100, + "angle": 0, + "x": 1435.094619989307, + "y": 524.3269649992566, + "strokeColor": "#000000", + "backgroundColor": "#40c057", + "width": 4.258292411216796, + "height": 4.258292411216796, + "seed": 446881623, + "groupIds": [ + "mHtE2_beeuBZZqBeFd8uk", + "zn9yyXtxpzOidCUM2Nfmw" + ], + "roundness": null, + "boundElements": [], + "updated": 1685897898021, + "link": null, + "locked": false + }, + { + "id": "25OnlmfhB-r-TsynF0Uev", + "type": "arrow", + "x": 1260.635452460707, + "y": 344.823733985304, + "width": 139.64398532432415, + "height": 2.270633907712522, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0bfff", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "roundness": { + "type": 2 + }, + "seed": 872527129, + "version": 329, + "versionNonce": 1709821719, + "isDeleted": false, + "boundElements": null, + "updated": 1685897938136, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 139.64398532432415, + 2.270633907712522 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "pLpIJplWqaPfCEUiDgvcg", + "focus": -0.28286378824901115, + "gap": 7.457443335566609 + }, + "endBinding": { + "elementId": "Fx-kFIl7C18W1ztf_n4ed", + "focus": -0.24810635635894993, + "gap": 8.164644224713811 + }, + "startArrowhead": "triangle", + "endArrowhead": "triangle" + }, + { + "type": "arrow", + "version": 379, + "versionNonce": 671414649, + "isDeleted": false, + "id": "CYUb4mHSViTfssWJ2bRgC", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1264.5195682582964, + "y": 454.2467140611279, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0bfff", + "width": 139.64398532432415, + "height": 2.270633907712522, + "seed": 2046047575, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1685897944220, + "link": null, + "locked": false, + "startBinding": { + "elementId": "bhy9fKY2dOcUCPkA3FJhQ", + "focus": 0.009809157388561935, + "gap": 12.341559133155556 + }, + "endBinding": { + "elementId": "vUKJdOZKMe54zVB1iiY7n", + "focus": -0.19871635775364016, + "gap": 6.865308897403224 + }, + "lastCommittedPoint": null, + "startArrowhead": "triangle", + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 139.64398532432415, + 2.270633907712522 + ] + ] + }, + { + "type": "arrow", + "version": 375, + "versionNonce": 89417561, + "isDeleted": false, + "id": "hCQIOEoXbzsBWRnd9zOcn", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1264.5195682582969, + "y": 551.8839720927691, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0bfff", + "width": 139.64398532432415, + "height": 2.270633907712522, + "seed": 210193335, + "groupIds": [], + "roundness": { + "type": 2 + }, + "boundElements": [], + "updated": 1685897949073, + "link": null, + "locked": false, + "startBinding": { + "elementId": "xr4EQu_CIBo_YOVuYXbp8", + "focus": -0.048139150906864256, + "gap": 10.34155913315601 + }, + "endBinding": { + "elementId": "Q3r5TEeI3onw0SPXLjMlV", + "focus": -0.046839607842803686, + "gap": 9.252777491610686 + }, + "lastCommittedPoint": null, + "startArrowhead": "triangle", + "endArrowhead": "triangle", + "points": [ + [ + 0, + 0 + ], + [ + 139.64398532432415, + 2.270633907712522 + ] + ] + }, + { + "id": "dYkQPv22bDf3zaztt17_f", + "type": "text", + "x": 1337.837005322935, + "y": 319.84676100046556, + "width": 31.391998291015625, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0bfff", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "roundness": null, + "seed": 249566455, + "version": 22, + "versionNonce": 1500669849, + "isDeleted": false, + "boundElements": null, + "updated": 1685897958371, + "link": null, + "locked": false, + "text": "WSS", + "fontSize": 16, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "baseline": 14, + "containerId": null, + "originalText": "WSS", + "lineHeight": 1.25 + }, + { + "type": "text", + "version": 32, + "versionNonce": 1268709337, + "isDeleted": false, + "id": "Pz3AuFOs7sPkQaGm6t0YE", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1343.7120283006966, + "y": 431.32567506308885, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0bfff", + "width": 31.391998291015625, + "height": 20, + "seed": 1842613369, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1685897965688, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WSS", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WSS", + "lineHeight": 1.25, + "baseline": 14 + }, + { + "type": "text", + "version": 37, + "versionNonce": 1668006745, + "isDeleted": false, + "id": "14HzT_5ZL57BE9IDh-_Ws", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 1348.2532961161219, + "y": 531.2335670024427, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0bfff", + "width": 31.391998291015625, + "height": 20, + "seed": 1258299609, + "groupIds": [], + "roundness": null, + "boundElements": [], + "updated": 1685903441421, + "link": null, + "locked": false, + "fontSize": 16, + "fontFamily": 1, + "text": "WSS", + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "WSS", + "lineHeight": 1.25, + "baseline": 14 + }, + { + "type": "line", + "version": 3663, + "versionNonce": 1765613143, + "isDeleted": false, + "id": "wIoF4LIsf_fYYb9myPL5N", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 4.70956944444772, + "x": 915.3744892179985, + "y": 360.3576959605611, + "strokeColor": "#000000", + "backgroundColor": "#eeeeee", + "width": 39.64422312336847, + "height": 172.78884496135933, + "seed": 177849465, + "groupIds": [ + "5WJRPTjiSRd0LNzhxXZ59", + "ydiktJyvsHTEbjEzSyFx8" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903465428, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 39.64422312336847, + -0.1524994690083242 + ], + [ + 39.30435161097406, + 155.38309533139062 + ], + [ + 19.063951729830343, + 172.636345492351 + ], + [ + 0.6216071081949475, + 154.43112894909638 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 1552, + "versionNonce": 728487577, + "isDeleted": false, + "id": "x7U2srB5sxU4FT6NvIu9X", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 854.714940003183, + "y": 438.6300757881076, + "strokeColor": "#000000", + "backgroundColor": "#868e96", + "width": 32.25263825470037, + "height": 21.437382313004523, + "seed": 1069337945, + "groupIds": [ + "pSDkRIY4p5YG3CmiMpsyu", + "5WJRPTjiSRd0LNzhxXZ59", + "ydiktJyvsHTEbjEzSyFx8" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903465428, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 2876, + "versionNonce": 1926577015, + "isDeleted": false, + "id": "yaDSXEuwykWN4TN-hI7IF", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 871.6942553234727, + "y": 449.1366254987451, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 31.396128328710297, + "height": 10.12714795520296, + "seed": 1900788281, + "groupIds": [ + "pSDkRIY4p5YG3CmiMpsyu", + "5WJRPTjiSRd0LNzhxXZ59", + "ydiktJyvsHTEbjEzSyFx8" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903465428, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 15.298060985073855, + -10.12714795520296 + ], + [ + -16.09806734363644, + -10.003146462991662 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "line", + "version": 1818, + "versionNonce": 1019714425, + "isDeleted": false, + "id": "HEbMFw380Q5f2_yp_KTXH", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "dotted", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 923.0463633322178, + "y": 450.9767248424941, + "strokeColor": "#000000", + "backgroundColor": "#868e96", + "width": 40.52602177715947, + "height": 0.7507625389953521, + "seed": 1881803545, + "groupIds": [ + "5WJRPTjiSRd0LNzhxXZ59", + "ydiktJyvsHTEbjEzSyFx8" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903465428, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 40.52602177715947, + -0.7507625389953521 + ] + ] + }, + { + "type": "rectangle", + "version": 1628, + "versionNonce": 162313367, + "isDeleted": false, + "id": "bfQFST4iAxrzxybame9zZ", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 893.2683922814239, + "y": 438.8157264411315, + "strokeColor": "#000000", + "backgroundColor": "#868e96", + "width": 32.25263825470037, + "height": 21.437382313004523, + "seed": 617299961, + "groupIds": [ + "1zuwBmm9JYg_vt06piYYZ", + "5WJRPTjiSRd0LNzhxXZ59", + "ydiktJyvsHTEbjEzSyFx8" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903465428, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 2949, + "versionNonce": 1008190553, + "isDeleted": false, + "id": "wpsnSCYyxzjngqtdWOtGI", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 910.2735168885437, + "y": 448.5721887207564, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 31.396128328710297, + "height": 10.12714795520296, + "seed": 489871577, + "groupIds": [ + "1zuwBmm9JYg_vt06piYYZ", + "5WJRPTjiSRd0LNzhxXZ59", + "ydiktJyvsHTEbjEzSyFx8" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903465428, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 15.298060985073855, + -10.12714795520296 + ], + [ + -16.09806734363644, + -10.003146462991662 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 1677, + "versionNonce": 91752887, + "isDeleted": false, + "id": "ZwPt-H9V2iE501sPxx8SO", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 962.4542023088164, + "y": 438.07312382902995, + "strokeColor": "#000000", + "backgroundColor": "#868e96", + "width": 32.25263825470037, + "height": 21.437382313004523, + "seed": 1525962169, + "groupIds": [ + "E7xd-nm0aebugkMaon1G0", + "5WJRPTjiSRd0LNzhxXZ59", + "ydiktJyvsHTEbjEzSyFx8" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903465428, + "link": null, + "locked": false + }, + { + "type": "line", + "version": 3034, + "versionNonce": 892326201, + "isDeleted": false, + "id": "mRCcvsEN-1p9PBjBrfsBQ", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 980.1104885722103, + "y": 447.36767689511265, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 31.396128328710297, + "height": 10.12714795520296, + "seed": 1630746265, + "groupIds": [ + "E7xd-nm0aebugkMaon1G0", + "5WJRPTjiSRd0LNzhxXZ59", + "ydiktJyvsHTEbjEzSyFx8" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903465428, + "link": null, + "locked": false, + "startBinding": null, + "endBinding": null, + "lastCommittedPoint": null, + "startArrowhead": null, + "endArrowhead": null, + "points": [ + [ + 0, + 0 + ], + [ + 15.298060985073855, + -10.12714795520296 + ], + [ + -16.09806734363644, + -10.003146462991662 + ], + [ + 0, + 0 + ] + ] + }, + { + "type": "rectangle", + "version": 1105, + "versionNonce": 75536695, + "isDeleted": false, + "id": "3CAkNftfchWCmrjdZUUk5", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 648.509014118128, + "y": 339.6118757386167, + "strokeColor": "#000000", + "backgroundColor": "#868e96", + "width": 180.85702434824051, + "height": 177.3790046492357, + "seed": 444780121, + "groupIds": [ + "tH7zKfH-JCTPv2GdHXdzD", + "skEv1UyO-DHmTHPMJ2mgx" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903559553, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1278, + "versionNonce": 1952053689, + "isDeleted": false, + "id": "nJFG155eF43JmHZNVd8dE", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 636.7011372400136, + "y": 328.0126800424355, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 180.85702434824051, + "height": 177.3790046492357, + "seed": 1705614137, + "groupIds": [ + "tH7zKfH-JCTPv2GdHXdzD", + "skEv1UyO-DHmTHPMJ2mgx" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903559553, + "link": null, + "locked": false + }, + { + "type": "rectangle", + "version": 1364, + "versionNonce": 296114775, + "isDeleted": false, + "id": "c-Gdz3XmlojmEAhI6OHmZ", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 623.5716128762726, + "y": 315.03966656514933, + "strokeColor": "#000000", + "backgroundColor": "#eeeeee", + "width": 180.85702434824051, + "height": 177.3790046492357, + "seed": 1076087833, + "groupIds": [ + "tH7zKfH-JCTPv2GdHXdzD", + "skEv1UyO-DHmTHPMJ2mgx" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903559553, + "link": null, + "locked": false + }, + { + "type": "text", + "version": 892, + "versionNonce": 1635694233, + "isDeleted": false, + "id": "BuNYX4I3TVoghv6EGHI2N", + "fillStyle": "solid", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "angle": 0, + "x": 630.700121998635, + "y": 382.4349065293235, + "strokeColor": "#000000", + "backgroundColor": "#ced4da", + "width": 166.60000610351562, + "height": 37.659391953325965, + "seed": 837083385, + "groupIds": [ + "skEv1UyO-DHmTHPMJ2mgx" + ], + "roundness": null, + "boundElements": [], + "updated": 1685903572156, + "link": null, + "locked": false, + "fontSize": 28.012635847750442, + "fontFamily": 1, + "text": "chat-service", + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "chat-service", + "lineHeight": 1.3443715956615416, + "baseline": 26.0169872748028 + }, + { + "id": "Z9EN-pKbWd3q-OxNcSl0F", + "type": "text", + "x": 846.2447643031596, + "y": 324.15866272360324, + "width": 168.52000427246094, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0bfff", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "roundness": null, + "seed": 1928504409, + "version": 83, + "versionNonce": 574235895, + "isDeleted": false, + "boundElements": null, + "updated": 1685903590262, + "link": null, + "locked": false, + "text": "Новые сообщения", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "baseline": 18, + "containerId": null, + "originalText": "Новые сообщения", + "lineHeight": 1.25 + }, + { + "id": "trM03yhPGK88rGvr8C2rf", + "type": "text", + "x": 850.7860321185849, + "y": 478.7911345403469, + "width": 171.6999969482422, + "height": 75, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#d0bfff", + "fillStyle": "hachure", + "strokeWidth": 1, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "roundness": null, + "seed": 617648951, + "version": 190, + "versionNonce": 796901977, + "isDeleted": false, + "boundElements": null, + "updated": 1685903625987, + "link": null, + "locked": false, + "text": "Отправка новых \nсообщений всем \nучастникам чата", + "fontSize": 20, + "fontFamily": 1, + "textAlign": "left", + "verticalAlign": "top", + "baseline": 68, + "containerId": null, + "originalText": "Отправка новых \nсообщений всем \nучастникам чата", + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": null, + "viewBackgroundColor": "#ffffff" + }, + "files": { + "ba30281f8050ab64bab2d5d2e5f9ef37c2490946": { + "mimeType": "image/png", + "id": "ba30281f8050ab64bab2d5d2e5f9ef37c2490946", + "dataURL": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAeYAAAH8CAYAAAAE3gwyAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAB5qADAAQAAAABAAAB/AAAAACEc4z7AABAAElEQVR4AeydCXxdRdn/Z869adKmpWnLoiW5Ow0QQKEsiooFBGRRQFlEERRRccMdcUHxVVxQ3PCvbAq+gMoioCIgChQEhVeQNUDbJHfpAljapqVLmtxz5v87adE0ZLnLzNnubz6ftPeeM/Ms3zlznzNz5swIwUQCJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACAScgA24fzSOBugnkcrkdpG3v5tiiU0iREkrNkVJOV0K14rsllNiA/1+Con6pVNFRapFlT1nUs7xnWd3KKYAESIAEqiTAwFwlMGYPPoFsNtshbPtQpdShUshDYfGra7EajWO1EmKhFOruspR3FQqFZ2uRwzIkQAIkUA0BBuZqaDFvYAmgV7ydGrSPE9J5r9gSjE1c289IKa63pbwyn88XAwuDhpEACYSagIkfr1ADofHhItCZTKbRmz0Xw9EIyGKqN9arMkbAfy+Ec0FPsfioNzqphQRIoFEIMDA3Sk1HzM95iUTGFtZ5eDb8HrjW5JN7CsPcf8KQ+fm9pdIjPtlAtSRAAhEjwMAcsQqNujvz589vWrNy1UcRkC/AxdsaEH/xKFpd01Quf/rZ5ctXBcQmmkECJBBSAgzMIa24RjQ7m0weiOfHV8D33QLq//NSyU/0lPI3BtQ+mkUCJBACAgzMIagkmihkJpE6GxOvvgcWfg1bV1EN6urNtv3hZcuWbaqiELOSAAmQwDABBmZeCIEmkMlkZsqy8ysMXR8baENfYZx62LGsEzh7+xVgeIAESGASAgzMkwDiaf8I4H3kHUXZvgMW7O2fFXVpft6x5FsRnB+vSwoLkwAJNBQBBuaGqu7wOJtOp5OWo+6ExfPCY/WYlq7BxLBjeovFv495lgdJgARIYBQBBuZRQPjVfwK5nXPtKl5+AJYk/LemfgswZXtDTIpDlxQKD9UvjRJIgASiToCBOeo1HDL/3BW8xFD5PgSz14TM9MnMXYVh7TdgWHvRZBl5ngRIoLEJWI3tPr0PEoFUKtWihsp/iGBQdjHPwdD8bRii3ylIzGkLCZBA8AgwMAevThrWIkvJi+D8myMMIIPgfB38i0XYR7pGAiRQJwH+QNQJkMX1EMgkk0fjucoPIC3qj1dSs9tmDq5Zu/ZveshRCgmQQNQIRP1HMGr1FUl/tk72egzOzYmkg69wSpVx6M2cqf0KMDxAAiQAAhzK5mXgOwHMwL4YRjRIUHZxyzj+LnPX/fYdPg0gARIIHAEG5sBVSWMZlElk3gKPj2ssr4e97RrejKMBHafLJEACExPgUPbEfHjWIAG3x7j2xVVPYBb2rgbVBFn0WrxC1YlXqF4IspG0jQRIwFsCGFJjIgF/CKx9cfXpHgfldfD0fkwvewp6C5YjX8J36VhqulQqjY9d+P5G/G2HPy/STMtxPgtF53ihjDpIgATCQYA95nDUUxSttLLJ1NNwrNOwc7ZQ4kZpiSt7CoW/Qpc9kb4FQsRLicwCSzrvR/A+EXlNPwd+aUg5yVKphKU7mUiABEgAXQRCIAE/CGBv5eNx+d1kWPfNwo6d07ust6cWPZ3JZLos5AUoe0ot5SsvI7/aW8x/o/L8zEkCJBBlAgzMUa7dAPuG/ZXvxf7KBxkysV8J9YG+YlFL4Mc71kehofwKNxLbG7L3ud5ioQOyJ+zNG9JNsSRAAgEjwMAcsAppBHOwx3JC2k4Bvmq//jD8nJd27PBae8nj8ccWlB1bt6Dcfbw89Rx3lDw8X8r/pR4ZLEsCJBANAnxdKhr1GC4vHOfdMFh7UMb2ik/F7fKbdAdlF25vb+/SpvKQ28N/xv2uO1mWOlW3TMojARIIJwEG5nDWW6itlsrIM9sVstx05OJly5abgvPs8uWrbCmOgnz9rzcpcXxXV9cUU7ZTLgmQQHgIMDCHp64iYSl2kHoVHNlTszMOesun9izvWaZZ7ivEFZDQ138fTmDUXGuasXHdxgO0SqQwEiCBUBJgYA5ltYXX6Lgj3eFgvcPYSlyGdafv8YpKb6FwB8Lyr3TrsyyxQLdMyiMBEggfAQbm8NVZqC12pPZtHV9qsoe+4jUU2xJfhM6NevWqBXrlURoJkEAYCTAwh7HWQmwzuspah2uVkJe7z369RoIR7eeh+0rNevfVLI/iSIAEQkiAgTmElRZuk9U8nfY7Ul2uU15Vsmx5aVX5J8+83dZn8JPnZA4SIIHIEmBgjmzVBs+xrUFnhi7LMPvqMfRcn9Ulr1o5fcv6nkQZra9PYfF600uUVusm85MACXhMgIHZY+CNrE7ache9/su79cqrRZq6q5ZS45VRjtQ6ojCeHh4nARIILgEG5uDWTeQsk5azg06npFT/1CmvJlnK0mqDstScmuxgIRIggcgQYGCOTFUG3xFLWa06rcRWjYt1yqtJlnRq2iBjPF1SSW1D/ePp4HESIIFgE2BgDnb9RMs66WgNOioeX+k3IGXH9Nog1XS/faJ+EiABfwkwMPvLv9G0t+h0OLYptkmnvFpkxcWg3neZlZpWix0sQwIkEB0CDMzRqcsweDKg00gZ2+T72tK2aJ6q0ychpd5Ar9U4CiMBEvCCAAOzF5SpY5iAUtZ6nSjKYspsnfJqkaWkmlVLuXHLKKmV0bh6eIIESCCwBBiYA1s10TNMSUdr0JExO+s3JRlztNqAmeZaGfnNh/pJgASqJ8DAXD0zlqiRgHIsvROlhNinRlO0FZOOeI02Ya4gpf6tVR6FkQAJhI4AA3Poqiy8BquYWqLTeqz8dbBOebXIUlIsqKXceGWkZWllNJ4eHicBEgguAQbm4NZN5CxzN36AU+u0OabEG+a1t++sTV6Vgjo7OuaiyP5VFpsw+1AQ3s2e0EKeJAESME2Agdk0YcofRUDq7BFadiz2vlEKPPsK3adDmc42tG7rzYtnPlARCZBA8Ajo/FEJnne0KHAEMPz8oF6j5Cfmzp3r+bu/7e3tU5USZ+v0BfI0s9FpHWWRAAl4RYCB2SvS1DNMwFLiPs0odpo2ZcoXNMucVFxzrOlzyPSqSTNWkcGS6t4qsjMrCZBARAkwMEe0YoPqVtlSbmBGx1lfQk/znHQ6vZc+iRNLwvaVu8KFcyfOVf1ZpRiYq6fGEiQQPQIMzNGr00B75D5DlUI8odnIlpijrkPAbNMs9xXiunbomh5T4jqc0D18vqZlxox/vkIhD5AACTQcAQbmhqty/x12lPiNbivQBd8VAfNmBOcW3bJHyIsNTNtwPb4b6J2r33V3dw+O0MWPJEACDUqAgblBK95Xt+OWG5gdAzYsQHC+rbOzU+suVq6dbsDPJlM34uORBux2RWq/WTFkJ8WSAAkYJsDAbBgwxb+SQF9fXwlH//bKM1qOHFwe2PxQLpHo0iINQnIdHVkE/Afw8ThdMreVo5b1FosLtz0W7m/z589vggf8fQl3NdJ6nwjgcR8TCXhPIJtKHYcpYDcb1DwgpfjuxsHBC1esWFHTjk1uLxmR5VOw8ytoKK3mbJXn9Bbz3zMn34zkXC63nSiX91EOhval2hNxeDdMitse2nbE36jNPdw1wKV7Q1YUUiyVSj2Oz/9oLxafXChEGZ+ZSIAEthJgYOal4BcBC0PDT0E5fsyNJqw9LS8WcetXvb29SyvRlNs51y6ayu/FbO9PIP+rKylTR55+2RRP9vT06FsRrQ5jJiua7cjuoaR9NG563CH9A/Hn9oxrTpgbsMGS4u94rnGTkvLmfD7/Qs3CWJAEIkKAgTkiFRlGNzKp1PulEr/0yHb89qt/QddCBICn0WMrYsOItcO6pZyJAJGSwtod3xcg397435NhWAT/b/eVCl8atiOg/7jP7MubBk8R0vkgbnL2NWimDdn3o0d9SUehcONC9qQNoqboIBNgYA5y7UTctgVCxJcmU4/BTW3Pg8OFTL242bY7ly1btjqIdmN1s9kt8fg5eL/6YwjI0z21UYmlQsqL4y1TLlm0aNFLnuqmMhLwmQADs88V0Ojqs8nkIfjRv6sROWBnqo/0FQqXBM13913tTVM3fBbD1Z+GbTN9tu8FoeRXe0v5X8AOt0fNRAKRJ8DAHPkqDr6DmWTyBinkCcG3VJ+FGDp/rK9YcIeFAxVs0on0YVga9HLYldTnrRZJTyhlfayv1He/FmkUQgIBJuDJc7QA+0/TAkCgyXE+iee6LwbAFK9MGJTKORPKAhOUMQO9LZNMX4Gg/GfYFbSg7NbLXlI6C7PJ9AVbX8VyjzGRQCQJsMccyWoNn1OZROYY/PD+AZY3wDWpPof3li8KSi3tkkq9Fqux3QJ7ghiQx8CkHo4pdfLiUqlvjJM8RAKhJxALvQd0IBIE1qxds3j2rLbZcOaASDg0jhOYhX1nX6mIyVTBSLlE+kRMV/8D7oZ2CIZFlVgh52Jm/alt2836R/+6/lIlJZiHBMJEgEPZYaqtiNva0tr6+YhPBFs0xR56N6oRj5j9T5h492Ul1XUIyq3+W1O1BXMsS/0ZNxYNNTehakosEEoCDTBsGMp6aVijh9+ZHdh8LwC47xJHKa0UduzA3mW9PUFwKpNIfQGzrr8TBFvqtAGj8Oq0vmLx2jrlsDgJBIYAA3NgqoKGvEygs6NjbtmK/R3fQ/LM82XLx/sfy1EqtaC3VHpkvBxeHs+lUudjSP1rXuo0q0u5S3qehOf2Jpd4NesCpZPACAIMzCNg8GNwCHQmk+mykH+FRZngWFWTJf0YuT4aQcO90fA9YbW1s7Da2s99N0S/AZuVco7uK5Xu0i+aEknAWwIMzN7yprYqCAz3nGOxO/BEFhskhDI9h3Wgj1pSKLirm/me0FN+M3rKf4Ehda1v7bsj4xuwyrHkfKy3XRw/C8+QQPAJcPJX8OuoYS1ctHTpinhz8xsQmH8fOghS/DNml/cLSlBOp9NJLK3p7icd1aDsXiJzLMe5EbteNYfueqHBJDCCAF+XGgGDH4NHYNWqVYNr1vZfP7tt5hBmbL8JFgb/mlXi0s12+V354KyBbc2Z2Ybnr9LdpCPiSc4Vjt22Zu3a2yPuKN2LMAEOZUe4cqPmGnp9+1uOuhp+zQuoby9IR36oZ2neXSglMCmbSH8C+yX/JDAGmTfE3UnsTUF5rm/eXWqIGgEG5qjVaMT9wdKRLTEl8b6z+iJcnRoQdx00pEsHlfPlUqm0JiA2DZuR6+jIKiv2BL5MC5JdHtjS3TK9dZ/u7u5BD3RRBQloJRD8YUGt7lJY2An09/eXMbR938xZbdfi4p2JAI2JYdK3uRLYIeqPMSlO6SkWfrF27dqBoPGdNXv2pbDptUGzywN7dixvHlqPa+UBD3RRBQloJcAes1acFOY1gWx7Nics+7MYqj0VAdqrPYOHMCHtJux2dFHf0r5/eu1zpfoyHZn9pOU8hPwN2s7Vi/GWlgz3c670imG+oBBo0AYbFPy0QxeBTCYzU9rqvZD3LvSiD8T/+q9tKZ7EQiHXxR3nSnfGuC7bTcnJJlPuO72HmJI/Su6/cbNyG5b4/JtwYk80iaEVQ01NG8plLBUTi22HTSfSYLePI+UCvEd9GMq2jCpv5CtWN/tST6HwbSPCKZQEDBHQ/+NlyFCKJYFKCWSz2Q5h20ch/6EIFm/B/7MqLTsq30bE93ukVHdJpe5YUiw+M+p8YL+it7wvesse9OblAxit+H5HoXDrQiHcFbgmTe6yq/bAwHuxYPinwTc3aYH6MqyKtzSn2WuuDyJLe0uAgdlb3tTmMYG6lp+U4vreQuFkj03Woi6bSF2FMYPTtQgbU4haJqT8BPi420XWlNx9lftXrvoI7PwmBMyoSUgFhTAP4CN9hcIlFWRlFhIIBAHfJs0EwnsaQQIRJDBv7rztEezM3VBIcYNsauqqJyi72B955JGh3lLhJzHlYHKaethUVaD3cYYp2ZRLAiYIMDCboEqZJOAjATs+6G4taegZrrzAHUXo6elZp8vFxaVS36ahoTcbW+FNif0y7ZmwLuuqCzPlhIgAA3OIKoumkkBFBKQ4tqJ8VWdCUC7mv4JieDysN61YsWJjy4zWk/DM+U96JW+RJuMOZu0zkUA4CDAwh6OeaCUJVESgvb19NuLmQRVlriIT9jy+EUH5vCqKVJ3VXQykZeM0d1b9U1UXnqyAEm+dLAvPk0BQCDAwB6UmaAcJaCAwJRY7Er3OuAZR/xWhxFIRi52JA9p7yv9VsuVT98ru9ZgBj+AsNo8+V+f3PbFq3KvqlMHiJOAJAQZmTzBTCQl4Q0BKqb23LCxxdl9f31pvPBCip1TqxvaUut89xmi2dN+fZiKBwBNgYA58FdFAEqiCgBJvqCJ3JVn/Ue/s60qUjM4zdVPrReigvzj6eD3fsaD5AfWUZ1kS8IoAA7NXpKmHBAwT2GunnVqhYjedaqSSP9Apr1JZw0PaUv6s0vyV5JNC7VFJPuYhAb8JMDD7XQPUTwKaCGxsaXG3w9TZple1l/I1LyBSr1tYxvMqyND4XFt11WsTy5OAFwR0NmIv7KUOEiCBcQgoKTvHOVXbYSn+tLDCZTZrUzBxqUXFYh6Lg7hbVmpKcvtcLreDJmEUQwLGCDAwG0NLwSTgMQGldtapEV3VhTrl1SRLiXtqKjdeocHBHcc7xeMkEBQCDMxBqQnaQQJ1EsCMbK29QfTAH6nTpLqLK0s8XreQEQJkLDZnxFd+JIFAEmBgDmS10CgSqJ6AUgqLi+hL2K6xoE9abZIwlJ2vreTYpRzNjMbWwqMkUB8BBub6+LE0CQSHgJJNGo0Z1Lkedq122VKuqbXsWOVw8zJtrOM8RgJBIsDAHKTaoC0kUA8BS+uM7Ir2Vq7H3ErKWmXLqSRfpXksZQ1Wmpf5SMAvAgzMfpGnXhLQTEAqoTOYtiwQQu/SnjX468QdvT1cSw3VYAaLkICnBBiYPcVNZSRgjoAjZL9G6VY+mdQ6mawW26TjtNdSbrwyGMpmj3k8ODweGAIMzIGpChpCAvURwEQpnYFZYIGP3euzSENpKd1FU7QlMHpemzAKIgFDBBiYDYGlWP8JYDehNkfJenpcLfAiNG1ESbVMJ3XLsvbXKa8WWRie12qDNdS8tBY7WIYEvCSAG0gmEogGgXnt7TvbVtNRSjoHSSH3g1dub6vea3wdZDyMAPF/jlT3DwwN3bNixYqNQSSWS6XejF2ZFmq07d7eYmGBRnlVierq6poysH7Dv1FoZlUFx8+8Ef6464kzkUCgCdT7oxVo52hc9Alk27M5GbdPRUB6G7zdG3+mr+lNUHE3eqe3ThkauuHZ5ctXBYVyZ0fH3LIVW67RHlvFrAy2fCxplFmxKNxoHIt61blW9xMIzK+p2ABmJAGfCIRmmM4nPlQbQAIYom7JJtKnZZOphSJmL8aP99dg5j74Mx2UXRpTsa/C0ehB/3wo3rQ8m0pdj7+34njMPelnWrR06Qrof06jDTFpqw9plFeVKOWIj1RVYJLMSsh/TpKFp0kgEAQYmANRDTSiEgKdnZ0zssn052NK5IVUv0KZN+PPi2A8nnnN2PvoRPzdnk0mn80l02e6w6/jZfbkuBSag4/6eHt7u9YVxSrhgN7y61Czh1eSt9I87uOISvMyHwn4SYCB2U/61F0RAbeHnEsmv2QPbC6gt3ohCr2qooKeZpI5JdTlA+vX9+Lm4ePz58/XuQpX5Z4o+bfKM1eUc2ZzLPadinLqy2RhFMTdB1rrTZdlKQZmfXVESQYJMDAbhEvR9RPIJdInoof8DIYhL1BCeN5zq94Ddxa4urj/xVWPo9d3RPXl6yshlX17fRLGKi3PTCfSh411xsQx3Nh8BnJfr1n28iWFgtYNMTTbR3Ek8B8CDMz/QcEPQSKQyWQS+IH+KyZZXQ+7UkGyrUJbdkOv7w48f/59Mpl8dYVl6s5WtqxeCNH6PjPkSUs6v+5MJtN1GziJADwSOBg3Nt+aJFvVp9H1/gMK4d6OiQSCT4CBOfh11HAWur1kaTuP4Xf00NA7r8Tb40I+DZ9ONe1LJpF5S1yJR6GnTb8uuX1ZiDvdV9L0y94iEY8rMKte/g7ftD8GcJR1kym7KZcEdBPAjSQTCQSDwF477dS6YerUX6Jfc1IwLNJrBZ5BX4P3oD+s+z3oXEdHVljWjzDcf4xei8eUVhBO7G29S3ufGvNsjQeHZ7Yr8VsU1/XO8khLVrRtPyf1yCOPcJ3skVT4ObAEGJgDWzWNZZg7TFoW0n1nda8oe46x1MeUJY/L5/PFev2cO3futKlNTV9EL/NzkOWuUuZVegkLuHymp5i/ol6F7iS5tatWfQXD/l+GLCOvnEH2+X2lwtfrtZXlScArAgzMXpGmnnEJYAj2jVLaNyPAbD9upmidWGlZ8oQl+fx9tbqVSSbfgeDozlxO1iqj7nJS/E0odW5vsfj3GmRJ9JKPxejIN1G2q4bylRYZKguVLBaLOt/vrlQ385FATQQYmGvCxkK6COTS6cOVoxCUxTRdMkMiZwBD2yf0FYt/qsbedDrdiUU/fiI1v+NbjQ2j82IU4CG82PSrJtv+/dZFTkZn+c932J+E/cfB/g/h4O7/OWHsg7oaNw6nGRNPwSRggAADswGoFFkZgVxH+u3KGp513VxZicjlGpRKntpTyt8wmWddO3RN39y6wR3y/TTyTpksv3/nVY87XI+9P0qWcIaXK1XCmq2Ek0QP310yNeuhbYMx5ey2uFTq81AnVZFA3QQYmOtGSAG1EMAw5rswDHo1hq/jtZSPUBkbvc0P9BYKvxrPpy2sxPdx3tiM6PF0h/u4uhi95bPD7QOtb0QCDMyNWOs++4x3VQ9BQL4NZjRqT3l0DQwhOL8dwfmOkSdyiUSXktbFOHbwyOP8XBGBfhGPdfb29v67otzMRAIBIsDAHKDKaARTsh3ZPYRl/w2+GnjXNtQEN+K566E9hcKD7mtjG6dO/TyGrTHjOsjD1gHmreTpvaX8/wbYQppGAuMSYGAeFw1P6CawdVtCd5OFubplR0Te85il/G30nt2AHMD1wMNCWf6pt5j34p3usAChnSEj0OjP90JWXaE218JewdfAAwbl8avxVQjKPx7/NM9UQGBl3Cm7M76ZSCC0BLgkZ2irLlyGY91rdwEJPisNV7WFzdohPA44abJXtsLmFO1tPAIcym68Ovfc460LiNzDGdieo28shVKchQl0lzaW0/Q2igTYY45irQbIJ3fZSCkdvhYVgDqRQt3qWPK1MKWWlboC4MH4JkglvsOgPD4fngkXAQbmcNVX6KzFWs7nwuhUAA3HOhhizdY/93N0kxJLsMrY0T3F4tuwRvfjKmYdBWf/FRmHpbiwp1RwJ8wxkUAkCHAoOxLVGEwnsu3ZnIjZT8I6LzdYGAvG09hC8m5E3welUs/Gp05dvGjRopdGZuzs7JxhDwzs4gixq5TyAMyOPhTnTa7hPFK9oc9qvVLym1NntP6wu7t7cKSSRCIxq0lK7FEs3zjyeOg+Iyijp/yF0NlNg0lgAgIMzBPA4an6CGSTKXcdaLd35kcqYCLQryzH+d9al2RMY11nyxFYZ1m9Dw5k/HCiRp0KDfs3ll0+Z/GyZcvHk9He3j61ORb/Nc4fN16eAB8fwtD8pzEK8P8CbCNNI4GaCDAw14SNhSYjgNW9DkZv7O7J8hk4/wzWn/52eyn/m4VClDXJj2E3p5Ox1rM7s9yDjRdqtxqjAo8pR56dX5p3F3GpJMlMInUebmK+hsxhebS10p19jcVYFlbiIPOQQNgIMDCHrcZCYm8ulboTK1cd5p25aj10nd9RLP54ob6APNr8GILYxxEUvoETM0af9PM7GvJq9OzPQw/SnZVsV2sL6usI1NcvUS7g75nLu1RMntHX11eq1kfmJ4GwEGBgDktNhcjOTEdmX2k57gpfniT0Eh9SljwZE5uKXijMZrMdouz8BoHwDV7om0SHjefhlzfZQ195dvny4d2cJsk/7umtz52xwIk8FZmC9tuwTij5eSyzeTlsQ5UzkUB0CYRl6Cq6NRBBzxCUPZshq4T8f1Ontx7kVVB2qwsbIyxt2372wXhF50f+Vp98AM9Z9+stFT5Sb1B2/SiVSmvcvYsdR74ZX4M0a/smbEixB4LyZbCLQdmtLKZIEwjaXXGkYTeCc53JZLosZA989eCmT34VayK7w8q+Jaxo9jnEigthgJdtaQWeo38B+zhfC72mApWF7SZPhPQvQcdePgB2/boXjw2+g2fJf/ZBP1WSgG8EYr5ppuJIEmhrm/1xOHaoaefwPPTcvlLhW6b1TCZ/zdr+v89um4nn2/LwyfJqOO9uD3lRvKX5pCV9PQ9rkDeRCLWmv78b/l06q23m/wkpt8OdRw4FTN9wvYRRkCscKU7PFwsXre7v753ISJ4jgSgS8PIuP4r86NMoAnhFahEOzRt1WO9XKX6Cd1c/qVdofdLQu/wxepdn1ydl4tLoQm4YGBrcccWKFRsnzmnm7Lz29p3L8fiJwhHHoyfrPl/XdWPvvlP+J4wC/G5jefNtfvlnhhqlkkD1BBiYq2fGEuMQ2CWVOsBR4sFxTus6fHdvseDO9sZaIIFKFoa178TIsuHRAvUOPAe+2W/PU6lUW0yp17uLsWD0Yn/4vQtGDZKwq2kS23BTofrQA38aNxr/QPf7webW1n+NXgBlEhk8TQKRJsBtHyNdvd46h0j5bsMaV5aFcmcMBy0ou2472G7wNGxt+Rg+7+AeMJEQzN4Bub4H5kKh0A87bt/697KrMcxYnyvL5e1xoMVRsdbhE0qsVXE1iM/PY5LeCy9n5v8kQAJjE2CPeWwuPFoDgVwy9QwCx641FK2oCNZ7PrWvWHQnPAU2YSGSd2MhEpM29rdMb92JPczAXgI0jATqJmB6IkfdBlJAOAgkk8lXmwzKGP68H0H510GnsdXGew3a2bbppU0YOmYiARKIKgEG5qjWrMd+YRbQwYZVuhsVIPYHP2FilLujlrEkhXOgMeEUTAIk4DsBBmbfqyAaBuCZiMnAfC8mPP09LKTw3u2DmBB1n0F7GZgNwqVoEvCbAAOz3zUQFf3uVomGEp4tX2xItDGx0hI/NCdcvB6ycS/ERAIkEEUCDMxRrFXvfbIwyIzXZYykfkdKd/vIUKWW1tbbYPAaQ0bviNnP7YZkUywJkIDPBBiYfa6AKKjHMpxJ+NFixBclbsGrOQNGZBsU6s6aRpfW2GtN0rYzBs2naBIgAR8JMDD7CD8qqm0pja30hVeP7gorJywr+VdTtuNFbgZmU3AplwR8JsDA7HMFREG9cswFZtVk3RtWRk22bcx2qRQDc1gvDNpNApMQYGCeBBBPV0BAip0qyFV1FgwFr3a3WKy6YEAKLFq6dAVMed6IOVImjMilUBIgAd8JMDD7XgURMECqGSa8wEvL7oYYIU/K3QJTf1JiO/1CKZEESCAIBBiYg1ALIbdBKmEkMANLIeRoXPPzZnxQ083IpVQSIAG/CTAw+10DEdDvCDM9ZryCtS7seJSwDPkgp4WdDe0nARIYmwAD89hceLQKApg5vWUXoSrKVJJVSbmhknxBzmNJtd6QfewxGwJLsSTgNwEGZr9rIAL6lRRlM25AcsiTUgrLiJtIw9somhBMmSRAAj4TYGD2uQKioB7PmI30CiE3/MO1Shrq2UojzKNwPdIHEgg7AQbmsNdgIOw3NFxriVmBcK8+Iwz5EP5h/vqwsjQJRJcAA3N069Yzz5Qw1HtTMuuZE6YUSZkzI1q9ZEYupZIACfhNgIHZ7xqIgn4l+s24odyNMcL8nBm2KyOBGTdDq8wwp1QSIAG/CTAw+10DEdBvCdlnyI3tUqlUpyHZxsXukkzuCiUzTSiyhGOKuQlzKZMESKAKAgzMVcBi1rEJ2DGxeOwz9R+NOfKg+qX4IwHbVRqzHa+SMTD7U63USgLGCTAwG0ccfQW2bS8x5qVURxqTbVqwEkeYUoGgz8BsCi7lkoDPBBiYfa6AKKgvlUpr4MdKQ74cieHsNkOyjYnNZDLuELapmwp7xsaNvcaMp2ASIAFfCTAw+4o/OsqlFI8Z8qYZw9knGZJtTKxlqxMhvMWEAmzu8eQTL7wQ+lXRTLChTBKIAgEG5ijUYgB8UEreZ8wMqT4D2WG6VrE2ivq0MR5CPmBONiWTAAn4TSBMP3Z+s6L+CQggMC+c4HS9pzqzqdTb6xXiVXnYeix07W5Kn6XEg6ZkUy4JkID/BBiY/a+DSFhgTbH+CUc2GXNGie90dXVNMSZfk+BhG2GrJnFjiilb6v4xT/AgCZBAJAgwMEeiGv13oqenZzMW0zA5xNo5sH7j2f57OrEFW200+O61eqqANLEVPEsCJBBmAvEwG0/bA0ZAWTcIqd5izir1P7lE4vaeUqnbnI7aJcO2Ljxb/p/aJUxeUkp50+S5vMuxQIj4smRyL0eI3XCXn8T71Unso92Om7SXRzfcGfVYGU5i1r5yVyt7Hj48FVPqiUXFYgHfMZeNiQRIYCSBMC93ONIPfg4AgUQiMatJWs/DlJd/lPVbJcWTm8vlA5YtW2Zu2LwGq9vb26c2x+MPIczsWUPxios4lnxtPp9/vOIC+jPKdDq9n+U4xyDYvhFRdX/8iNS6H/c6yPibFM5tllJ3LC6V+G62/vqixBASYGAOYaUF2eRsInULVrd2Jz+ZTDf3FgsnQAE6aoFIVjaZuhGWHG/Ymv54S3Ni0aJFnm9ggeVFd7OFdaYU6mT4uLMRP3HTJRx5uW2pqzFaj142Ewk0JgFDm7g3Jkx6LcScmbMcBGb3HV6TabdZbbO2X7O2/3aTSiqULbPJ5E/Q8zu9wvz1ZGtxyvbpYPzv1Wv7n6xHUIVlZSaZPHrWzFlXCCkvxF3861FuuwrL1pJtJ1w7R2JI/JOzZ7blZs2Z/cyaNWu4WUctJFkm1ATYYw519QXPeHdW8sD6De6QpJle1QiXcfH+sqdY+BAO2SMOe/ZxwfDz1dRlGM59v2dK/6NIPoAh4LN7isV//eeQxg+42ThSCvlt+PYajWKrFKXKKHCVLDd9vWd5z7IqCzM7CYSWAANzaKsuuIZnk+nPYU7P9zyy8LbNdvm9eOa82iN9w2rmzZ23vR0fvAY9PGPrYVfgjzs6cUVscMqXF69Y/GIF+SfNkmnP7CJjDkYAxFsnzexdhgFsc/mzgaHN561YsWKjd2qpiQT8IcDA7A/3SGvN5XLbqaFyEU66M3K9SEWlrFP7Sn2evN+bS6UWKKWuxvA1Zh8HIq0RSn6to5T/+UIh3F5mLcmCX59USlyAwlNrEWC8jBJLpFSnY5TgH8Z1UQEJ+EiAz5h9hB9V1atXr948a2bbTKyf/SaPfGzDD/b7ZrfNbG+dMeOBdevWGZmxnc1md5w9Y+b/g08/QFB2N6kISprqPptdN6vt+NkzZz67Zu3aQjWGDfu13cw/oMxZ+GuqpqyneaWYA+6o51lTML/gXujGSDsTCUSPAHvM0avTQHg0PNTbNOju0zzLY4PW4vf64qZy+UfPLl+uZeKQG7hEuYzhefERBIbpHvtTvTopblCW9bm+vr7SZIXT6fT+ePXpd/ArKL3/yUwePo/Z4bfGWlre7ccM9YoMZCYSqIMAA3Md8Fh0YgLZRPoTWHDEfV7pRxqA0lsQpP/XlvIevH7jfq84zZ07d9q0KVMOw9DuaSh0NP6aKy4cjIx4Fiu/s9ke+v5473y7M64xwet6mDstGCZXaQVer8K+1G/De93uYxMmEogMAQbmyFRl8BxZgFnLS1Opf5ledKMCzzG07S4Xav0ftn16RliqB8F6zZRy+SXbabbKTeXpWIlqNl4Jmicd0ank8GtBr4PcsAXjV6DAWG8evcvP9haLN488iaD8HjT+qxC8w776X9GWYgFuvAoj/eNnEggzAQbmMNdeCGzPJDJvlNK5D6byWvO3vu4RTuzs3qW9T2GS17GYvIYFUUIflF8mWooLtQBLfOZfPsD/SSDMBPhjGebaC4ntmWT6CvTaPhASc6Ns5hBuj36DEQx39a7QjwaMqqje2NCU1+l6bWyUbH4lAU8JMDB7irsxle21006tG1umPoxh1V0bkwC99oaAur9l+vRDu7u7B73RRy0kYIYAVr9jIgGzBJ544YUNjm2dBC1VTcAyaxWlR4+AfOOm9RvxKhsTCYSbAN9jDnf9hcb6NevW/BvvNq/Hu81BWlEqNPxoaGUEMAS4P9YSfxpriT9dWQnmIoHgEWBgDl6dRNYiLArxEDaf2BE/nvtF1sn6HLtH2dax0lJpiMnVJ6qBS0t1cNvs2f/b39+/oYEp0PUQE+BQdogrL4ym9xXzZ8NuvF/MNIrAEypmHd+3rO9JbGl5pFTy7e6rTqPy8GtFBOT2lq0urSgrM5FAAAmwxxzASom4SaqjpfnWcqzpLZghbHwHqpCw7HMsuQALZfxnpTIMxS5unTH9sqaY5a59fQD+grtUZhAhS7Fr28xZf+9f2+/udMZEAqEiwFnZoaqu6BiLjS52UENDd+Nd2j2i41Utnqhlwo4f3Lust2e80imkuJLfV0K9c7w8PD4mgX9h9GFfnMHgAxMJhIcAA3N46ipylu66885zhuLxOxCc3R/PRky9WBjjsEoXxsgkEodKablLnO4eUFjPYRTkfqHU41JZRUc6myxlDSpLzcGxLFZW2wsh8s2w3bsNQKQ4pbdQ+G1AedEsEhiTAAPzmFh40CsCmUxmprTVn9CpeYNXOgOip7uMoFwsFp+rxp758+c3rVm16mNYWvRrKOfVtpoTmbgWtlzpKOvavqV9jyDjhL3TBVimdVk6fQhWHvsAcrojAKYfpz2NXrM7KjOhXTjPRAKBIcDAHJiqaFxD2tvbp7bE4lfgl/PdDULhdqzv/G4s79xfq7/ujleybH8bzN4HGX5M4tyMV99+5FjWt7GLFXb0qj5hhH7XmHK30BRHVl+6mhLqUKwVjscmTCQQDgIMzOGop0awUmYSqXPwY/8tOOtHoPGCsYJ/F/YUCl+GMluHwkxHZj9hORejIbsTxLxK3Zis9h5MVntch8JcIv1eJZW7z/UMHfLGkHETes18Pj8GGB4KJgEG5mDWS8NalUunD1eOuhIA5kYLgnoRvdsP9xWLNxnwS2ZTqdMxWPttyH6VAfkjRd4eb2k+Wfc+yLlEoktJ61YoSo1UpuezKmM3sQ6MUDyvRx6lkIBZAlHtmZilRunGCPTk83dutst7YhKRu09wJBI28LgVgWFPQ0HZZaQwwekq2RTvVEK6Q8NaeuNjwL+9ZXrrcbqDsqunp1Rye+ELcPOSH0NvnYdkHA+yj61TCIuTgGcE2GP2DDUVVUsAQ5wnKelchFnb7dWWDUj+53GDcS6C5q+8sgfPbduwt/RK3Vs64ofi8Y1DgweuWLFio0lf0HPeHT3nf0DHdpr13I7h7KM0y6Q4EjBCgD1mI1gpVAeBnlL++taBAexIpb4JeWHaAGMQAflCtwfrZVB2mccdeZjuoIxe7AZlx04wHZRd+9FzflpJcZb7WXM6pLOz09QzbM2mUlyjE2BgbvQrIOD+uztTYUbteViu0h2mdScIBTlAb0ZAviymnN0QkL/Q09Ozzmu8jhRH6NYphTx/ogVQdOvrKxR+4w7/a5bbPLhp8HWaZVIcCRghwMBsBCuF6iaAV3JKWGf743jNKD3cGxVitW4ddchbg17qRXHHziAgf3hxqeTbMpAIaLqDT6Fl+jR3URNvk1JfgEKtz8ot6ezvrRPURgK1EeAz5tq4sZTPBLCkZ7MYtI/FMpVnIFBj+NbzV6wcDLHfjfeffokPN2PGr+89eXeotjywGTcJ+hbtwLDyR9CDvcSP6s4mUzdD73HadEvxB9w4cRKYNqAUZIpA3JRgyiUBkwQwTLwZ8t2Z29e7i20I23Yn9hyFV4YOx/8z8WcivQShf0Gv9LYhIW6rdtUuEwaNlGlv3tyF79pW0nKfLTc1N187Uoe3n9VlGInQF5gdsbe39lMbCdRGgD3m2rixVEAJLMD8p+Wp1B6OI/cT0tkPP+xuwK51F6vn0UBuw3PbfwrbenjWjrMef+SRRxCTg5kyyeR78Dz4Gl3WYTTiRrzidaIuedXK6erqmjKwfsMLKKdr6VGnbfs5LUGuw2oZMX80CbDHHM16bVivFgpRFoXCYwDg/l2eS6R+iOHYT9UGRP2xp1j80H/KLv3Pp0B+kMLK6FwS2hLWn/10tLu7ezCbSN2LRxW6hp+tVatWuTdpBT/9om4SmIwAJ39NRojnSSA0BJwd9Jrq/EuvvOqlSUs8VH2p8UtYjhXWd+LHd4pnIkeAgTlyVUqHGpaAlFqfrW8cGnrWb5aOUot12mApNVunPMoiARMEGJhNUKVMEvCDgNK5WpZa78WCIpNhwjP+5ybLU815x3L4+K4aYMzrCwEGZl+wUykJGCGAidS6ktykS1I9chCYtdphKUvbrPV6/GJZEpiIAAPzRHR4jgRCRAAzsvFKtbY0RZukegRJ2VxP8dFllaUYmEdD4ffAEWBgDlyV0CASqI2AEo7ODSa2w4YYLbVZoq+U41jb65MGSUqt1SqPwkjAAAEGZgNQKZIE/CCghIVdpbQl2aRUWpu0GgVJqV5bY9Exiykn9uKYJ3iQBAJEgIE5QJVBU0igLgLS3e5RX1LK2leftFolqfm1lhyrnBNztDIaSwePkUC9BBiY6yXI8iQQEAKWI7VunoGVvw7x2TXM/RI6N55wN8V43mefqJ4EJiXAwDwpImYggXAQsGNC73vHUh0zf/78Jr+8zyYSB0L3XI36e4Kw2YhGfygqogQYmCNasXSr8QgMDQ0tgdca1/KW269ZteoE/0haH9SqW4ontMqjMBIwRICB2RBYiiUBrwksW7Zsk1TiUZ16Ie/zkOf570Ru51w71sg+RacvmJH9uFZ5FEYChgh43uAM+UGxJEACIICdsB7QDGLvXCL9Hs0yJxWnmsoXIJPWd6nx6tV9kypmBhIIAAEG5gBUAk0gAX0E1F/1ydoiSUnnB3in+VW65Y4nL5dOH459td873vkaj6+dvePsB2ssy2Ik4CkBBmZPcVMZCZglYDU13QUNL+nVIrePKfFrLyaCuUPYylFXw353RrbOdDf3YdaJk7JMEmBgNkmXsknAYwI9PT2bEdLuMKD24P6Vqy6DXGO/GbvuvPMcFR+6HTp21G6/FH/QLpMCScAQAWONzJC9FEsCJDA5gd9NnqWGHFK8L5tKXdnV1aX12a9ridtTHoo33Y2O8h41WDZZkZdaNrTeOFkmnieBoBBgYA5KTdAOEtBEoKW19WaIekGTuG3FKHHawIYNf53X3r7ztidq/5ZOpA9T8fJDkLBX7VImKqmu617ZvX6iHDxHAkEiwMAcpNqgLVoJZLPZHZUUidqFWnNdGbWX96dkd3f3oJTiUmPalXiTHYs/lUsmP1pP7zmZTL46l0z90pLqz7BV50Ii27huSXnFNgf4hQQCTkD3BIuAu0vzIk5AIli8zhHyRFzYR8DX3fBX7zXu7nH8NMTcgQByw5JC4f/wXeO+x5BmIHV2dMwtW7ECRBtduQsg8pYUF4t4/Bo8365kHWqZTqf3sxznTDA9FfZNNeD+SJH39BYLh4w8wM8kEHQC9f5oBd0/2tcABNyeV1xY+KFX+Kunh1wRrIIU6nLbsn6Rz+fNDBdXZMbkmTLJ9E9h68cmz6klh7sO9T9RB/cLKbvxw/Kccpz1SsSnWkLNdoRKo+e6H9bffh3yaRsGn8xyy5JvXpLP3zdZPp4ngSARYGAOUm3QlqoIzEskMrawzkOf2F0Aw2jPcAzDBpWQVztSfRPrLxfGOO/7oeFh+LLdA0Nm+G6MPwawt+wPd2qtk0CszvIsTgKeE3Bfq5k5a84PlJS/RFB2twX04zqO4a52H0zS+Oistlmvap0x/cF169Zt8hzGBArXrFmzYXbbrDiyHDxBtqiesjFacOrqtWuXRdVB+hVdApz8Fd26jaJn7jPkD+C1mkUIyGfBQa97yWMxneIOFzfHYosyyeQZyBCoUajN9tD3YZDeXafGohC4Y/JHPcXiPwJnFg0igQoIBOpHpAJ7maVBCeRyuR1UuXwFpl29PdAIlPhzWar3F4vF54JiJ25m9sGwu7scZRBuZLzAsnizXX6tu6mHF8qogwR0E/BjCFC3D5QXcQLYl/cNwlH3wE132DrYSYqchdnGbdvNeqh/XX8pCMZiOPc5DGk3w5aDgmCPWRtUWanYO4pLi31m9VA6CZgjwKFsc2wpWQOBTCr1fiEtd/1nzzZR0GD2Tpal/ppNpD+oQZYWEW3bz/46BGFlrWgnKeU5faW++6PtJb2LOgEOZUe9hkPsXzaZxl7A6sIQu4A3h8R3ewqFc4PgQ3t7++xmK/4gnoLvEgR7dNsghby2p5h3341mIoFQE2CPOdTVF13jEZTPC3tQdmtHKfEFrC/93SDUFJ65rrat4Wf0a4Jgj14b1P0D9lBgRij0+kZpjUaAz5gbrcZD4C8C2Ydh5kUhMLVSE98wa2bbwJq1/Q9UWsBUvv7+/hfntM28E5PHT4COaab0eCkXq489hO0uj8Tr5Bu81EtdJGCKAIeyTZGl3JoI4JWjd2JI8joUjtpNo5JKnt5Tyl9dExjNhTBTe2/M1P4LxM7RLNpjcephW8rDEJT7PVZMdSRgjAADszG0FFwtgVwq9ToM/bqzr1uqLRuS/EMY3D6it1h0ffQ9Zdoze8qY4+5TnPLdmJoMkH+Kt0w5ZdGiRS/VVJyFSCCgBBiYA1oxjWZWKpVqiynxKPxORdz3F7Ak1WvRw3s+CH66q6gNxadghEIdGgR7KrZBiss6CoWPLRSiXHEZZiSBkBDg5K+QVFTUzURQ/gV8TEXdT/i3E3y9Ev8H4qb42eXLV3UU82/Fc9ofwib8F/i0Fo8ETustFD68kEE58JVFA2sjELXneLVRYClfCbjvKiNKBeKVIo9A5LDgx4uYDOZuIel7KgjhwJY/bz971j1KqTfhnmG270aNbcA9KmYd0VvI/23s0zxKAtEgEIi79migpBe1EEgkErOapLUIZXeopXyIy/Q7ltw1aFtHzp07d9rUpqbvIDi720UGZUTtOWwX+ZW+YvEq2OSEuM5pOglURCAoDa8iY5kpegTiMvZNeNVoQdmtyDbLVgiAwUorVqzYiMlpZ0vl7InZ8b+Ddb4Nb0MxXn+SF7RsbJ2HoPxL2MKgHKzLhdYYIsAesyGwFDs5gVwi0aWk9Thy+vBIRWHSkHx5Nm8bbPCjLaAj6OzXWyo9Mjktf3K4G2AgGn4aQdp979mr2fIrlBQXD5bLl7mLovjjObWSgH8E/Pgx8s9bag4UgVwyfQ0i03u8MAoX+rOOEjdblrhXDMW7e5b3jNynV2YymQ7pOLsj3wK8suXuYLWbF3ZBx029xcI7PdJVs5rhRw4idipuX05HJ3ofCNL927EJwf829M+va54x7ffd3d2DNRvLgiQQcgK6G1fIcdB8rwh0JpNpdFkX4/c9blCnQs/rVqnUdzA8+/dq9Gx9p/oclDkOfybbiWMJtceSYvGZauzzM286nd7JssURUqojMNx8AGxJ46/ax2J4p1s+gfIPQs7fWja0/ql7Zfd6P/2ibhIICgGTPzhB8ZF2BJBAJpn+qRTKnWBkKiHoq7PqXcxjeMtJKS9BENnDlKFohFf2FAtnmJJvWm7XDl3TB1s3dGHIezcsDj4Hd0NzpLXtimLSEasRhJ/Dq04FGVOlTeXyIu6XbLpmKD+sBBiYw1pzIbY7l8s1q6Hyc3BhlhE3MBzasqn1TF09MOzKNLUlFv8pAouR4Am5G6YPbNrpiRde4FrPRi4ICiWBcBHwYdJNuADRWv0E2mbMOA7PE0/XL3l4zPnnvaXCB1ZuXLlZl/x169aVV6/t/4O7EQW2cXyLLrkvy8Hd8ZRybMoz0PHEy8f4PwmQQOMSqPa5UOOSoufaCCAoG9kzF5O2vo0h4Y/CUCOv1fSVCt+FjvO1gRghCDtceDIJboRKfiQBEggoAQ5lB7RiomoW1sRuwZKU7iswUzX7eAtmN78DMjEybDZhW8rroeVEzVqG8L7ubF3D75ptozgSIAEPCbDH7CFsqhICU7DdWby6g/JzTeWhMyHXeFB269AW4kP4b7n7WWNqGmjd8EaN8iiKBEggpAQYmENacSE2+2DdtmOm7znuZgy65Y4nz937F+9fu69SaU0YvlqgVSCFkQAJhJIAA3Moqy28RuMZ7UF6rVdP9ZTy1+qVObk0LBH5G+Tqnjxn5Tn0s6lcN3OSAAkEhwADc3DqolEs6dLpqJLyB5DnyRD2KLsRR5WrW2fSykanYZRFAiTgHQEGZu9YN7wmTPxy16TeUSOIjVM3tN6gUV5VoqymphtRYFNVhSbOvB0YvWriLDxLAiQQdQIMzFGv4QD51yREp05zsNzmXX7OYu7p6VmHbrPWvYExOU4rI528KYsESMAbAgzM3nCmFhDAy8UpnSAw6es+nfJqkYV1nrXagDF5rYxq8YllSIAE/CXAwOwv/8bS7sjtdDqM9Zj9XylLSq0TwIRmRjp5UxYJkIA3BBiYveFMLSAgLTVdJwhHyoJOebXIgg35WsqNV0ZKZ8Z453icBEigMQgwMDdGPQfCS8cRWoOO4zjr/HZsimYbHCW13rz4zYf6SYAEqifAwFw9M5aokYBlVb1n74Sa4vE4FuHyN9nlJuwrrC9heJ4by+jDSUkkEEoCDMyhrLZwGo19etfrtNy27Wk65dUiq9xU1tvD1cyoFp9YhgRIwF8CDMz+8m8o7dhBSet+wzHHmes3wJhSWm3ALG+tNy9+86F+EiCB6gkwMFfPjCVqJGA58qUai45ZDBOvdh3zhIcHsfLYPK3qlF5GWm2jMBIgAU8IMDB7gplKXAIyJko6SUilDtQprxZZum2wlSjWYgfLkAAJRIcAA3N06jLwngwptVirkUoeAXl+XsPQLQ/R6ZOy1BKd8iiLBEggfAT8/FELHy1aXBcBbJf4PAToe8VJio5cKvWmuoyqo/BW3TvXIWJ00QEwKo0+yO8kQAKNRYCBubHqOwjePq3TCKxVfbZOedXIwvKZH6sm/2R5Ie9Z5MHKpUwkQAKNTICBuZFr3w/fpbhPs9rj0un0azTLnFRcLpHowmaT75g0YxUZpH42VWhnVhIggaAQYGAOSk00iB1KqXs1u2pZSl0MmV5ey9g/Q/4IOrUuBmKAjWbUFEcCJOAFAS9/zLzwhzoCTgB7GN8vhCprNVOJN2WT6c9qlTmBMOjCELZ8ywRZajml4kPNukcTarGDZUiABHwmwMDscwU0mvotexjLu/X7rS7AZCx3lrbRBB1vxo3F97UrkeL+xSsWv6hdLgWSAAmEjgADc+iqLPwGYz3oXxvwoglDwTdmEpk3GpA9LNJ9lo3JZrfgS7NuHZj49RvdMimPBEggnAQYmMNZb6G2WsbjN8OBTfqdkNOxbeKfs8nk8bplu71xyxl+Pt6mWzbkDcUHp9xgQC5FkgAJhJCA1skrIfSfJvtAYPXq1Ztnz2pzl7I0MZu6Cc9/T5ozs62tvaX5/hc2bKhr96dUKtUye7u2/4GtP8ffVBO4pJC3ciHGrgAAM7xJREFULFnWe5UJ2ZRJAiQQPgLsMYevzqJhsR37HhzBCK6RhFnT4lMbWqY+nU2lTp8/fz6CddUplkkm3x1T4im8xvRFlDbWVmxLXFi1dSxAAiQQWQIysp7RscATyCWTf1RCHmPcUCWWCimvVo68uW9p36PQN94+zlY2kdhbWtbb8Sz5NORLGbdNiHt6i4VDPNBDFSRAAiEhwMAckoqKopl4FoxNKCRenxJeXodroe8Z/OWh9SWphO0INQPDyRkc2x1/Jp4hQ+zYSSnnLX2l0l1jn+VREiCBRiTg5Q9iI/Klz5MQyCZT7gztUybJFs3TSvy+t1Q4LprO0SsSIIFaCRh7blarQSzXWARidvnzeNS8vrG8HvZ2s3Is+M5EAiRAAtsS4KzsbXnwm8cEVq1b99LsttnuSmCHeazaV3WYUPat3lL+d74aQeUkQAKBJMAecyCrpbGM6i3mL4LHBlYDCypH9XBza+sFQbWOdpEACfhLgIHZX/7UvoWAgyFtzIJWDbAkpVqvYrH3dHd3D7LySYAESGAsAgzMY1HhMc8JLF62bDlean4fFI/3KpPnNhlQqKSyPtDX17fYgGyKJAESiAgBPmOOSEVGwY01a9cuwYpgL8AX8+82+wAM70Z/EbOwL/NBNVWSAAmEiAADc4gqqxFMXdPf/wiW02zBO8bGNqPwhaMSl/aVCu4KYkwkQAIkMCEBBuYJ8fCkHwRWr+2/e05b2y7Qvacf+nXrxPKgf+wrFtyVxEwtQarbZMojARLwkQCfMfsIn6rHJaB63ECGXua4OUJyAiv4/HrWnDnvhLlRfnYektqgmSQQDgLsMYejnhrRSrVmbf+ts9tmuRtQHBRGAOge/xDrYJ/13HPPMSiHsQJpMwn4RIBLcvoEnmorJ4Adoo5D7/lKlPB0HevKLXxFzk1Ye/vsnmL+ilec4QESIAESmIQAA/MkgHg6GAQy7ZldrJhzA3qhJvZw1unk01I5J/aUSk/rFEpZJEACjUOAz5gbp65D7Wnfsr4lM7efsx9eOfoUgvOGADozhJnkP2kd2LQ/g3IAa4cmkUCICLDHHKLKoqlbCOQ6OrLKil2Mb0cGg4m8y7HEx/L5/KJg2EMrSIAEwkyAgTnMtdfgtrv7OeMC/qIS8mig8OFalg9gP+fv9pTyf2zwqqD7JEACGgn48GOm0XqKIgEQSKfT+1u2+qiQCq8lyemGoWxUQt0klbqkt1R6wLAuiicBEmhAAgzMDVjpUXW5a4eu6QOtG96JGdwnYS0PvGKlJ0i7z7TRUP6GgHyD1dR0Y09Pz7qoMqRfJEAC/hNgYPa/DmiBAQLz589vWv3v1a+TUh1iSbkngupuUJPD35RJ1Lm7PvXi7xnsmfwU/r8bWzT+g7tBTUKNp0mABLQRYGDWhpKCgk5ggRDxUiYzVynVZjnOdCnlDNdm5TjrnVjsJXxcm8znly8UouweZyIBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiCBYBOQwTaP1pEACXhIwEokEjPj8fiMeLkct1XTNCGHml39vaXSIx7a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KtmDOn4yniWB4gjImgt0RU0tQGq10hnpUPpyiPtRsWH4+Jf553tlpMmXgscvRNxrrqMNHwsH96U8D3kTdAfdT+nULjg9+EZXV9cHOmV6kQWHP8c7rmNsyMqpdV4SL15edNGZBvWahgAAK7DM9GrM7S6G7Dqd8ieQhW0s9rqBbHodnKPk/aMCncO4F3MmkJ33acdx01jJ3pF3Rm8ZHGytWeYtqbdUWTvbmUgk/uUtdXGp5p04rzE1PfXp4qSMyR2wtkp0pTFnjX9taWk5DpNT55gqyHXcl/F2jp0E/h5DNmxZyzHAvcq2rSUovextOAIviHbWPkUXKdhwBjbcrkueDjll2zDrqBxl+Edg9uzZ9dNra2WhyfmYJzof48uno3QdUyV4M7ba0Rhvtt3MFhlW969WLIkEqoeA2HB9be1nlBL7FTu2zkDtddlwB34bNluus0Wm+KqHamE1ZcNcGDfmmoRAU1PT9GAw2FaTteZiUdZcZblteLOehTfrBmRtHP43A58pPIQHkeYDS6n/wqfwXixSeRsN+zvKcTqbY7Fouyyk40ECJOArAXmbxrbPubDhtmEbnjuRDUMxjEQqGY08gE70PvzdCRt+17XtznA83t1OG/b13rEwEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiCB0hD4P39djDXCPslsAAAAAElFTkSuQmCC", + "created": 1685897567108, + "lastRetrieved": 1685897567108 + } + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index d481463..b091ba6 100644 --- a/pom.xml +++ b/pom.xml @@ -6,17 +6,21 @@ dev.struchkov.example quarkus-websocket 1.0-SNAPSHOT + 3.11.0 17 UTF-8 UTF-8 + quarkus-bom io.quarkus.platform 3.1.0.Final + true 3.0.0 + @@ -28,6 +32,7 @@ + io.quarkus @@ -41,16 +46,26 @@ io.quarkus quarkus-smallrye-graphql + + io.quarkus + quarkus-reactive-routes + io.quarkus quarkus-arc io.quarkus - quarkus-junit5 - test + quarkus-jackson + + + + org.projectlombok + lombok + 1.18.26 + @@ -109,6 +124,7 @@ + native @@ -123,4 +139,14 @@ + + + + uPagge + Struchkov Mark + mark@struchkov.dev + https://mark.struchkov.dev + + + diff --git a/src/main/java/dev/struchkov/example/ChatInputMessage.java b/src/main/java/dev/struchkov/example/ChatInputMessage.java new file mode 100644 index 0000000..1ebbe10 --- /dev/null +++ b/src/main/java/dev/struchkov/example/ChatInputMessage.java @@ -0,0 +1,14 @@ +package dev.struchkov.example; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +@Getter +@Setter +@ToString +public class ChatInputMessage { + + private String text; + +} diff --git a/src/main/java/dev/struchkov/example/ChatMessageDecoder.java b/src/main/java/dev/struchkov/example/ChatMessageDecoder.java new file mode 100644 index 0000000..7c6ab67 --- /dev/null +++ b/src/main/java/dev/struchkov/example/ChatMessageDecoder.java @@ -0,0 +1,34 @@ +package dev.struchkov.example; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import jakarta.websocket.DecodeException; +import jakarta.websocket.Decoder; +import lombok.SneakyThrows; + +public class ChatMessageDecoder implements Decoder.Text { + + private final ObjectMapper jackson = ChatMessageDecoder.getJackson(); + + @Override + @SneakyThrows + public ChatInputMessage decode(String s) throws DecodeException { + return jackson.readValue(s, ChatInputMessage.class); + } + + @Override + public boolean willDecode(String s) { + return s != null; + } + + public static ObjectMapper getJackson() { + ObjectMapper om = new ObjectMapper(); + om.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); + om.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); + om.registerModule(new JavaTimeModule()); + return om; + } + +} diff --git a/src/main/java/dev/struchkov/example/ChatMessageEncoder.java b/src/main/java/dev/struchkov/example/ChatMessageEncoder.java new file mode 100644 index 0000000..2798f58 --- /dev/null +++ b/src/main/java/dev/struchkov/example/ChatMessageEncoder.java @@ -0,0 +1,18 @@ +package dev.struchkov.example; + +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.websocket.EncodeException; +import jakarta.websocket.Encoder; +import lombok.SneakyThrows; + +public class ChatMessageEncoder implements Encoder.Text { + + private final ObjectMapper jackson = ChatMessageDecoder.getJackson(); + + @Override + @SneakyThrows + public String encode(ChatOutputMessage chatOutputMessage) throws EncodeException { + return jackson.writeValueAsString(chatOutputMessage); + } + +} diff --git a/src/main/java/dev/struchkov/example/ChatOutputMessage.java b/src/main/java/dev/struchkov/example/ChatOutputMessage.java new file mode 100644 index 0000000..0c3867c --- /dev/null +++ b/src/main/java/dev/struchkov/example/ChatOutputMessage.java @@ -0,0 +1,19 @@ +package dev.struchkov.example; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.util.UUID; + +@Getter +@Setter +@ToString +@AllArgsConstructor +public class ChatOutputMessage { + + private UUID fromUserId; + private String text; + +} diff --git a/src/main/java/dev/struchkov/example/StartWebSocket.java b/src/main/java/dev/struchkov/example/StartWebSocket.java index aa02f71..7c2234c 100644 --- a/src/main/java/dev/struchkov/example/StartWebSocket.java +++ b/src/main/java/dev/struchkov/example/StartWebSocket.java @@ -1,7 +1,6 @@ package dev.struchkov.example; import jakarta.enterprise.context.ApplicationScoped; -import jakarta.websocket.EncodeException; import jakarta.websocket.OnClose; import jakarta.websocket.OnError; import jakarta.websocket.OnMessage; @@ -9,33 +8,73 @@ import jakarta.websocket.OnOpen; import jakarta.websocket.Session; import jakarta.websocket.server.PathParam; import jakarta.websocket.server.ServerEndpoint; +import lombok.RequiredArgsConstructor; -import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; -import static java.util.Objects.requireNonNull; - -@ServerEndpoint("/start-websocket/{name}") @ApplicationScoped +@ServerEndpoint( + value = "/chat/{chatId}", + decoders = ChatMessageDecoder.class, + encoders = ChatMessageEncoder.class +) +@RequiredArgsConstructor public class StartWebSocket { + public static final ThreadLocal CURRENT_USER = new ThreadLocal<>(); + private final Map> sessions = new ConcurrentHashMap<>(); + @OnOpen - public void onOpen(Session session, @PathParam("name") String name) { - System.out.println("onOpen> " + name); + public void onOpen(Session session, @PathParam("chatId") String chatId) { + System.out.println("onOpen> " + chatId); + sessions.computeIfAbsent(chatId, key -> new ArrayList<>()).add(session); } @OnClose - public void onClose(Session session, @PathParam("name") String name) { - System.out.println("onClose> " + name); + public void onClose(Session session, @PathParam("chatId") String chatId) { + System.out.println("onClose> " + chatId); + closeSession(session, chatId); } @OnError - public void onError(Session session, @PathParam("name") String name, Throwable throwable) { - System.out.println("onError> " + name + ": " + throwable); + public void onError(Session session, @PathParam("chatId") String chatId, Throwable throwable) { + System.out.println("onError> " + chatId + ": " + throwable); } @OnMessage - public void onMessage(String message, @PathParam("name") String name) { - System.out.println("onMessage> " + name + ": " + message); + public void onMessage(Session session, @PathParam("chatId") String chatId, ChatInputMessage message) { + System.out.println("onMessage> " + chatId + ": " + message); + sendMessage(session, chatId, message); + } + + private void sendMessage(Session session, String chatId, ChatInputMessage message) { + final List chatSessions = sessions.get(chatId); + for (Session chatSession : chatSessions) { + if (session.getId().equals(chatSession.getId())) { + continue; + } + final UUID fromUserId = CURRENT_USER.get(); + final ChatOutputMessage outputMessage = new ChatOutputMessage(fromUserId, message.getText()); + chatSession.getAsyncRemote().sendObject(outputMessage); + CURRENT_USER.remove(); + } + } + + private void closeSession(Session session, String chatId) { + final List chatSessions = sessions.get(chatId); + final Iterator sessionIterator = chatSessions.iterator(); + while (sessionIterator.hasNext()) { + final Session chatSession = sessionIterator.next(); + if (session.getId().equals(chatSession.getId())) { + sessionIterator.remove(); + break; + } + } } } diff --git a/src/main/java/dev/struchkov/example/WebsocketAuthFilter.java b/src/main/java/dev/struchkov/example/WebsocketAuthFilter.java new file mode 100644 index 0000000..97c03be --- /dev/null +++ b/src/main/java/dev/struchkov/example/WebsocketAuthFilter.java @@ -0,0 +1,52 @@ +package dev.struchkov.example; + +import io.quarkus.vertx.web.RouteFilter; +import io.vertx.core.http.Cookie; +import io.vertx.core.http.HttpServerRequest; +import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.HttpException; +import lombok.RequiredArgsConstructor; + +import java.util.UUID; + +@RequiredArgsConstructor +public class WebsocketAuthFilter { + + @RouteFilter(100) + void authFilter(RoutingContext rc) { + final HttpServerRequest currentRequest = rc.request(); + + if (isWebsocketRequest(currentRequest)) { + final Cookie authCookie = currentRequest.getCookie("sessionId"); + if (authCookie == null) { + throw new HttpException(401, "Не передан параметр авторизации."); + } + + final String authValue = authCookie.getValue(); + if (!authLogic(authValue)) { + throw new HttpException(403, "Пользователь не авторизован."); + } + } + + rc.next(); + } + + private static boolean isWebsocketRequest(HttpServerRequest currentRequest) { + return currentRequest.headers().contains("Upgrade") + && "websocket".equals(currentRequest.getHeader("Upgrade")); + } + + private boolean authLogic(String sessionId) { + // your auth logic here + if (sessionId.equals("user1")) { + StartWebSocket.CURRENT_USER.set(UUID.fromString("09e429de-a302-40b6-9d10-6b113ab9e89d")); + return true; + } else if (sessionId.equals("user2")) { + StartWebSocket.CURRENT_USER.set(UUID.fromString("f84dbae1-f9a9-4c37-8922-4eb207103676")); + return true; + } else { + return false; + } + } + +}